From 3d4b10d82abbc0ce0b06afce0339f049136d55a8 Mon Sep 17 00:00:00 2001 From: sjonpaulbrown Date: Wed, 5 Apr 2023 14:38:58 -0600 Subject: [PATCH 001/815] Update workflow to add summary for GCS location for tools --- .github/workflows/tools.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 2e297adb6ff..ac7e11717fd 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -53,6 +53,9 @@ jobs: if [[ "${{ inputs.promote }}" = true ]]; then echo "promoting boot-tools.tar" gsutil cp boot-tools.tar gs://flow-genesis-bootstrap/boot-tools.tar + SUMMARY=$'# Tool Build and Upload Summary \n Your tools were uploaded to the following GCS objects \n * Boot Tools gs://flow-genesis-bootstrap/boot-tools.tar \n * Util util.tar gs://flow-genesis-bootstrap/tools/${{ inputs.tag }}/util.tar' else echo "not promoting boot-tools.tar" + SUMMARY=$'# Tool Build and Upload Summary \n Your tools were uploaded to the following GCS objects \n * Boot Tools gs://flow-genesis-bootstrap/tools/${{ inputs.tag }}/boot-tools.tar \n * Util util.tar gs://flow-genesis-bootstrap/tools/${{ inputs.tag }}/util.tar' fi + echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY From 8b8dd463efcaa7ea6f46027613eb3b383db87df8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 17 May 2023 16:22:38 -0700 Subject: [PATCH 002/815] fixes a godoc --- network/alsp/model/params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 54e0c3fe57f..e675ee6b990 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -29,7 +29,7 @@ const ( // 10 times less than the default penalty value and the node will be disallow-listed after 10 times more misbehavior/sec. DefaultPenaltyValue = 0.01 * misbehaviorDisallowListingThreshold // (Don't change this value) - // initialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. + // InitialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. // The decay speed is applied on an arithmetic progression. The penalty value of the node is the first term of the // progression and the decay speed is the common difference of the progression, i.e., p(n) = p(0) + n * d, where // p(n) is the penalty value of the node after n decay intervals, p(0) is the initial penalty value of the node, and From 9ef7ed54a05a93afcd1757cf046017fa1034476f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 17 May 2023 16:22:57 -0700 Subject: [PATCH 003/815] adds heartbeat --- network/alsp/manager/manager.go | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index cc4d8bda392..1f6205c559d 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -4,12 +4,15 @@ import ( crand "crypto/rand" "errors" "fmt" + "math" + "time" "github.com/rs/zerolog" "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/mempool/queue" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" @@ -220,6 +223,68 @@ func (m *MisbehaviorReportManager) HandleMisbehaviorReport(channel channels.Chan lg.Debug().Msg("misbehavior report submitted") } +// startHeartbeatTicks starts the heartbeat ticks ticker to tick at the given intervals. It is a blocking function, and +// should be called in a separate goroutine. It returns when the context is canceled. +// Args: +// ctx: the context. +// interval: the interval between two ticks. +// Returns: +// none. +func (m *MisbehaviorReportManager) startHeartbeatTicks(ctx irrecoverable.SignalerContext, interval time.Duration) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + m.logger.Debug().Msg("heartbeat ticks stopped") + return + case <-ticker.C: + m.logger.Trace().Msg("new heartbeat ticked") + m.onHeartbeat(ctx) + } + } +} + +// onHeartbeat is called upon a startHeartbeatTicks. It decays the penalty of all spam records based on their individual decay speed. +// Args: +// ctx: the context. +// Returns: +// none. +func (m *MisbehaviorReportManager) onHeartbeat(ctx irrecoverable.SignalerContext) { + allIds := m.cache.Identities() + + for _, id := range allIds { + penalty, err := m.cache.Adjust(id, func(record model.ProtocolSpamRecord) (model.ProtocolSpamRecord, error) { + if record.Penalty > 0 { + // sanity check; this should never happen. + return record, fmt.Errorf("illegal state: spam record %x has positive penalty %f", id, record.Penalty) + } + if record.Decay <= 0 { + // sanity check; this should never happen. + return record, fmt.Errorf("illegal state: spam record %x has non-positive decay %f", id, record.Decay) + } + + // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed + // is a positive number. So the penalty is getting closer to zero. + // We use math.Min() to make sure the penalty is never positive. + record.Penalty = math.Min(record.Penalty+record.Decay, 0) + return record, nil + }) + + // any error here is fatal because it indicates a bug in the cache. All ids being iterated over are in the cache, + // and adjust function above should not return an error unless there is a bug. + if err != nil { + ctx.Throw(fmt.Errorf("failed to decay spam record %x: %w", id, err)) + return // should be no-op because Throw() is fatal. But just in case to avoid continuing on an invalid state. + } + + m.logger.Trace(). + Hex("identifier", logging.ID(id)). + Float64("updated_penalty", penalty). + Msg("spam record decayed") + } +} + // processMisbehaviorReport is the worker function that processes the misbehavior reports. // It is called by the worker pool. // It applies the penalty to the misbehaving node and updates the spam record cache. From ec1886736e31333a00c7664ffcc2ff9b4fe20080 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 17 May 2023 16:39:22 -0700 Subject: [PATCH 004/815] wires in alsp parameters in code --- .../node_builder/access_node_builder.go | 1 + cmd/node_builder.go | 5 +++ cmd/observer/node_builder/observer_builder.go | 1 + cmd/scaffold.go | 2 ++ follower/follower_builder.go | 1 + network/alsp/cache.go | 8 +++++ network/alsp/manager/manager.go | 35 +++++++++++++++---- network/internal/testutils/testUtil.go | 6 ++-- network/stub/network.go | 6 ++-- 9 files changed, 54 insertions(+), 11 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 310e77ec7d4..b0a0c920252 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -725,6 +725,7 @@ func (builder *FlowAccessNodeBuilder) initNetwork(nodeID module.Local, SpamRecordCacheSize: builder.AlspConfig.SpamRecordCacheSize, SpamReportQueueSize: builder.AlspConfig.SpamReportQueueSize, DisablePenalty: builder.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.AlspConfig.HearBeatInterval, AlspMetrics: builder.Metrics.Network, NetworkType: network.PublicNetwork, HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 42ec36a99d1..0408f583cc2 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -220,6 +220,10 @@ type AlspConfig struct { // This is useful for managing production incidents. // Note: under normal circumstances, the ALSP module should not be disabled. DisablePenalty bool + + // HearBeatInterval is the interval between heartbeats sent by the ALSP module. The heartbeats are recurring + // events that are used to perform critical ALSP tasks, such as updating the spam records cache. + HearBeatInterval time.Duration } // UnicastRateLimitersConfig unicast rate limiter configuration for the message and bandwidth rate limiters. @@ -332,6 +336,7 @@ func DefaultBaseConfig() *BaseConfig { AlspConfig: &AlspConfig{ SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, + HearBeatInterval: alsp.DefaultHeartBeatInterval, DisablePenalty: false, // by default, apply the penalty }, }, diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index aee36c79d9d..6f064912994 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -631,6 +631,7 @@ func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, SpamRecordCacheSize: builder.AlspConfig.SpamRecordCacheSize, SpamReportQueueSize: builder.AlspConfig.SpamReportQueueSize, DisablePenalty: builder.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.AlspConfig.HearBeatInterval, AlspMetrics: builder.Metrics.Network, HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 3a471d68402..17bab6e3241 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -233,6 +233,7 @@ func (fnb *FlowNodeBuilder) BaseFlags() { fnb.flags.BoolVar(&fnb.BaseConfig.AlspConfig.DisablePenalty, "alsp-disable", defaultConfig.AlspConfig.DisablePenalty, "disable the penalty mechanism of the alsp protocol. default value (recommended) is false") fnb.flags.Uint32Var(&fnb.BaseConfig.AlspConfig.SpamRecordCacheSize, "alsp-spam-record-cache-size", defaultConfig.AlspConfig.SpamRecordCacheSize, "size of spam record cache, recommended to be 10x the number of authorized nodes") fnb.flags.Uint32Var(&fnb.BaseConfig.AlspConfig.SpamReportQueueSize, "alsp-spam-report-queue-size", defaultConfig.AlspConfig.SpamReportQueueSize, "size of spam report queue, recommended to be 100x the number of authorized nodes") + fnb.flags.DurationVar(&fnb.BaseConfig.AlspConfig.HearBeatInterval, "alsp-heartbeat-interval", defaultConfig.AlspConfig.HearBeatInterval, "interval between two consecutive heartbeat events at alsp, recommended to leave it as default unless you know what you are doing.") } func (fnb *FlowNodeBuilder) EnqueuePingService() { @@ -416,6 +417,7 @@ func (fnb *FlowNodeBuilder) EnqueueNetworkInit() { SpamRecordCacheSize: fnb.AlspConfig.SpamRecordCacheSize, SpamReportQueueSize: fnb.AlspConfig.SpamReportQueueSize, DisablePenalty: fnb.AlspConfig.DisablePenalty, + HeartBeatInterval: fnb.AlspConfig.HearBeatInterval, AlspMetrics: fnb.Metrics.Network, HeroCacheMetricsFactory: fnb.HeroCacheMetricsFactory(), NetworkType: network.PrivateNetwork, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 8393378e0cc..47d41ed1b82 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -366,6 +366,7 @@ func (builder *FollowerServiceBuilder) initNetwork(nodeID module.Local, SpamRecordCacheSize: builder.AlspConfig.SpamRecordCacheSize, SpamReportQueueSize: builder.AlspConfig.SpamReportQueueSize, DisablePenalty: builder.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.AlspConfig.HearBeatInterval, AlspMetrics: builder.Metrics.Network, HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, diff --git a/network/alsp/cache.go b/network/alsp/cache.go index 21099e67029..f22124a4b22 100644 --- a/network/alsp/cache.go +++ b/network/alsp/cache.go @@ -1,6 +1,8 @@ package alsp import ( + "time" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/alsp/model" ) @@ -17,6 +19,12 @@ const ( // node after 100 spam reports are received (if no penalty value are amplified). Therefore, the queue size should // be at least 100 * number of nodes in the network. DefaultSpamReportQueueSize = 100 * 1000 // considering max 1000 authorized (staked) nodes in the network. + + // DefaultHeartBeatInterval is the default heartbeat interval for the misbehavior report manager. + // The heartbeat interval is the interval between two consecutive heartbeats. The heartbeat is used to + // perform the periodic tasks, such as decaying the penalty of the misbehaving nodes. + // It is always recommended to use this default value as it is part of the ALSP protocol invariants. + DefaultHeartBeatInterval = 1 * time.Second ) // SpamRecordCache is a cache of spam records for the ALSP module. diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 1f6205c559d..a43c14263bb 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -36,6 +36,8 @@ var ( // ErrSpamReportQueueSizeNotSet is returned when the spam report queue size is not set, it is a fatal irrecoverable error, // and the ALSP module cannot be initialized. ErrSpamReportQueueSizeNotSet = errors.New("spam report queue size is not set") + // ErrHeartBeatIntervalNotSet is returned when the heartbeat interval is not set, it is a fatal irrecoverable error, + ErrHeartBeatIntervalNotSet = errors.New("heartbeat interval is not set") ) // MisbehaviorReportManager is responsible for handling misbehavior reports. @@ -81,6 +83,9 @@ type MisbehaviorReportManagerConfig struct { // NetworkType is the type of the network it is used to determine whether the ALSP module is utilized in the // public (unstaked) or private (staked) network. NetworkType network.NetworkingType + // HeartBeatInterval is the interval between the heartbeats. Heartbeat is a recurring event that is used to + // apply recurring actions, e.g., decay the penalty of the misbehaving nodes. + HeartBeatInterval time.Duration } // validate validates the MisbehaviorReportManagerConfig instance. It returns an error if the config is invalid. @@ -100,6 +105,9 @@ func (c MisbehaviorReportManagerConfig) validate() error { if c.SpamReportQueueSize == 0 { return ErrSpamReportQueueSizeNotSet } + if c.HeartBeatInterval == 0 { + return ErrHeartBeatIntervalNotSet + } return nil } @@ -167,6 +175,10 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, opts ...Mi } builder := component.NewComponentManagerBuilder() + builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + m.startHeartbeatTicks(ctx, cfg.HeartBeatInterval) // blocking call + }) for i := 0; i < defaultMisbehaviorReportManagerWorkers; i++ { builder.AddWorker(m.workerPool.WorkerLogic()) } @@ -224,14 +236,19 @@ func (m *MisbehaviorReportManager) HandleMisbehaviorReport(channel channels.Chan } // startHeartbeatTicks starts the heartbeat ticks ticker to tick at the given intervals. It is a blocking function, and -// should be called in a separate goroutine. It returns when the context is canceled. +// should be called in a separate goroutine. It returns when the context is canceled. Hearbeats are recurring events that +// are used to perform periodic tasks. // Args: -// ctx: the context. -// interval: the interval between two ticks. +// +// ctx: the context. +// interval: the interval between two ticks. +// // Returns: -// none. +// +// none. func (m *MisbehaviorReportManager) startHeartbeatTicks(ctx irrecoverable.SignalerContext, interval time.Duration) { ticker := time.NewTicker(interval) + m.logger.Info().Dur("interval", interval).Msg("starting heartbeat ticks") defer ticker.Stop() for { select { @@ -245,11 +262,15 @@ func (m *MisbehaviorReportManager) startHeartbeatTicks(ctx irrecoverable.Signale } } -// onHeartbeat is called upon a startHeartbeatTicks. It decays the penalty of all spam records based on their individual decay speed. +// onHeartbeat is called upon a startHeartbeatTicks. It encapsulates the recurring tasks that should be performed +// during a heartbeat, which currently includes decay of the spam records. // Args: -// ctx: the context. +// +// ctx: the context. +// // Returns: -// none. +// +// none. func (m *MisbehaviorReportManager) onHeartbeat(ctx irrecoverable.SignalerContext) { allIds := m.cache.Identities() diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 6e05c3e1619..7b3cd4eb51f 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -31,6 +31,7 @@ import ( "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/module/observable" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/alsp" alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" @@ -247,8 +248,9 @@ func GenerateNetworks(t *testing.T, receiveCache := netcache.NewHeroReceiveCache(p2p.DefaultReceiveCacheSize, log, metrics.NewNoopCollector()) cf, err := conduit.NewDefaultConduitFactory(&alspmgr.MisbehaviorReportManagerConfig{ Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(1000), - SpamReportQueueSize: uint32(1000), + SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, + SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, + HeartBeatInterval: alsp.DefaultHeartBeatInterval, AlspMetrics: metrics.NewNoopCollector(), HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), }) diff --git a/network/stub/network.go b/network/stub/network.go index 8f471d290cf..0828ff47427 100644 --- a/network/stub/network.go +++ b/network/stub/network.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/alsp" alspmgr "github.com/onflow/flow-go/network/alsp/manager" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/mocknetwork" @@ -49,8 +50,9 @@ func WithConduitFactory(factory network.ConduitFactory) func(*Network) { // in order for a mock hub to find each other. func NewNetwork(t testing.TB, myId flow.Identifier, hub *Hub, opts ...func(*Network)) *Network { cf, err := conduit.NewDefaultConduitFactory(&alspmgr.MisbehaviorReportManagerConfig{ - SpamRecordCacheSize: uint32(1000), - SpamReportQueueSize: uint32(1000), + SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, + SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, + HeartBeatInterval: alsp.DefaultHeartBeatInterval, Logger: unittest.Logger(), AlspMetrics: metrics.NewNoopCollector(), HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), From 734785bf2bc0bf5938f6f225461364da924d30b3 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 17 May 2023 16:49:31 -0700 Subject: [PATCH 005/815] wires in alsp parameters for testing --- network/alsp/manager/manager_test.go | 202 ++++++--------------------- 1 file changed, 42 insertions(+), 160 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 9019be3ac98..ff93d76eea3 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -39,6 +39,7 @@ import ( // The test ensures that the MisbehaviorReportManager receives and handles all reported misbehavior // without any duplicate reports and within a specified time. func TestNetworkPassesReportedMisbehavior(t *testing.T) { + cfg := managerCfgFixture() misbehaviorReportManger := mocknetwork.NewMisbehaviorReportManager(t) misbehaviorReportManger.On("Start", mock.Anything).Return().Once() @@ -51,13 +52,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() conduitFactory, err := conduit.NewDefaultConduitFactory( - &alspmgr.MisbehaviorReportManagerConfig{ - SpamReportQueueSize: uint32(100), - SpamRecordCacheSize: uint32(100), - Logger: unittest.Logger(), - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - }, + cfg, conduit.WithMisbehaviorManager(misbehaviorReportManger)) require.NoError(t, err) @@ -115,13 +110,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { // The test ensures that the MisbehaviorReportManager receives and handles all reported misbehavior // without any duplicate reports and within a specified time. func TestHandleReportedMisbehavior_Integration(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -134,13 +123,7 @@ func TestHandleReportedMisbehavior_Integration(t *testing.T) { require.NoError(t, err) conduitFactory, err := conduit.NewDefaultConduitFactory( - &alspmgr.MisbehaviorReportManagerConfig{ - SpamReportQueueSize: uint32(100), - SpamRecordCacheSize: uint32(100), - Logger: unittest.Logger(), - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - }, + cfg, conduit.WithMisbehaviorManager(m)) require.NoError(t, err) @@ -226,15 +209,10 @@ func TestHandleReportedMisbehavior_Integration(t *testing.T) { // It checks that when a misbehavior report is received by the ALSP manager, the metrics are recorded. // It fails the test if the metrics are not recorded or if they are recorded incorrectly. func TestMisbehaviorReportMetrics(t *testing.T) { + cfg := managerCfgFixture() alspMetrics := mockmodule.NewAlspMetrics(t) - conduitFactory, err := conduit.NewDefaultConduitFactory( - &alspmgr.MisbehaviorReportManagerConfig{ - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - Logger: unittest.Logger(), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - }) + cfg.AlspMetrics = alspMetrics + conduitFactory, err := conduit.NewDefaultConduitFactory(cfg) require.NoError(t, err) ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( @@ -325,34 +303,16 @@ func TestReportCreation(t *testing.T) { // It is a minimum viable test that ensures that a non-nil ALSP manager is created with expected set of inputs. // In other words, variation of input values do not cause a nil ALSP manager to be created or a panic. func TestNewMisbehaviorReportManager(t *testing.T) { - logger := unittest.Logger() - alspMetrics := metrics.NewNoopCollector() - cacheMetrics := metrics.NewNoopCollector() + cfg := managerCfgFixture() t.Run("with default values", func(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) require.NoError(t, err) assert.NotNil(t, m) }) t.Run("with a custom spam record cache", func(t *testing.T) { - customCache := internal.NewSpamRecordCache(100, logger, cacheMetrics, model.SpamRecordFactory()) - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + customCache := internal.NewSpamRecordCache(100, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(customCache)) require.NoError(t, err) @@ -360,28 +320,12 @@ func TestNewMisbehaviorReportManager(t *testing.T) { }) t.Run("with ALSP module enabled", func(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) require.NoError(t, err) assert.NotNil(t, m) }) t.Run("with ALSP module disabled", func(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) require.NoError(t, err) assert.NotNil(t, m) @@ -391,17 +335,10 @@ func TestNewMisbehaviorReportManager(t *testing.T) { // TestMisbehaviorReportManager_InitializationError tests the creation of a new ALSP manager with invalid inputs. // It is a minimum viable test that ensures that a nil ALSP manager is created with invalid set of inputs. func TestMisbehaviorReportManager_InitializationError(t *testing.T) { - logger := unittest.Logger() - alspMetrics := metrics.NewNoopCollector() + cfg := managerCfgFixture() t.Run("missing spam report queue size", func(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } - + cfg.SpamReportQueueSize = 0 m, err := alspmgr.NewMisbehaviorReportManager(cfg) require.Error(t, err) require.ErrorIs(t, err, alspmgr.ErrSpamReportQueueSizeNotSet) @@ -409,13 +346,15 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { }) t.Run("missing spam record cache size", func(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg.SpamRecordCacheSize = 0 + m, err := alspmgr.NewMisbehaviorReportManager(cfg) + require.Error(t, err) + require.ErrorIs(t, err, alspmgr.ErrSpamRecordCacheSizeNotSet) + assert.Nil(t, m) + }) + t.Run("missing heartbeat intervals", func(t *testing.T) { + cfg.HeartBeatInterval = 0 m, err := alspmgr.NewMisbehaviorReportManager(cfg) require.Error(t, err) require.ErrorIs(t, err, alspmgr.ErrSpamRecordCacheSizeNotSet) @@ -426,16 +365,7 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { // TestHandleMisbehaviorReport_SinglePenaltyReport tests the handling of a single misbehavior report. // The test ensures that the misbehavior report is handled correctly and the penalty is applied to the peer in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { - logger := unittest.Logger() - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: logger, - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -487,16 +417,10 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { // TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable tests the handling of a single misbehavior report when the penalty is disabled. // The test ensures that the misbehavior is reported on metrics but the penalty is not applied to the peer in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T) { + cfg := managerCfgFixture() + cfg.DisablePenalty = true // disable penalty for misbehavior reports alspMetrics := mockmodule.NewAlspMetrics(t) - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - DisablePenalty: true, // disable penalty for misbehavior reports - } + cfg.AlspMetrics = alspMetrics // we use a mock cache but we do not expect any calls to the cache, since the penalty is disabled. cache := mockalsp.NewSpamRecordCache(t) @@ -546,15 +470,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T // Reports are coming in sequentially. // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the peer in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentially(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -614,15 +530,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall // Reports are coming in concurrently. // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the peer in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrently(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -691,15 +599,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl // Reports are coming in sequentially. // The test ensures that each misbehavior report is handled correctly and the penalties are applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -758,15 +658,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential // Reports are coming in concurrently. // The test ensures that each misbehavior report is handled correctly and the penalties are applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -835,15 +727,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent // Reports are coming in sequentially. // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -924,15 +808,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti // Reports are coming in concurrently. // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { - alspMetrics := metrics.NewNoopCollector() - - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: alspMetrics, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache( cfg.SpamRecordCacheSize, @@ -1006,13 +882,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre // a different misbehavior even though they are coming with the same description. This is similar to the traffic tickets, where each ticket // is uniquely identifying a traffic violation, even though the description of the violation is the same. func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *testing.T) { - cfg := &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: uint32(100), - SpamReportQueueSize: uint32(100), - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } + cfg := managerCfgFixture() cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) @@ -1121,3 +991,15 @@ func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.Misb return reports } + +// managerCfgFixture creates a new MisbehaviorReportManagerConfig with default values for testing. +func managerCfgFixture() *alspmgr.MisbehaviorReportManagerConfig { + return &alspmgr.MisbehaviorReportManagerConfig{ + Logger: unittest.Logger(), + SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, + SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, + HeartBeatInterval: alsp.DefaultHeartBeatInterval, + AlspMetrics: metrics.NewNoopCollector(), + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + } +} From ea31c5f816a9d0328956b91e392cc977785e14dd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 18 May 2023 09:55:33 -0700 Subject: [PATCH 006/815] adds test for a single heartbeat --- network/alsp/manager/manager_test.go | 87 +++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index ff93d76eea3..04e9bbda347 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -941,6 +941,91 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") } +// TestDecayMisbehaviorPenalty_SingleHeartbeat tests the decay of the misbehavior penalty. The test ensures that the misbehavior penalty +// is decayed after a single heartbeat. The test guarantees waiting for at least one heartbeat by waiting for the first decay to happen. +func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { + cfg := managerCfgFixture() + + cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) + + // create a new MisbehaviorReportManager + m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + require.NoError(t, err) + + // start the ALSP manager + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + cancel() + unittest.RequireCloseBefore(t, m.Done(), 100*time.Millisecond, "ALSP manager did not stop") + }() + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + m.Start(signalerCtx) + unittest.RequireCloseBefore(t, m.Ready(), 100*time.Millisecond, "ALSP manager did not start") + + // creates a single misbehavior report + originId := unittest.IdentifierFixture() + report := createMisbehaviorReportForOriginId(t, originId) + require.Less(t, report.Penalty(), float64(0)) // ensure the penalty is negative + + channel := channels.Channel("test-channel") + + // number of times the duplicate misbehavior report is reported concurrently + times := 10 + wg := sync.WaitGroup{} + wg.Add(times) + + // concurrently reports the same misbehavior report twice + for i := 0; i < times; i++ { + go func() { + defer wg.Done() + + m.HandleMisbehaviorReport(channel, report) + }() + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // phase-1: eventually all the misbehavior reports should be processed. + penaltyBeforeDecay := float64(0) + require.Eventually(t, func() bool { + // check if the misbehavior reports have been processed by verifying that the Adjust method was called on the cache + record, ok := cache.Get(originId) + if !ok { + return false + } + require.NotNil(t, record) + + // eventually, the penalty should be the accumulated penalty of all the duplicate misbehavior reports. + if record.Penalty != report.Penalty()*float64(times) { + return false + } + // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. + require.Equal(t, uint64(0), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) + + penaltyBeforeDecay = record.Penalty + return true + }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") + + // phase-2: wait enough for at least one heartbeat to be processed. + time.Sleep(2 * time.Second) + + // phase-3: check if the penalty was decayed for at least one heartbeat. + record, ok := cache.Get(originId) + require.True(t, ok) // the record should be in the cache + require.NotNil(t, record) + + // with at least a single heartbeat, the penalty should be greater than the penalty before the decay. + fmt.Println(penaltyBeforeDecay) + require.Greater(t, record.Penalty, penaltyBeforeDecay) + // we waited for at most 2 heartbeats, so the decayed penalty should be still less than the value after 3 decays. + require.Less(t, record.Penalty, penaltyBeforeDecay+3*record.Decay) + // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. + require.Equal(t, uint64(0), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) +} + // createMisbehaviorReportForOriginId creates a mock misbehavior report for a single origin id. // Args: // - t: the testing.T instance @@ -952,7 +1037,7 @@ func createMisbehaviorReportForOriginId(t *testing.T, originID flow.Identifier) report := mocknetwork.NewMisbehaviorReport(t) report.On("OriginId").Return(originID) report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) - report.On("Penalty").Return(float64(-1 * rand.Intn(10))) // random penalty between -1 and -10 + report.On("Penalty").Return(model.DefaultPenaltyValue) // random penalty between -1 and -10 return report } From e5034689a5197e48ab8cb127cfffcfdbef70af08 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 18 May 2023 10:20:51 -0700 Subject: [PATCH 007/815] decouples misbehavior fixture functions --- network/alsp/manager/manager_test.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 04e9bbda347..db81028ecab 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -3,6 +3,7 @@ package alspmgr_test import ( "context" "fmt" + "math" "math/rand" "sync" "testing" @@ -902,7 +903,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t // creates a single misbehavior report originId := unittest.IdentifierFixture() - report := createMisbehaviorReportForOriginId(t, originId) + report := misbehaviorReportFixture(t, originId) channel := channels.Channel("test-channel") @@ -964,7 +965,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { // creates a single misbehavior report originId := unittest.IdentifierFixture() - report := createMisbehaviorReportForOriginId(t, originId) + report := misbehaviorReportFixtureWithDefaultPenalty(t, originId) require.Less(t, report.Penalty(), float64(0)) // ensure the penalty is negative channel := channels.Channel("test-channel") @@ -1026,18 +1027,26 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) } -// createMisbehaviorReportForOriginId creates a mock misbehavior report for a single origin id. +// misbehaviorReportFixture creates a mock misbehavior report for a single origin id. // Args: // - t: the testing.T instance // - originID: the origin id of the misbehavior report // Returns: // - network.MisbehaviorReport: the misbehavior report // Note: the penalty of the misbehavior report is randomly chosen between -1 and -10. -func createMisbehaviorReportForOriginId(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { +func misbehaviorReportFixture(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, math.Min(-1, float64(-1-rand.Intn(10)))) +} + +func misbehaviorReportFixtureWithDefaultPenalty(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, model.DefaultPenaltyValue) +} + +func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, penalty float64) network.MisbehaviorReport { report := mocknetwork.NewMisbehaviorReport(t) report.On("OriginId").Return(originID) report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) - report.On("Penalty").Return(model.DefaultPenaltyValue) // random penalty between -1 and -10 + report.On("Penalty").Return(penalty) return report } @@ -1054,7 +1063,7 @@ func createRandomMisbehaviorReportsForOriginId(t *testing.T, originID flow.Ident reports := make([]network.MisbehaviorReport, numReports) for i := 0; i < numReports; i++ { - reports[i] = createMisbehaviorReportForOriginId(t, originID) + reports[i] = misbehaviorReportFixture(t, originID) } return reports @@ -1071,7 +1080,7 @@ func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.Misb reports := make([]network.MisbehaviorReport, numReports) for i := 0; i < numReports; i++ { - reports[i] = createMisbehaviorReportForOriginId(t, unittest.IdentifierFixture()) + reports[i] = misbehaviorReportFixture(t, unittest.IdentifierFixture()) } return reports From 388fbfeeee18bbd4e61b06ee6712dffac1b26fdf Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 18 May 2023 10:22:46 -0700 Subject: [PATCH 008/815] reduces sleep time of the test --- network/alsp/manager/manager_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index db81028ecab..dfdb4752502 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -1009,7 +1009,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") // phase-2: wait enough for at least one heartbeat to be processed. - time.Sleep(2 * time.Second) + time.Sleep(1 * time.Second) // phase-3: check if the penalty was decayed for at least one heartbeat. record, ok := cache.Get(originId) @@ -1017,10 +1017,9 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { require.NotNil(t, record) // with at least a single heartbeat, the penalty should be greater than the penalty before the decay. - fmt.Println(penaltyBeforeDecay) require.Greater(t, record.Penalty, penaltyBeforeDecay) - // we waited for at most 2 heartbeats, so the decayed penalty should be still less than the value after 3 decays. - require.Less(t, record.Penalty, penaltyBeforeDecay+3*record.Decay) + // we waited for at most one heartbeat, so the decayed penalty should be still less than the value after 2 heartbeats. + require.Less(t, record.Penalty, penaltyBeforeDecay+2*record.Decay) // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. From 32e8aec0ffe4a9f562c53de0e59b74937609f4e7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 18 May 2023 10:40:57 -0700 Subject: [PATCH 009/815] adds decay to zero test --- network/alsp/manager/manager_test.go | 163 +++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index dfdb4752502..805414b31cd 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -1026,6 +1026,169 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) } +// TestDecayMisbehaviorPenalty_MultipleHeartbeat tests the decay of the misbehavior penalty under multiple heartbeats. +// The test ensures that the misbehavior penalty is decayed with a linear progression within multiple heartbeats. +func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { + cfg := managerCfgFixture() + + cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) + + // create a new MisbehaviorReportManager + m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + require.NoError(t, err) + + // start the ALSP manager + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + cancel() + unittest.RequireCloseBefore(t, m.Done(), 100*time.Millisecond, "ALSP manager did not stop") + }() + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + m.Start(signalerCtx) + unittest.RequireCloseBefore(t, m.Ready(), 100*time.Millisecond, "ALSP manager did not start") + + // creates a single misbehavior report + originId := unittest.IdentifierFixture() + report := misbehaviorReportFixtureWithDefaultPenalty(t, originId) + require.Less(t, report.Penalty(), float64(0)) // ensure the penalty is negative + + channel := channels.Channel("test-channel") + + // number of times the duplicate misbehavior report is reported concurrently + times := 10 + wg := sync.WaitGroup{} + wg.Add(times) + + // concurrently reports the same misbehavior report twice + for i := 0; i < times; i++ { + go func() { + defer wg.Done() + + m.HandleMisbehaviorReport(channel, report) + }() + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // phase-1: eventually all the misbehavior reports should be processed. + penaltyBeforeDecay := float64(0) + require.Eventually(t, func() bool { + // check if the misbehavior reports have been processed by verifying that the Adjust method was called on the cache + record, ok := cache.Get(originId) + if !ok { + return false + } + require.NotNil(t, record) + + // eventually, the penalty should be the accumulated penalty of all the duplicate misbehavior reports. + if record.Penalty != report.Penalty()*float64(times) { + return false + } + // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. + require.Equal(t, uint64(0), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) + + penaltyBeforeDecay = record.Penalty + return true + }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") + + // phase-2: wait for 3 heartbeats to be processed. + time.Sleep(3 * time.Second) + + // phase-3: check if the penalty was decayed in a linear progression. + record, ok := cache.Get(originId) + require.True(t, ok) // the record should be in the cache + require.NotNil(t, record) + + // with 3 heartbeats processed, the penalty should be greater than the penalty before the decay. + require.Greater(t, record.Penalty, penaltyBeforeDecay) + // with 3 heartbeats processed, the decayed penalty should be less than the value after 4 heartbeats. + require.Less(t, record.Penalty, penaltyBeforeDecay+4*record.Decay) + // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. + require.Equal(t, uint64(0), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) +} + +// TestDecayMisbehaviorPenalty_MultipleHeartbeat tests the decay of the misbehavior penalty under multiple heartbeats. +// The test ensures that the misbehavior penalty is decayed with a linear progression within multiple heartbeats. +func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { + cfg := managerCfgFixture() + + cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) + + // create a new MisbehaviorReportManager + m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + require.NoError(t, err) + + // start the ALSP manager + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + cancel() + unittest.RequireCloseBefore(t, m.Done(), 100*time.Millisecond, "ALSP manager did not stop") + }() + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + m.Start(signalerCtx) + unittest.RequireCloseBefore(t, m.Ready(), 100*time.Millisecond, "ALSP manager did not start") + + // creates a single misbehavior report + originId := unittest.IdentifierFixture() + report := misbehaviorReportFixture(t, originId) // penalties are between -1 and -10 + require.Less(t, report.Penalty(), float64(0)) // ensure the penalty is negative + + channel := channels.Channel("test-channel") + + // number of times the duplicate misbehavior report is reported concurrently + times := 10 + wg := sync.WaitGroup{} + wg.Add(times) + + // concurrently reports the same misbehavior report twice + for i := 0; i < times; i++ { + go func() { + defer wg.Done() + + m.HandleMisbehaviorReport(channel, report) + }() + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // phase-1: eventually all the misbehavior reports should be processed. + require.Eventually(t, func() bool { + // check if the misbehavior reports have been processed by verifying that the Adjust method was called on the cache + record, ok := cache.Get(originId) + if !ok { + return false + } + require.NotNil(t, record) + + // eventually, the penalty should be the accumulated penalty of all the duplicate misbehavior reports. + if record.Penalty != report.Penalty()*float64(times) { + return false + } + // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. + require.Equal(t, uint64(0), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) + + return true + }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") + + // phase-2: default decay speed is 1000 and with 10 penalties in range of [-1, -10], the penalty should be decayed to zero in + // a single heartbeat. + time.Sleep(1 * time.Second) + + // phase-3: check if the penalty was decayed to zero. + record, ok := cache.Get(originId) + require.True(t, ok) // the record should be in the cache + require.NotNil(t, record) + + // with a single heartbeat and decay speed of 1000, the penalty should be decayed to zero. + require.Equal(t, float64(0), record.Penalty) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) +} + // misbehaviorReportFixture creates a mock misbehavior report for a single origin id. // Args: // - t: the testing.T instance From 71684fc2fc51316010f604ca506138cb7dcb8a5f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 14:57:34 -0700 Subject: [PATCH 010/815] lint fix --- network/alsp/manager/manager.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 4afd8c733c4..5e7915d49d1 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -36,6 +36,9 @@ var ( // ErrSpamReportQueueSizeNotSet is returned when the spam report queue size is not set, it is a fatal irrecoverable error, // and the ALSP module cannot be initialized. ErrSpamReportQueueSizeNotSet = errors.New("spam report queue size is not set") + // ErrHeartBeatIntervalNotSet is returned when the heartbeat interval is not set, it is a fatal irrecoverable error, + // and the ALSP module cannot be initialized. + ErrHeartBeatIntervalNotSet = errors.New("heartbeat interval is not set") ) type SpamRecordCacheFactory func(zerolog.Logger, uint32, module.HeroCacheMetrics) alsp.SpamRecordCache @@ -51,8 +54,6 @@ func defaultSpamRecordCacheFactory() SpamRecordCacheFactory { } } - ErrHeartBeatIntervalNotSet = errors.New("heartbeat interval is not set") - // ErrHeartBeatIntervalNotSet is returned when the heartbeat interval is not set, it is a fatal irrecoverable error, // MisbehaviorReportManager is responsible for handling misbehavior reports. // The current version is at the minimum viable product stage and only logs the reports. // TODO: the mature version should be able to handle the reports and take actions accordingly, i.e., penalize the misbehaving node From c59b9d469576ef1199e1c3eb8d52bcc061fc64c4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 14:57:40 -0700 Subject: [PATCH 011/815] test fix --- network/alsp/manager/manager_test.go | 162 +++++++++++++-------------- 1 file changed, 77 insertions(+), 85 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 0ca2bbce4c8..288555b9c07 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -115,14 +115,13 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { func TestHandleReportedMisbehavior_Integration(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) conduitFactory, err := conduit.NewDefaultConduitFactory( @@ -315,9 +314,10 @@ func TestNewMisbehaviorReportManager(t *testing.T) { }) t.Run("with a custom spam record cache", func(t *testing.T) { - customCache := internal.NewSpamRecordCache(100, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) - - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(customCache)) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + return internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + })) require.NoError(t, err) assert.NotNil(t, m) }) @@ -370,14 +370,13 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.NewNoopCollector(), - model.SpamRecordFactory()) - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -434,10 +433,6 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T })) require.NoError(t, err) - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) - require.NoError(t, err) - // start the ALSP manager ctx, cancel := context.WithCancel(context.Background()) defer func() { @@ -481,14 +476,13 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentially(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.NewNoopCollector(), - model.SpamRecordFactory()) - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -541,14 +535,12 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrently(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -610,14 +602,12 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -669,14 +659,12 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -738,14 +726,12 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -819,14 +805,12 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache( - cfg.SpamRecordCacheSize, - cfg.Logger, - metrics.ApplicationLayerSpamRecordQueueMetricsFactory(cfg.HeroCacheMetricsFactory), - model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -893,10 +877,12 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -955,10 +941,12 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -1039,10 +1027,12 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -1123,10 +1113,12 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { cfg := managerCfgFixture() - cache := internal.NewSpamRecordCache(cfg.SpamRecordCacheSize, cfg.Logger, metrics.NewNoopCollector(), model.SpamRecordFactory()) - - // create a new MisbehaviorReportManager - m, err := alspmgr.NewMisbehaviorReportManager(cfg, alspmgr.WithSpamRecordsCache(cache)) + var cache alsp.SpamRecordCache + m, err := alspmgr.NewMisbehaviorReportManager(cfg, + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + })) require.NoError(t, err) // start the ALSP manager @@ -1178,7 +1170,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) - + return true }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") From 4e7117abe86be11042532cf89a743cbbbbe0daf2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 15:00:47 -0700 Subject: [PATCH 012/815] lint fix --- .../node_builder/access_node_builder.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 6ad810e2358..2bb46daf2e2 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1162,17 +1162,17 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri builder.IdentityProvider, builder.GossipSubConfig.LocalMeshLogInterval) - // setup RPC inspectors - rpcInspectorBuilder := inspector.NewGossipSubInspectorBuilder(builder.Logger, builder.SporkID, builder.GossipSubConfig.RpcInspector) - rpcInspectorSuite, err := rpcInspectorBuilder. - SetNetworkType(network.PublicNetwork). - SetMetrics(&p2pconfig.MetricsConfig{ - HeroCacheFactory: builder.HeroCacheMetricsFactory(), - Metrics: builder.Metrics.Network, - }).Build() - if err != nil { - return nil, fmt.Errorf("failed to create gossipsub rpc inspectors for access node: %w", err) - } + // setup RPC inspectors + rpcInspectorBuilder := inspector.NewGossipSubInspectorBuilder(builder.Logger, builder.SporkID, builder.GossipSubConfig.RpcInspector) + rpcInspectorSuite, err := rpcInspectorBuilder. + SetNetworkType(network.PublicNetwork). + SetMetrics(&p2pconfig.MetricsConfig{ + HeroCacheFactory: builder.HeroCacheMetricsFactory(), + Metrics: builder.Metrics.Network, + }).Build() + if err != nil { + return nil, fmt.Errorf("failed to create gossipsub rpc inspectors for access node: %w", err) + } libp2pNode, err := p2pbuilder.NewNodeBuilder( builder.Logger, From 141bc1d5ec48749544683f79d5b82d4b0ea1abd2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 15:32:00 -0700 Subject: [PATCH 013/815] dissolves start method of middleware --- network/p2p/middleware/middleware.go | 39 ++++++++++------------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 58e15638943..0931667c4e2 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -188,13 +188,24 @@ func NewMiddleware( }) } builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - // TODO: refactor to avoid storing ctx altogether mw.ctx = ctx + if mw.ov == nil { + ctx.Throw(fmt.Errorf("overlay has not been set")) + } + + mw.authorizedSenderValidator = validator.NewAuthorizedSenderValidator( + mw.log, + mw.slashingViolationsConsumer, + mw.ov.Identity) - if err := mw.start(ctx); err != nil { - ctx.Throw(err) + err := mw.libP2PNode.WithDefaultUnicastProtocol(m.handleIncomingStream, m.preferredUnicasts) + if err != nil { + ctx.Throw(fmt.Errorf("could not register preferred unicast protocols on libp2p node: %w", err)) } + mw.UpdateNodeAddresses() + mw.libP2PNode.WithPeersProvider(mw.topologyPeers) + ready() <-ctx.Done() @@ -202,7 +213,6 @@ func NewMiddleware( // wait for the readConnection and readSubscription routines to stop mw.wg.Wait() - mw.log.Info().Str("component", "middleware").Msg("stopped subroutines") }) @@ -298,27 +308,6 @@ func (m *Middleware) SetOverlay(ov network.Overlay) { m.ov = ov } -// start will start the middleware. -// No errors are expected during normal operation. -func (m *Middleware) start(ctx context.Context) error { - if m.ov == nil { - return fmt.Errorf("could not start middleware: overlay must be configured by calling SetOverlay before middleware can be started") - } - - m.authorizedSenderValidator = validator.NewAuthorizedSenderValidator(m.log, m.slashingViolationsConsumer, m.ov.Identity) - - err := m.libP2PNode.WithDefaultUnicastProtocol(m.handleIncomingStream, m.preferredUnicasts) - if err != nil { - return fmt.Errorf("could not register preferred unicast protocols on libp2p node: %w", err) - } - - m.UpdateNodeAddresses() - - m.libP2PNode.WithPeersProvider(m.topologyPeers) - - return nil -} - // topologyPeers callback used by the peer manager to get the list of peer ID's // which this node should be directly connected to as peers. The peer ID list // returned will be filtered through any configured m.peerManagerFilters. If the From 780c59494301e02f2763bb4f843a76db446f59b5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 15:39:30 -0700 Subject: [PATCH 014/815] chore: renames topology method and middleware option --- network/p2p/middleware/middleware.go | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 0931667c4e2..425ecbedcad 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -103,15 +103,15 @@ type Middleware struct { component.Component } -type MiddlewareOption func(*Middleware) +type OptionFn func(*Middleware) -func WithMessageValidators(validators ...network.MessageValidator) MiddlewareOption { +func WithMessageValidators(validators ...network.MessageValidator) OptionFn { return func(mw *Middleware) { mw.validators = validators } } -func WithPreferredUnicastProtocols(unicasts []protocols.ProtocolName) MiddlewareOption { +func WithPreferredUnicastProtocols(unicasts []protocols.ProtocolName) OptionFn { return func(mw *Middleware) { mw.preferredUnicasts = unicasts } @@ -119,14 +119,14 @@ func WithPreferredUnicastProtocols(unicasts []protocols.ProtocolName) Middleware // WithPeerManagerFilters sets a list of p2p.PeerFilter funcs that are used to // filter out peers provided by the peer manager PeersProvider. -func WithPeerManagerFilters(peerManagerFilters []p2p.PeerFilter) MiddlewareOption { +func WithPeerManagerFilters(peerManagerFilters []p2p.PeerFilter) OptionFn { return func(mw *Middleware) { mw.peerManagerFilters = peerManagerFilters } } // WithUnicastRateLimiters sets the unicast rate limiters. -func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) MiddlewareOption { +func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { return func(mw *Middleware) { mw.unicastRateLimiters = rateLimiters } @@ -151,7 +151,7 @@ func NewMiddleware( idTranslator p2p.IDTranslator, codec network.Codec, slashingViolationsConsumer slashing.ViolationsConsumer, - opts ...MiddlewareOption) *Middleware { + opts ...OptionFn) *Middleware { if unicastMessageTimeout <= 0 { unicastMessageTimeout = DefaultUnicastTimeout @@ -204,7 +204,7 @@ func NewMiddleware( } mw.UpdateNodeAddresses() - mw.libP2PNode.WithPeersProvider(mw.topologyPeers) + mw.libP2PNode.WithPeersProvider(mw.authorizedPeers) ready() @@ -308,12 +308,18 @@ func (m *Middleware) SetOverlay(ov network.Overlay) { m.ov = ov } -// topologyPeers callback used by the peer manager to get the list of peer ID's -// which this node should be directly connected to as peers. The peer ID list -// returned will be filtered through any configured m.peerManagerFilters. If the -// underlying libp2p node has a peer manager configured this func will be used as the -// peers provider. -func (m *Middleware) topologyPeers() peer.IDSlice { +// authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as +// well as who this node can connect to). +// and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components +// of libp2p. +// +// Args: +// none +// Returns: +// - peer.IDSlice: a list of peer IDs that are allowed to connect to this node (and that this node can connect to). Any peer +// not in this list is assumed to be disconnected from this node (if connected) and not allowed to connect to this node. +// This is the guarantee that the underlying libp2p node implementation makes. +func (m *Middleware) authorizedPeers() peer.IDSlice { peerIDs := make([]peer.ID, 0) for _, id := range m.peerIDs(m.ov.Topology().NodeIDs()) { peerAllowed := true From 0d21785bb2e61b152c592ba856aecc6aee3f960c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 15:39:46 -0700 Subject: [PATCH 015/815] chore: renames middleware option --- cmd/scaffold.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 23f8bb546b2..f40ff7ea5f8 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -454,7 +454,7 @@ func (fnb *FlowNodeBuilder) HeroCacheMetricsFactory() metrics.HeroCacheMetricsFa } func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory(node *NodeConfig, cf network.ConduitFactory, unicastRateLimiters *ratelimit.RateLimiters, peerManagerFilters []p2p.PeerFilter) (network.Network, error) { - var mwOpts []middleware.MiddlewareOption + var mwOpts []middleware.OptionFn if len(fnb.MsgValidators) > 0 { mwOpts = append(mwOpts, middleware.WithMessageValidators(fnb.MsgValidators...)) } From 4fb9a8f5c23c02e0b6e90ab150bf8e20783e52f1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 16:11:54 -0700 Subject: [PATCH 016/815] chores middleware --- network/p2p/middleware/disallowListCache.go | 1 + network/p2p/middleware/middleware.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 network/p2p/middleware/disallowListCache.go diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go new file mode 100644 index 00000000000..c870d7c1649 --- /dev/null +++ b/network/p2p/middleware/disallowListCache.go @@ -0,0 +1 @@ +package middleware diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 425ecbedcad..58a5f384585 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -78,9 +78,11 @@ var ( // our neighbours on the peer-to-peer network. type Middleware struct { sync.Mutex + component.Component ctx context.Context log zerolog.Logger ov network.Overlay + // TODO: using a waitgroup here doesn't actually guarantee that we'll wait for all // goroutines to exit, because new goroutines could be started after we've already // returned from wg.Wait(). We need to solve this the right way using ComponentManager @@ -100,7 +102,6 @@ type Middleware struct { slashingViolationsConsumer slashing.ViolationsConsumer unicastRateLimiters *ratelimit.RateLimiters authorizedSenderValidator *validator.AuthorizedSenderValidator - component.Component } type OptionFn func(*Middleware) @@ -142,10 +143,10 @@ func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { // During normal operations any error returned by Middleware.start is considered to be catastrophic // and will be thrown by the irrecoverable.SignalerContext causing the node to crash. func NewMiddleware( - log zerolog.Logger, + logger zerolog.Logger, libP2PNode p2p.LibP2PNode, flowID flow.Identifier, - bitswapMet module.BitswapMetrics, + bitswapMetrics module.BitswapMetrics, rootBlockID flow.Identifier, unicastMessageTimeout time.Duration, idTranslator p2p.IDTranslator, @@ -159,12 +160,12 @@ func NewMiddleware( // create the node entity and inject dependencies & config mw := &Middleware{ - log: log, + log: logger, me: flowID, libP2PNode: libP2PNode, - bitswapMetrics: bitswapMet, + bitswapMetrics: bitswapMetrics, rootBlockID: rootBlockID, - validators: DefaultValidators(log, flowID), + validators: DefaultValidators(logger, flowID), unicastMessageTimeout: unicastMessageTimeout, idTranslator: idTranslator, codec: codec, @@ -198,7 +199,7 @@ func NewMiddleware( mw.slashingViolationsConsumer, mw.ov.Identity) - err := mw.libP2PNode.WithDefaultUnicastProtocol(m.handleIncomingStream, m.preferredUnicasts) + err := mw.libP2PNode.WithDefaultUnicastProtocol(mw.handleIncomingStream, mw.preferredUnicasts) if err != nil { ctx.Throw(fmt.Errorf("could not register preferred unicast protocols on libp2p node: %w", err)) } From 0e05b5952c0ba0e9aadc842b6c52f76b534de99f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 24 May 2023 16:12:06 -0700 Subject: [PATCH 017/815] adds disallow list cache interface --- network/p2p/middleware/disallowListCache.go | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index c870d7c1649..de4bb2b6405 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -1 +1,42 @@ package middleware + +// DisallowListedCause is a type representing the cause of disallow listing. A remote node may be disallow-listed by the +// current node for a variety of reasons. This type is used to represent the reason for disallow-listing, so that if +// a node is disallow-listed for reasons X and Y, allow-listing it back for reason X does not automatically allow-list +// it for reason Y. +type DisallowListedCause string + +const ( + // DisallowListedCauseAdmin is the cause of disallow-listing a node by an admin command. + DisallowListedCauseAdmin DisallowListedCause = "disallow-listed-admin" + // DisallowListedCauseAlsp is the cause of disallow-listing a node by the ALSP (Application Layer Spam Prevention). + DisallowListedCauseAlsp DisallowListedCause = "disallow-listed-alsp" +) + +// DisallowListCache is an interface for a cache that keeps the list of disallow-listed peers. +// It is designed to present a centralized interface for keeping track of disallow-listed peers for different reasons. +type DisallowListCache interface { + // IsDisallowedFor returns true if the given peer is disallow-listed for the given cause. + IsDisallowedFor(peerID string, cause DisallowListedCause) bool + + // DisallowFor disallow-lists the given peer for the given cause. + // If the peer is already disallow-listed for the given cause, this method is a no-op. + // Args: + // - peerID: the peer to disallow-list. + // - cause: the cause for disallow-listing. + // Returns: + // - nil if the peer was successfully disallow-listed. + // - an error if the peer could not be disallow-listed in the cache, the error is irrecoverable and the current node should + // crash. + DisallowFor(peerID string, cause DisallowListedCause) error + + // AllowFor allow-lists the given peer for the given cause. + // If the peer is not disallow-listed for the given cause, this method is a no-op. + // Args: + // - peerID: the peer to allow-list. + // - cause: the cause for allow-listing. + // Returns: + // - nil if the peer was successfully allow-listed. + // - an error if the peer could not be allow-listed in the cache, the error is irrecoverable and the current node should + AllowFor(peerID string, cause DisallowListedCause) error +} From ddcf7d8885bc8cf04e8cf78305e44c5048707ab0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 09:47:49 -0700 Subject: [PATCH 018/815] changes signature of a single method --- network/p2p/middleware/disallowListCache.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index de4bb2b6405..ffc97655e43 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -16,8 +16,14 @@ const ( // DisallowListCache is an interface for a cache that keeps the list of disallow-listed peers. // It is designed to present a centralized interface for keeping track of disallow-listed peers for different reasons. type DisallowListCache interface { - // IsDisallowedFor returns true if the given peer is disallow-listed for the given cause. - IsDisallowedFor(peerID string, cause DisallowListedCause) bool + // IsDisallowed returns true if the given peer is disallow-listed for any cause. + // Args: + // - peerID: the peer to check. + // Returns: + // - a list of causes for which the peer is disallow-listed. + // - true if the peer is disallow-listed for any cause. + // - false if the peer is not disallow-listed for any cause. + IsDisallowed(peerID string) ([]DisallowListedCause, bool) // DisallowFor disallow-lists the given peer for the given cause. // If the peer is already disallow-listed for the given cause, this method is a no-op. From a7b93353ccd48acf9c0264f786e96b685fe65e77 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 14:38:46 -0700 Subject: [PATCH 019/815] adds cache entity --- network/p2p/middleware/internal/cache.go | 32 ++++++++++++++ .../p2p/middleware/internal/cacheEntity.go | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 network/p2p/middleware/internal/cache.go create mode 100644 network/p2p/middleware/internal/cacheEntity.go diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go new file mode 100644 index 00000000000..a76bc7b8b10 --- /dev/null +++ b/network/p2p/middleware/internal/cache.go @@ -0,0 +1,32 @@ +package internal + +import ( + "github.com/onflow/flow-go/module/mempool/stdmap" + "github.com/onflow/flow-go/network/p2p/middleware" +) + +type DisallowListCache struct { + c *stdmap.Backend +} + +var _ middleware.DisallowListCache = (*DisallowListCache)(nil) + +func NewDisallowListCache() DisallowListCache { + return DisallowListCache{ + c: stdmap.NewBackend(), + } +} + +func (d *DisallowListCache) IsDisallowed(peerID string) ([]middleware.DisallowListedCause, bool) { + d.c.ByID() +} + +func (d *DisallowListCache) DisallowFor(peerID string, cause middleware.DisallowListedCause) error { + //TODO implement me + panic("implement me") +} + +func (d *DisallowListCache) AllowFor(peerID string, cause middleware.DisallowListedCause) error { + //TODO implement me + panic("implement me") +} diff --git a/network/p2p/middleware/internal/cacheEntity.go b/network/p2p/middleware/internal/cacheEntity.go new file mode 100644 index 00000000000..401b6dfc998 --- /dev/null +++ b/network/p2p/middleware/internal/cacheEntity.go @@ -0,0 +1,44 @@ +package internal + +import ( + "github.com/libp2p/go-libp2p/core/peer" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/network/p2p/middleware" +) + +// disallowListCacheEntity is the model data type for the disallow list cache. +// It represents a single peer that is disallow-listed and the reasons for it. +// The key for storing this entity is the id field which is the hash of the peerID. +// This means that the entities are deduplicated by their peerID. +type disallowListCacheEntity struct { + peerID peer.ID + causes []middleware.DisallowListedCause + // id is the hash of the peerID which is used as the key for storing the entity in the cache. + // we cache it internally to avoid hashing the peerID multiple times. + id flow.Identifier +} + +var _ flow.Entity = (*disallowListCacheEntity)(nil) + +// ID returns the hash of the peerID which is used as the key for storing the entity in the cache. +// Returns: +// - the hash of the peerID as a flow.Identifier. +func (d *disallowListCacheEntity) ID() flow.Identifier { + return d.id +} + +// Checksum returns the hash of the peerID, there is no use for this method in the cache. It is implemented to satisfy +// the flow.Entity interface. +// Returns: +// - the hash of the peerID as a flow.Identifier. +func (d *disallowListCacheEntity) Checksum() flow.Identifier { + return d.id +} + +// makeId is a helper function for creating the id field of the disallowListCacheEntity by hashing the peerID. +// Returns: +// - the hash of the peerID as a flow.Identifier. +func makeId(peerID peer.ID) flow.Identifier { + return flow.MakeID([]byte(peerID)) +} From b63cb9e77efddd70dde19c496b06a06509273731 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 15:12:14 -0700 Subject: [PATCH 020/815] revises the interface of cache --- network/p2p/middleware/disallowListCache.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index ffc97655e43..d9b299d22ae 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -1,5 +1,7 @@ package middleware +import "github.com/libp2p/go-libp2p/core/peer" + // DisallowListedCause is a type representing the cause of disallow listing. A remote node may be disallow-listed by the // current node for a variety of reasons. This type is used to represent the reason for disallow-listing, so that if // a node is disallow-listed for reasons X and Y, allow-listing it back for reason X does not automatically allow-list @@ -23,7 +25,7 @@ type DisallowListCache interface { // - a list of causes for which the peer is disallow-listed. // - true if the peer is disallow-listed for any cause. // - false if the peer is not disallow-listed for any cause. - IsDisallowed(peerID string) ([]DisallowListedCause, bool) + IsDisallowed(peerID peer.ID) ([]DisallowListedCause, bool) // DisallowFor disallow-lists the given peer for the given cause. // If the peer is already disallow-listed for the given cause, this method is a no-op. @@ -34,7 +36,7 @@ type DisallowListCache interface { // - nil if the peer was successfully disallow-listed. // - an error if the peer could not be disallow-listed in the cache, the error is irrecoverable and the current node should // crash. - DisallowFor(peerID string, cause DisallowListedCause) error + DisallowFor(peerID peer.ID, cause DisallowListedCause) error // AllowFor allow-lists the given peer for the given cause. // If the peer is not disallow-listed for the given cause, this method is a no-op. @@ -44,5 +46,5 @@ type DisallowListCache interface { // Returns: // - nil if the peer was successfully allow-listed. // - an error if the peer could not be allow-listed in the cache, the error is irrecoverable and the current node should - AllowFor(peerID string, cause DisallowListedCause) error + AllowFor(peerID peer.ID, cause DisallowListedCause) error } From ac7739b7e91d9fbc2ba66c8d9d2404795d0136a6 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 15:12:31 -0700 Subject: [PATCH 021/815] changes cause type to map --- network/p2p/middleware/internal/cacheEntity.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/middleware/internal/cacheEntity.go b/network/p2p/middleware/internal/cacheEntity.go index 401b6dfc998..9d8353dd912 100644 --- a/network/p2p/middleware/internal/cacheEntity.go +++ b/network/p2p/middleware/internal/cacheEntity.go @@ -13,7 +13,7 @@ import ( // This means that the entities are deduplicated by their peerID. type disallowListCacheEntity struct { peerID peer.ID - causes []middleware.DisallowListedCause + causes map[middleware.DisallowListedCause]struct{} // id is the hash of the peerID which is used as the key for storing the entity in the cache. // we cache it internally to avoid hashing the peerID multiple times. id flow.Identifier From f28ff45fa76788680f452ba65531b475d89af6c1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 15:12:39 -0700 Subject: [PATCH 022/815] adds adjust function --- network/p2p/middleware/internal/cache.go | 72 +++++++++++++++++++++--- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go index a76bc7b8b10..06e18eb2712 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/middleware/internal/cache.go @@ -1,12 +1,19 @@ package internal import ( + "fmt" + + "github.com/libp2p/go-libp2p/core/peer" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/mempool/stdmap" "github.com/onflow/flow-go/network/p2p/middleware" ) type DisallowListCache struct { - c *stdmap.Backend + c *stdmap.Backend + logger zerolog.Logger } var _ middleware.DisallowListCache = (*DisallowListCache)(nil) @@ -17,16 +24,67 @@ func NewDisallowListCache() DisallowListCache { } } -func (d *DisallowListCache) IsDisallowed(peerID string) ([]middleware.DisallowListedCause, bool) { - d.c.ByID() +func (d *DisallowListCache) IsDisallowed(peerID peer.ID) ([]middleware.DisallowListedCause, bool) { + entity, exists := d.c.ByID(makeId(peerID)) + if !exists { + return nil, false + } + dEntity := mustBeDisallowListEntity(entity) + + // returning a deep copy of causes (to avoid being mutated externally). + causes := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + for cause := range dEntity.causes { + causes = append(causes, cause) + } + return causes, true } -func (d *DisallowListCache) DisallowFor(peerID string, cause middleware.DisallowListedCause) error { - //TODO implement me - panic("implement me") +func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause middleware.DisallowListedCause) error { + } -func (d *DisallowListCache) AllowFor(peerID string, cause middleware.DisallowListedCause) error { +func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { + var rErr error + adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { + dEntity := mustBeDisallowListEntity(entity) + dEntity.causes[cause] = struct{}{} + + return dEntity + }) + + if rErr != nil { + return nil, fmt.Errorf("failed to update disallow list cache entity: %w", rErr) + } + + if !adjusted { + return ErrDisallowCacheEntityNotFound, nil + } + + dEntity := mustBeDisallowListEntity(adjustedEntity) + updatedCauses := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + for cause := range dEntity.causes { + updatedCauses = append(updatedCauses, cause) + } + + return updatedCauses, nil +} + +func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) error { //TODO implement me panic("implement me") } + +// mustBeDisallowListEntity is a helper function for type assertion of the flow.Entity to disallowListCacheEntity. +// It panics if the type assertion fails. +// Args: +// - entity: the flow.Entity to be type asserted. +// Returns: +// - the disallowListCacheEntity. +func mustBeDisallowListEntity(entity flow.Entity) *disallowListCacheEntity { + dEntity, ok := entity.(*disallowListCacheEntity) + if !ok { + // this should never happen, unless there is a bug. We should crash the node and do not proceed. + panic(fmt.Errorf("disallow list cache entity is not of type disallowListCacheEntity, got: %T", entity)) + } + return dEntity +} From 14a616d59165fb7d83e1397e70e2d077084937d5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 15:31:52 -0700 Subject: [PATCH 023/815] adds disallow for --- network/p2p/middleware/internal/cache.go | 67 +++++++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go index 06e18eb2712..671084d0887 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/middleware/internal/cache.go @@ -1,6 +1,7 @@ package internal import ( + "errors" "fmt" "github.com/libp2p/go-libp2p/core/peer" @@ -11,6 +12,10 @@ import ( "github.com/onflow/flow-go/network/p2p/middleware" ) +var ( + ErrDisallowCacheEntityNotFound = errors.New("disallow list cache entity not found") +) + type DisallowListCache struct { c *stdmap.Backend logger zerolog.Logger @@ -39,25 +44,71 @@ func (d *DisallowListCache) IsDisallowed(peerID peer.ID) ([]middleware.DisallowL return causes, true } -func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause middleware.DisallowListedCause) error { +// init initializes the disallow list cache entity for the peerID. +// Args: +// - peerID: the peerID of the peer to be disallow-listed. +// Returns: +// - true if the entity is successfully added to the cache. +// - false if the entity already exists in the cache. +func (d *DisallowListCache) init(peerID peer.ID) bool { + return d.c.Add(&disallowListCacheEntity{ + peerID: peerID, + causes: make(map[middleware.DisallowListedCause]struct{}), + id: makeId(peerID), + }) +} +// DisallowFor disallow-lists a peer for a cause. +// Args: +// - peerID: the peerID of the peer to be disallow-listed. +// - cause: the cause for disallow-listing the peer. +// Returns: +// - the list of causes for which the peer is disallow-listed. +// - error if the operation fails, error is irrecoverable. +func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { + // first, we try to optimistically add the peer to the disallow list. + causes, err := d.disallowListFor(peerID, cause) + + switch { + case err == nil: + return causes, nil + case err == ErrDisallowCacheEntityNotFound: + // if the entity not exist, we initialize it and try again. + // Note: there is an edge case where the entity is initialized by another goroutine between the two calls. + // In this case, the init function is invoked twice, but it is not a problem because the underlying + // cache is thread-safe. Hence, we do not need to synchronize the two calls. In such cases, one of the + // two calls returns false, and the other call returns true. We do not care which call returns false, hence, + // we ignore the return value of the init function. + _ = d.init(peerID) + causes, err = d.disallowListFor(peerID, cause) + if err != nil { + // any error after the init is irrecoverable. + return nil, fmt.Errorf("failed to disallow list peer %s for cause %s: %w", peerID, cause, err) + } + return causes, nil + default: + return nil, fmt.Errorf("failed to disallow list peer %s for cause %s: %w", peerID, cause, err) + } } +// disallowListFor is a helper function for disallowing a peer for a cause. +// It adds the cause to the disallow list cache entity for the peerID and returns the updated list of causes for the peer. +// Args: +// - peerID: the peerID of the peer to be disallow-listed. +// - cause: the cause for disallow-listing the peer. +// Returns: +// - the updated list of causes for the peer. +// - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { - var rErr error adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { dEntity := mustBeDisallowListEntity(entity) dEntity.causes[cause] = struct{}{} - return dEntity }) - if rErr != nil { - return nil, fmt.Errorf("failed to update disallow list cache entity: %w", rErr) - } - if !adjusted { - return ErrDisallowCacheEntityNotFound, nil + // if the entity is not found in the cache, we return a benign error. + return nil, ErrDisallowCacheEntityNotFound } dEntity := mustBeDisallowListEntity(adjustedEntity) From 6a6c9d6d46c23bbfcb91f07e78ea83486db7edda Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 15:59:16 -0700 Subject: [PATCH 024/815] refactors interface and adds missing methods --- network/alsp/internal/cache.go | 2 +- network/p2p/middleware/disallowListCache.go | 36 +++++----- network/p2p/middleware/internal/cache.go | 78 +++++++++++++++++---- 3 files changed, 80 insertions(+), 36 deletions(-) diff --git a/network/alsp/internal/cache.go b/network/alsp/internal/cache.go index 2b7dc8236cd..e414b1fb3f5 100644 --- a/network/alsp/internal/cache.go +++ b/network/alsp/internal/cache.go @@ -44,7 +44,7 @@ func NewSpamRecordCache(sizeLimit uint32, logger zerolog.Logger, collector modul // the spam records of the authorized nodes. Also, this cache is keeping at most one record per origin id, so the // size of the cache must be at least the number of authorized nodes. heropool.NoEjection, - logger.With().Str("mempool", "aslp=spam-records").Logger(), + logger.With().Str("mempool", "aslp-spam-records").Logger(), collector) return &SpamRecordCache{ diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index d9b299d22ae..dfe0b35e8a6 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -18,33 +18,29 @@ const ( // DisallowListCache is an interface for a cache that keeps the list of disallow-listed peers. // It is designed to present a centralized interface for keeping track of disallow-listed peers for different reasons. type DisallowListCache interface { - // IsDisallowed returns true if the given peer is disallow-listed for any cause. + // GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. // Args: // - peerID: the peer to check. // Returns: - // - a list of causes for which the peer is disallow-listed. - // - true if the peer is disallow-listed for any cause. - // - false if the peer is not disallow-listed for any cause. - IsDisallowed(peerID peer.ID) ([]DisallowListedCause, bool) + // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, + // an empty list is returned. + GetAllDisallowedListCausesFor(peerID peer.ID) []DisallowListedCause - // DisallowFor disallow-lists the given peer for the given cause. - // If the peer is already disallow-listed for the given cause, this method is a no-op. + // DisallowFor disallow-lists a peer for a cause. // Args: - // - peerID: the peer to disallow-list. - // - cause: the cause for disallow-listing. + // - peerID: the peerID of the peer to be disallow-listed. + // - cause: the cause for disallow-listing the peer. // Returns: - // - nil if the peer was successfully disallow-listed. - // - an error if the peer could not be disallow-listed in the cache, the error is irrecoverable and the current node should - // crash. - DisallowFor(peerID peer.ID, cause DisallowListedCause) error + // - the list of causes for which the peer is disallow-listed. + // - error if the operation fails, error is irrecoverable. + DisallowFor(peerID peer.ID, cause DisallowListedCause) ([]DisallowListedCause, error) - // AllowFor allow-lists the given peer for the given cause. - // If the peer is not disallow-listed for the given cause, this method is a no-op. + // AllowFor removes a cause from the disallow list cache entity for the peerID. // Args: - // - peerID: the peer to allow-list. - // - cause: the cause for allow-listing. + // - peerID: the peerID of the peer to be allow-listed. + // - cause: the cause for allow-listing the peer. // Returns: - // - nil if the peer was successfully allow-listed. - // - an error if the peer could not be allow-listed in the cache, the error is irrecoverable and the current node should - AllowFor(peerID peer.ID, cause DisallowListedCause) error + // - the list of causes for which the peer is disallow-listed. + // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. + AllowFor(peerID peer.ID, cause DisallowListedCause) ([]DisallowListedCause, error) } diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go index 671084d0887..3597d818541 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/middleware/internal/cache.go @@ -8,6 +8,9 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" + herocache "github.com/onflow/flow-go/module/mempool/herocache/backdata" + "github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool" "github.com/onflow/flow-go/module/mempool/stdmap" "github.com/onflow/flow-go/network/p2p/middleware" ) @@ -16,35 +19,57 @@ var ( ErrDisallowCacheEntityNotFound = errors.New("disallow list cache entity not found") ) +// DisallowListCache is the disallow-list cache. It is used to keep track of the disallow-listed peers and the reasons for it. type DisallowListCache struct { - c *stdmap.Backend - logger zerolog.Logger + c *stdmap.Backend } var _ middleware.DisallowListCache = (*DisallowListCache)(nil) -func NewDisallowListCache() DisallowListCache { - return DisallowListCache{ - c: stdmap.NewBackend(), +// NewDisallowListCache creates a new disallow-list cache. The cache is backed by a stdmap.Backend. +// Args: +// - sizeLimit: the size limit of the cache, i.e., the maximum number of records that the cache can hold, recommended size is 100 * number of authorized nodes. +// - logger: the logger used by the cache. +// - collector: the metrics collector used by the cache. +// Returns: +// - *DisallowListCache, the created cache. +func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector module.HeroCacheMetrics) *DisallowListCache { + backData := herocache.NewCache(sizeLimit, + herocache.DefaultOversizeFactor, + // this cache is supposed to keep the disallow-list causes for the authorized (staked) nodes. Since the number of such nodes is + // expected to be small, we do not eject any records from the cache. The cache size must be large enough to hold all + // the spam records of the authorized nodes. Also, this cache is keeping at most one record per peer id, so the + // size of the cache must be at least the number of authorized nodes. + heropool.NoEjection, + logger.With().Str("mempool", "disallow-list-records").Logger(), + collector) + + return &DisallowListCache{ + c: stdmap.NewBackend(stdmap.WithBackData(backData)), } } -func (d *DisallowListCache) IsDisallowed(peerID peer.ID) ([]middleware.DisallowListedCause, bool) { +// GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. +// Args: +// - peerID: the peer to check. +// Returns: +// - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, +// an empty list is returned. +func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []middleware.DisallowListedCause { + causes := make([]middleware.DisallowListedCause, 0) entity, exists := d.c.ByID(makeId(peerID)) if !exists { - return nil, false + return causes } - dEntity := mustBeDisallowListEntity(entity) - // returning a deep copy of causes (to avoid being mutated externally). - causes := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + dEntity := mustBeDisallowListEntity(entity) for cause := range dEntity.causes { causes = append(causes, cause) } - return causes, true + return causes } -// init initializes the disallow list cache entity for the peerID. +// init initializes the disallow-list cache entity for the peerID. // Args: // - peerID: the peerID of the peer to be disallow-listed. // Returns: @@ -120,9 +145,32 @@ func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.Dis return updatedCauses, nil } -func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) error { - //TODO implement me - panic("implement me") +// AllowFor removes a cause from the disallow list cache entity for the peerID. +// Args: +// - peerID: the peerID of the peer to be allow-listed. +// - cause: the cause for allow-listing the peer. +// Returns: +// - the list of causes for which the peer is disallow-listed. +// - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. +func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { + adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { + dEntity := mustBeDisallowListEntity(entity) + delete(dEntity.causes, cause) + return dEntity + }) + + if !adjusted { + // if the entity is not found in the cache, we return a benign error. + return nil, ErrDisallowCacheEntityNotFound + } + + dEntity := mustBeDisallowListEntity(adjustedEntity) + // returning a deep copy of causes (to avoid being mutated externally). + causes := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + for cause := range dEntity.causes { + causes = append(causes, cause) + } + return causes, nil } // mustBeDisallowListEntity is a helper function for type assertion of the flow.Entity to disallowListCacheEntity. From e08e7cbc581c8f54c1177aaab85d19a4e333d3b2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 16:14:32 -0700 Subject: [PATCH 025/815] adds TestNewDisallowListCache --- network/p2p/middleware/internal/cache_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 network/p2p/middleware/internal/cache_test.go diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go new file mode 100644 index 00000000000..bf222de5531 --- /dev/null +++ b/network/p2p/middleware/internal/cache_test.go @@ -0,0 +1,20 @@ +package internal_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network/p2p/middleware/internal" + "github.com/onflow/flow-go/utils/unittest" +) + +// TestNewDisallowListCache tests the NewDisallowListCache function. It verifies that the returned disallowListCache +// is not nil. +func TestNewDisallowListCache(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + + // Verify that the new disallowListCache is not nil + assert.NotNil(t, disallowListCache) +} From aab01c66a3892d30c7930eaafdf467165742743e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 16:21:43 -0700 Subject: [PATCH 026/815] adds TestDisallowFor_SinglePeer --- network/p2p/middleware/internal/cache_test.go | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index bf222de5531..74d1becdbbd 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -3,9 +3,12 @@ package internal_test import ( "testing" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/middleware/internal" "github.com/onflow/flow-go/utils/unittest" ) @@ -18,3 +21,28 @@ func TestNewDisallowListCache(t *testing.T) { // Verify that the new disallowListCache is not nil assert.NotNil(t, disallowListCache) } + +// TestDisallowFor_SinglePeer tests the DisallowFor function for a single peer. It verifies that the peerID is +// disallow-listed for the given cause and that the cause is returned when the peerID is disallow-listed again. +func TestDisallowFor_SinglePeer(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + require.NotNil(t, disallowListCache) + + // disallowing a peerID for a cause when the peerID doesn't exist in the cache + causes, err := disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + + // disallowing a peerID for a cause when the peerID already exists in the cache + causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + + // disallowing a peerID for a duplicate cause + causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) +} From 53a75e8ee93641cf7118b4614970bbc2cd8241a5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 25 May 2023 17:03:54 -0700 Subject: [PATCH 027/815] adds TestDisallowFor_MultiplePeers --- network/p2p/middleware/internal/cache_test.go | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index 74d1becdbbd..756f9cf8e37 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -1,6 +1,7 @@ package internal_test import ( + "fmt" "testing" "github.com/libp2p/go-libp2p/core/peer" @@ -46,3 +47,33 @@ func TestDisallowFor_SinglePeer(t *testing.T) { require.Len(t, causes, 2) require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) } + +// TestDisallowFor_MultiplePeers tests the DisallowFor function for multiple peers. It verifies that the peerIDs are +// disallow-listed for the given cause and that the cause is returned when the peerIDs are disallow-listed again. +func TestDisallowFor_MultiplePeers(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + require.NotNil(t, disallowListCache) + + for i := 0; i <= 10; i++ { + // disallowing a peerID for a cause when the peerID doesn't exist in the cache + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + } + + for i := 0; i <= 10; i++ { + // disallowing a peerID for a cause when the peerID already exists in the cache + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + } + + for i := 0; i <= 10; i++ { + // getting the disallow-listed causes for a peerID + causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + } +} From b57e0ad94ad591dcd2b6db912c604b1983852d8a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 14:38:51 -0700 Subject: [PATCH 028/815] refactors interface of the cache --- network/p2p/middleware/disallowListCache.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index dfe0b35e8a6..9a717690db2 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -41,6 +41,5 @@ type DisallowListCache interface { // - cause: the cause for allow-listing the peer. // Returns: // - the list of causes for which the peer is disallow-listed. - // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. - AllowFor(peerID peer.ID, cause DisallowListedCause) ([]DisallowListedCause, error) + AllowFor(peerID peer.ID, cause DisallowListedCause) []DisallowListedCause } From 0eda3d055a7dab22e4024ade22fd7291f8510b3c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 14:39:12 -0700 Subject: [PATCH 029/815] refactors implementation of the cache --- network/p2p/middleware/internal/cache.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go index 3597d818541..46aa189a5cc 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/middleware/internal/cache.go @@ -152,7 +152,7 @@ func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.Dis // Returns: // - the list of causes for which the peer is disallow-listed. // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. -func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { +func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) []middleware.DisallowListedCause { adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { dEntity := mustBeDisallowListEntity(entity) delete(dEntity.causes, cause) @@ -160,8 +160,9 @@ func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowLi }) if !adjusted { - // if the entity is not found in the cache, we return a benign error. - return nil, ErrDisallowCacheEntityNotFound + // if the entity is not found in the cache, we return an empty list. + // we don't return a nil to be consistent with the case that entity is found but the list of causes is empty. + return make([]middleware.DisallowListedCause, 0) } dEntity := mustBeDisallowListEntity(adjustedEntity) @@ -170,7 +171,7 @@ func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowLi for cause := range dEntity.causes { causes = append(causes, cause) } - return causes, nil + return causes } // mustBeDisallowListEntity is a helper function for type assertion of the flow.Entity to disallowListCacheEntity. From 4d52d74aaf1b5732f241f1ce0ab890d2cdd7550c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 14:39:27 -0700 Subject: [PATCH 030/815] implements test AllowFor Single peer --- network/p2p/middleware/internal/cache_test.go | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index 756f9cf8e37..1607c7bfc5f 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -77,3 +77,67 @@ func TestDisallowFor_MultiplePeers(t *testing.T) { require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) } } + +// TestAllowFor_SinglePeer tests the AllowFor function for a single peer. +// The test verifies the behavior of cache for: +// 1. Allowing a peerID for a cause when it is not disallow-listed. +// 2. Allowing a peerID for a cause when it is disallow-listed for the same cause. +// 3. Disallowing a peerID for a cause when it is disallow-listed for a different cause. +// 4. Disallowing a peerID for a cause when it is not disallow-listed. +func TestAllowFor_SinglePeer(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + require.NotNil(t, disallowListCache) + peerID := peer.ID("peer1") + + // allowing the peerID for a cause when the peerID already exists in the cache + causes := disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 0) + + // disallowing the peerID for a cause when the peerID doesn't exist in the cache + causes, err := disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + + // getting the disallow-listed causes for the peerID + causes = disallowListCache.GetAllDisallowedListCausesFor(peerID) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + + // allowing a peerID for a cause when the peerID already exists in the cache + causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 0) + + // getting the disallow-listed causes for the peerID + causes = disallowListCache.GetAllDisallowedListCausesFor(peerID) + require.Len(t, causes, 0) + + // disallowing the peerID for a cause + causes, err = disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 1) + + // allowing the peerID for a different cause than it is disallowed when the peerID already exists in the cache + causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAdmin) // the peerID is still disallow-listed for the previous cause + + // disallowing the peerID for another cause + causes, err = disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + + // allowing the peerID for the first cause + causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) // the peerID is still disallow-listed for the previous cause + + // allowing the peerID for the second cause + causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 0) +} From c850faf4e73195b1846eb5ac501c29805c556baf Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 14:58:48 -0700 Subject: [PATCH 031/815] updates godocs --- network/p2p/middleware/internal/cache_test.go | 81 +++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index 1607c7bfc5f..a3716eea814 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -78,12 +78,18 @@ func TestDisallowFor_MultiplePeers(t *testing.T) { } } -// TestAllowFor_SinglePeer tests the AllowFor function for a single peer. -// The test verifies the behavior of cache for: -// 1. Allowing a peerID for a cause when it is not disallow-listed. -// 2. Allowing a peerID for a cause when it is disallow-listed for the same cause. -// 3. Disallowing a peerID for a cause when it is disallow-listed for a different cause. -// 4. Disallowing a peerID for a cause when it is not disallow-listed. +// TestAllowFor_SinglePeer is a unit test function to verify the behavior of DisallowListCache for a single peer. +// The test checks the following functionalities in sequence: +// 1. Allowing a peerID for a cause when the peerID already exists in the cache. +// 2. Disallowing the peerID for a cause when the peerID doesn't exist in the cache. +// 3. Getting the disallow-listed causes for the peerID. +// 4. Allowing a peerID for a cause when the peerID already exists in the cache. +// 5. Getting the disallow-listed causes for the peerID. +// 6. Disallowing the peerID for a cause. +// 7. Allowing the peerID for a different cause than it is disallowed when the peerID already exists in the cache. +// 8. Disallowing the peerID for another cause. +// 9. Allowing the peerID for the first cause. +// 10. Allowing the peerID for the second cause. func TestAllowFor_SinglePeer(t *testing.T) { disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) require.NotNil(t, disallowListCache) @@ -141,3 +147,66 @@ func TestAllowFor_SinglePeer(t *testing.T) { require.NoError(t, err) require.Len(t, causes, 0) } + +// TestAllowFor_MultiplePeers_Sequentially is a unit test function to test the behavior of DisallowListCache with multiple peers. +// The test checks the following functionalities in sequence: +// 1. Allowing a peerID for a cause when the peerID doesn't exist in the cache. +// 2. Disallowing peers for a cause. +// 3. Getting the disallow-listed causes for a peerID. +// 4. Allowing the peer ids for a cause different than the one they are disallow-listed for. +// 5. Disallowing the peer ids for a different cause. +// 6. Allowing the peer ids for the first cause. +// 7. Allowing the peer ids for the second cause. +func TestAllowFor_MultiplePeers_Sequentially(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + require.NotNil(t, disallowListCache) + + for i := 0; i <= 10; i++ { + // allowing a peerID for a cause when the peerID doesn't exist in the cache + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 0) + } + + for i := 0; i <= 10; i++ { + // disallowing peers for a cause + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + } + + for i := 0; i <= 10; i++ { + // getting the disallow-listed causes for a peerID + causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + } + + for i := 0; i <= 10; i++ { + // allowing the peer ids for a cause different than the one they are disallow-listed for + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + } + + for i := 0; i <= 10; i++ { + // disallowing the peer ids for a different cause + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + } + + for i := 0; i <= 10; i++ { + // allowing the peer ids for the first cause + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + } + + for i := 0; i <= 10; i++ { + // allowing the peer ids for the second cause + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + require.Len(t, causes, 0) + } +} From c015531830c9140bfb32ea2f972ac469d235cb0d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 15:14:19 -0700 Subject: [PATCH 032/815] adds test for multiple peers concurrently --- network/p2p/middleware/internal/cache_test.go | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index a3716eea814..c18380f3b73 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -2,7 +2,9 @@ package internal_test import ( "fmt" + "sync" "testing" + "time" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/assert" @@ -210,3 +212,138 @@ func TestAllowFor_MultiplePeers_Sequentially(t *testing.T) { require.Len(t, causes, 0) } } + +// TestAllowFor_MultiplePeers_Concurrently is a unit test function that verifies the behavior of DisallowListCache +// when multiple peerIDs are added and managed concurrently. This test is designed to confirm that DisallowListCache +// works as expected under concurrent access, an important aspect for a system dealing with multiple connections. +// +// The test runs multiple goroutines simultaneously, each handling a different peerID and performs the following +// operations in the sequence: +// 1. Allowing a peerID for a cause when the peerID doesn't exist in the cache. +// 2. Disallowing peers for a cause. +// 3. Getting the disallow-listed causes for a peerID. +// 4. Allowing the peer ids for a cause different than the one they are disallow-listed for. +// 5. Disallowing the peer ids for a different cause. +// 6. Allowing the peer ids for the first cause. +// 7. Allowing the peer ids for the second cause. +// 8. Getting the disallow-listed causes for a peerID. +// 9. Allowing a peerID for a cause when the peerID doesn't exist in the cache for a new set of peers. +func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { + disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector()) + require.NotNil(t, disallowListCache) + + var wg sync.WaitGroup + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // allowing a peerID for a cause when the peerID doesn't exist in the cache + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 0) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // disallowing peers for a cause + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + require.NoError(t, err) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // getting the disallow-listed causes for a peerID + causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // allowing the peer ids for a cause different than the one they are disallow-listed for + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // disallowing the peer ids for a different cause + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.NoError(t, err) + require.Len(t, causes, 2) + require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // allowing the peer ids for the first cause + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 1) + require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // allowing the peer ids for the second cause + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + require.Len(t, causes, 0) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 0; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // getting the disallow-listed causes for a peerID + causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + require.Len(t, causes, 0) + }(i) + } + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") + + for i := 11; i <= 20; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + // allowing a peerID for a cause when the peerID doesn't exist in the cache + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + require.Len(t, causes, 0) + }(i) + } +} From c64ddfceed3a4dd673cfb544db4d36576f0d9de3 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 15:17:12 -0700 Subject: [PATCH 033/815] adds a comment --- network/p2p/middleware/disallowListCache.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index 9a717690db2..f04665b141c 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -40,6 +40,7 @@ type DisallowListCache interface { // - peerID: the peerID of the peer to be allow-listed. // - cause: the cause for allow-listing the peer. // Returns: - // - the list of causes for which the peer is disallow-listed. + // - the list of causes for which the peer is disallow-listed. If the peer is not disallow-listed for any reason, + // an empty list is returned. AllowFor(peerID peer.ID, cause DisallowListedCause) []DisallowListedCause } From 0456c665c9f5d6b8fe6599e9f423e94be1ec3149 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 15:19:39 -0700 Subject: [PATCH 034/815] removes unused methods --- network/p2p/middleware/disallowListCache.go | 2 +- network/p2p/middleware/middleware.go | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index f04665b141c..5d6293f40d6 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -41,6 +41,6 @@ type DisallowListCache interface { // - cause: the cause for allow-listing the peer. // Returns: // - the list of causes for which the peer is disallow-listed. If the peer is not disallow-listed for any reason, - // an empty list is returned. + // an empty list is returned. AllowFor(peerID peer.ID, cause DisallowListedCause) []DisallowListedCause } diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 58a5f384585..abdaa06dc50 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -264,22 +264,6 @@ func (m *Middleware) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { return result } -// Me returns the flow identifier of this middleware -func (m *Middleware) Me() flow.Identifier { - return m.me -} - -// GetIPPort returns the ip address and port number associated with the middleware -// All errors returned from this function can be considered benign. -func (m *Middleware) GetIPPort() (string, string, error) { - ipOrHostname, port, err := m.libP2PNode.GetIPPort() - if err != nil { - return "", "", fmt.Errorf("failed to get ip and port from libP2P node: %w", err) - } - - return ipOrHostname, port, nil -} - func (m *Middleware) UpdateNodeAddresses() { m.log.Info().Msg("Updating protocol state node addresses") From b3147f4ad0f48b36d3ed147b480f46e7d44c5ced Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 26 May 2023 15:28:19 -0700 Subject: [PATCH 035/815] adds disallow list cache config --- network/p2p/middleware/middleware.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index abdaa06dc50..13eb4a41710 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -90,7 +90,6 @@ type Middleware struct { wg sync.WaitGroup libP2PNode p2p.LibP2PNode preferredUnicasts []protocols.ProtocolName - me flow.Identifier bitswapMetrics module.BitswapMetrics rootBlockID flow.Identifier validators []network.MessageValidator @@ -102,6 +101,9 @@ type Middleware struct { slashingViolationsConsumer slashing.ViolationsConsumer unicastRateLimiters *ratelimit.RateLimiters authorizedSenderValidator *validator.AuthorizedSenderValidator + // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer + // are closed and further connections are not allowed till the peer is removed from the disallow-list. + disallowListedCache DisallowListCache } type OptionFn func(*Middleware) @@ -133,6 +135,18 @@ func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { } } +// DisallowListCacheConfig is the configuration for the disallow-list cache. +// The disallow-list cache is used to temporarily disallow-list peers. +type DisallowListCacheConfig struct { + // MaxSize is the maximum number of peers that can be disallow-listed at any given time. + // When the cache is full, no further new peers can be disallow-listed. + // Recommended size is 100 * number of staked nodes. + MaxSize uint32 + + // Metrics is the HeroCache metrics collector to be used for the disallow-list cache. + Metrics module.HeroCacheMetrics +} + // NewMiddleware creates a new middleware instance // libP2PNodeFactory is the factory used to create a LibP2PNode // flowID is this node's Flow ID @@ -161,7 +175,6 @@ func NewMiddleware( // create the node entity and inject dependencies & config mw := &Middleware{ log: logger, - me: flowID, libP2PNode: libP2PNode, bitswapMetrics: bitswapMetrics, rootBlockID: rootBlockID, From c2f0fa6731a36b9ba049dd2f899d7985dcdad046 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:21:22 -0700 Subject: [PATCH 036/815] replaces middleware parameters with config --- network/p2p/middleware/middleware.go | 49 +++++++++++++++------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 13eb4a41710..a5b85fec0e5 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -147,6 +147,20 @@ type DisallowListCacheConfig struct { Metrics module.HeroCacheMetrics } +// Config is the configuration for the middleware. +type Config struct { + Logger zerolog.Logger + Libp2pNode p2p.LibP2PNode + FlowId flow.Identifier // This node's Flow ID + BitSwapMetrics module.BitswapMetrics + RootBlockID flow.Identifier + UnicastMessageTimeout time.Duration + IdTranslator p2p.IDTranslator + Codec network.Codec + SlashingViolationsConsumer slashing.ViolationsConsumer + DisallowListCacheConfig DisallowListCacheConfig +} + // NewMiddleware creates a new middleware instance // libP2PNodeFactory is the factory used to create a LibP2PNode // flowID is this node's Flow ID @@ -156,33 +170,22 @@ type DisallowListCacheConfig struct { // validators are the set of the different message validators that each inbound messages is passed through // During normal operations any error returned by Middleware.start is considered to be catastrophic // and will be thrown by the irrecoverable.SignalerContext causing the node to crash. -func NewMiddleware( - logger zerolog.Logger, - libP2PNode p2p.LibP2PNode, - flowID flow.Identifier, - bitswapMetrics module.BitswapMetrics, - rootBlockID flow.Identifier, - unicastMessageTimeout time.Duration, - idTranslator p2p.IDTranslator, - codec network.Codec, - slashingViolationsConsumer slashing.ViolationsConsumer, - opts ...OptionFn) *Middleware { - - if unicastMessageTimeout <= 0 { - unicastMessageTimeout = DefaultUnicastTimeout +func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { + if cfg.UnicastMessageTimeout <= 0 { + cfg.UnicastMessageTimeout = DefaultUnicastTimeout } // create the node entity and inject dependencies & config mw := &Middleware{ - log: logger, - libP2PNode: libP2PNode, - bitswapMetrics: bitswapMetrics, - rootBlockID: rootBlockID, - validators: DefaultValidators(logger, flowID), - unicastMessageTimeout: unicastMessageTimeout, - idTranslator: idTranslator, - codec: codec, - slashingViolationsConsumer: slashingViolationsConsumer, + log: cfg.Logger, + libP2PNode: cfg.Libp2pNode, + bitswapMetrics: cfg.BitSwapMetrics, + rootBlockID: cfg.RootBlockID, + validators: DefaultValidators(cfg.Logger, cfg.FlowId), + unicastMessageTimeout: cfg.UnicastMessageTimeout, + idTranslator: cfg.IdTranslator, + codec: cfg.Codec, + slashingViolationsConsumer: cfg.SlashingViolationsConsumer, unicastRateLimiters: ratelimit.NoopRateLimiters(), } From 56ba9be04e0f4a763e17734c532afd12be667b47 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:47:23 -0700 Subject: [PATCH 037/815] adds networking disallow list cache metrics factory --- module/metrics/herocache.go | 15 +++++++++++++++ module/metrics/labels.go | 1 + 2 files changed, 16 insertions(+) diff --git a/module/metrics/herocache.go b/module/metrics/herocache.go index dcc941cfebc..de77da682d0 100644 --- a/module/metrics/herocache.go +++ b/module/metrics/herocache.go @@ -72,6 +72,21 @@ func NetworkReceiveCacheMetricsFactory(f HeroCacheMetricsFactory, networkType ne return f(namespaceNetwork, r) } +// DisallowListCacheMetricsFactory is the factory method for creating a new HeroCacheCollector for the disallow list cache. +// The disallow-list cache is used to keep track of peers that are disallow-listed and the reasons for it. +// Args: +// - f: the HeroCacheMetricsFactory to create the collector +// - networkingType: the networking type of the cache, i.e., whether it is used for the public or the private network +// Returns: +// - a HeroCacheMetrics for the disallow list cache +func DisallowListCacheMetricsFactory(f HeroCacheMetricsFactory, networkingType network.NetworkingType) module.HeroCacheMetrics { + r := ResourceNetworkingDisallowListCache + if networkingType == network.PublicNetwork { + r = PrependPublicPrefix(r) + } + return f(namespaceNetwork, r) +} + func NetworkDnsTxtCacheMetricsFactory(registrar prometheus.Registerer) *HeroCacheCollector { return NewHeroCacheCollector(namespaceNetwork, ResourceNetworkingDnsTxtCache, registrar) } diff --git a/module/metrics/labels.go b/module/metrics/labels.go index 87e3ab1ce41..e567e55a664 100644 --- a/module/metrics/labels.go +++ b/module/metrics/labels.go @@ -85,6 +85,7 @@ const ( ResourceNetworkingDnsIpCache = "networking_dns_ip_cache" // networking layer ResourceNetworkingDnsTxtCache = "networking_dns_txt_cache" // networking layer ResourceNetworkingDisallowListNotificationQueue = "networking_disallow_list_notification_queue" + ResourceNetworkingDisallowListCache = "networking_disallow_list_cache" ResourceNetworkingRpcInspectorNotificationQueue = "networking_rpc_inspector_notification_queue" ResourceNetworkingRpcValidationInspectorQueue = "networking_rpc_validation_inspector_queue" ResourceNetworkingRpcMetricsObserverInspectorQueue = "networking_rpc_metrics_observer_inspector_queue" From 746e5654569f091ae24ef35138d8be0b26796223 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:54:25 -0700 Subject: [PATCH 038/815] adds default middleware size --- network/p2p/middleware/middleware.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index a5b85fec0e5..944d2491319 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -62,6 +62,13 @@ const ( // LargeMsgUnicastTimeout is the maximum time to wait for a unicast request to complete for large message size LargeMsgUnicastTimeout = 1000 * time.Second + + // DisallowListCacheSize is the maximum number of peers that can be disallow-listed at a time. The recommended + // size is 100 * number of staked nodes. Note that when the cache is full, there is no eviction policy and + // disallow-listing a new peer will fail. Hence, the cache size should be set to a value that is large enough + // to accommodate all the peers that can be disallow-listed at a time. Also, note that this cache is only taking + // the staked (authorized) peers. Hence, Sybil attacks are not possible. + DisallowListCacheSize = 100 * 1000 ) var ( @@ -158,7 +165,7 @@ type Config struct { IdTranslator p2p.IDTranslator Codec network.Codec SlashingViolationsConsumer slashing.ViolationsConsumer - DisallowListCacheConfig DisallowListCacheConfig + DisallowListCacheConfig *DisallowListCacheConfig } // NewMiddleware creates a new middleware instance From 66af1fc6f8ebcaf03bac6e53811cff959832cbb8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:54:46 -0700 Subject: [PATCH 039/815] adds default middleware size to cache --- cmd/node_builder.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 0408f583cc2..ea977a44b59 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -199,6 +199,8 @@ type NetworkConfig struct { UnicastCreateStreamRetryDelay time.Duration // size of the queue for notifications about new peers in the disallow list. DisallowListNotificationCacheSize uint32 + // size of the cache keeping the status of disallow-listed peers. Recommended to be 100 * number of authorized nodes. + DisallowListCacheSize uint32 // UnicastRateLimitersConfig configuration for all unicast rate limiters. UnicastRateLimitersConfig *UnicastRateLimitersConfig AlspConfig *AlspConfig @@ -333,6 +335,7 @@ func DefaultBaseConfig() *BaseConfig { ConnectionManagerConfig: connection.DefaultConnManagerConfig(), NetworkConnectionPruning: connection.PruningEnabled, DisallowListNotificationCacheSize: distributor.DefaultDisallowListNotificationQueueCacheSize, + DisallowListCacheSize: middleware.DisallowListCacheSize, AlspConfig: &AlspConfig{ SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, From 8e4062bdc704d48338c038dbb84069b0fca5f2f0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:56:12 -0700 Subject: [PATCH 040/815] refactors middleware for access node builder --- .../node_builder/access_node_builder.go | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 39656e03130..f3c6bc52659 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1227,17 +1227,21 @@ func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, validators ...network.MessageValidator, ) network.Middleware { logger := builder.Logger.With().Bool("staked", false).Logger() - slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(logger, networkMetrics) - mw := middleware.NewMiddleware( - logger, - libp2pNode, - nodeID, - builder.Metrics.Bitswap, - builder.SporkID, - middleware.DefaultUnicastTimeout, - builder.IDTranslator, - builder.CodecFactory(), - slashingViolationsConsumer, + mw := middleware.NewMiddleware(&middleware.Config{ + Logger: logger, + Libp2pNode: libp2pNode, + FlowId: nodeID, + BitSwapMetrics: builder.Metrics.Bitswap, + RootBlockID: builder.SporkID, + UnicastMessageTimeout: middleware.DefaultUnicastTimeout, + IdTranslator: builder.IDTranslator, + Codec: builder.CodecFactory(), + SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(logger, networkMetrics), + DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + }, + }, middleware.WithMessageValidators(validators...), // use default identifier provider ) builder.NodeDisallowListDistributor.AddConsumer(mw) From b2e4e3ee043bfeb022983319ecaf24e851f24968 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 10:59:18 -0700 Subject: [PATCH 041/815] adds validate to nuddkeware config --- network/p2p/middleware/middleware.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 944d2491319..001694830a1 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -168,6 +168,17 @@ type Config struct { DisallowListCacheConfig *DisallowListCacheConfig } +// Validate validates the configuration, and sets default values for any missing fields. +func (cfg *Config) Validate() { + if cfg.UnicastMessageTimeout <= 0 { + cfg.UnicastMessageTimeout = DefaultUnicastTimeout + } + + if cfg.DisallowListCacheConfig.MaxSize == uint32(0) { + cfg.DisallowListCacheConfig.MaxSize = DisallowListCacheSize + } +} + // NewMiddleware creates a new middleware instance // libP2PNodeFactory is the factory used to create a LibP2PNode // flowID is this node's Flow ID @@ -178,9 +189,7 @@ type Config struct { // During normal operations any error returned by Middleware.start is considered to be catastrophic // and will be thrown by the irrecoverable.SignalerContext causing the node to crash. func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { - if cfg.UnicastMessageTimeout <= 0 { - cfg.UnicastMessageTimeout = DefaultUnicastTimeout - } + cfg.Validate() // create the node entity and inject dependencies & config mw := &Middleware{ From 31a3e1938bbc76378692d2da2e101b61cc20137b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 11:02:05 -0700 Subject: [PATCH 042/815] refactors middleware initialization for observer --- cmd/observer/node_builder/observer_builder.go | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 666d30f0f9f..e57041dc3e2 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -1078,15 +1078,21 @@ func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, validators ...network.MessageValidator, ) network.Middleware { slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network) - mw := middleware.NewMiddleware( - builder.Logger, - libp2pNode, nodeID, - builder.Metrics.Bitswap, - builder.SporkID, - middleware.DefaultUnicastTimeout, - builder.IDTranslator, - builder.CodecFactory(), - slashingViolationsConsumer, + mw := middleware.NewMiddleware(&middleware.Config{ + Logger: builder.Logger, + Libp2pNode: libp2pNode, + FlowId: nodeID, + BitSwapMetrics: builder.Metrics.Bitswap, + RootBlockID: builder.SporkID, + UnicastMessageTimeout: middleware.DefaultUnicastTimeout, + IdTranslator: builder.IDTranslator, + Codec: builder.CodecFactory(), + SlashingViolationsConsumer: slashingViolationsConsumer, + DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + }, + }, middleware.WithMessageValidators(validators...), // use default identifier provider ) builder.NodeDisallowListDistributor.AddConsumer(mw) From 643ea52a8a630958bea7d3dce9d0e6341705237b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 14:56:24 -0700 Subject: [PATCH 043/815] refactors middleware initialization in scaffold --- cmd/scaffold.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index f40ff7ea5f8..71264866cd2 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -471,18 +471,21 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory(node *NodeConfig, if len(peerManagerFilters) > 0 { mwOpts = append(mwOpts, middleware.WithPeerManagerFilters(peerManagerFilters)) } - - slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network) - mw := middleware.NewMiddleware( - fnb.Logger, - fnb.LibP2PNode, - fnb.Me.NodeID(), - fnb.Metrics.Bitswap, - fnb.SporkID, - fnb.BaseConfig.UnicastMessageTimeout, - fnb.IDTranslator, - fnb.CodecFactory(), - slashingViolationsConsumer, + mw := middleware.NewMiddleware(&middleware.Config{ + Logger: fnb.Logger, + Libp2pNode: fnb.LibP2PNode, + FlowId: fnb.Me.NodeID(), + BitSwapMetrics: fnb.Metrics.Bitswap, + RootBlockID: fnb.SporkID, + UnicastMessageTimeout: fnb.BaseConfig.UnicastMessageTimeout, + IdTranslator: fnb.IDTranslator, + Codec: fnb.CodecFactory(), + SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network), + DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ + MaxSize: fnb.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(fnb.HeroCacheMetricsFactory(), network.PrivateNetwork), + }, + }, mwOpts...) fnb.NodeDisallowListDistributor.AddConsumer(mw) fnb.Middleware = mw From e960230f1567b04308a943730f6b6bb8e30a2b02 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 14:57:15 -0700 Subject: [PATCH 044/815] refactors middleware initialization in follower --- follower/follower_builder.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 7860b408882..902580850e9 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -744,17 +744,21 @@ func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, libp2pNode p2p.LibP2PNode, validators ...network.MessageValidator, ) network.Middleware { - slashingViolationsConsumer := slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network) - mw := middleware.NewMiddleware( - builder.Logger, - libp2pNode, - nodeID, - builder.Metrics.Bitswap, - builder.SporkID, - middleware.DefaultUnicastTimeout, - builder.IDTranslator, - builder.CodecFactory(), - slashingViolationsConsumer, + mw := middleware.NewMiddleware(&middleware.Config{ + Logger: builder.Logger, + Libp2pNode: libp2pNode, + FlowId: nodeID, + BitSwapMetrics: builder.Metrics.Bitswap, + RootBlockID: builder.SporkID, + UnicastMessageTimeout: middleware.DefaultUnicastTimeout, + IdTranslator: builder.IDTranslator, + Codec: builder.CodecFactory(), + SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network), + DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + }, + }, middleware.WithMessageValidators(validators...), // use default identifier provider ) From ab9cce5d0b3afc3757505c1211335ec333bf231b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 15:14:28 -0700 Subject: [PATCH 045/815] adds interface for disallow list oracle --- network/middleware.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/network/middleware.go b/network/middleware.go index 7bc600fbc8f..c66883dec3c 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -57,6 +57,23 @@ type Middleware interface { IsConnected(nodeID flow.Identifier) (bool, error) } +// DisallowedListOracle represents the interface that is exposed to the lower-level networking primitives (e.g. libp2p), +// which allows them to check if a given peer ID is disallowed (by Flow protocol) from connecting to this node. +// Disallow-listing is considered a temporary measure to prevent malicious nodes from connecting to the network. Hence, +// the disallow-list status of a peer ID should not be treated as a permanent state. +type DisallowedListOracle interface { + // IsDisallowed returns true if the given peer ID is disallowed from connecting to this node. + // This function should be called by the lower-level networking primitives (e.g. libp2p) before establishing a + // connection with a peer. + // Implementations of this function should be thread-safe. + // Args: + // peer.ID: the peer ID of the node that is attempting to connect to (or be connected by) this node. + // Returns: + // bool: true if the given peer ID is disallowed from connecting to this node. + // false otherwise. + IsDisallowed(peer.ID) bool +} + // Overlay represents the interface that middleware uses to interact with the // overlay network layer. type Overlay interface { From 99d3daa5bc958416adcec8795e1374240b8a2757 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:13:32 -0700 Subject: [PATCH 046/815] moves all disallow listing components to network root package --- network/disallow.go | 45 +++++++++ network/p2p/consumers.go | 15 +-- network/p2p/distributor/disallow_list.go | 13 +-- network/p2p/distributor/disallow_list_test.go | 20 ++-- network/p2p/middleware/disallowListCache.go | 21 ++-- network/p2p/middleware/internal/cache.go | 19 ++-- .../p2p/middleware/internal/cacheEntity.go | 4 +- network/p2p/middleware/internal/cache_test.go | 96 +++++++++---------- network/p2p/middleware/middleware.go | 24 ++++- .../disallow_list_notification_consumer.go | 2 +- .../disallow_list_notification_distributor.go | 5 +- 11 files changed, 152 insertions(+), 112 deletions(-) create mode 100644 network/disallow.go diff --git a/network/disallow.go b/network/disallow.go new file mode 100644 index 00000000000..9b182a0f5c1 --- /dev/null +++ b/network/disallow.go @@ -0,0 +1,45 @@ +package network + +import ( + "github.com/onflow/flow-go/model/flow" +) + +// DisallowListedCause is a type representing the cause of disallow listing. A remote node may be disallow-listed by the +// current node for a variety of reasons. This type is used to represent the reason for disallow-listing, so that if +// a node is disallow-listed for reasons X and Y, allow-listing it back for reason X does not automatically allow-list +// it for reason Y. +type DisallowListedCause string + +const ( + // DisallowListedCauseAdmin is the cause of disallow-listing a node by an admin command. + DisallowListedCauseAdmin DisallowListedCause = "disallow-listed-admin" + // DisallowListedCauseAlsp is the cause of disallow-listing a node by the ALSP (Application Layer Spam Prevention). + DisallowListedCauseAlsp DisallowListedCause = "disallow-listed-alsp" +) + +// DisallowListingUpdate is a notification of a new disallow list update, it contains a list of Flow identities that +// are now disallow listed for a specific reason. +type DisallowListingUpdate struct { + FlowIds flow.IdentifierList + Cause DisallowListedCause +} + +// AllowListingUpdate is a notification of a new allow list update, it contains a list of Flow identities that +// are now allow listed for a specific reason, i.e., their disallow list entry for that reason is removed. +type AllowListingUpdate struct { + FlowIds flow.IdentifierList + Cause DisallowListedCause +} + +// DisallowListNotificationConsumer is an interface for consuming disallow/allow list update notifications. +type DisallowListNotificationConsumer interface { + // OnDisallowListNotification is called when a new disallow list update notification is distributed. + // Any error on consuming event must handle internally. + // The implementation must be concurrency safe. + OnDisallowListNotification(*DisallowListingUpdate) + + // OnAllowListNotification is called when a new allow list update notification is distributed. + // Any error on consuming event must handle internally. + // The implementation must be concurrency safe. + OnAllowListNotification(*AllowListingUpdate) +} diff --git a/network/p2p/consumers.go b/network/p2p/consumers.go index afe7b9e6efd..3798915e50a 100644 --- a/network/p2p/consumers.go +++ b/network/p2p/consumers.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/network" ) // DisallowListConsumer consumes notifications from the cache.NodeBlocklistWrapper whenever the block list is updated. @@ -39,18 +40,6 @@ func ControlMessageTypes() []ControlMessageType { return []ControlMessageType{CtrlMsgIHave, CtrlMsgIWant, CtrlMsgGraft, CtrlMsgPrune} } -// DisallowListUpdateNotification is the event that is submitted to the distributor when the disallow list is updated. -type DisallowListUpdateNotification struct { - DisallowList flow.IdentifierList -} - -type DisallowListNotificationConsumer interface { - // OnDisallowListNotification is called when a new disallow list update notification is distributed. - // Any error on consuming event must handle internally. - // The implementation must be concurrency safe, but can be blocking. - OnDisallowListNotification(*DisallowListUpdateNotification) -} - type DisallowListNotificationDistributor interface { component.Component // DistributeBlockListNotification distributes the event to all the consumers. @@ -61,7 +50,7 @@ type DisallowListNotificationDistributor interface { // AddConsumer adds a consumer to the distributor. The consumer will be called the distributor distributes a new event. // AddConsumer must be concurrency safe. Once a consumer is added, it must be called for all future events. // There is no guarantee that the consumer will be called for events that were already received by the distributor. - AddConsumer(DisallowListNotificationConsumer) + AddConsumer(network.DisallowListNotificationConsumer) } // GossipSubInspectorNotifDistributor is the interface for the distributor that distributes gossip sub inspector notifications. diff --git a/network/p2p/distributor/disallow_list.go b/network/p2p/distributor/disallow_list.go index 848baa925bb..20b1fbd5a6a 100644 --- a/network/p2p/distributor/disallow_list.go +++ b/network/p2p/distributor/disallow_list.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/mempool/queue" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p" ) @@ -27,8 +28,8 @@ type DisallowListNotificationDistributor struct { logger zerolog.Logger consumerLock sync.RWMutex // protects the consumer field from concurrent updates - consumers []p2p.DisallowListNotificationConsumer - workerPool *worker.Pool[*p2p.DisallowListUpdateNotification] + consumers []network.DisallowListNotificationConsumer + workerPool *worker.Pool[*p2p.RemoteNodesAllowListingUpdate] } var _ p2p.DisallowListNotificationDistributor = (*DisallowListNotificationDistributor)(nil) @@ -58,7 +59,7 @@ func NewDisallowListConsumer(logger zerolog.Logger, store engine.MessageStore) * logger: lg, } - pool := worker.NewWorkerPoolBuilder[*p2p.DisallowListUpdateNotification]( + pool := worker.NewWorkerPoolBuilder[*p2p.RemoteNodesAllowListingUpdate]( lg, store, d.distribute).Build() @@ -77,7 +78,7 @@ func NewDisallowListConsumer(logger zerolog.Logger, store engine.MessageStore) * // distribute is called by the workers to process the event. It calls the OnDisallowListNotification method on all registered // consumers. // It does not return an error because the event is already in the store, so it will be retried. -func (d *DisallowListNotificationDistributor) distribute(notification *p2p.DisallowListUpdateNotification) error { +func (d *DisallowListNotificationDistributor) distribute(notification *p2p.RemoteNodesAllowListingUpdate) error { d.consumerLock.RLock() defer d.consumerLock.RUnlock() @@ -91,7 +92,7 @@ func (d *DisallowListNotificationDistributor) distribute(notification *p2p.Disal // AddConsumer adds a consumer to the distributor. The consumer will be called the distributor distributes a new event. // AddConsumer must be concurrency safe. Once a consumer is added, it must be called for all future events. // There is no guarantee that the consumer will be called for events that were already received by the distributor. -func (d *DisallowListNotificationDistributor) AddConsumer(consumer p2p.DisallowListNotificationConsumer) { +func (d *DisallowListNotificationDistributor) AddConsumer(consumer network.DisallowListNotificationConsumer) { d.consumerLock.Lock() defer d.consumerLock.Unlock() @@ -104,7 +105,7 @@ func (d *DisallowListNotificationDistributor) AddConsumer(consumer p2p.DisallowL // If the worker pool is full, the event will be dropped and a warning will be logged. // This implementation returns no error. func (d *DisallowListNotificationDistributor) DistributeBlockListNotification(disallowList flow.IdentifierList) error { - ok := d.workerPool.Submit(&p2p.DisallowListUpdateNotification{DisallowList: disallowList}) + ok := d.workerPool.Submit(&p2p.RemoteNodesAllowListingUpdate{FlowIds: disallowList}) if !ok { // we use a queue to buffer the events, so this may happen if the queue is full or the event is duplicate. In this case, we log a warning. d.logger.Warn().Msg("node disallow list update notification queue is full or the event is duplicate, dropping event") diff --git a/network/p2p/distributor/disallow_list_test.go b/network/p2p/distributor/disallow_list_test.go index 39cf9532f46..4d5a668eb09 100644 --- a/network/p2p/distributor/disallow_list_test.go +++ b/network/p2p/distributor/disallow_list_test.go @@ -36,13 +36,13 @@ func TestDisallowListNotificationDistributor(t *testing.T) { c1Done.Add(len(tt)) c1Seen := unittest.NewProtectedMap[flow.Identifier, struct{}]() c1.On("OnDisallowListNotification", mock.Anything).Run(func(args mock.Arguments) { - n, ok := args.Get(0).(*p2p.DisallowListUpdateNotification) + n, ok := args.Get(0).(*p2p.RemoteNodesAllowListingUpdate) require.True(t, ok) require.Contains(t, tt, n) // ensure consumer see each peer once - hash := flow.MerkleRoot(n.DisallowList...) + hash := flow.MerkleRoot(n.FlowIds...) require.False(t, c1Seen.Has(hash)) c1Seen.Add(hash, struct{}{}) @@ -53,13 +53,13 @@ func TestDisallowListNotificationDistributor(t *testing.T) { c2Done.Add(len(tt)) c2Seen := unittest.NewProtectedMap[flow.Identifier, struct{}]() c2.On("OnDisallowListNotification", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - n, ok := args.Get(0).(*p2p.DisallowListUpdateNotification) + n, ok := args.Get(0).(*p2p.RemoteNodesAllowListingUpdate) require.True(t, ok) require.Contains(t, tt, n) // ensure consumer see each peer once - hash := flow.MerkleRoot(n.DisallowList...) + hash := flow.MerkleRoot(n.FlowIds...) require.False(t, c2Seen.Has(hash)) c2Seen.Add(hash, struct{}{}) @@ -75,7 +75,7 @@ func TestDisallowListNotificationDistributor(t *testing.T) { for i := 0; i < len(tt); i++ { go func(i int) { - require.NoError(t, d.DistributeBlockListNotification(tt[i].DisallowList)) + require.NoError(t, d.DistributeBlockListNotification(tt[i].FlowIds)) }(i) } @@ -85,16 +85,16 @@ func TestDisallowListNotificationDistributor(t *testing.T) { unittest.RequireCloseBefore(t, d.Done(), 100*time.Millisecond, "could not stop distributor") } -func disallowListUpdateNotificationsFixture(n int) []*p2p.DisallowListUpdateNotification { - tt := make([]*p2p.DisallowListUpdateNotification, n) +func disallowListUpdateNotificationsFixture(n int) []*p2p.RemoteNodesAllowListingUpdate { + tt := make([]*p2p.RemoteNodesAllowListingUpdate, n) for i := 0; i < n; i++ { tt[i] = disallowListUpdateNotificationFixture() } return tt } -func disallowListUpdateNotificationFixture() *p2p.DisallowListUpdateNotification { - return &p2p.DisallowListUpdateNotification{ - DisallowList: unittest.IdentifierListFixture(rand.Int()%100 + 1), +func disallowListUpdateNotificationFixture() *p2p.RemoteNodesAllowListingUpdate { + return &p2p.RemoteNodesAllowListingUpdate{ + FlowIds: unittest.IdentifierListFixture(rand.Int()%100 + 1), } } diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/middleware/disallowListCache.go index 5d6293f40d6..5f8ebcc0831 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/middleware/disallowListCache.go @@ -1,18 +1,9 @@ package middleware -import "github.com/libp2p/go-libp2p/core/peer" +import ( + "github.com/libp2p/go-libp2p/core/peer" -// DisallowListedCause is a type representing the cause of disallow listing. A remote node may be disallow-listed by the -// current node for a variety of reasons. This type is used to represent the reason for disallow-listing, so that if -// a node is disallow-listed for reasons X and Y, allow-listing it back for reason X does not automatically allow-list -// it for reason Y. -type DisallowListedCause string - -const ( - // DisallowListedCauseAdmin is the cause of disallow-listing a node by an admin command. - DisallowListedCauseAdmin DisallowListedCause = "disallow-listed-admin" - // DisallowListedCauseAlsp is the cause of disallow-listing a node by the ALSP (Application Layer Spam Prevention). - DisallowListedCauseAlsp DisallowListedCause = "disallow-listed-alsp" + "github.com/onflow/flow-go/network" ) // DisallowListCache is an interface for a cache that keeps the list of disallow-listed peers. @@ -24,7 +15,7 @@ type DisallowListCache interface { // Returns: // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, // an empty list is returned. - GetAllDisallowedListCausesFor(peerID peer.ID) []DisallowListedCause + GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause // DisallowFor disallow-lists a peer for a cause. // Args: @@ -33,7 +24,7 @@ type DisallowListCache interface { // Returns: // - the list of causes for which the peer is disallow-listed. // - error if the operation fails, error is irrecoverable. - DisallowFor(peerID peer.ID, cause DisallowListedCause) ([]DisallowListedCause, error) + DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) // AllowFor removes a cause from the disallow list cache entity for the peerID. // Args: @@ -42,5 +33,5 @@ type DisallowListCache interface { // Returns: // - the list of causes for which the peer is disallow-listed. If the peer is not disallow-listed for any reason, // an empty list is returned. - AllowFor(peerID peer.ID, cause DisallowListedCause) []DisallowListedCause + AllowFor(peerID peer.ID, cause network.DisallowListedCause) []network.DisallowListedCause } diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/middleware/internal/cache.go index 46aa189a5cc..a3ea1ab198b 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/middleware/internal/cache.go @@ -12,6 +12,7 @@ import ( herocache "github.com/onflow/flow-go/module/mempool/herocache/backdata" "github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool" "github.com/onflow/flow-go/module/mempool/stdmap" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p/middleware" ) @@ -55,8 +56,8 @@ func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector mod // Returns: // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, // an empty list is returned. -func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []middleware.DisallowListedCause { - causes := make([]middleware.DisallowListedCause, 0) +func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause { + causes := make([]network.DisallowListedCause, 0) entity, exists := d.c.ByID(makeId(peerID)) if !exists { return causes @@ -78,7 +79,7 @@ func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []midd func (d *DisallowListCache) init(peerID peer.ID) bool { return d.c.Add(&disallowListCacheEntity{ peerID: peerID, - causes: make(map[middleware.DisallowListedCause]struct{}), + causes: make(map[network.DisallowListedCause]struct{}), id: makeId(peerID), }) } @@ -90,7 +91,7 @@ func (d *DisallowListCache) init(peerID peer.ID) bool { // Returns: // - the list of causes for which the peer is disallow-listed. // - error if the operation fails, error is irrecoverable. -func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { +func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) { // first, we try to optimistically add the peer to the disallow list. causes, err := d.disallowListFor(peerID, cause) @@ -124,7 +125,7 @@ func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause middleware.Disallo // Returns: // - the updated list of causes for the peer. // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. -func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.DisallowListedCause) ([]middleware.DisallowListedCause, error) { +func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) { adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { dEntity := mustBeDisallowListEntity(entity) dEntity.causes[cause] = struct{}{} @@ -137,7 +138,7 @@ func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.Dis } dEntity := mustBeDisallowListEntity(adjustedEntity) - updatedCauses := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + updatedCauses := make([]network.DisallowListedCause, 0, len(dEntity.causes)) for cause := range dEntity.causes { updatedCauses = append(updatedCauses, cause) } @@ -152,7 +153,7 @@ func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause middleware.Dis // Returns: // - the list of causes for which the peer is disallow-listed. // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error. -func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowListedCause) []middleware.DisallowListedCause { +func (d *DisallowListCache) AllowFor(peerID peer.ID, cause network.DisallowListedCause) []network.DisallowListedCause { adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity { dEntity := mustBeDisallowListEntity(entity) delete(dEntity.causes, cause) @@ -162,12 +163,12 @@ func (d *DisallowListCache) AllowFor(peerID peer.ID, cause middleware.DisallowLi if !adjusted { // if the entity is not found in the cache, we return an empty list. // we don't return a nil to be consistent with the case that entity is found but the list of causes is empty. - return make([]middleware.DisallowListedCause, 0) + return make([]network.DisallowListedCause, 0) } dEntity := mustBeDisallowListEntity(adjustedEntity) // returning a deep copy of causes (to avoid being mutated externally). - causes := make([]middleware.DisallowListedCause, 0, len(dEntity.causes)) + causes := make([]network.DisallowListedCause, 0, len(dEntity.causes)) for cause := range dEntity.causes { causes = append(causes, cause) } diff --git a/network/p2p/middleware/internal/cacheEntity.go b/network/p2p/middleware/internal/cacheEntity.go index 9d8353dd912..e55b0d127b5 100644 --- a/network/p2p/middleware/internal/cacheEntity.go +++ b/network/p2p/middleware/internal/cacheEntity.go @@ -4,7 +4,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network" ) // disallowListCacheEntity is the model data type for the disallow list cache. @@ -13,7 +13,7 @@ import ( // This means that the entities are deduplicated by their peerID. type disallowListCacheEntity struct { peerID peer.ID - causes map[middleware.DisallowListedCause]struct{} + causes map[network.DisallowListedCause]struct{} // id is the hash of the peerID which is used as the key for storing the entity in the cache. // we cache it internally to avoid hashing the peerID multiple times. id flow.Identifier diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/middleware/internal/cache_test.go index c18380f3b73..2de5522d32d 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/middleware/internal/cache_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p/middleware/internal" "github.com/onflow/flow-go/utils/unittest" ) @@ -32,22 +32,22 @@ func TestDisallowFor_SinglePeer(t *testing.T) { require.NotNil(t, disallowListCache) // disallowing a peerID for a cause when the peerID doesn't exist in the cache - causes, err := disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAdmin) + causes, err := disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + require.Contains(t, causes, network.DisallowListedCauseAdmin) // disallowing a peerID for a cause when the peerID already exists in the cache - causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAlsp) + causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) // disallowing a peerID for a duplicate cause - causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), middleware.DisallowListedCauseAdmin) + causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) } // TestDisallowFor_MultiplePeers tests the DisallowFor function for multiple peers. It verifies that the peerIDs are @@ -58,25 +58,25 @@ func TestDisallowFor_MultiplePeers(t *testing.T) { for i := 0; i <= 10; i++ { // disallowing a peerID for a cause when the peerID doesn't exist in the cache - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + require.Contains(t, causes, network.DisallowListedCauseAdmin) } for i := 0; i <= 10; i++ { // disallowing a peerID for a cause when the peerID already exists in the cache - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) } for i := 0; i <= 10; i++ { // getting the disallow-listed causes for a peerID causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) } } @@ -98,22 +98,22 @@ func TestAllowFor_SinglePeer(t *testing.T) { peerID := peer.ID("peer1") // allowing the peerID for a cause when the peerID already exists in the cache - causes := disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin) require.Len(t, causes, 0) // disallowing the peerID for a cause when the peerID doesn't exist in the cache - causes, err := disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAdmin) + causes, err := disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + require.Contains(t, causes, network.DisallowListedCauseAdmin) // getting the disallow-listed causes for the peerID causes = disallowListCache.GetAllDisallowedListCausesFor(peerID) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAdmin) + require.Contains(t, causes, network.DisallowListedCauseAdmin) // allowing a peerID for a cause when the peerID already exists in the cache - causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 0) @@ -122,30 +122,30 @@ func TestAllowFor_SinglePeer(t *testing.T) { require.Len(t, causes, 0) // disallowing the peerID for a cause - causes, err = disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAdmin) + causes, err = disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 1) // allowing the peerID for a different cause than it is disallowed when the peerID already exists in the cache - causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAlsp) + causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAdmin) // the peerID is still disallow-listed for the previous cause + require.Contains(t, causes, network.DisallowListedCauseAdmin) // the peerID is still disallow-listed for the previous cause // disallowing the peerID for another cause - causes, err = disallowListCache.DisallowFor(peerID, middleware.DisallowListedCauseAlsp) + causes, err = disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) // allowing the peerID for the first cause - causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAdmin) + causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) // the peerID is still disallow-listed for the previous cause + require.Contains(t, causes, network.DisallowListedCauseAlsp) // the peerID is still disallow-listed for the previous cause // allowing the peerID for the second cause - causes = disallowListCache.AllowFor(peerID, middleware.DisallowListedCauseAlsp) + causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 0) } @@ -165,50 +165,50 @@ func TestAllowFor_MultiplePeers_Sequentially(t *testing.T) { for i := 0; i <= 10; i++ { // allowing a peerID for a cause when the peerID doesn't exist in the cache - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 0) } for i := 0; i <= 10; i++ { // disallowing peers for a cause - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) } for i := 0; i <= 10; i++ { // getting the disallow-listed causes for a peerID causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) } for i := 0; i <= 10; i++ { // allowing the peer ids for a cause different than the one they are disallow-listed for - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) } for i := 0; i <= 10; i++ { // disallowing the peer ids for a different cause - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) } for i := 0; i <= 10; i++ { // allowing the peer ids for the first cause - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) } for i := 0; i <= 10; i++ { // allowing the peer ids for the second cause - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp) require.Len(t, causes, 0) } } @@ -239,7 +239,7 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // allowing a peerID for a cause when the peerID doesn't exist in the cache - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 0) }(i) } @@ -251,10 +251,10 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // disallowing peers for a cause - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp) require.NoError(t, err) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) }(i) } unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") @@ -267,7 +267,7 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { // getting the disallow-listed causes for a peerID causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) }(i) } unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") @@ -278,9 +278,9 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // allowing the peer ids for a cause different than the one they are disallow-listed for - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) }(i) } unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") @@ -291,10 +291,10 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // disallowing the peer ids for a different cause - causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.NoError(t, err) require.Len(t, causes, 2) - require.ElementsMatch(t, causes, []middleware.DisallowListedCause{middleware.DisallowListedCauseAdmin, middleware.DisallowListedCauseAlsp}) + require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) }(i) } unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") @@ -305,9 +305,9 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // allowing the peer ids for the first cause - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 1) - require.Contains(t, causes, middleware.DisallowListedCauseAlsp) + require.Contains(t, causes, network.DisallowListedCauseAlsp) }(i) } unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") @@ -318,7 +318,7 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // allowing the peer ids for the second cause - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAlsp) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp) require.Len(t, causes, 0) }(i) } @@ -342,7 +342,7 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // allowing a peerID for a cause when the peerID doesn't exist in the cache - causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), middleware.DisallowListedCauseAdmin) + causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin) require.Len(t, causes, 0) }(i) } diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 001694830a1..248efacd7d6 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -72,8 +72,8 @@ const ( ) var ( - _ network.Middleware = (*Middleware)(nil) - _ p2p.DisallowListNotificationConsumer = (*Middleware)(nil) + _ network.Middleware = (*Middleware)(nil) + _ network.DisallowListNotificationConsumer = (*Middleware)(nil) // ErrUnicastMsgWithoutSub error is provided to the slashing violations consumer in the case where // the middleware receives a message via unicast but does not have a corresponding subscription for @@ -362,9 +362,23 @@ func (m *Middleware) authorizedPeers() peer.IDSlice { // OnDisallowListNotification is called when a new disallow list update notification is distributed. // It disconnects from all peers in the disallow list. -func (m *Middleware) OnDisallowListNotification(notification *p2p.DisallowListUpdateNotification) { - for _, pid := range m.peerIDs(notification.DisallowList) { - err := m.libP2PNode.RemovePeer(pid) +func (m *Middleware) OnDisallowListNotification(notification *p2p.RemoteNodesAllowListingUpdate) { + for _, pid := range m.peerIDs(notification.FlowIds) { + causes, err := m.disallowListedCache.DisallowFor(pid, notification.Cause) + if err != nil { + // returned error is fatal. + m.log.Fatal().Err(err).Str("peer_id", pid.String()).Msg("failed to add peer to disallow list") + } + + // TODO: this code should further be refactored to also log the Flow id. + m.log.Warn(). + Str("peer_id", pid.String()). + Str("causes", fmt.Sprintf("%v", causes)). + Msg("peer added to disallow list cache") + + // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) + // hence, this code part can be removed. + err = m.libP2PNode.RemovePeer(pid) if err != nil { m.log.Error().Err(err).Str("peer_id", pid.String()).Msg("failed to disconnect from blocklisted peer") } diff --git a/network/p2p/mock/disallow_list_notification_consumer.go b/network/p2p/mock/disallow_list_notification_consumer.go index 7df8437ddcf..3bc4251530e 100644 --- a/network/p2p/mock/disallow_list_notification_consumer.go +++ b/network/p2p/mock/disallow_list_notification_consumer.go @@ -13,7 +13,7 @@ type DisallowListNotificationConsumer struct { } // OnDisallowListNotification provides a mock function with given fields: _a0 -func (_m *DisallowListNotificationConsumer) OnDisallowListNotification(_a0 *p2p.DisallowListUpdateNotification) { +func (_m *DisallowListNotificationConsumer) OnDisallowListNotification(_a0 *p2p.RemoteNodesAllowListingUpdate) { _m.Called(_a0) } diff --git a/network/p2p/mock/disallow_list_notification_distributor.go b/network/p2p/mock/disallow_list_notification_distributor.go index 82419cb87e1..130f18a2726 100644 --- a/network/p2p/mock/disallow_list_notification_distributor.go +++ b/network/p2p/mock/disallow_list_notification_distributor.go @@ -5,10 +5,9 @@ package mockp2p import ( flow "github.com/onflow/flow-go/model/flow" irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" mock "github.com/stretchr/testify/mock" - - p2p "github.com/onflow/flow-go/network/p2p" ) // DisallowListNotificationDistributor is an autogenerated mock type for the DisallowListNotificationDistributor type @@ -17,7 +16,7 @@ type DisallowListNotificationDistributor struct { } // AddConsumer provides a mock function with given fields: _a0 -func (_m *DisallowListNotificationDistributor) AddConsumer(_a0 p2p.DisallowListNotificationConsumer) { +func (_m *DisallowListNotificationDistributor) AddConsumer(_a0 network.DisallowListNotificationConsumer) { _m.Called(_a0) } From 69e8de1420c7980986241dd1d69e09e870b7a55c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:45:56 -0700 Subject: [PATCH 047/815] replaces disallow listing distributor with middleware --- .../node_builder/access_node_builder.go | 10 +- cmd/node_builder.go | 2 - cmd/observer/node_builder/observer_builder.go | 9 +- cmd/scaffold.go | 13 +- cmd/utils.go | 15 --- follower/follower_builder.go | 11 +- network/p2p/distributor/disallow_list.go | 115 ------------------ network/p2p/distributor/disallow_list_test.go | 100 --------------- 8 files changed, 5 insertions(+), 270 deletions(-) delete mode 100644 network/p2p/distributor/disallow_list.go delete mode 100644 network/p2p/distributor/disallow_list_test.go diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index bb6f33cf167..3666dbdec71 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -784,11 +784,9 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { } builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) - builder.NodeDisallowListDistributor = cmd.BuildDisallowListNotificationDisseminator(builder.DisallowListNotificationCacheSize, builder.MetricsRegisterer, builder.Logger, builder.MetricsEnabled) - // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeBlocklistWrapper(idCache, node.DB, builder.NodeDisallowListDistributor) + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } @@ -813,11 +811,6 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { } return nil }) - - builder.Component("disallow list notification distributor", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { - // distributor is returned as a component to be started and stopped. - return builder.NodeDisallowListDistributor, nil - }) } func (builder *FlowAccessNodeBuilder) Initialize() error { @@ -1244,7 +1237,6 @@ func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) - builder.NodeDisallowListDistributor.AddConsumer(mw) builder.Middleware = mw return builder.Middleware } diff --git a/cmd/node_builder.go b/cmd/node_builder.go index a21e006a5cb..32e119cf06b 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -293,8 +293,6 @@ type NodeConfig struct { // UnicastRateLimiterDistributor notifies consumers when a peer's unicast message is rate limited. UnicastRateLimiterDistributor p2p.UnicastRateLimiterDistributor - // NodeDisallowListDistributor notifies consumers of updates to disallow listing of nodes. - NodeDisallowListDistributor p2p.DisallowListNotificationDistributor } // StateExcerptAtBoot stores information about the root snapshot and latest finalized block for use in bootstrapping. diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 0ae14b58e1c..eae470bdc4a 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -743,11 +743,9 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { } builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) - builder.NodeDisallowListDistributor = cmd.BuildDisallowListNotificationDisseminator(builder.DisallowListNotificationCacheSize, builder.MetricsRegisterer, builder.Logger, builder.MetricsEnabled) - // The following wrapper allows to black-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - builder.IdentityProvider, err = cache.NewNodeBlocklistWrapper(idCache, node.DB, builder.NodeDisallowListDistributor) + builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } @@ -778,11 +776,6 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { return nil }) - - builder.Component("disallow list notification distributor", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { - // distributor is returned as a component to be started and stopped. - return builder.NodeDisallowListDistributor, nil - }) } func (builder *ObserverServiceBuilder) Initialize() error { diff --git a/cmd/scaffold.go b/cmd/scaffold.go index ce14b9778b2..56539f709d1 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -501,7 +501,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory(node *NodeConfig, }, }, mwOpts...) - fnb.NodeDisallowListDistributor.AddConsumer(mw) + fnb.Middleware = mw subscriptionManager := subscription.NewChannelSubscriptionManager(fnb.Middleware) @@ -1039,13 +1039,6 @@ func (fnb *FlowNodeBuilder) initStorage() error { } func (fnb *FlowNodeBuilder) InitIDProviders() { - fnb.Component("disallow list notification distributor", func(node *NodeConfig) (module.ReadyDoneAware, error) { - // distributor is returned as a component to be started and stopped. - if fnb.NodeDisallowListDistributor == nil { - return nil, fmt.Errorf("disallow list notification distributor has not been set") - } - return fnb.NodeDisallowListDistributor, nil - }) fnb.Module("id providers", func(node *NodeConfig) error { idCache, err := cache.NewProtocolStateIDCache(node.Logger, node.State, node.ProtocolEvents) if err != nil { @@ -1053,11 +1046,9 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { } node.IDTranslator = idCache - fnb.NodeDisallowListDistributor = BuildDisallowListNotificationDisseminator(fnb.DisallowListNotificationCacheSize, fnb.MetricsRegisterer, fnb.Logger, fnb.MetricsEnabled) - // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeBlocklistWrapper(idCache, node.DB, fnb.NodeDisallowListDistributor) + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, fnb.Middleware) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } diff --git a/cmd/utils.go b/cmd/utils.go index bfc77542c8d..05763933ebc 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -6,15 +6,10 @@ import ( "path/filepath" "github.com/libp2p/go-libp2p/core/peer" - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/mempool/queue" - "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/distributor" "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/utils/io" ) @@ -68,13 +63,3 @@ func rateLimiterPeerFilter(rateLimiter p2p.RateLimiter) p2p.PeerFilter { return nil } } - -// BuildDisallowListNotificationDisseminator builds the disallow list notification distributor. -func BuildDisallowListNotificationDisseminator(size uint32, metricsRegistry prometheus.Registerer, logger zerolog.Logger, metricsEnabled bool) p2p.DisallowListNotificationDistributor { - heroStoreOpts := []queue.HeroStoreConfigOption{queue.WithHeroStoreSizeLimit(size)} - if metricsEnabled { - collector := metrics.DisallowListNotificationQueueMetricFactory(metricsRegistry) - heroStoreOpts = append(heroStoreOpts, queue.WithHeroStoreCollector(collector)) - } - return distributor.DefaultDisallowListNotificationDistributor(logger, heroStoreOpts...) -} diff --git a/follower/follower_builder.go b/follower/follower_builder.go index fd367f21192..e0c66313a75 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -480,11 +480,9 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { } builder.IDTranslator = translator.NewHierarchicalIDTranslator(idCache, translator.NewPublicNetworkIDTranslator()) - builder.NodeDisallowListDistributor = cmd.BuildDisallowListNotificationDisseminator(builder.DisallowListNotificationCacheSize, builder.MetricsRegisterer, builder.Logger, builder.MetricsEnabled) - // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of the disallow-listed nodes to true - builder.IdentityProvider, err = cache.NewNodeBlocklistWrapper(idCache, node.DB, builder.NodeDisallowListDistributor) + builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } @@ -515,11 +513,6 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { return nil }) - - builder.Component("disallow list notification distributor", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { - // distributor is returned as a component to be started and stopped. - return builder.NodeDisallowListDistributor, nil - }) } func (builder *FollowerServiceBuilder) Initialize() error { @@ -760,9 +753,7 @@ func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, }, }, middleware.WithMessageValidators(validators...), - // use default identifier provider ) - builder.NodeDisallowListDistributor.AddConsumer(mw) builder.Middleware = mw return builder.Middleware } diff --git a/network/p2p/distributor/disallow_list.go b/network/p2p/distributor/disallow_list.go deleted file mode 100644 index 20b1fbd5a6a..00000000000 --- a/network/p2p/distributor/disallow_list.go +++ /dev/null @@ -1,115 +0,0 @@ -package distributor - -import ( - "sync" - - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/engine" - "github.com/onflow/flow-go/engine/common/worker" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/module/mempool/queue" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/p2p" -) - -const ( - // DefaultDisallowListNotificationQueueCacheSize is the default size of the disallow list notification queue. - DefaultDisallowListNotificationQueueCacheSize = 100 -) - -// DisallowListNotificationDistributor is a component that distributes disallow list updates to registered consumers in an -// asynchronous, fan-out manner. It is thread-safe and can be used concurrently from multiple goroutines. -type DisallowListNotificationDistributor struct { - component.Component - cm *component.ComponentManager - logger zerolog.Logger - - consumerLock sync.RWMutex // protects the consumer field from concurrent updates - consumers []network.DisallowListNotificationConsumer - workerPool *worker.Pool[*p2p.RemoteNodesAllowListingUpdate] -} - -var _ p2p.DisallowListNotificationDistributor = (*DisallowListNotificationDistributor)(nil) - -// DefaultDisallowListNotificationDistributor creates a new disallow list notification distributor with default configuration. -func DefaultDisallowListNotificationDistributor(logger zerolog.Logger, opts ...queue.HeroStoreConfigOption) *DisallowListNotificationDistributor { - cfg := &queue.HeroStoreConfig{ - SizeLimit: DefaultDisallowListNotificationQueueCacheSize, - Collector: metrics.NewNoopCollector(), - } - - for _, opt := range opts { - opt(cfg) - } - - store := queue.NewHeroStore(cfg.SizeLimit, logger, cfg.Collector) - return NewDisallowListConsumer(logger, store) -} - -// NewDisallowListConsumer creates a new disallow list notification distributor. -// It takes a message store as a parameter, which is used to store the events that are distributed to the consumers. -// The message store is used to ensure that DistributeBlockListNotification is non-blocking. -func NewDisallowListConsumer(logger zerolog.Logger, store engine.MessageStore) *DisallowListNotificationDistributor { - lg := logger.With().Str("component", "node_disallow_distributor").Logger() - - d := &DisallowListNotificationDistributor{ - logger: lg, - } - - pool := worker.NewWorkerPoolBuilder[*p2p.RemoteNodesAllowListingUpdate]( - lg, - store, - d.distribute).Build() - - d.workerPool = pool - - cm := component.NewComponentManagerBuilder() - cm.AddWorker(d.workerPool.WorkerLogic()) - - d.cm = cm.Build() - d.Component = d.cm - - return d -} - -// distribute is called by the workers to process the event. It calls the OnDisallowListNotification method on all registered -// consumers. -// It does not return an error because the event is already in the store, so it will be retried. -func (d *DisallowListNotificationDistributor) distribute(notification *p2p.RemoteNodesAllowListingUpdate) error { - d.consumerLock.RLock() - defer d.consumerLock.RUnlock() - - for _, consumer := range d.consumers { - consumer.OnDisallowListNotification(notification) - } - - return nil -} - -// AddConsumer adds a consumer to the distributor. The consumer will be called the distributor distributes a new event. -// AddConsumer must be concurrency safe. Once a consumer is added, it must be called for all future events. -// There is no guarantee that the consumer will be called for events that were already received by the distributor. -func (d *DisallowListNotificationDistributor) AddConsumer(consumer network.DisallowListNotificationConsumer) { - d.consumerLock.Lock() - defer d.consumerLock.Unlock() - - d.consumers = append(d.consumers, consumer) -} - -// DistributeBlockListNotification distributes the event to all the consumers. -// Implementation is non-blocking, it submits the event to the worker pool and returns immediately. -// The event will be distributed to the consumers in the order it was submitted but asynchronously. -// If the worker pool is full, the event will be dropped and a warning will be logged. -// This implementation returns no error. -func (d *DisallowListNotificationDistributor) DistributeBlockListNotification(disallowList flow.IdentifierList) error { - ok := d.workerPool.Submit(&p2p.RemoteNodesAllowListingUpdate{FlowIds: disallowList}) - if !ok { - // we use a queue to buffer the events, so this may happen if the queue is full or the event is duplicate. In this case, we log a warning. - d.logger.Warn().Msg("node disallow list update notification queue is full or the event is duplicate, dropping event") - } - - return nil -} diff --git a/network/p2p/distributor/disallow_list_test.go b/network/p2p/distributor/disallow_list_test.go deleted file mode 100644 index 4d5a668eb09..00000000000 --- a/network/p2p/distributor/disallow_list_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package distributor_test - -import ( - "context" - "math/rand" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/distributor" - mockp2p "github.com/onflow/flow-go/network/p2p/mock" - "github.com/onflow/flow-go/utils/unittest" -) - -// TestDisallowListNotificationDistributor tests the disallow list notification distributor by adding two consumers to the -// notification distributor component and sending a random set of notifications to the notification component. The test -// verifies that the consumers receive the notifications and that each consumer sees each notification only once. -func TestDisallowListNotificationDistributor(t *testing.T) { - d := distributor.DefaultDisallowListNotificationDistributor(unittest.Logger()) - - c1 := mockp2p.NewDisallowListNotificationConsumer(t) - c2 := mockp2p.NewDisallowListNotificationConsumer(t) - - d.AddConsumer(c1) - d.AddConsumer(c2) - - tt := disallowListUpdateNotificationsFixture(50) - - c1Done := sync.WaitGroup{} - c1Done.Add(len(tt)) - c1Seen := unittest.NewProtectedMap[flow.Identifier, struct{}]() - c1.On("OnDisallowListNotification", mock.Anything).Run(func(args mock.Arguments) { - n, ok := args.Get(0).(*p2p.RemoteNodesAllowListingUpdate) - require.True(t, ok) - - require.Contains(t, tt, n) - - // ensure consumer see each peer once - hash := flow.MerkleRoot(n.FlowIds...) - require.False(t, c1Seen.Has(hash)) - c1Seen.Add(hash, struct{}{}) - - c1Done.Done() - }).Return() - - c2Done := sync.WaitGroup{} - c2Done.Add(len(tt)) - c2Seen := unittest.NewProtectedMap[flow.Identifier, struct{}]() - c2.On("OnDisallowListNotification", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - n, ok := args.Get(0).(*p2p.RemoteNodesAllowListingUpdate) - require.True(t, ok) - - require.Contains(t, tt, n) - - // ensure consumer see each peer once - hash := flow.MerkleRoot(n.FlowIds...) - require.False(t, c2Seen.Has(hash)) - c2Seen.Add(hash, struct{}{}) - - c2Done.Done() - }).Return() - - cancelCtx, cancel := context.WithCancel(context.Background()) - defer cancel() - ctx, _ := irrecoverable.WithSignaler(cancelCtx) - d.Start(ctx) - - unittest.RequireCloseBefore(t, d.Ready(), 100*time.Millisecond, "could not start distributor") - - for i := 0; i < len(tt); i++ { - go func(i int) { - require.NoError(t, d.DistributeBlockListNotification(tt[i].FlowIds)) - }(i) - } - - unittest.RequireReturnsBefore(t, c1Done.Wait, 1*time.Second, "events are not received by consumer 1") - unittest.RequireReturnsBefore(t, c2Done.Wait, 1*time.Second, "events are not received by consumer 2") - cancel() - unittest.RequireCloseBefore(t, d.Done(), 100*time.Millisecond, "could not stop distributor") -} - -func disallowListUpdateNotificationsFixture(n int) []*p2p.RemoteNodesAllowListingUpdate { - tt := make([]*p2p.RemoteNodesAllowListingUpdate, n) - for i := 0; i < n; i++ { - tt[i] = disallowListUpdateNotificationFixture() - } - return tt -} - -func disallowListUpdateNotificationFixture() *p2p.RemoteNodesAllowListingUpdate { - return &p2p.RemoteNodesAllowListingUpdate{ - FlowIds: unittest.IdentifierListFixture(rand.Int()%100 + 1), - } -} From be1ae68e93effa6013529f3272fb0a53a2968816 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:46:21 -0700 Subject: [PATCH 048/815] revises middleware interface --- network/middleware.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/network/middleware.go b/network/middleware.go index c66883dec3c..a33cabb2caf 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -17,6 +17,8 @@ import ( // connections, as well as reading & writing to/from the connections. type Middleware interface { component.Component + DisallowListOracle + DisallowListNotificationConsumer // SetOverlay sets the overlay used by the middleware. This must be called before the middleware can be Started. SetOverlay(Overlay) @@ -57,21 +59,18 @@ type Middleware interface { IsConnected(nodeID flow.Identifier) (bool, error) } -// DisallowedListOracle represents the interface that is exposed to the lower-level networking primitives (e.g. libp2p), +// DisallowListOracle represents the interface that is exposed to the lower-level networking primitives (e.g. libp2p), // which allows them to check if a given peer ID is disallowed (by Flow protocol) from connecting to this node. // Disallow-listing is considered a temporary measure to prevent malicious nodes from connecting to the network. Hence, // the disallow-list status of a peer ID should not be treated as a permanent state. -type DisallowedListOracle interface { - // IsDisallowed returns true if the given peer ID is disallowed from connecting to this node. - // This function should be called by the lower-level networking primitives (e.g. libp2p) before establishing a - // connection with a peer. - // Implementations of this function should be thread-safe. +type DisallowListOracle interface { + // GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. // Args: - // peer.ID: the peer ID of the node that is attempting to connect to (or be connected by) this node. + // - peerID: the peer to check. // Returns: - // bool: true if the given peer ID is disallowed from connecting to this node. - // false otherwise. - IsDisallowed(peer.ID) bool + // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, + // an empty list is returned. + GetAllDisallowedListCausesFor(peer.ID) []DisallowListedCause } // Overlay represents the interface that middleware uses to interact with the From 6c51a0015d2a81a6abdb830b07b0ed60b2c66735 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:46:41 -0700 Subject: [PATCH 049/815] adds string method to disallow list wrapper --- network/disallow.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/network/disallow.go b/network/disallow.go index 9b182a0f5c1..092fbd1e4c1 100644 --- a/network/disallow.go +++ b/network/disallow.go @@ -10,6 +10,10 @@ import ( // it for reason Y. type DisallowListedCause string +func (c DisallowListedCause) String() string { + return string(c) +} + const ( // DisallowListedCauseAdmin is the cause of disallow-listing a node by an admin command. DisallowListedCauseAdmin DisallowListedCause = "disallow-listed-admin" From d955a3134694886df6019dd23f891e815298a1cd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:48:00 -0700 Subject: [PATCH 050/815] removes disallow list consumer and distributor interfaces --- network/p2p/consumers.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/network/p2p/consumers.go b/network/p2p/consumers.go index 3798915e50a..ee12521aadb 100644 --- a/network/p2p/consumers.go +++ b/network/p2p/consumers.go @@ -4,23 +4,9 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/network" ) -// DisallowListConsumer consumes notifications from the cache.NodeBlocklistWrapper whenever the block list is updated. -// Implementations must: -// - be concurrency safe -// - be non-blocking -type DisallowListConsumer interface { - // OnNodeDisallowListUpdate notifications whenever the node block list is updated. - // Prerequisites: - // Implementation must be concurrency safe; Non-blocking; - // and must handle repetition of the same events (with some processing overhead). - OnNodeDisallowListUpdate(list flow.IdentifierList) -} - // ControlMessageType is the type of control message, as defined in the libp2p pubsub spec. type ControlMessageType string @@ -40,19 +26,6 @@ func ControlMessageTypes() []ControlMessageType { return []ControlMessageType{CtrlMsgIHave, CtrlMsgIWant, CtrlMsgGraft, CtrlMsgPrune} } -type DisallowListNotificationDistributor interface { - component.Component - // DistributeBlockListNotification distributes the event to all the consumers. - // Any error returned by the distributor is non-recoverable and will cause the node to crash. - // Implementation must be concurrency safe, and non-blocking. - DistributeBlockListNotification(list flow.IdentifierList) error - - // AddConsumer adds a consumer to the distributor. The consumer will be called the distributor distributes a new event. - // AddConsumer must be concurrency safe. Once a consumer is added, it must be called for all future events. - // There is no guarantee that the consumer will be called for events that were already received by the distributor. - AddConsumer(network.DisallowListNotificationConsumer) -} - // GossipSubInspectorNotifDistributor is the interface for the distributor that distributes gossip sub inspector notifications. // It is used to distribute notifications to the consumers in an asynchronous manner and non-blocking manner. // The implementation should guarantee that all registered consumers are called upon distribution of a new event. From f31b5b3fbf64c1654dffa4de78de38073abbf891 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:48:29 -0700 Subject: [PATCH 051/815] adds disallow listing event handlers to middleware --- network/p2p/middleware/middleware.go | 29 +++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 248efacd7d6..eede7783c43 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -72,8 +72,7 @@ const ( ) var ( - _ network.Middleware = (*Middleware)(nil) - _ network.DisallowListNotificationConsumer = (*Middleware)(nil) + _ network.Middleware = (*Middleware)(nil) // ErrUnicastMsgWithoutSub error is provided to the slashing violations consumer in the case where // the middleware receives a message via unicast but does not have a corresponding subscription for @@ -360,9 +359,17 @@ func (m *Middleware) authorizedPeers() peer.IDSlice { return peerIDs } -// OnDisallowListNotification is called when a new disallow list update notification is distributed. -// It disconnects from all peers in the disallow list. -func (m *Middleware) OnDisallowListNotification(notification *p2p.RemoteNodesAllowListingUpdate) { +// GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. +// Args: +// - peerID: the peer to check. +// Returns: +// - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, +// an empty list is returned. +func (m *Middleware) GetAllDisallowedListCausesFor(pid peer.ID) []network.DisallowListedCause { + return m.disallowListedCache.GetAllDisallowedListCausesFor(pid) +} + +func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { for _, pid := range m.peerIDs(notification.FlowIds) { causes, err := m.disallowListedCache.DisallowFor(pid, notification.Cause) if err != nil { @@ -373,6 +380,7 @@ func (m *Middleware) OnDisallowListNotification(notification *p2p.RemoteNodesAll // TODO: this code should further be refactored to also log the Flow id. m.log.Warn(). Str("peer_id", pid.String()). + Str("notification_cause", notification.Cause.String()). Str("causes", fmt.Sprintf("%v", causes)). Msg("peer added to disallow list cache") @@ -385,6 +393,17 @@ func (m *Middleware) OnDisallowListNotification(notification *p2p.RemoteNodesAll } } +func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { + for _, pid := range m.peerIDs(notification.FlowIds) { + m.disallowListedCache.AllowFor(pid, notification.Cause) + + m.log.Debug(). + Str("peer_id", pid.String()). + Str("causes", fmt.Sprintf("%v", notification.Cause)). + Msg("peer added to disallow list cache") + } +} + // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous // direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized // as the router. From e8794d6a574e40fb4b158069f38ab189e48decf0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:48:53 -0700 Subject: [PATCH 052/815] refactors node blocklist wrappers --- network/p2p/cache/node_blocklist_wrapper.go | 60 ++++++++++--------- .../p2p/cache/node_blocklist_wrapper_test.go | 56 ++++++++--------- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/network/p2p/cache/node_blocklist_wrapper.go b/network/p2p/cache/node_blocklist_wrapper.go index ae045ecff62..728b40edbd5 100644 --- a/network/p2p/cache/node_blocklist_wrapper.go +++ b/network/p2p/cache/node_blocklist_wrapper.go @@ -10,7 +10,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/operation" ) @@ -25,48 +25,51 @@ func (s IdentifierSet) Contains(id flow.Identifier) bool { } // NodeBlocklistWrapper is a wrapper for an `module.IdentityProvider` instance, where the -// wrapper overrides the `Ejected` flag to true for all NodeIDs in a `blocklist`. +// wrapper overrides the `Ejected` flag to true for all NodeIDs in a `disallowList`. // To avoid modifying the source of the identities, the wrapper creates shallow copies // of the identities (whenever necessary) and modifies the `Ejected` flag only in // the copy. -// The `NodeBlocklistWrapper` internally represents the `blocklist` as a map, to enable +// The `NodeBlocklistWrapper` internally represents the `disallowList` as a map, to enable // performant lookup. However, the exported API works with `flow.IdentifierList` for -// blocklist, as this is a broadly supported data structure which lends itself better +// disallowList, as this is a broadly supported data structure which lends itself better // to config or command-line inputs. +// TODO: terminology change - rename `blocklist` to `disallowList` everywhere to be consistent with the code. type NodeBlocklistWrapper struct { m sync.RWMutex db *badger.DB identityProvider module.IdentityProvider - blocklist IdentifierSet // `IdentifierSet` is a map, hence efficient O(1) lookup - distributor p2p.DisallowListNotificationDistributor // distributor for the blocklist update notifications + disallowList IdentifierSet // `IdentifierSet` is a map, hence efficient O(1) lookup + + // updateConsumer is called whenever the disallow-list is updated. + updateConsumer network.DisallowListNotificationConsumer } var _ module.IdentityProvider = (*NodeBlocklistWrapper)(nil) -// NewNodeBlocklistWrapper wraps the given `IdentityProvider`. The blocklist is +// NewNodeDisallowListWrapper wraps the given `IdentityProvider`. The disallow-list is // loaded from the database (or assumed to be empty if no database entry is present). -func NewNodeBlocklistWrapper( +func NewNodeDisallowListWrapper( identityProvider module.IdentityProvider, db *badger.DB, - distributor p2p.DisallowListNotificationDistributor) (*NodeBlocklistWrapper, error) { + updateConsumer network.DisallowListNotificationConsumer) (*NodeBlocklistWrapper, error) { - blocklist, err := retrieveBlocklist(db) + disallowList, err := retrieveBlocklist(db) if err != nil { - return nil, fmt.Errorf("failed to read set of blocked node IDs from data base: %w", err) + return nil, fmt.Errorf("failed to read set of disallowed node IDs from data base: %w", err) } return &NodeBlocklistWrapper{ db: db, identityProvider: identityProvider, - blocklist: blocklist, - distributor: distributor, + disallowList: disallowList, + updateConsumer: updateConsumer, }, nil } -// Update sets the wrapper's internal set of blocked nodes to `blocklist`. Empty list and `nil` +// Update sets the wrapper's internal set of blocked nodes to `disallowList`. Empty list and `nil` // (equivalent to empty list) are accepted inputs. To avoid legacy entries in the data base, this -// function purges the entire data base entry if `blocklist` is empty. +// function purges the entire data base entry if `disallowList` is empty. // This implementation is _eventually consistent_, where changes are written to the data base first // and then (non-atomically!) the in-memory set of blocked nodes is updated. This strongly // benefits performance and modularity. No errors are expected during normal operations. @@ -79,12 +82,11 @@ func (w *NodeBlocklistWrapper) Update(blocklist flow.IdentifierList) error { if err != nil { return fmt.Errorf("failed to persist set of blocked nodes to the data base: %w", err) } - w.blocklist = b - err = w.distributor.DistributeBlockListNotification(blocklist) - - if err != nil { - return fmt.Errorf("failed to distribute blocklist update notification: %w", err) - } + w.disallowList = b + w.updateConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ + FlowIds: blocklist, + Cause: network.DisallowListedCauseAdmin, + }) return nil } @@ -100,8 +102,8 @@ func (w *NodeBlocklistWrapper) GetBlocklist() flow.IdentifierList { w.m.RLock() defer w.m.RUnlock() - identifiers := make(flow.IdentifierList, 0, len(w.blocklist)) - for i := range w.blocklist { + identifiers := make(flow.IdentifierList, 0, len(w.disallowList)) + for i := range w.disallowList { identifiers = append(identifiers, i) } return identifiers @@ -123,7 +125,7 @@ func (w *NodeBlocklistWrapper) Identities(filter flow.IdentityFilter) flow.Ident idtx := make(flow.IdentityList, 0, len(identities)) w.m.RLock() for _, identity := range identities { - if w.blocklist.Contains(identity.NodeID) { + if w.disallowList.Contains(identity.NodeID) { var i = *identity // shallow copy is sufficient, because `Ejected` flag is in top-level struct i.Ejected = true if filter(&i) { // we need to check the filter here again, because the filter might drop ejected nodes and we are modifying the ejected status here @@ -148,17 +150,17 @@ func (w *NodeBlocklistWrapper) ByNodeID(identifier flow.Identifier) (*flow.Ident return w.setEjectedIfBlocked(identity), b } -// setEjectedIfBlocked checks whether the node with the given identity is on the `blocklist`. +// setEjectedIfBlocked checks whether the node with the given identity is on the `disallowList`. // Shortcuts: // - If the node's identity is nil, there is nothing to do because we don't generate identities here. -// - If the node is already ejected, we don't have to check the blocklist. +// - If the node is already ejected, we don't have to check the disallowList. func (w *NodeBlocklistWrapper) setEjectedIfBlocked(identity *flow.Identity) *flow.Identity { if identity == nil || identity.Ejected { return identity } w.m.RLock() - isBlocked := w.blocklist.Contains(identity.NodeID) + isBlocked := w.disallowList.Contains(identity.NodeID) w.m.RUnlock() if !isBlocked { return identity @@ -183,8 +185,8 @@ func (w *NodeBlocklistWrapper) ByPeerID(p peer.ID) (*flow.Identity, bool) { return w.setEjectedIfBlocked(identity), b } -// persistBlocklist writes the given blocklist to the database. To avoid legacy -// entries in the database, we prune the entire data base entry if `blocklist` is +// persistBlocklist writes the given disallowList to the database. To avoid legacy +// entries in the database, we prune the entire data base entry if `disallowList` is // empty. No errors are expected during normal operations. func persistBlocklist(blocklist IdentifierSet, db *badger.DB) error { if len(blocklist) == 0 { diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index cdc32b546f5..dbd0b5d1b51 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -35,7 +35,7 @@ func (s *NodeBlocklistWrapperTestSuite) SetupTest() { var err error s.distributor = mockp2p.NewDisallowListNotificationDistributor(s.T()) - s.wrapper, err = cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + s.wrapper, err = cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) } @@ -44,7 +44,7 @@ func TestNodeBlocklistWrapperTestSuite(t *testing.T) { } // TestHonestNode verifies: -// For nodes _not_ on the blocklist, the `cache.NodeBlocklistWrapper` should forward +// For nodes _not_ on the disallowList, the `cache.NodeBlocklistWrapper` should forward // the identities from the wrapped `IdentityProvider` without modification. func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { s.Run("ByNodeID", func() { @@ -78,8 +78,8 @@ func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { }) } -// TestDenylistedNode tests proper handling of identities _on_ the blocklist: -// - For any identity `i` with `i.NodeID ∈ blocklist`, the returned identity +// TestDenylistedNode tests proper handling of identities _on_ the disallowList: +// - For any identity `i` with `i.NodeID ∈ disallowList`, the returned identity // should have `i.Ejected` set to `true` (irrespective of the `Ejected` // flag's initial returned by the wrapped `IdentityProvider`). // - The wrapper should _copy_ the identity and _not_ write into the wrapped @@ -210,12 +210,12 @@ func (s *NodeBlocklistWrapperTestSuite) TestUnknownNode() { } } -// TestBlocklistAddRemove checks that adding and subsequently removing a node from the blocklist +// TestBlocklistAddRemove checks that adding and subsequently removing a node from the disallowList // it in combination a no-op. We test two scenarious // - Node whose original `Identity` has `Ejected = false`: -// After adding the node to the blocklist and then removing it again, the `Ejected` should be false. +// After adding the node to the disallowList and then removing it again, the `Ejected` should be false. // - Node whose original `Identity` has `Ejected = true`: -// After adding the node to the blocklist and then removing it again, the `Ejected` should be still be true. +// After adding the node to the disallowList and then removing it again, the `Ejected` should be still be true. func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { for _, originalEjected := range []bool{true, false} { s.Run(fmt.Sprintf("Add & remove node with Ejected = %v", originalEjected), func() { @@ -225,7 +225,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { s.provider.On("ByNodeID", originalIdentity.NodeID).Return(originalIdentity, true) s.provider.On("ByPeerID", peerID).Return(originalIdentity, true) - // step 1: before putting node on blocklist, + // step 1: before putting node on disallowList, // an Identity with `Ejected` equal to the original value should be returned i, found := s.wrapper.ByNodeID(originalIdentity.NodeID) require.True(s.T(), found) @@ -235,7 +235,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { require.True(s.T(), found) require.Equal(s.T(), originalEjected, i.Ejected) - // step 2: _after_ putting node on blocklist, + // step 2: _after_ putting node on disallowList, // an Identity with `Ejected` equal to `true` should be returned s.distributor.On("DistributeBlockListNotification", flow.IdentifierList{originalIdentity.NodeID}).Return(nil).Once() err := s.wrapper.Update(flow.IdentifierList{originalIdentity.NodeID}) @@ -249,7 +249,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { require.True(s.T(), found) require.True(s.T(), i.Ejected) - // step 3: after removing the node from the blocklist, + // step 3: after removing the node from the disallowList, // an Identity with `Ejected` equal to the original value should be returned s.distributor.On("DistributeBlockListNotification", flow.IdentifierList{}).Return(nil).Once() err = s.wrapper.Update(flow.IdentifierList{}) @@ -266,10 +266,10 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { } } -// TestUpdate tests updating, clearing and retrieving the blocklist. +// TestUpdate tests updating, clearing and retrieving the disallowList. // This test verifies that the wrapper updates _its own internal state_ correctly. // Note: -// conceptually, the blocklist is a set, i.e. not order dependent. +// conceptually, the disallowList is a set, i.e. not order dependent. // The wrapper internally converts the list to a set and vice versa. Therefore // the order is not preserved by `GetBlocklist`. Consequently, we compare // map-based representations here. @@ -300,7 +300,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { } // TestDataBasePersist verifies database interactions of the wrapper with the data base. -// This test verifies that the blocklist updates are persisted across restarts. +// This test verifies that the disallowList updates are persisted across restarts. // To decouple this test from the lower-level data base design, we proceed as follows: // - We do data-base operation through the exported methods from `NodeBlocklistWrapper` // - Then, we create a new `NodeBlocklistWrapper` backed by the same data base. Since it is a @@ -314,34 +314,34 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { blocklist := unittest.IdentifierListFixture(8) blocklist2 := unittest.IdentifierListFixture(8) - s.Run("Get blocklist from empty database", func() { + s.Run("Get disallowList from empty database", func() { require.Empty(s.T(), s.wrapper.GetBlocklist()) }) - s.Run("Clear blocklist on empty database", func() { + s.Run("Clear disallowList on empty database", func() { s.distributor.On("DistributeBlockListNotification", (flow.IdentifierList)(nil)).Return(nil).Once() err := s.wrapper.ClearBlocklist() // No-op as data base does not contain any block list require.NoError(s.T(), err) require.Empty(s.T(), s.wrapper.GetBlocklist()) - // newly created wrapper should read `blocklist` from data base during initialization - w, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + // newly created wrapper should read `disallowList` from data base during initialization + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Empty(s.T(), w.GetBlocklist()) }) - s.Run("Update blocklist and init new wrapper from database", func() { + s.Run("Update disallowList and init new wrapper from database", func() { s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) - // newly created wrapper should read `blocklist` from data base during initialization - w, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + // newly created wrapper should read `disallowList` from data base during initialization + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Equal(s.T(), blocklist.Lookup(), w.GetBlocklist().Lookup()) }) - s.Run("Update and overwrite blocklist and then init new wrapper from database", func() { + s.Run("Update and overwrite disallowList and then init new wrapper from database", func() { s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) @@ -351,29 +351,29 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { require.NoError(s.T(), err) // newly created wrapper should read initial state from data base - w, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Equal(s.T(), blocklist2.Lookup(), w.GetBlocklist().Lookup()) }) s.Run("Update & clear & update and then init new wrapper from database", func() { - // set blocklist -> + // set disallowList -> // newly created wrapper should now read this list from data base during initialization s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) - w0, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Equal(s.T(), blocklist.Lookup(), w0.GetBlocklist().Lookup()) - // clear blocklist -> - // newly created wrapper should now read empty blocklist from data base during initialization + // clear disallowList -> + // newly created wrapper should now read empty disallowList from data base during initialization s.distributor.On("DistributeBlockListNotification", (flow.IdentifierList)(nil)).Return(nil).Once() err = s.wrapper.ClearBlocklist() require.NoError(s.T(), err) - w1, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Empty(s.T(), w1.GetBlocklist()) @@ -383,7 +383,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { err = s.wrapper.Update(blocklist2) require.NoError(s.T(), err) - w2, err := cache.NewNodeBlocklistWrapper(s.provider, s.DB, s.distributor) + w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) require.NoError(s.T(), err) require.Equal(s.T(), blocklist2.Lookup(), w2.GetBlocklist().Lookup()) }) From 3b8864a1c078b818139800fea7a22ef923284dc1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:52:09 -0700 Subject: [PATCH 053/815] lint fix --- network/internal/testutils/testUtil.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index d8868daf559..7c6e4919998 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -210,16 +210,21 @@ func GenerateMiddlewares(t *testing.T, idProviders[i] = NewUpdatableIDProvider(identities) // creating middleware of nodes - mws[i] = middleware.NewMiddleware( - logger, - node, - nodeId, - bitswapmet, - sporkID, - middleware.DefaultUnicastTimeout, - translator.NewIdentityProviderIDTranslator(idProviders[i]), - codec, - consumer, + mws[i] = middleware.NewMiddleware(&middleware.Config{ + Logger: logger, + Libp2pNode: node, + FlowId: nodeId, + BitSwapMetrics: bitswapmet, + RootBlockID: sporkID, + UnicastMessageTimeout: middleware.DefaultUnicastTimeout, + IdTranslator: translator.NewIdentityProviderIDTranslator(idProviders[i]), + Codec: codec, + SlashingViolationsConsumer: consumer, + DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ + MaxSize: uint32(1000), + Metrics: metrics.NewNoopCollector(), + }, + }, middleware.WithUnicastRateLimiters(o.unicastRateLimiters), middleware.WithPeerManagerFilters(o.peerManagerFilters)) } From 02edadd3262d212be452e67c48461dfa29ad671f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:53:20 -0700 Subject: [PATCH 054/815] lint fix --- cmd/node_builder.go | 16 ++++++---------- cmd/scaffold.go | 1 - 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 32e119cf06b..dcf77b65f8d 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -27,7 +27,6 @@ import ( "github.com/onflow/flow-go/network/codec/cbor" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/connection" - "github.com/onflow/flow-go/network/p2p/distributor" "github.com/onflow/flow-go/network/p2p/dns" "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pbuilder" @@ -197,8 +196,6 @@ type NetworkConfig struct { ConnectionManagerConfig *connection.ManagerConfig // UnicastCreateStreamRetryDelay initial delay used in the exponential backoff for create stream retries UnicastCreateStreamRetryDelay time.Duration - // size of the queue for notifications about new peers in the disallow list. - DisallowListNotificationCacheSize uint32 // size of the cache keeping the status of disallow-listed peers. Recommended to be 100 * number of authorized nodes. DisallowListCacheSize uint32 // UnicastRateLimitersConfig configuration for all unicast rate limiters. @@ -329,13 +326,12 @@ func DefaultBaseConfig() *BaseConfig { BandwidthRateLimit: 0, BandwidthBurstLimit: middleware.LargeMsgMaxUnicastMsgSize, }, - GossipSubConfig: p2pbuilder.DefaultGossipSubConfig(), - DNSCacheTTL: dns.DefaultTimeToLive, - LibP2PResourceManagerConfig: p2pbuilder.DefaultResourceManagerConfig(), - ConnectionManagerConfig: connection.DefaultConnManagerConfig(), - NetworkConnectionPruning: connection.PruningEnabled, - DisallowListNotificationCacheSize: distributor.DefaultDisallowListNotificationQueueCacheSize, - DisallowListCacheSize: middleware.DisallowListCacheSize, + GossipSubConfig: p2pbuilder.DefaultGossipSubConfig(), + DNSCacheTTL: dns.DefaultTimeToLive, + LibP2PResourceManagerConfig: p2pbuilder.DefaultResourceManagerConfig(), + ConnectionManagerConfig: connection.DefaultConnManagerConfig(), + NetworkConnectionPruning: connection.PruningEnabled, + DisallowListCacheSize: middleware.DisallowListCacheSize, AlspConfig: &AlspConfig{ SpamRecordCacheSize: alsp.DefaultSpamRecordCacheSize, SpamReportQueueSize: alsp.DefaultSpamReportQueueSize, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 56539f709d1..e86a154c43e 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -225,7 +225,6 @@ func (fnb *FlowNodeBuilder) BaseFlags() { // networking event notifications fnb.flags.Uint32Var(&fnb.BaseConfig.GossipSubConfig.RpcInspector.GossipSubRPCInspectorNotificationCacheSize, "gossipsub-rpc-inspector-notification-cache-size", defaultConfig.GossipSubConfig.RpcInspector.GossipSubRPCInspectorNotificationCacheSize, "cache size for notification events from gossipsub rpc inspector") - fnb.flags.Uint32Var(&fnb.BaseConfig.DisallowListNotificationCacheSize, "disallow-list-notification-cache-size", defaultConfig.DisallowListNotificationCacheSize, "cache size for notification events from disallow list") // unicast manager options fnb.flags.DurationVar(&fnb.BaseConfig.UnicastCreateStreamRetryDelay, "unicast-manager-create-stream-retry-delay", defaultConfig.NetworkConfig.UnicastCreateStreamRetryDelay, "Initial delay between failing to establish a connection with another node and retrying. This delay increases exponentially (exponential backoff) with the number of subsequent failures to establish a connection.") From 9894b9f79c25ac9461f63e0fb30f0a7276d3060c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:57:47 -0700 Subject: [PATCH 055/815] generates mocks --- .../disallow_list_notification_consumer.go | 11 ++- network/mocknetwork/disallow_list_oracle.go | 46 ++++++++++ network/mocknetwork/middleware.go | 28 ++++++ network/p2p/mock/disallow_list_consumer.go | 33 ------- .../disallow_list_notification_distributor.go | 87 ------------------- 5 files changed, 82 insertions(+), 123 deletions(-) rename network/{p2p/mock => mocknetwork}/disallow_list_notification_consumer.go (76%) create mode 100644 network/mocknetwork/disallow_list_oracle.go delete mode 100644 network/p2p/mock/disallow_list_consumer.go delete mode 100644 network/p2p/mock/disallow_list_notification_distributor.go diff --git a/network/p2p/mock/disallow_list_notification_consumer.go b/network/mocknetwork/disallow_list_notification_consumer.go similarity index 76% rename from network/p2p/mock/disallow_list_notification_consumer.go rename to network/mocknetwork/disallow_list_notification_consumer.go index 3bc4251530e..802caddf023 100644 --- a/network/p2p/mock/disallow_list_notification_consumer.go +++ b/network/mocknetwork/disallow_list_notification_consumer.go @@ -1,9 +1,9 @@ // Code generated by mockery v2.21.4. DO NOT EDIT. -package mockp2p +package mocknetwork import ( - p2p "github.com/onflow/flow-go/network/p2p" + network "github.com/onflow/flow-go/network" mock "github.com/stretchr/testify/mock" ) @@ -12,8 +12,13 @@ type DisallowListNotificationConsumer struct { mock.Mock } +// OnAllowListNotification provides a mock function with given fields: _a0 +func (_m *DisallowListNotificationConsumer) OnAllowListNotification(_a0 *network.AllowListingUpdate) { + _m.Called(_a0) +} + // OnDisallowListNotification provides a mock function with given fields: _a0 -func (_m *DisallowListNotificationConsumer) OnDisallowListNotification(_a0 *p2p.RemoteNodesAllowListingUpdate) { +func (_m *DisallowListNotificationConsumer) OnDisallowListNotification(_a0 *network.DisallowListingUpdate) { _m.Called(_a0) } diff --git a/network/mocknetwork/disallow_list_oracle.go b/network/mocknetwork/disallow_list_oracle.go new file mode 100644 index 00000000000..3bae6e851f3 --- /dev/null +++ b/network/mocknetwork/disallow_list_oracle.go @@ -0,0 +1,46 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + network "github.com/onflow/flow-go/network" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" +) + +// DisallowListOracle is an autogenerated mock type for the DisallowListOracle type +type DisallowListOracle struct { + mock.Mock +} + +// GetAllDisallowedListCausesFor provides a mock function with given fields: _a0 +func (_m *DisallowListOracle) GetAllDisallowedListCausesFor(_a0 peer.ID) []network.DisallowListedCause { + ret := _m.Called(_a0) + + var r0 []network.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + return r0 +} + +type mockConstructorTestingTNewDisallowListOracle interface { + mock.TestingT + Cleanup(func()) +} + +// NewDisallowListOracle creates a new instance of DisallowListOracle. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDisallowListOracle(t mockConstructorTestingTNewDisallowListOracle) *DisallowListOracle { + mock := &DisallowListOracle{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index 457d8fd7360..2262046f289 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -14,6 +14,8 @@ import ( network "github.com/onflow/flow-go/network" + peer "github.com/libp2p/go-libp2p/core/peer" + protocol "github.com/libp2p/go-libp2p/core/protocol" ) @@ -38,6 +40,22 @@ func (_m *Middleware) Done() <-chan struct{} { return r0 } +// GetAllDisallowedListCausesFor provides a mock function with given fields: _a0 +func (_m *Middleware) GetAllDisallowedListCausesFor(_a0 peer.ID) []network.DisallowListedCause { + ret := _m.Called(_a0) + + var r0 []network.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + return r0 +} + // IsConnected provides a mock function with given fields: nodeID func (_m *Middleware) IsConnected(nodeID flow.Identifier) (bool, error) { ret := _m.Called(nodeID) @@ -101,6 +119,16 @@ func (_m *Middleware) NewPingService(pingProtocol protocol.ID, provider network. return r0 } +// OnAllowListNotification provides a mock function with given fields: _a0 +func (_m *Middleware) OnAllowListNotification(_a0 *network.AllowListingUpdate) { + _m.Called(_a0) +} + +// OnDisallowListNotification provides a mock function with given fields: _a0 +func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpdate) { + _m.Called(_a0) +} + // Publish provides a mock function with given fields: msg func (_m *Middleware) Publish(msg *network.OutgoingMessageScope) error { ret := _m.Called(msg) diff --git a/network/p2p/mock/disallow_list_consumer.go b/network/p2p/mock/disallow_list_consumer.go deleted file mode 100644 index 2800a5aa909..00000000000 --- a/network/p2p/mock/disallow_list_consumer.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mockp2p - -import ( - flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" -) - -// DisallowListConsumer is an autogenerated mock type for the DisallowListConsumer type -type DisallowListConsumer struct { - mock.Mock -} - -// OnNodeDisallowListUpdate provides a mock function with given fields: list -func (_m *DisallowListConsumer) OnNodeDisallowListUpdate(list flow.IdentifierList) { - _m.Called(list) -} - -type mockConstructorTestingTNewDisallowListConsumer interface { - mock.TestingT - Cleanup(func()) -} - -// NewDisallowListConsumer creates a new instance of DisallowListConsumer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDisallowListConsumer(t mockConstructorTestingTNewDisallowListConsumer) *DisallowListConsumer { - mock := &DisallowListConsumer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/network/p2p/mock/disallow_list_notification_distributor.go b/network/p2p/mock/disallow_list_notification_distributor.go deleted file mode 100644 index 130f18a2726..00000000000 --- a/network/p2p/mock/disallow_list_notification_distributor.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mockp2p - -import ( - flow "github.com/onflow/flow-go/model/flow" - irrecoverable "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network" - - mock "github.com/stretchr/testify/mock" -) - -// DisallowListNotificationDistributor is an autogenerated mock type for the DisallowListNotificationDistributor type -type DisallowListNotificationDistributor struct { - mock.Mock -} - -// AddConsumer provides a mock function with given fields: _a0 -func (_m *DisallowListNotificationDistributor) AddConsumer(_a0 network.DisallowListNotificationConsumer) { - _m.Called(_a0) -} - -// DistributeBlockListNotification provides a mock function with given fields: list -func (_m *DisallowListNotificationDistributor) DistributeBlockListNotification(list flow.IdentifierList) error { - ret := _m.Called(list) - - var r0 error - if rf, ok := ret.Get(0).(func(flow.IdentifierList) error); ok { - r0 = rf(list) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Done provides a mock function with given fields: -func (_m *DisallowListNotificationDistributor) Done() <-chan struct{} { - ret := _m.Called() - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// Ready provides a mock function with given fields: -func (_m *DisallowListNotificationDistributor) Ready() <-chan struct{} { - ret := _m.Called() - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// Start provides a mock function with given fields: _a0 -func (_m *DisallowListNotificationDistributor) Start(_a0 irrecoverable.SignalerContext) { - _m.Called(_a0) -} - -type mockConstructorTestingTNewDisallowListNotificationDistributor interface { - mock.TestingT - Cleanup(func()) -} - -// NewDisallowListNotificationDistributor creates a new instance of DisallowListNotificationDistributor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDisallowListNotificationDistributor(t mockConstructorTestingTNewDisallowListNotificationDistributor) *DisallowListNotificationDistributor { - mock := &DisallowListNotificationDistributor{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From 2eababa36e54a6889ff0b7e42401921bfd73b609 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 16:58:00 -0700 Subject: [PATCH 056/815] lint fix --- module/metrics/labels.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/metrics/labels.go b/module/metrics/labels.go index c2d36639972..57c905b4b92 100644 --- a/module/metrics/labels.go +++ b/module/metrics/labels.go @@ -91,7 +91,7 @@ const ( ResourceNetworkingApplicationLayerSpamRecordCache = "application_layer_spam_record_cache" ResourceNetworkingApplicationLayerSpamReportQueue = "application_layer_spam_report_queue" ResourceNetworkingRpcClusterPrefixReceivedCache = "rpc_cluster_prefixed_received_cache" - ResourceNetworkingDisallowListCache = "networking_disallow_list_cache" + ResourceNetworkingDisallowListCache = "networking_disallow_list_cache" ResourceFollowerPendingBlocksCache = "follower_pending_block_cache" // follower engine ResourceClusterBlockProposalQueue = "cluster_compliance_proposal_queue" // collection node, compliance engine From a01d718c5b13b8da9ed8847481ec242b282e94d2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 17:14:51 -0700 Subject: [PATCH 057/815] fixes tests --- .../p2p/cache/node_blocklist_wrapper_test.go | 93 ++++++++++++++----- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index dbd0b5d1b51..7315de99f08 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -14,9 +14,10 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" mocks "github.com/onflow/flow-go/module/mock" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/cache" - mockp2p "github.com/onflow/flow-go/network/p2p/mock" "github.com/onflow/flow-go/utils/unittest" ) @@ -25,8 +26,8 @@ type NodeBlocklistWrapperTestSuite struct { DB *badger.DB provider *mocks.IdentityProvider - wrapper *cache.NodeBlocklistWrapper - distributor *mockp2p.DisallowListNotificationDistributor + wrapper *cache.NodeBlocklistWrapper + updateConsumer *mocknetwork.DisallowListNotificationConsumer } func (s *NodeBlocklistWrapperTestSuite) SetupTest() { @@ -34,8 +35,8 @@ func (s *NodeBlocklistWrapperTestSuite) SetupTest() { s.provider = new(mocks.IdentityProvider) var err error - s.distributor = mockp2p.NewDisallowListNotificationDistributor(s.T()) - s.wrapper, err = cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + s.updateConsumer = mocknetwork.NewDisallowListNotificationConsumer(s.T()) + s.wrapper, err = cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) } @@ -93,7 +94,10 @@ func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { // generality. func (s *NodeBlocklistWrapperTestSuite) TestDenylistedNode() { blocklist := unittest.IdentityListFixture(11) - s.distributor.On("DistributeBlockListNotification", blocklist.NodeIDs()).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist.NodeIDs(), + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(blocklist.NodeIDs()) require.NoError(s.T(), err) @@ -237,7 +241,10 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { // step 2: _after_ putting node on disallowList, // an Identity with `Ejected` equal to `true` should be returned - s.distributor.On("DistributeBlockListNotification", flow.IdentifierList{originalIdentity.NodeID}).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: flow.IdentifierList{originalIdentity.NodeID}, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(flow.IdentifierList{originalIdentity.NodeID}) require.NoError(s.T(), err) @@ -251,7 +258,10 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { // step 3: after removing the node from the disallowList, // an Identity with `Ejected` equal to the original value should be returned - s.distributor.On("DistributeBlockListNotification", flow.IdentifierList{}).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: flow.IdentifierList{}, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.Update(flow.IdentifierList{}) require.NoError(s.T(), err) @@ -278,22 +288,34 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { blocklist2 := unittest.IdentifierListFixture(11) blocklist3 := unittest.IdentifierListFixture(5) - s.distributor.On("DistributeBlockListNotification", blocklist1).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist1, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(blocklist1) require.NoError(s.T(), err) require.Equal(s.T(), blocklist1.Lookup(), s.wrapper.GetBlocklist().Lookup()) - s.distributor.On("DistributeBlockListNotification", blocklist2).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist2, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.Update(blocklist2) require.NoError(s.T(), err) require.Equal(s.T(), blocklist2.Lookup(), s.wrapper.GetBlocklist().Lookup()) - s.distributor.On("DistributeBlockListNotification", (flow.IdentifierList)(nil)).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: nil, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.ClearBlocklist() require.NoError(s.T(), err) require.Empty(s.T(), s.wrapper.GetBlocklist()) - s.distributor.On("DistributeBlockListNotification", blocklist3).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist3, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.Update(blocklist3) require.NoError(s.T(), err) require.Equal(s.T(), blocklist3.Lookup(), s.wrapper.GetBlocklist().Lookup()) @@ -319,39 +341,51 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { }) s.Run("Clear disallowList on empty database", func() { - s.distributor.On("DistributeBlockListNotification", (flow.IdentifierList)(nil)).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: nil, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.ClearBlocklist() // No-op as data base does not contain any block list require.NoError(s.T(), err) require.Empty(s.T(), s.wrapper.GetBlocklist()) // newly created wrapper should read `disallowList` from data base during initialization - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Empty(s.T(), w.GetBlocklist()) }) s.Run("Update disallowList and init new wrapper from database", func() { - s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) // newly created wrapper should read `disallowList` from data base during initialization - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Equal(s.T(), blocklist.Lookup(), w.GetBlocklist().Lookup()) }) s.Run("Update and overwrite disallowList and then init new wrapper from database", func() { - s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) - s.distributor.On("DistributeBlockListNotification", blocklist2).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist2, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.Update(blocklist2) require.NoError(s.T(), err) // newly created wrapper should read initial state from data base - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Equal(s.T(), blocklist2.Lookup(), w.GetBlocklist().Lookup()) }) @@ -359,31 +393,40 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { s.Run("Update & clear & update and then init new wrapper from database", func() { // set disallowList -> // newly created wrapper should now read this list from data base during initialization - s.distributor.On("DistributeBlockListNotification", blocklist).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err := s.wrapper.Update(blocklist) require.NoError(s.T(), err) - w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Equal(s.T(), blocklist.Lookup(), w0.GetBlocklist().Lookup()) // clear disallowList -> // newly created wrapper should now read empty disallowList from data base during initialization - s.distributor.On("DistributeBlockListNotification", (flow.IdentifierList)(nil)).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: nil, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.ClearBlocklist() require.NoError(s.T(), err) - w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Empty(s.T(), w1.GetBlocklist()) // set blocklist2 -> // newly created wrapper should now read this list from data base during initialization - s.distributor.On("DistributeBlockListNotification", blocklist2).Return(nil).Once() + s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: blocklist2, + Cause: network.DisallowListedCauseAdmin, + }).Return().Once() err = s.wrapper.Update(blocklist2) require.NoError(s.T(), err) - w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.distributor) + w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) require.Equal(s.T(), blocklist2.Lookup(), w2.GetBlocklist().Lookup()) }) From acab9d389c8cdeb30844c130e4ad473b8235d37d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 29 May 2023 17:20:13 -0700 Subject: [PATCH 058/815] fixes tests --- cmd/observer/node_builder/observer_builder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index eae470bdc4a..5d2bf089285 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -1088,7 +1088,6 @@ func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) - builder.NodeDisallowListDistributor.AddConsumer(mw) builder.Middleware = mw return builder.Middleware } From c93c56871bf29ad8b9a64f47ded0a54f67d67147 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 1 Jun 2023 13:55:46 +0300 Subject: [PATCH 059/815] Added config flags and connect circuit breaker. --- cmd/access/node_builder/access_node_builder.go | 13 +++++++++++++ .../access/rpc/backend/connection_factory.go | 18 +++++++++++++++--- engine/access/rpc/engine.go | 2 ++ go.mod | 1 + go.sum | 2 ++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 66355eaed39..b44f8592526 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -158,6 +158,11 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { FixedExecutionNodeIDs: nil, ArchiveAddressList: nil, MaxMsgSize: grpcutils.DefaultMaxMsgSize, + CircuitBreakerConfig: backend.CircuitBreakerConfig{ + CircuitBreakerEnabled: false, + RestoreTimeout: time.Duration(60) * time.Second, + MaxRequestToBreak: 5, + }, }, stateStreamConf: state_stream.Config{ MaxExecutionDataMsgSize: grpcutils.DefaultMaxMsgSize, @@ -644,6 +649,9 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringToIntVar(&builder.apiBurstlimits, "api-burst-limits", defaultConfig.apiBurstlimits, "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") flags.BoolVar(&builder.supportsObserver, "supports-observer", defaultConfig.supportsObserver, "true if this staked access node supports observer or follower connections") flags.StringVar(&builder.PublicNetworkConfig.BindAddress, "public-network-address", defaultConfig.PublicNetworkConfig.BindAddress, "staked access node's public network bind address") + flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled, "whether to enable the circuit breaker for collection and execution node connections") + flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "initial timeout for circuit breaker to try connect again. Default value is 60s") + flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "circuit-breaker-max-request-to-break", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "number of consecutive failures to break connection. Default value is 5") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") @@ -704,6 +712,11 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { } } } + if builder.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled { + if builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak == 0 { + return errors.New("circuit-breaker-max-request-to-break must be greater than 0") + } + } return nil }) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 63ead3d3e32..f56ddc55471 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -12,6 +12,7 @@ import ( "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" + "github.com/sony/gobreaker" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" @@ -61,6 +62,14 @@ type ConnectionFactoryImpl struct { AccessMetrics module.AccessMetrics Log zerolog.Logger mutex sync.Mutex + CircuitBreakerConfig CircuitBreakerConfig +} + +// TODO: describe +type CircuitBreakerConfig struct { + CircuitBreakerEnabled bool + RestoreTimeout time.Duration + MaxRequestToBreak uint32 } type CachedClient struct { @@ -250,7 +259,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { } func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { - + circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{}) clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -265,9 +274,12 @@ func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) defer cancel() + _, err := circuitBreaker.Execute(func() (interface{}, error) { + // call the remote GRPC using the short context + err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) - // call the remote GRPC using the short context - err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) + return nil, err + }) return err } diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 76df14a2127..ae25ecfcb76 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -48,6 +48,7 @@ type Config struct { PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node + CircuitBreakerConfig backend.CircuitBreakerConfig //TODO: } // Engine exposes the server with a simplified version of the Access API. @@ -171,6 +172,7 @@ func NewBuilder(log zerolog.Logger, MaxMsgSize: config.MaxMsgSize, AccessMetrics: accessMetrics, Log: log, + CircuitBreakerConfig: config.CircuitBreakerConfig, } backend := backend.New(state, diff --git a/go.mod b/go.mod index 602fb4c15fd..6e2a08a10de 100644 --- a/go.mod +++ b/go.mod @@ -242,6 +242,7 @@ require ( github.com/psiemens/sconfig v0.1.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.0 // indirect diff --git a/go.sum b/go.sum index ed305eed14f..16dd9935de9 100644 --- a/go.sum +++ b/go.sum @@ -1421,6 +1421,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= From 3f37d4fea94bba9b657c2ecfc0b2182e6c90918f Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 1 Jun 2023 15:01:25 +0300 Subject: [PATCH 060/815] Created CB baed on configuration. --- .../rpc/backend/node_connection_guard.go | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 engine/access/rpc/backend/node_connection_guard.go diff --git a/engine/access/rpc/backend/node_connection_guard.go b/engine/access/rpc/backend/node_connection_guard.go new file mode 100644 index 00000000000..a46029219a1 --- /dev/null +++ b/engine/access/rpc/backend/node_connection_guard.go @@ -0,0 +1,243 @@ +package backend + +import ( + "context" + "fmt" + "github.com/onflow/flow-go/storage" + "time" + + "github.com/rs/zerolog" + "github.com/sony/gobreaker" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/state/protocol" +) + +type NodeSelector interface { + GetExecutionNodesForBlockID(ctx context.Context, blockID flow.Identifier) (flow.IdentityList, error) + GetCollectionNodes(txID flow.Identifier) ([]string, error) +} + +type NodeConnectionGuard struct { + state protocol.State + executionReceipts storage.ExecutionReceipts + log zerolog.Logger + circuitBreaker *gobreaker.CircuitBreaker + connectionFactory ConnectionFactory +} + +var _ NodeSelector = (*NodeConnectionGuard)(nil) + +func NewNodeConnectionGuard(connectionFactory ConnectionFactory, state protocol.State, executionReceipts storage.ExecutionReceipts, log zerolog.Logger) NodeConnectionGuard { + return NodeConnectionGuard{ + state: state, + executionReceipts: executionReceipts, + log: log, + circuitBreaker: gobreaker.NewCircuitBreaker(gobreaker.Settings{}), + connectionFactory: connectionFactory, + } +} + +func (ncg *NodeConnectionGuard) Invoke(req func() (interface{}, error)) (interface{}, error) { + result, err := ncg.circuitBreaker.Execute(req) + return result, err +} + +func (ncg *NodeConnectionGuard) GetCollectionNodes(txId flow.Identifier) ([]string, error) { + // retrieve the set of collector clusters + clusters, err := ncg.state.Final().Epochs().Current().Clustering() + if err != nil { + return nil, fmt.Errorf("could not cluster collection nodes: %w", err) + } + + // get the cluster responsible for the transaction + txCluster, ok := clusters.ByTxID(txId) + if !ok { + return nil, fmt.Errorf("could not get local cluster by txID: %x", txId) + } + + // select a random subset of collection nodes from the cluster to be tried in order + //TODO: Change to cb selection of nodes. + targetNodes := txCluster.Sample(3) + + // collect the addresses of all the chosen collection nodes + var targetAddrs = make([]string, len(targetNodes)) + for i, id := range targetNodes { + targetAddrs[i] = id.Address + } + + return targetAddrs, nil +} + +// GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities +// which have executed the given block ID. +// If no such execution node is found, an InsufficientExecutionReceipts error is returned. +func (ncg *NodeConnectionGuard) GetExecutionNodesForBlockID( + ctx context.Context, + blockID flow.Identifier) (flow.IdentityList, error) { + + var executorIDs flow.IdentifierList + + // check if the block ID is of the root block. If it is then don't look for execution receipts since they + // will not be present for the root block. + rootBlock, err := ncg.state.Params().Root() + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + + if rootBlock.ID() == blockID { + executorIdentities, err := ncg.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = executorIdentities.NodeIDs() + } else { + // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { + executorIDs, err = ncg.findAllExecutionNodes(blockID) + if err != nil { + return nil, err + } + + if len(executorIDs) >= minExecutionNodesCnt { + break + } + + // log the attempt + ncg.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + Int("execution_receipts_found", len(executorIDs)). + Str("block_id", blockID.String()). + Msg("insufficient execution receipts") + + // if one or less execution receipts may have been received then re-query + // in the hope that more might have been received by now + //TODO: Should be removed + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(100 * time.Millisecond << time.Duration(attempt)): + //retry after an exponential backoff + } + } + + receiptCnt := len(executorIDs) + // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs + if receiptCnt < minExecutionNodesCnt { + newExecutorIDs, err := ncg.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = newExecutorIDs.NodeIDs() + } + } + + // choose from the preferred or fixed execution nodes + subsetENs, err := ncg.chooseExecutionNodes(executorIDs) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + + // randomly choose upto maxExecutionNodesCnt identities + executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) + + if len(executionIdentitiesRandom) == 0 { + return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) + } + + return executionIdentitiesRandom, nil +} + +// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the +// given blockID +func (ncg *NodeConnectionGuard) findAllExecutionNodes( + blockID flow.Identifier) (flow.IdentifierList, error) { + + // lookup the receipt's storage with the block ID + allReceipts, err := ncg.executionReceipts.ByBlockID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) + } + + executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) + for _, r := range allReceipts { + executionResultMetaList = append(executionResultMetaList, r.Meta()) + } + executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() + + // maximum number of matching receipts found so far for any execution result id + maxMatchedReceiptCnt := 0 + // execution result id key for the highest number of matching receipts in the identicalReceipts map + var maxMatchedReceiptResultID flow.Identifier + + // find the largest list of receipts which have the same result ID + for resultID, executionReceiptList := range executionResultGroupedMetaList { + currentMatchedReceiptCnt := executionReceiptList.Size() + if currentMatchedReceiptCnt > maxMatchedReceiptCnt { + maxMatchedReceiptCnt = currentMatchedReceiptCnt + maxMatchedReceiptResultID = resultID + } + } + + // if there are more than one execution result for the same block ID, log as error + if executionResultGroupedMetaList.NumberGroups() > 1 { + identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) + ncg.log.Error(). + Str("block_id", blockID.String()). + Str("execution_receipts", identicalReceiptsStr). + Msg("execution receipt mismatch") + } + + // pick the largest list of matching receipts + matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) + + metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() + + // collect all unique execution node ids from the receipts + var executorIDs flow.IdentifierList + for executorID := range metaReceiptGroupedByExecutorID { + executorIDs = append(executorIDs, executorID) + } + + return executorIDs, nil +} + +// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first +// choosing the preferred execution nodes which have executed the transaction. If no such preferred +// execution nodes are found, then the fixed execution nodes defined in the identity table are returned +// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. +// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} +// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs +func (ncg *NodeConnectionGuard) chooseExecutionNodes(executorIDs flow.IdentifierList) (flow.IdentityList, error) { + + allENs, err := ncg.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive all execution IDs: %w", err) + } + + // first try and choose from the preferred EN IDs + var chosenIDs flow.IdentityList + if len(preferredENIdentifiers) > 0 { + // find the preferred execution node IDs which have executed the transaction + chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(preferredENIdentifiers...), + filter.HasNodeID(executorIDs...))) + if len(chosenIDs) > 0 { + return chosenIDs, nil + } + } + + // if no preferred EN ID is found, then choose from the fixed EN IDs + if len(fixedENIdentifiers) > 0 { + // choose fixed ENs which have executed the transaction + chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(fixedENIdentifiers...), filter.HasNodeID(executorIDs...))) + if len(chosenIDs) > 0 { + return chosenIDs, nil + } + // if no such ENs are found then just choose all fixed ENs + chosenIDs = allENs.Filter(filter.HasNodeID(fixedENIdentifiers...)) + return chosenIDs, nil + } + + // If no preferred or fixed ENs have been specified, then return all executor IDs i.e. no preference at all + return allENs.Filter(filter.HasNodeID(executorIDs...)), nil +} From 41e1b99ef3a1f6804d822f88eeb6c009bb47442c Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 1 Jun 2023 15:02:42 +0300 Subject: [PATCH 061/815] Created CB baed on configuration. --- apiproxy/access_api_proxy.go | 8 +++- .../node_builder/access_node_builder.go | 12 +++--- engine/access/apiproxy/access_api_proxy.go | 8 +++- .../access/rpc/backend/connection_factory.go | 41 ++++++++++++++----- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/apiproxy/access_api_proxy.go b/apiproxy/access_api_proxy.go index 8e0b781af5e..d54b1dab483 100644 --- a/apiproxy/access_api_proxy.go +++ b/apiproxy/access_api_proxy.go @@ -86,7 +86,9 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithInsecure(), //nolint:staticcheck - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ + Enabled: false, + })) if err != nil { return err } @@ -100,7 +102,9 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ + Enabled: false, + })) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index b44f8592526..0716ed6de62 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -159,9 +159,9 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { ArchiveAddressList: nil, MaxMsgSize: grpcutils.DefaultMaxMsgSize, CircuitBreakerConfig: backend.CircuitBreakerConfig{ - CircuitBreakerEnabled: false, - RestoreTimeout: time.Duration(60) * time.Second, - MaxRequestToBreak: 5, + Enabled: false, + RestoreTimeout: time.Duration(60) * time.Second, + MaxRequestToBreak: 5, }, }, stateStreamConf: state_stream.Config{ @@ -649,7 +649,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringToIntVar(&builder.apiBurstlimits, "api-burst-limits", defaultConfig.apiBurstlimits, "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") flags.BoolVar(&builder.supportsObserver, "supports-observer", defaultConfig.supportsObserver, "true if this staked access node supports observer or follower connections") flags.StringVar(&builder.PublicNetworkConfig.BindAddress, "public-network-address", defaultConfig.PublicNetworkConfig.BindAddress, "staked access node's public network bind address") - flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled, "whether to enable the circuit breaker for collection and execution node connections") + flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "whether to enable the circuit breaker for collection and execution node connections") flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "initial timeout for circuit breaker to try connect again. Default value is 60s") flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "circuit-breaker-max-request-to-break", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "number of consecutive failures to break connection. Default value is 5") @@ -712,7 +712,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { } } } - if builder.rpcConf.CircuitBreakerConfig.CircuitBreakerEnabled { + if builder.rpcConf.CircuitBreakerConfig.Enabled { if builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak == 0 { return errors.New("circuit-breaker-max-request-to-break must be greater than 0") } @@ -876,7 +876,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.rpcConf.CollectionAddr, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout)) + backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout, builder.rpcConf.CircuitBreakerConfig)) if err != nil { return err } diff --git a/engine/access/apiproxy/access_api_proxy.go b/engine/access/apiproxy/access_api_proxy.go index d72ec5bb5e2..7123411cc2b 100644 --- a/engine/access/apiproxy/access_api_proxy.go +++ b/engine/access/apiproxy/access_api_proxy.go @@ -65,7 +65,9 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ + Enabled: false, + })) if err != nil { return err } @@ -79,7 +81,9 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ + Enabled: false, + })) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index f56ddc55471..bb4f5e3548f 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -67,9 +67,9 @@ type ConnectionFactoryImpl struct { // TODO: describe type CircuitBreakerConfig struct { - CircuitBreakerEnabled bool - RestoreTimeout time.Duration - MaxRequestToBreak uint32 + Enabled bool + RestoreTimeout time.Duration + MaxRequestToBreak uint32 } type CachedClient struct { @@ -102,7 +102,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(cf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithKeepaliveParams(keepaliveParams), - WithClientUnaryInterceptor(timeout)) + WithClientUnaryInterceptor(timeout, cf.CircuitBreakerConfig)) if err != nil { return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) } @@ -258,8 +258,18 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { - circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{}) +func WithClientUnaryInterceptor(timeout time.Duration, circuitBreakerConfig CircuitBreakerConfig) grpc.DialOption { + var circuitBreaker *gobreaker.CircuitBreaker + + if circuitBreakerConfig.Enabled { + circuitBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{ + Timeout: circuitBreakerConfig.RestoreTimeout, + ReadyToTrip: func(counts gobreaker.Counts) bool { + return counts.ConsecutiveFailures > circuitBreakerConfig.MaxRequestToBreak + }, + }) + } + clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -269,17 +279,26 @@ func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { + exec := func() (interface{}, error) { + // create a context that expires after timeout + ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - // create a context that expires after timeout - ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) + defer cancel() - defer cancel() - _, err := circuitBreaker.Execute(func() (interface{}, error) { // call the remote GRPC using the short context err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) + //TODO: As invoker do not return any results, for now nil returned return nil, err - }) + } + + var err error + + if circuitBreakerConfig.Enabled { + _, err = circuitBreaker.Execute(exec) + } else { + _, err = exec() + } return err } From d0067c4343c74d5a01ef0c869a977b7e3f74b287 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 10:00:23 -0700 Subject: [PATCH 062/815] fix lint --- network/alsp/manager/manager_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 55fb4e8ca21..fb63a2609c2 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -41,7 +41,6 @@ import ( // The test ensures that the MisbehaviorReportManager receives and handles all reported misbehavior // without any duplicate reports and within a specified time. func TestNetworkPassesReportedMisbehavior(t *testing.T) { - cfg := managerCfgFixture() misbehaviorReportManger := mocknetwork.NewMisbehaviorReportManager(t) misbehaviorReportManger.On("Start", mock.Anything).Return().Once() From 3e3f07a4dc7599652ccd3b3c6e60e327915ecd46 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 10:42:21 -0700 Subject: [PATCH 063/815] libp2p implements disallow list consumer --- network/p2p/libp2pNode.go | 4 ++++ network/p2p/p2pnode/libp2pNode.go | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 1a7a87bd03d..e2db6f12599 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) @@ -31,6 +32,9 @@ type LibP2PNode interface { PeerConnections // PeerScore exposes the peer score API. PeerScore + // DisallowListNotificationConsumer exposes the disallow list notification consumer API for the node so that + // it will be notified when a new disallow list update is distributed. + network.DisallowListNotificationConsumer // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 977a5b393d3..2f759a3e65d 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -46,6 +46,8 @@ const ( findPeerQueryTimeout = 10 * time.Second ) +var _ p2p.LibP2PNode = (*Node)(nil) + // Node is a wrapper around the LibP2P host. type Node struct { component.Component @@ -444,3 +446,13 @@ func (n *Node) SetUnicastManager(uniMgr p2p.UnicastManager) { } n.uniMgr = uniMgr } + +func (n *Node) OnDisallowListNotification(update *flownet.DisallowListingUpdate) { + //TODO implement me + panic("implement me") +} + +func (n *Node) OnAllowListNotification(update *flownet.AllowListingUpdate) { + //TODO implement me + panic("implement me") +} From 7884cfe7e365f58500b25c6605aca2900192151f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 10:46:00 -0700 Subject: [PATCH 064/815] removes disallow list oracle --- network/middleware.go | 15 --------------- network/p2p/middleware/middleware.go | 10 ---------- 2 files changed, 25 deletions(-) diff --git a/network/middleware.go b/network/middleware.go index a33cabb2caf..c2eeef98905 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -17,7 +17,6 @@ import ( // connections, as well as reading & writing to/from the connections. type Middleware interface { component.Component - DisallowListOracle DisallowListNotificationConsumer // SetOverlay sets the overlay used by the middleware. This must be called before the middleware can be Started. @@ -59,20 +58,6 @@ type Middleware interface { IsConnected(nodeID flow.Identifier) (bool, error) } -// DisallowListOracle represents the interface that is exposed to the lower-level networking primitives (e.g. libp2p), -// which allows them to check if a given peer ID is disallowed (by Flow protocol) from connecting to this node. -// Disallow-listing is considered a temporary measure to prevent malicious nodes from connecting to the network. Hence, -// the disallow-list status of a peer ID should not be treated as a permanent state. -type DisallowListOracle interface { - // GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. - // Args: - // - peerID: the peer to check. - // Returns: - // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, - // an empty list is returned. - GetAllDisallowedListCausesFor(peer.ID) []DisallowListedCause -} - // Overlay represents the interface that middleware uses to interact with the // overlay network layer. type Overlay interface { diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index eede7783c43..4fe9d1add0f 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -359,16 +359,6 @@ func (m *Middleware) authorizedPeers() peer.IDSlice { return peerIDs } -// GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. -// Args: -// - peerID: the peer to check. -// Returns: -// - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, -// an empty list is returned. -func (m *Middleware) GetAllDisallowedListCausesFor(pid peer.ID) []network.DisallowListedCause { - return m.disallowListedCache.GetAllDisallowedListCausesFor(pid) -} - func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { for _, pid := range m.peerIDs(notification.FlowIds) { causes, err := m.disallowListedCache.DisallowFor(pid, notification.Cause) From 2d289b4147c82b467f51a41413364bc248f087b5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 10:49:22 -0700 Subject: [PATCH 065/815] moves disallow list logic to libp2p node --- network/p2p/middleware/middleware.go | 31 ++------------------------ network/p2p/p2pnode/libp2pNode.go | 33 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 4fe9d1add0f..9c7090ff5f1 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -360,38 +360,11 @@ func (m *Middleware) authorizedPeers() peer.IDSlice { } func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - causes, err := m.disallowListedCache.DisallowFor(pid, notification.Cause) - if err != nil { - // returned error is fatal. - m.log.Fatal().Err(err).Str("peer_id", pid.String()).Msg("failed to add peer to disallow list") - } - - // TODO: this code should further be refactored to also log the Flow id. - m.log.Warn(). - Str("peer_id", pid.String()). - Str("notification_cause", notification.Cause.String()). - Str("causes", fmt.Sprintf("%v", causes)). - Msg("peer added to disallow list cache") - - // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) - // hence, this code part can be removed. - err = m.libP2PNode.RemovePeer(pid) - if err != nil { - m.log.Error().Err(err).Str("peer_id", pid.String()).Msg("failed to disconnect from blocklisted peer") - } - } + m.libP2PNode.OnDisallowListNotification(notification) } func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.disallowListedCache.AllowFor(pid, notification.Cause) - - m.log.Debug(). - Str("peer_id", pid.String()). - Str("causes", fmt.Sprintf("%v", notification.Cause)). - Msg("peer added to disallow list cache") - } + m.libP2PNode.OnAllowListNotification(notification) } // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 2f759a3e65d..537f9f6b5f7 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -448,11 +448,36 @@ func (n *Node) SetUnicastManager(uniMgr p2p.UnicastManager) { } func (n *Node) OnDisallowListNotification(update *flownet.DisallowListingUpdate) { - //TODO implement me - panic("implement me") + for _, pid := range m.peerIDs(update.FlowIds) { + causes, err := m.disallowListedCache.DisallowFor(pid, update.Cause) + if err != nil { + // returned error is fatal. + n.logger.Fatal().Err(err).Str("peer_id", pid.String()).Msg("failed to add peer to disallow list") + } + + // TODO: this code should further be refactored to also log the Flow id. + n.logger.Warn(). + Str("peer_id", pid.String()). + Str("notification_cause", notification.Cause.String()). + Str("causes", fmt.Sprintf("%v", causes)). + Msg("peer added to disallow list cache") + + // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) + // hence, this code part can be removed. + err = n.RemovePeer(pid) + if err != nil { + n.logger.Error().Err(err).Str("peer_id", pid.String()).Msg("failed to disconnect from blocklisted peer") + } + } } func (n *Node) OnAllowListNotification(update *flownet.AllowListingUpdate) { - //TODO implement me - panic("implement me") + for _, pid := range m.peerIDs(notification.FlowIds) { + m.disallowListedCache.AllowFor(pid, notification.Cause) + + m.log.Debug(). + Str("peer_id", pid.String()). + Str("causes", fmt.Sprintf("%v", notification.Cause)). + Msg("peer added to disallow list cache") + } } From 38dc0823be2a44e3c6abfe903390a6708504e71b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 11:18:47 -0700 Subject: [PATCH 066/815] moves disallow list cache to libp2p node --- cmd/scaffold.go | 19 +++-- insecure/cmd/corrupted_builder.go | 7 ++ insecure/corruptlibp2p/libp2p_node_factory.go | 5 +- insecure/corruptlibp2p/p2p_node.go | 9 ++- network/p2p/builder.go | 3 +- network/p2p/libp2pNode.go | 15 +++- network/p2p/middleware/middleware.go | 28 ++----- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 33 ++++---- .../disallowListCache.go | 2 +- .../{middleware => p2pnode}/internal/cache.go | 4 +- .../internal/cacheEntity.go | 0 .../internal/cache_test.go | 2 +- network/p2p/p2pnode/libp2pNode.go | 78 ++++++++++++------- 13 files changed, 125 insertions(+), 80 deletions(-) rename network/p2p/{middleware => p2pnode}/disallowListCache.go (98%) rename network/p2p/{middleware => p2pnode}/internal/cache.go (98%) rename network/p2p/{middleware => p2pnode}/internal/cacheEntity.go (100%) rename network/p2p/{middleware => p2pnode}/internal/cache_test.go (99%) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 522309c115d..d6bb3ac98d5 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -55,6 +55,7 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/p2pbuilder/inspector" + "github.com/onflow/flow-go/network/p2p/p2pnode" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/unicast/protocols" @@ -412,7 +413,12 @@ func (fnb *FlowNodeBuilder) EnqueueNetworkInit() { fnb.GossipSubConfig, fnb.GossipSubRpcInspectorSuite, fnb.LibP2PResourceManagerConfig, - uniCfg) + uniCfg, + &p2pnode.DisallowListCacheConfig{ + MaxSize: fnb.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(fnb.HeroCacheMetricsFactory(), network.PrivateNetwork), + }, + ) if err != nil { return nil, fmt.Errorf("could not create libp2p node builder: %w", err) @@ -457,7 +463,12 @@ func (fnb *FlowNodeBuilder) HeroCacheMetricsFactory() metrics.HeroCacheMetricsFa return metrics.NewNoopHeroCacheMetricsFactory() } -func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory(node *NodeConfig, cf network.ConduitFactory, unicastRateLimiters *ratelimit.RateLimiters, peerManagerFilters []p2p.PeerFilter) (network.Network, error) { +func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( + node *NodeConfig, + cf network.ConduitFactory, + unicastRateLimiters *ratelimit.RateLimiters, + peerManagerFilters []p2p.PeerFilter) (network.Network, error) { + var mwOpts []middleware.OptionFn if len(fnb.MsgValidators) > 0 { mwOpts = append(mwOpts, middleware.WithMessageValidators(fnb.MsgValidators...)) @@ -485,10 +496,6 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory(node *NodeConfig, IdTranslator: fnb.IDTranslator, Codec: fnb.CodecFactory(), SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network), - DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ - MaxSize: fnb.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(fnb.HeroCacheMetricsFactory(), network.PrivateNetwork), - }, }, mwOpts...) diff --git a/insecure/cmd/corrupted_builder.go b/insecure/cmd/corrupted_builder.go index 7de352609a8..cda7e1c458b 100644 --- a/insecure/cmd/corrupted_builder.go +++ b/insecure/cmd/corrupted_builder.go @@ -11,8 +11,11 @@ import ( "github.com/onflow/flow-go/insecure/corruptnet" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + "github.com/onflow/flow-go/network/p2p/p2pnode" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" "github.com/onflow/flow-go/utils/logging" ) @@ -101,6 +104,10 @@ func (cnb *CorruptedNodeBuilder) enqueueNetworkingLayer() { peerManagerCfg, uniCfg, cnb.GossipSubConfig, + &p2pnode.DisallowListCacheConfig{ + MaxSize: cnb.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(cnb.HeroCacheMetricsFactory(), network.PrivateNetwork), + }, cnb.TopicValidatorDisabled, cnb.WithPubSubMessageSigning, cnb.WithPubSubStrictSignatureVerification, diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index f65c06ce731..055ab2c8f6c 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/p2pbuilder/inspector" + "github.com/onflow/flow-go/network/p2p/p2pnode" ) // InitCorruptLibp2pNode initializes and returns a corrupt libp2p node that should only be used for BFT testing in @@ -59,6 +60,7 @@ func InitCorruptLibp2pNode( peerManagerCfg *p2pconfig.PeerManagerConfig, uniCfg *p2pconfig.UnicastConfig, gossipSubCfg *p2pbuilder.GossipSubConfig, + disallowListCacheCfg *p2pnode.DisallowListCacheConfig, topicValidatorDisabled, withMessageSigning, withStrictSignatureVerification bool, @@ -94,7 +96,8 @@ func InitCorruptLibp2pNode( gossipSubCfg, rpcInspectorSuite, p2pbuilder.DefaultResourceManagerConfig(), - uniCfg) + uniCfg, + disallowListCacheCfg) if err != nil { return nil, fmt.Errorf("could not create corrupt libp2p node builder: %w", err) diff --git a/insecure/corruptlibp2p/p2p_node.go b/insecure/corruptlibp2p/p2p_node.go index 143e1a9e938..0174727d4bb 100644 --- a/insecure/corruptlibp2p/p2p_node.go +++ b/insecure/corruptlibp2p/p2p_node.go @@ -51,7 +51,12 @@ func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ p2p.TopicValidatorFun } // NewCorruptLibP2PNode returns corrupted libP2PNode that will subscribe to topics using the AcceptAllTopicValidator. -func NewCorruptLibP2PNode(logger zerolog.Logger, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager) p2p.LibP2PNode { - node := p2pnode.NewNode(logger, host, pCache, peerManager) +func NewCorruptLibP2PNode( + logger zerolog.Logger, + host host.Host, + pCache p2p.ProtocolPeerCache, + peerManager p2p.PeerManager, + disallowListCacheCfg *p2pnode.DisallowListCacheConfig) p2p.LibP2PNode { + node := p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) return &CorruptP2PNode{Node: node, logger: logger, codec: cbor.NewCodec()} } diff --git a/network/p2p/builder.go b/network/p2p/builder.go index 43037f5a90e..4b64e3e1278 100644 --- a/network/p2p/builder.go +++ b/network/p2p/builder.go @@ -16,10 +16,11 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/p2p/p2pnode" ) type GossipSubFactoryFunc func(context.Context, zerolog.Logger, host.Host, PubSubAdapterConfig) (PubSubAdapter, error) -type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager) LibP2PNode +type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager, *p2pnode.DisallowListCacheConfig) LibP2PNode type GossipSubAdapterConfigFunc func(*BasePubSubAdapterConfig) PubSubAdapterConfig // GossipSubBuilder provides a builder pattern for creating a GossipSub pubsub system. diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index e2db6f12599..999a786d33a 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -34,7 +34,7 @@ type LibP2PNode interface { PeerScore // DisallowListNotificationConsumer exposes the disallow list notification consumer API for the node so that // it will be notified when a new disallow list update is distributed. - network.DisallowListNotificationConsumer + DisallowListNotificationConsumer // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. @@ -113,3 +113,16 @@ type PeerConnections interface { // to the peer is not empty. This indicates a bug within libp2p. IsConnected(peerID peer.ID) (bool, error) } + +// DisallowListNotificationConsumer is an interface for consuming disallow/allow list update notifications. +type DisallowListNotificationConsumer interface { + // OnDisallowListNotification is called when a new disallow list update notification is distributed. + // Any error on consuming event must handle internally. + // The implementation must be concurrency safe. + OnDisallowListNotification(id peer.ID, cause network.DisallowListedCause) + + // OnAllowListNotification is called when a new allow list update notification is distributed. + // Any error on consuming event must handle internally. + // The implementation must be concurrency safe. + OnAllowListNotification(id peer.ID, cause network.DisallowListedCause) +} diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 9c7090ff5f1..ac5f349264a 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -107,9 +107,6 @@ type Middleware struct { slashingViolationsConsumer slashing.ViolationsConsumer unicastRateLimiters *ratelimit.RateLimiters authorizedSenderValidator *validator.AuthorizedSenderValidator - // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer - // are closed and further connections are not allowed till the peer is removed from the disallow-list. - disallowListedCache DisallowListCache } type OptionFn func(*Middleware) @@ -141,18 +138,6 @@ func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { } } -// DisallowListCacheConfig is the configuration for the disallow-list cache. -// The disallow-list cache is used to temporarily disallow-list peers. -type DisallowListCacheConfig struct { - // MaxSize is the maximum number of peers that can be disallow-listed at any given time. - // When the cache is full, no further new peers can be disallow-listed. - // Recommended size is 100 * number of staked nodes. - MaxSize uint32 - - // Metrics is the HeroCache metrics collector to be used for the disallow-list cache. - Metrics module.HeroCacheMetrics -} - // Config is the configuration for the middleware. type Config struct { Logger zerolog.Logger @@ -164,7 +149,6 @@ type Config struct { IdTranslator p2p.IDTranslator Codec network.Codec SlashingViolationsConsumer slashing.ViolationsConsumer - DisallowListCacheConfig *DisallowListCacheConfig } // Validate validates the configuration, and sets default values for any missing fields. @@ -172,10 +156,6 @@ func (cfg *Config) Validate() { if cfg.UnicastMessageTimeout <= 0 { cfg.UnicastMessageTimeout = DefaultUnicastTimeout } - - if cfg.DisallowListCacheConfig.MaxSize == uint32(0) { - cfg.DisallowListCacheConfig.MaxSize = DisallowListCacheSize - } } // NewMiddleware creates a new middleware instance @@ -360,11 +340,15 @@ func (m *Middleware) authorizedPeers() peer.IDSlice { } func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { - m.libP2PNode.OnDisallowListNotification(notification) + for _, pid := range m.peerIDs(notification.FlowIds) { + m.libP2PNode.OnDisallowListNotification(pid, notification.Cause) + } } func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { - m.libP2PNode.OnAllowListNotification(notification) + for _, pid := range m.peerIDs(notification.FlowIds) { + m.libP2PNode.OnAllowListNotification(pid, notification.Cause) + } } // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index e388f35397e..2a67e6ecbc2 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -136,6 +136,7 @@ type LibP2PNodeBuilder struct { createStreamRetryInterval time.Duration rateLimiterDistributor p2p.UnicastRateLimiterDistributor gossipSubTracer p2p.PubSubTracer + disallowListCacheCfg *p2pnode.DisallowListCacheConfig } func NewNodeBuilder(logger zerolog.Logger, @@ -143,16 +144,18 @@ func NewNodeBuilder(logger zerolog.Logger, addr string, networkKey fcrypto.PrivateKey, sporkID flow.Identifier, - rCfg *ResourceManagerConfig) *LibP2PNodeBuilder { + rCfg *ResourceManagerConfig, + disallowListCacheCfg *p2pnode.DisallowListCacheConfig) *LibP2PNodeBuilder { return &LibP2PNodeBuilder{ - logger: logger, - sporkID: sporkID, - addr: addr, - networkKey: networkKey, - createNode: DefaultCreateNodeFunc, - metrics: metrics, - resourceManagerCfg: rCfg, - gossipSubBuilder: gossipsubbuilder.NewGossipSubBuilder(logger, metrics), + logger: logger, + sporkID: sporkID, + addr: addr, + networkKey: networkKey, + createNode: DefaultCreateNodeFunc, + metrics: metrics, + resourceManagerCfg: rCfg, + gossipSubBuilder: gossipsubbuilder.NewGossipSubBuilder(logger, metrics), + disallowListCacheCfg: disallowListCacheCfg, } } @@ -368,7 +371,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { } } - node := builder.createNode(builder.logger, h, pCache, peerManager) + node := builder.createNode(builder.logger, h, pCache, peerManager, builder.disallowListCacheCfg) unicastManager := unicast.NewUnicastManager(builder.logger, stream.NewLibP2PStreamFactory(h), @@ -482,8 +485,9 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti func DefaultCreateNodeFunc(logger zerolog.Logger, host host.Host, pCache p2p.ProtocolPeerCache, - peerManager p2p.PeerManager) p2p.LibP2PNode { - return p2pnode.NewNode(logger, host, pCache, peerManager) + peerManager p2p.PeerManager, + disallowListCacheCfg *p2pnode.DisallowListCacheConfig) p2p.LibP2PNode { + return p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) } // DefaultNodeBuilder returns a node builder. @@ -500,7 +504,8 @@ func DefaultNodeBuilder(log zerolog.Logger, gossipCfg *GossipSubConfig, rpcInspectorSuite p2p.GossipSubInspectorSuite, rCfg *ResourceManagerConfig, - uniCfg *p2pconfig.UnicastConfig) (p2p.NodeBuilder, error) { + uniCfg *p2pconfig.UnicastConfig, + disallowListCacheCfg *p2pnode.DisallowListCacheConfig) (p2p.NodeBuilder, error) { connManager, err := connection.NewConnManager(log, metricsCfg.Metrics, connection.DefaultConnManagerConfig()) if err != nil { @@ -516,7 +521,7 @@ func DefaultNodeBuilder(log zerolog.Logger, connection.WithOnInterceptPeerDialFilters(append(peerFilters, connGaterCfg.InterceptPeerDialFilters...)), connection.WithOnInterceptSecuredFilters(append(peerFilters, connGaterCfg.InterceptSecuredFilters...))) - builder := NewNodeBuilder(log, metricsCfg.Metrics, address, flowKey, sporkId, rCfg). + builder := NewNodeBuilder(log, metricsCfg.Metrics, address, flowKey, sporkId, rCfg, disallowListCacheCfg). SetBasicResolver(resolver). SetConnectionManager(connManager). SetConnectionGater(connGater). diff --git a/network/p2p/middleware/disallowListCache.go b/network/p2p/p2pnode/disallowListCache.go similarity index 98% rename from network/p2p/middleware/disallowListCache.go rename to network/p2p/p2pnode/disallowListCache.go index 5f8ebcc0831..e65318b4877 100644 --- a/network/p2p/middleware/disallowListCache.go +++ b/network/p2p/p2pnode/disallowListCache.go @@ -1,4 +1,4 @@ -package middleware +package p2pnode import ( "github.com/libp2p/go-libp2p/core/peer" diff --git a/network/p2p/middleware/internal/cache.go b/network/p2p/p2pnode/internal/cache.go similarity index 98% rename from network/p2p/middleware/internal/cache.go rename to network/p2p/p2pnode/internal/cache.go index a3ea1ab198b..1c33af8ce8d 100644 --- a/network/p2p/middleware/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -13,7 +13,7 @@ import ( "github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool" "github.com/onflow/flow-go/module/mempool/stdmap" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network/p2p/p2pnode" ) var ( @@ -25,7 +25,7 @@ type DisallowListCache struct { c *stdmap.Backend } -var _ middleware.DisallowListCache = (*DisallowListCache)(nil) +var _ p2pnode.DisallowListCache = (*DisallowListCache)(nil) // NewDisallowListCache creates a new disallow-list cache. The cache is backed by a stdmap.Backend. // Args: diff --git a/network/p2p/middleware/internal/cacheEntity.go b/network/p2p/p2pnode/internal/cacheEntity.go similarity index 100% rename from network/p2p/middleware/internal/cacheEntity.go rename to network/p2p/p2pnode/internal/cacheEntity.go diff --git a/network/p2p/middleware/internal/cache_test.go b/network/p2p/p2pnode/internal/cache_test.go similarity index 99% rename from network/p2p/middleware/internal/cache_test.go rename to network/p2p/p2pnode/internal/cache_test.go index 2de5522d32d..365ce66cc2c 100644 --- a/network/p2p/middleware/internal/cache_test.go +++ b/network/p2p/p2pnode/internal/cache_test.go @@ -12,7 +12,7 @@ import ( "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/p2p/middleware/internal" + "github.com/onflow/flow-go/network/p2p/p2pnode/internal" "github.com/onflow/flow-go/utils/unittest" ) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 537f9f6b5f7..be6b0e7e1ef 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -18,12 +18,14 @@ import ( "github.com/libp2p/go-libp2p/core/routing" "github.com/rs/zerolog" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pnode/internal" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/utils/logging" ) @@ -62,6 +64,21 @@ type Node struct { pCache p2p.ProtocolPeerCache peerManager p2p.PeerManager peerScoreExposer p2p.PeerScoreExposer + // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer + // are closed and further connections are not allowed till the peer is removed from the disallow-list. + disallowListedCache DisallowListCache +} + +// DisallowListCacheConfig is the configuration for the disallow-list cache. +// The disallow-list cache is used to temporarily disallow-list peers. +type DisallowListCacheConfig struct { + // MaxSize is the maximum number of peers that can be disallow-listed at any given time. + // When the cache is full, no further new peers can be disallow-listed. + // Recommended size is 100 * number of staked nodes. + MaxSize uint32 + + // Metrics is the HeroCache metrics collector to be used for the disallow-list cache. + Metrics module.HeroCacheMetrics } // NewNode creates a new libp2p node and sets its parameters. @@ -70,14 +87,20 @@ func NewNode( host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, + disallowLstCacheCfg *DisallowListCacheConfig, ) *Node { + lg := logger.With().Str("component", "libp2p-node").Logger() return &Node{ host: host, - logger: logger.With().Str("component", "libp2p-node").Logger(), + logger: lg, topics: make(map[channels.Topic]p2p.Topic), subs: make(map[channels.Topic]p2p.Subscription), pCache: pCache, peerManager: peerManager, + disallowListedCache: internal.NewDisallowListCache( + disallowLstCacheCfg.MaxSize, + logger.With().Str("module", "disallow-list-cache").Logger(), + disallowLstCacheCfg.Metrics), } } @@ -447,37 +470,34 @@ func (n *Node) SetUnicastManager(uniMgr p2p.UnicastManager) { n.uniMgr = uniMgr } -func (n *Node) OnDisallowListNotification(update *flownet.DisallowListingUpdate) { - for _, pid := range m.peerIDs(update.FlowIds) { - causes, err := m.disallowListedCache.DisallowFor(pid, update.Cause) - if err != nil { - // returned error is fatal. - n.logger.Fatal().Err(err).Str("peer_id", pid.String()).Msg("failed to add peer to disallow list") - } - - // TODO: this code should further be refactored to also log the Flow id. - n.logger.Warn(). - Str("peer_id", pid.String()). - Str("notification_cause", notification.Cause.String()). - Str("causes", fmt.Sprintf("%v", causes)). - Msg("peer added to disallow list cache") +func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { + causes, err := n.disallowListedCache.DisallowFor(peerId, cause) + if err != nil { + // returned error is fatal. + n.logger.Fatal().Err(err).Str("peer_id", peerId.String()).Msg("failed to add peer to disallow list") + } - // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) - // hence, this code part can be removed. - err = n.RemovePeer(pid) - if err != nil { - n.logger.Error().Err(err).Str("peer_id", pid.String()).Msg("failed to disconnect from blocklisted peer") - } + // TODO: this code should further be refactored to also log the Flow id. + n.logger.Warn(). + Str("peer_id", peerId.String()). + Str("notification_cause", cause.String()). + Str("causes", fmt.Sprintf("%v", causes)). + Msg("peer added to disallow list cache") + + // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) + // hence, this code part can be removed. + err = n.RemovePeer(peerId) + if err != nil { + n.logger.Error().Err(err).Str("peer_id", peerId.String()).Msg("failed to disconnect from blocklisted peer") } + } -func (n *Node) OnAllowListNotification(update *flownet.AllowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.disallowListedCache.AllowFor(pid, notification.Cause) +func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { + n.disallowListedCache.AllowFor(peerId, cause) - m.log.Debug(). - Str("peer_id", pid.String()). - Str("causes", fmt.Sprintf("%v", notification.Cause)). - Msg("peer added to disallow list cache") - } + n.logger.Debug(). + Str("peer_id", peerId.String()). + Str("causes", fmt.Sprintf("%v", cause)). + Msg("peer added to disallow list cache") } From cc468c85375e8f435e92d7c6e2bd23983cede7ad Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 11:20:57 -0700 Subject: [PATCH 067/815] fixes import cycle --- network/p2p/p2pnode/internal/cache.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 1c33af8ce8d..355be3dd7ce 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -13,7 +13,6 @@ import ( "github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool" "github.com/onflow/flow-go/module/mempool/stdmap" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/p2p/p2pnode" ) var ( @@ -25,8 +24,6 @@ type DisallowListCache struct { c *stdmap.Backend } -var _ p2pnode.DisallowListCache = (*DisallowListCache)(nil) - // NewDisallowListCache creates a new disallow-list cache. The cache is backed by a stdmap.Backend. // Args: // - sizeLimit: the size limit of the cache, i.e., the maximum number of records that the cache can hold, recommended size is 100 * number of authorized nodes. From 97b6683f09131cb0f45b028cb904b59620b47212 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 11:28:46 -0700 Subject: [PATCH 068/815] moves disallowlist cache to p2p package --- cmd/scaffold.go | 3 +-- insecure/cmd/corrupted_builder.go | 3 +-- insecure/corruptlibp2p/libp2p_node_factory.go | 3 +-- insecure/corruptlibp2p/p2p_node.go | 2 +- network/p2p/builder.go | 3 +-- network/p2p/{p2pnode => }/disallowListCache.go | 15 ++++++++++++++- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 8 ++++---- network/p2p/p2pnode/libp2pNode.go | 17 ++--------------- 8 files changed, 25 insertions(+), 29 deletions(-) rename network/p2p/{p2pnode => }/disallowListCache.go (73%) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index d6bb3ac98d5..206ff3a85f7 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -55,7 +55,6 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/p2pbuilder/inspector" - "github.com/onflow/flow-go/network/p2p/p2pnode" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/unicast/protocols" @@ -414,7 +413,7 @@ func (fnb *FlowNodeBuilder) EnqueueNetworkInit() { fnb.GossipSubRpcInspectorSuite, fnb.LibP2PResourceManagerConfig, uniCfg, - &p2pnode.DisallowListCacheConfig{ + &p2p.DisallowListCacheConfig{ MaxSize: fnb.BaseConfig.NetworkConfig.DisallowListCacheSize, Metrics: metrics.DisallowListCacheMetricsFactory(fnb.HeroCacheMetricsFactory(), network.PrivateNetwork), }, diff --git a/insecure/cmd/corrupted_builder.go b/insecure/cmd/corrupted_builder.go index cda7e1c458b..5ce4c3411ad 100644 --- a/insecure/cmd/corrupted_builder.go +++ b/insecure/cmd/corrupted_builder.go @@ -15,7 +15,6 @@ import ( "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" - "github.com/onflow/flow-go/network/p2p/p2pnode" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" "github.com/onflow/flow-go/utils/logging" ) @@ -104,7 +103,7 @@ func (cnb *CorruptedNodeBuilder) enqueueNetworkingLayer() { peerManagerCfg, uniCfg, cnb.GossipSubConfig, - &p2pnode.DisallowListCacheConfig{ + &p2p.DisallowListCacheConfig{ MaxSize: cnb.BaseConfig.NetworkConfig.DisallowListCacheSize, Metrics: metrics.DisallowListCacheMetricsFactory(cnb.HeroCacheMetricsFactory(), network.PrivateNetwork), }, diff --git a/insecure/corruptlibp2p/libp2p_node_factory.go b/insecure/corruptlibp2p/libp2p_node_factory.go index 055ab2c8f6c..1f3e539079c 100644 --- a/insecure/corruptlibp2p/libp2p_node_factory.go +++ b/insecure/corruptlibp2p/libp2p_node_factory.go @@ -19,7 +19,6 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/p2pbuilder/inspector" - "github.com/onflow/flow-go/network/p2p/p2pnode" ) // InitCorruptLibp2pNode initializes and returns a corrupt libp2p node that should only be used for BFT testing in @@ -60,7 +59,7 @@ func InitCorruptLibp2pNode( peerManagerCfg *p2pconfig.PeerManagerConfig, uniCfg *p2pconfig.UnicastConfig, gossipSubCfg *p2pbuilder.GossipSubConfig, - disallowListCacheCfg *p2pnode.DisallowListCacheConfig, + disallowListCacheCfg *p2p.DisallowListCacheConfig, topicValidatorDisabled, withMessageSigning, withStrictSignatureVerification bool, diff --git a/insecure/corruptlibp2p/p2p_node.go b/insecure/corruptlibp2p/p2p_node.go index 0174727d4bb..b3fbd0cb36d 100644 --- a/insecure/corruptlibp2p/p2p_node.go +++ b/insecure/corruptlibp2p/p2p_node.go @@ -56,7 +56,7 @@ func NewCorruptLibP2PNode( host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, - disallowListCacheCfg *p2pnode.DisallowListCacheConfig) p2p.LibP2PNode { + disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { node := p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) return &CorruptP2PNode{Node: node, logger: logger, codec: cbor.NewCodec()} } diff --git a/network/p2p/builder.go b/network/p2p/builder.go index 4b64e3e1278..f82ecfd7934 100644 --- a/network/p2p/builder.go +++ b/network/p2p/builder.go @@ -16,11 +16,10 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/p2p/p2pnode" ) type GossipSubFactoryFunc func(context.Context, zerolog.Logger, host.Host, PubSubAdapterConfig) (PubSubAdapter, error) -type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager, *p2pnode.DisallowListCacheConfig) LibP2PNode +type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager, *DisallowListCacheConfig) LibP2PNode type GossipSubAdapterConfigFunc func(*BasePubSubAdapterConfig) PubSubAdapterConfig // GossipSubBuilder provides a builder pattern for creating a GossipSub pubsub system. diff --git a/network/p2p/p2pnode/disallowListCache.go b/network/p2p/disallowListCache.go similarity index 73% rename from network/p2p/p2pnode/disallowListCache.go rename to network/p2p/disallowListCache.go index e65318b4877..5fea5292f8f 100644 --- a/network/p2p/p2pnode/disallowListCache.go +++ b/network/p2p/disallowListCache.go @@ -1,8 +1,9 @@ -package p2pnode +package p2p import ( "github.com/libp2p/go-libp2p/core/peer" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/network" ) @@ -35,3 +36,15 @@ type DisallowListCache interface { // an empty list is returned. AllowFor(peerID peer.ID, cause network.DisallowListedCause) []network.DisallowListedCause } + +// DisallowListCacheConfig is the configuration for the disallow-list cache. +// The disallow-list cache is used to temporarily disallow-list peers. +type DisallowListCacheConfig struct { + // MaxSize is the maximum number of peers that can be disallow-listed at any given time. + // When the cache is full, no further new peers can be disallow-listed. + // Recommended size is 100 * number of staked nodes. + MaxSize uint32 + + // Metrics is the HeroCache metrics collector to be used for the disallow-list cache. + Metrics module.HeroCacheMetrics +} diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 2a67e6ecbc2..40d806d10a6 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -136,7 +136,7 @@ type LibP2PNodeBuilder struct { createStreamRetryInterval time.Duration rateLimiterDistributor p2p.UnicastRateLimiterDistributor gossipSubTracer p2p.PubSubTracer - disallowListCacheCfg *p2pnode.DisallowListCacheConfig + disallowListCacheCfg *p2p.DisallowListCacheConfig } func NewNodeBuilder(logger zerolog.Logger, @@ -145,7 +145,7 @@ func NewNodeBuilder(logger zerolog.Logger, networkKey fcrypto.PrivateKey, sporkID flow.Identifier, rCfg *ResourceManagerConfig, - disallowListCacheCfg *p2pnode.DisallowListCacheConfig) *LibP2PNodeBuilder { + disallowListCacheCfg *p2p.DisallowListCacheConfig) *LibP2PNodeBuilder { return &LibP2PNodeBuilder{ logger: logger, sporkID: sporkID, @@ -486,7 +486,7 @@ func DefaultCreateNodeFunc(logger zerolog.Logger, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, - disallowListCacheCfg *p2pnode.DisallowListCacheConfig) p2p.LibP2PNode { + disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { return p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) } @@ -505,7 +505,7 @@ func DefaultNodeBuilder(log zerolog.Logger, rpcInspectorSuite p2p.GossipSubInspectorSuite, rCfg *ResourceManagerConfig, uniCfg *p2pconfig.UnicastConfig, - disallowListCacheCfg *p2pnode.DisallowListCacheConfig) (p2p.NodeBuilder, error) { + disallowListCacheCfg *p2p.DisallowListCacheConfig) (p2p.NodeBuilder, error) { connManager, err := connection.NewConnManager(log, metricsCfg.Metrics, connection.DefaultConnManagerConfig()) if err != nil { diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index be6b0e7e1ef..b00f96bc26d 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -18,7 +18,6 @@ import ( "github.com/libp2p/go-libp2p/core/routing" "github.com/rs/zerolog" - "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" flownet "github.com/onflow/flow-go/network" @@ -66,19 +65,7 @@ type Node struct { peerScoreExposer p2p.PeerScoreExposer // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer // are closed and further connections are not allowed till the peer is removed from the disallow-list. - disallowListedCache DisallowListCache -} - -// DisallowListCacheConfig is the configuration for the disallow-list cache. -// The disallow-list cache is used to temporarily disallow-list peers. -type DisallowListCacheConfig struct { - // MaxSize is the maximum number of peers that can be disallow-listed at any given time. - // When the cache is full, no further new peers can be disallow-listed. - // Recommended size is 100 * number of staked nodes. - MaxSize uint32 - - // Metrics is the HeroCache metrics collector to be used for the disallow-list cache. - Metrics module.HeroCacheMetrics + disallowListedCache p2p.DisallowListCache } // NewNode creates a new libp2p node and sets its parameters. @@ -87,7 +74,7 @@ func NewNode( host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, - disallowLstCacheCfg *DisallowListCacheConfig, + disallowLstCacheCfg *p2p.DisallowListCacheConfig, ) *Node { lg := logger.With().Str("component", "libp2p-node").Logger() return &Node{ From b87c449e719aa9c0926d8477478a181854efe20c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 11:31:18 -0700 Subject: [PATCH 069/815] lint fix --- network/internal/p2pfixtures/fixtures.go | 6 +++++- network/internal/testutils/testUtil.go | 10 +++++----- network/p2p/test/fixtures.go | 6 +++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 0d4b0b549f5..477b529302c 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -115,7 +115,11 @@ func CreateNode(t *testing.T, networkKey crypto.PrivateKey, sporkID flow.Identif unittest.DefaultAddress, networkKey, sporkID, - p2pbuilder.DefaultResourceManagerConfig()). + p2pbuilder.DefaultResourceManagerConfig(), + &p2p.DisallowListCacheConfig{ + MaxSize: uint32(1000), + Metrics: metrics.NewNoopCollector(), + }). SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { return p2pdht.NewDHT(c, h, protocols.FlowDHTProtocolID(sporkID), zerolog.Nop(), metrics.NewNoopCollector()) }). diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index f8e86b25dd1..611330bd31e 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -220,10 +220,6 @@ func GenerateMiddlewares(t *testing.T, IdTranslator: translator.NewIdentityProviderIDTranslator(idProviders[i]), Codec: codec, SlashingViolationsConsumer: consumer, - DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ - MaxSize: uint32(1000), - Metrics: metrics.NewNoopCollector(), - }, }, middleware.WithUnicastRateLimiters(o.unicastRateLimiters), middleware.WithPeerManagerFilters(o.peerManagerFilters)) @@ -506,7 +502,11 @@ func generateLibP2PNode(t *testing.T, logger zerolog.Logger, key crypto.PrivateK unittest.DefaultAddress, key, sporkID, - p2pbuilder.DefaultResourceManagerConfig()). + p2pbuilder.DefaultResourceManagerConfig(), + &p2p.DisallowListCacheConfig{ + MaxSize: uint32(1000), + Metrics: metrics.NewNoopCollector(), + }). SetConnectionManager(connManager). SetResourceManager(NewResourceManager(t)). SetStreamCreationRetryInterval(unicast.DefaultRetryDelay). diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 505a90a3f47..cb48d8bc014 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -97,7 +97,11 @@ func NodeFixture( parameters.Address, parameters.Key, sporkID, - p2pbuilder.DefaultResourceManagerConfig()). + p2pbuilder.DefaultResourceManagerConfig(), + &p2p.DisallowListCacheConfig{ + MaxSize: uint32(1000), + Metrics: metrics.NewNoopCollector(), + }). SetConnectionManager(connManager). SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { return p2pdht.NewDHT(c, h, From 0f1eb953fb519c7d0929622428a406c4126b9e6f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 11:44:09 -0700 Subject: [PATCH 070/815] wires disallow lists to peer manager --- network/p2p/p2pnode/libp2pNode.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index b00f96bc26d..2b6cb142d92 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -358,8 +358,26 @@ func (n *Node) WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler // WithPeersProvider sets the PeersProvider for the peer manager. // If a peer manager factory is set, this method will set the peer manager's PeersProvider. func (n *Node) WithPeersProvider(peersProvider p2p.PeersProvider) { + // TODO: chore: we should not allow overriding the peers provider if one is already set. if n.peerManager != nil { - n.peerManager.SetPeersProvider(peersProvider) + n.peerManager.SetPeersProvider(func() peer.IDSlice { + authorizedPeersIds := peersProvider() + for i, id := range authorizedPeersIds { + // exclude the disallowed peers from the authorized peers list + causes := n.disallowListedCache.GetAllDisallowedListCausesFor(id) + if len(causes) > 0 { + n.logger.Warn(). + Str("peer_id", id.String()). + Str("causes", fmt.Sprintf("%v", causes)). + Msg("peer is disallowed for a cause, removing from authorized peers of peer manager") + + // exclude the peer from the authorized peers list + authorizedPeersIds = append(authorizedPeersIds[:i], authorizedPeersIds[i+1:]...) + } + } + + return authorizedPeersIds + }) } } @@ -477,7 +495,6 @@ func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.Disallow if err != nil { n.logger.Error().Err(err).Str("peer_id", peerId.String()).Msg("failed to disconnect from blocklisted peer") } - } func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { From 5c80d741a0c074b0352d8f25056f9118c2c08c03 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 14:22:15 -0700 Subject: [PATCH 071/815] adds disallow list oracle --- network/p2p/libp2pNode.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 999a786d33a..09fdbff93fd 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -126,3 +126,15 @@ type DisallowListNotificationConsumer interface { // The implementation must be concurrency safe. OnAllowListNotification(id peer.ID, cause network.DisallowListedCause) } + +// DisallowListOracle is an interface for querying disallow-listed peers. +type DisallowListOracle interface { + // GetAllDisallowListedCauses for a disallow-listed peer returns all disallow-listed causes. + // If the peer is not disallow-listed, returns an empty slice (not nil). + // The implementation must be concurrency safe. + // Args: + // none + // Returns: + // []network.DisallowListedCause: list of disallow-listed causes for the peer or empty slice if the peer is not disallow-listed. + GetAllDisallowListedCauses() []network.DisallowListedCause +} From 8d1bcd6a1ae53b44f45fd27447678e574f9cfbb0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 14:35:09 -0700 Subject: [PATCH 072/815] adds disallow list oracle to connection gater interface and implementation --- network/p2p/connection/connection_gater.go | 50 +++++++++++++++++++++- network/p2p/connectionGater.go | 35 +++++++-------- network/p2p/libp2pNode.go | 4 +- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/network/p2p/connection/connection_gater.go b/network/p2p/connection/connection_gater.go index 2ee0df16331..0d6682ff664 100644 --- a/network/p2p/connection/connection_gater.go +++ b/network/p2p/connection/connection_gater.go @@ -1,9 +1,9 @@ package connection import ( + "fmt" "sync" - "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/control" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -15,7 +15,7 @@ import ( "github.com/onflow/flow-go/utils/logging" ) -var _ connmgr.ConnectionGater = (*ConnGater)(nil) +var _ p2p.ConnectionGater = (*ConnGater)(nil) // ConnGaterOption allow the connection gater to be configured with a list of PeerFilter funcs for a specific conn gater callback. // In the current implementation of the ConnGater the following callbacks can be configured with peer filters. @@ -44,6 +44,11 @@ type ConnGater struct { onInterceptPeerDialFilters []p2p.PeerFilter onInterceptSecuredFilters []p2p.PeerFilter + // disallowListOracle is consulted upon every incoming or outgoing connection attempt, and the connection is only + // allowed if the remote peer is not on the disallow list. + // A ConnGater must have a disallowListOracle set, and if one is not set the ConnGater will panic. + disallowListOracle p2p.DisallowListOracle + // identityProvider provides the identity of a node given its peer ID for logging purposes only. // It is not used for allowlisting or filtering. We use the onInterceptPeerDialFilters and onInterceptSecuredFilters // to determine if a node should be allowed to connect. @@ -68,6 +73,14 @@ func NewConnGater(log zerolog.Logger, identityProvider module.IdentityProvider, func (c *ConnGater) InterceptPeerDial(p peer.ID) bool { lg := c.log.With().Str("peer_id", p.String()).Logger() + disallowListCauses := c.disallowListOracle.GetAllDisallowListedCauses(p) + if len(disallowListCauses) > 0 { + lg.Warn(). + Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). + Msg("outbound connection attempt to disallow listed peer is rejected") + return false + } + if len(c.onInterceptPeerDialFilters) == 0 { lg.Warn(). Msg("outbound connection established with no intercept peer dial filters") @@ -119,6 +132,14 @@ func (c *ConnGater) InterceptSecured(dir network.Direction, p peer.ID, addr netw Str("remote_address", addr.RemoteMultiaddr().String()). Logger() + disallowListCauses := c.disallowListOracle.GetAllDisallowListedCauses(p) + if len(disallowListCauses) > 0 { + lg.Warn(). + Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). + Msg("inbound connection attempt to disallow listed peer is rejected") + return false + } + if len(c.onInterceptSecuredFilters) == 0 { lg.Warn().Msg("inbound connection established with no intercept secured filters") return true @@ -169,3 +190,28 @@ func (c *ConnGater) peerIDPassesAllFilters(p peer.ID, filters []p2p.PeerFilter) return nil } + +// SetDisallowListOracle sets the disallow list oracle for the connection gater. +// If one is set, the oracle is consulted upon every incoming or outgoing connection attempt, and +// the connection is only allowed if the remote peer is not on the disallow list. +// In Flow blockchain, it is not optional to dismiss the disallow list oracle, and if one is not set +// the connection gater will panic. +// Also, it follows a dependency injection pattern and does not allow to set the disallow list oracle more than once, +// any subsequent calls to this method will result in a panic. +// Args: +// +// oracle: the disallow list oracle to set. +// +// Returns: +// +// none +// +// Panics: +// +// if the disallow list oracle is already set. +func (c *ConnGater) SetDisallowListOracle(oracle p2p.DisallowListOracle) { + if c.disallowListOracle != nil { + panic("disallow list oracle already set") + } + c.disallowListOracle = oracle +} diff --git a/network/p2p/connectionGater.go b/network/p2p/connectionGater.go index d2732fbd713..212dea51102 100644 --- a/network/p2p/connectionGater.go +++ b/network/p2p/connectionGater.go @@ -1,23 +1,24 @@ package p2p -import ( - "github.com/libp2p/go-libp2p/core/control" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" -) +import "github.com/libp2p/go-libp2p/core/connmgr" -// ConnectionGater is a copy of the libp2p ConnectionGater interface: -// https://github.com/libp2p/go-libp2p/blob/master/core/connmgr/gater.go#L54 -// We use it here to generate a mock for testing through testify mock. +// ConnectionGater the customized interface for the connection gater in the p2p package. +// It acts as a wrapper around the libp2p connmgr.ConnectionGater interface and adds some custom methods. type ConnectionGater interface { - InterceptPeerDial(p peer.ID) (allow bool) + connmgr.ConnectionGater - InterceptAddrDial(peer.ID, multiaddr.Multiaddr) (allow bool) - - InterceptAccept(network.ConnMultiaddrs) (allow bool) - - InterceptSecured(network.Direction, peer.ID, network.ConnMultiaddrs) (allow bool) - - InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) + // SetDisallowListOracle sets the disallow list oracle for the connection gater. + // If one is set, the oracle is consulted upon every incoming or outgoing connection attempt, and + // the connection is only allowed if the remote peer is not on the disallow list. + // In Flow blockchain, it is not optional to dismiss the disallow list oracle, and if one is not set + // the connection gater will panic. + // Also, it follows a dependency injection pattern and does not allow to set the disallow list oracle more than once, + // any subsequent calls to this method will result in a panic. + // Args: + // oracle: the disallow list oracle to set. + // Returns: + // none + // Panics: + // if the disallow list oracle is already set. + SetDisallowListOracle(oracle DisallowListOracle) } diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 09fdbff93fd..489a81e88d4 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -35,6 +35,8 @@ type LibP2PNode interface { // DisallowListNotificationConsumer exposes the disallow list notification consumer API for the node so that // it will be notified when a new disallow list update is distributed. DisallowListNotificationConsumer + // DisallowListOracle exposes the disallow list oracle API for external consumers to query about the disallow list. + DisallowListOracle // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. @@ -136,5 +138,5 @@ type DisallowListOracle interface { // none // Returns: // []network.DisallowListedCause: list of disallow-listed causes for the peer or empty slice if the peer is not disallow-listed. - GetAllDisallowListedCauses() []network.DisallowListedCause + GetAllDisallowListedCauses(peerId peer.ID) []network.DisallowListedCause } From 03f36b2d81f7c8a3e855bbea1207ccdba432c8c9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 14:42:54 -0700 Subject: [PATCH 073/815] wires disallow listing oracle to connection gater --- network/p2p/builder.go | 2 +- network/p2p/libp2pNode.go | 10 ++++++ network/p2p/p2pbuilder/libp2pNodeBuilder.go | 8 +++-- network/p2p/p2pnode/libp2pNode.go | 36 +++++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/network/p2p/builder.go b/network/p2p/builder.go index f82ecfd7934..a8a89367013 100644 --- a/network/p2p/builder.go +++ b/network/p2p/builder.go @@ -96,7 +96,7 @@ type NodeBuilder interface { SetSubscriptionFilter(pubsub.SubscriptionFilter) NodeBuilder SetResourceManager(network.ResourceManager) NodeBuilder SetConnectionManager(connmgr.ConnManager) NodeBuilder - SetConnectionGater(connmgr.ConnectionGater) NodeBuilder + SetConnectionGater(ConnectionGater) NodeBuilder SetRoutingSystem(func(context.Context, host.Host) (routing.Routing, error)) NodeBuilder SetPeerManagerOptions(bool, time.Duration) NodeBuilder diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 489a81e88d4..74297c1b00d 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -121,11 +121,21 @@ type DisallowListNotificationConsumer interface { // OnDisallowListNotification is called when a new disallow list update notification is distributed. // Any error on consuming event must handle internally. // The implementation must be concurrency safe. + // Args: + // id: peer ID of the peer being disallow-listed. + // cause: cause of the peer being disallow-listed (only this cause is added to the peer's disallow-listed causes). + // Returns: + // none OnDisallowListNotification(id peer.ID, cause network.DisallowListedCause) // OnAllowListNotification is called when a new allow list update notification is distributed. // Any error on consuming event must handle internally. // The implementation must be concurrency safe. + // Args: + // id: peer ID of the peer being allow-listed. + // cause: cause of the peer being allow-listed (only this cause is removed from the peer's disallow-listed causes). + // Returns: + // none OnAllowListNotification(id peer.ID, cause network.DisallowListedCause) } diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 40d806d10a6..3e3ef3364d4 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -128,7 +128,7 @@ type LibP2PNodeBuilder struct { resourceManager network.ResourceManager resourceManagerCfg *ResourceManagerConfig connManager connmgr.ConnManager - connGater connmgr.ConnectionGater + connGater p2p.ConnectionGater routingFactory func(context.Context, host.Host) (routing.Routing, error) peerManagerEnablePruning bool peerManagerUpdateInterval time.Duration @@ -184,7 +184,7 @@ func (builder *LibP2PNodeBuilder) SetConnectionManager(manager connmgr.ConnManag } // SetConnectionGater sets the connection gater for the node. -func (builder *LibP2PNodeBuilder) SetConnectionGater(gater connmgr.ConnectionGater) p2p.NodeBuilder { +func (builder *LibP2PNodeBuilder) SetConnectionGater(gater p2p.ConnectionGater) p2p.NodeBuilder { builder.connGater = gater return builder } @@ -373,6 +373,10 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { node := builder.createNode(builder.logger, h, pCache, peerManager, builder.disallowListCacheCfg) + if builder.connGater != nil { + builder.connGater.SetDisallowListOracle(node) + } + unicastManager := unicast.NewUnicastManager(builder.logger, stream.NewLibP2PStreamFactory(h), builder.sporkID, diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 2b6cb142d92..7d1527191f6 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -475,6 +475,17 @@ func (n *Node) SetUnicastManager(uniMgr p2p.UnicastManager) { n.uniMgr = uniMgr } +// OnDisallowListNotification is called when a new disallow list update notification is distributed. +// Any error on consuming event must handle internally. +// The implementation must be concurrency safe. +// Args: +// +// id: peer ID of the peer being disallow-listed. +// cause: cause of the peer being disallow-listed (only this cause is added to the peer's disallow-listed causes). +// +// Returns: +// +// none func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { causes, err := n.disallowListedCache.DisallowFor(peerId, cause) if err != nil { @@ -497,6 +508,17 @@ func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.Disallow } } +// OnAllowListNotification is called when a new allow list update notification is distributed. +// Any error on consuming event must handle internally. +// The implementation must be concurrency safe. +// Args: +// +// id: peer ID of the peer being allow-listed. +// cause: cause of the peer being allow-listed (only this cause is removed from the peer's disallow-listed causes). +// +// Returns: +// +// none func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { n.disallowListedCache.AllowFor(peerId, cause) @@ -505,3 +527,17 @@ func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowLis Str("causes", fmt.Sprintf("%v", cause)). Msg("peer added to disallow list cache") } + +// GetAllDisallowListedCauses for a disallow-listed peer returns all disallow-listed causes. +// If the peer is not disallow-listed, returns an empty slice (not nil). +// The implementation must be concurrency safe. +// Args: +// +// none +// +// Returns: +// +// []network.DisallowListedCause: list of disallow-listed causes for the peer or empty slice if the peer is not disallow-listed. +func (n *Node) GetAllDisallowListedCauses(peerId peer.ID) []flownet.DisallowListedCause { + return n.disallowListedCache.GetAllDisallowedListCausesFor(peerId) +} From 7d9242f0749eac81007dd016e118d9323c559984 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 14:58:44 -0700 Subject: [PATCH 074/815] wires disallow list consumer to alsp manager --- network/alsp/manager/manager.go | 26 +++++++++++++++++--------- network/p2p/network.go | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 6f35ba8c311..66c83a86304 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -75,6 +75,11 @@ type MisbehaviorReportManager struct { // Note: under normal circumstances, the ALSP module should not be disabled. disablePenalty bool + // disallowListingConsumer is the consumer for the disallow-listing notifications. + // It is notified when a node is disallow-listed by this manager. + // Exactly one consumer must be set. The code should panic if the consumer is not set or set more than once. + disallowListingConsumer network.DisallowListNotificationConsumer + // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. workerPool *worker.Pool[internal.ReportedMisbehaviorWork] } @@ -152,26 +157,29 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana // NewMisbehaviorReportManager creates a new instance of the MisbehaviorReportManager. // Args: -// -// logger: the logger instance. -// metrics: the metrics instance. -// cache: the spam record cache instance. +// cfg: the configuration for the MisbehaviorReportManager. +// consumer: the consumer for the disallow-listing notifications. When the manager decides to disallow-list a node, it notifies the consumer to +// perform the lower-level disallow-listing action at the networking layer. +// All connections to the disallow-listed node are closed and the node is removed from the overlay, and +// no further connections are established to the disallow-listed node, either inbound or outbound. +// Note: A consumer must be set before the manager is started. The manager panics if the consumer is not set. // // Returns: // // A new instance of the MisbehaviorReportManager. // An error if the config is invalid. The error is considered irrecoverable. -func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig) (*MisbehaviorReportManager, error) { +func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer network.DisallowListNotificationConsumer) (*MisbehaviorReportManager, error) { if err := cfg.validate(); err != nil { return nil, fmt.Errorf("invalid configuration for MisbehaviorReportManager: %w", err) } lg := cfg.Logger.With().Str("module", "misbehavior_report_manager").Logger() m := &MisbehaviorReportManager{ - logger: lg, - metrics: cfg.AlspMetrics, - disablePenalty: cfg.DisablePenalty, - cacheFactory: defaultSpamRecordCacheFactory(), + logger: lg, + metrics: cfg.AlspMetrics, + disablePenalty: cfg.DisablePenalty, + disallowListingConsumer: consumer, + cacheFactory: defaultSpamRecordCacheFactory(), } store := queue.NewHeroStore( diff --git a/network/p2p/network.go b/network/p2p/network.go index dea89b92eda..54d1a0fc285 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -152,7 +152,7 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { if err != nil { return nil, fmt.Errorf("could not create middleware: %w", err) } - misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg) + misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg, mw) if err != nil { return nil, fmt.Errorf("could not create misbehavior report manager: %w", err) } From 5b5147122d773bfd5dee4023279c3af946f43058 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:09:15 -0700 Subject: [PATCH 075/815] implements disallow listing below threshold --- network/alsp/manager/manager.go | 22 ++++++++++++++++++++++ network/alsp/model/params.go | 8 ++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 66c83a86304..7f43368bd02 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -10,6 +10,7 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/engine/common/worker" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" @@ -320,6 +321,27 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // is a positive number. So the penalty is getting closer to zero. // We use math.Min() to make sure the penalty is never positive. record.Penalty = math.Min(record.Penalty+record.Decay, 0) + + // TODO: this can be done in batch but at this stage let's send individual notifications. + if record.Penalty == float64(0) { + m.disallowListingConsumer.OnAllowListNotification(&network.AllowListingUpdate{ + FlowIds: flow.IdentifierList{id}, + Cause: network.DisallowListedCauseAlsp, // clears the ALSP disallow listing cause from node + }) + } + + // TODO: this can be done in batch but at this stage let's send individual notifications. + if record.Penalty < model.DisallowListingThreshold { + m.logger.Warn(). + Str("key", logging.KeySuspicious). + Hex("identifier", logging.ID(id)). + Msg("node penalty is below threshold, disallow listing") + m.disallowListingConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ + FlowIds: flow.IdentifierList{id}, + Cause: network.DisallowListedCauseAlsp, // sets the ALSP disallow listing cause on node + }) + } + return record, nil }) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index e675ee6b990..04a53a8f0c8 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -1,7 +1,7 @@ package model // To give a summary with the default value: -// 1. The penalty of each misbehavior is 0.01 * misbehaviorDisallowListingThreshold = -864 +// 1. The penalty of each misbehavior is 0.01 * DisallowListingThreshold = -864 // 2. The penalty of each misbehavior is decayed by a decay value at each decay interval. The default decay value is 1000. // This means that by default if a node misbehaves 100 times in a second, it gets disallow-listed, and takes 86.4 seconds to recover. // We emphasize on the default penalty value can be amplified by the engine that reports the misbehavior. @@ -13,12 +13,12 @@ package model // around a day to recover. From this point on, the decay speed is 1, and it takes around a day to recover from each // disallow-listing. const ( - // misbehaviorDisallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. + // DisallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. // If the overall penalty of this node drops below this threshold, the node is reported to be disallow-listed by // the networking layer, i.e., existing connections to the node are closed and the node is no longer allowed to connect till // its penalty is decayed back to zero. // maximum block-list period is 1 day - misbehaviorDisallowListingThreshold = -24 * 60 * 60 // (Don't change this value) + DisallowListingThreshold = -24 * 60 * 60 // (Don't change this value) // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified @@ -27,7 +27,7 @@ const ( // decrease the number of misbehavior/sec that will result in disallow-listing the node. For example, if the engine // amplifies the penalty by 10, the number of misbehavior/sec that will result in disallow-listing the node will be // 10 times less than the default penalty value and the node will be disallow-listed after 10 times more misbehavior/sec. - DefaultPenaltyValue = 0.01 * misbehaviorDisallowListingThreshold // (Don't change this value) + DefaultPenaltyValue = 0.01 * DisallowListingThreshold // (Don't change this value) // InitialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. // The decay speed is applied on an arithmetic progression. The penalty value of the node is the first term of the From 5bc0be90f9f8987502c211911a04c0a6c1412751 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:22:42 -0700 Subject: [PATCH 076/815] generates mocks --- module/mock/grpc_connection_pool_metrics.go | 60 +++++++++++++ network/mocknetwork/middleware.go | 18 ---- network/p2p/mock/connection_gater.go | 7 ++ network/p2p/mock/create_node_func.go | 10 +-- network/p2p/mock/disallow_list_cache.go | 88 +++++++++++++++++++ .../disallow_list_notification_consumer.go | 40 +++++++++ network/p2p/mock/disallow_list_oracle.go | 46 ++++++++++ network/p2p/mock/lib_p2_p_node.go | 28 ++++++ network/p2p/mock/node_builder.go | 4 +- 9 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 module/mock/grpc_connection_pool_metrics.go create mode 100644 network/p2p/mock/disallow_list_cache.go create mode 100644 network/p2p/mock/disallow_list_notification_consumer.go create mode 100644 network/p2p/mock/disallow_list_oracle.go diff --git a/module/mock/grpc_connection_pool_metrics.go b/module/mock/grpc_connection_pool_metrics.go new file mode 100644 index 00000000000..2eddb3cf002 --- /dev/null +++ b/module/mock/grpc_connection_pool_metrics.go @@ -0,0 +1,60 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import mock "github.com/stretchr/testify/mock" + +// GRPCConnectionPoolMetrics is an autogenerated mock type for the GRPCConnectionPoolMetrics type +type GRPCConnectionPoolMetrics struct { + mock.Mock +} + +// ConnectionAddedToPool provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) ConnectionAddedToPool() { + _m.Called() +} + +// ConnectionFromPoolEvicted provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) ConnectionFromPoolEvicted() { + _m.Called() +} + +// ConnectionFromPoolInvalidated provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) ConnectionFromPoolInvalidated() { + _m.Called() +} + +// ConnectionFromPoolReused provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) ConnectionFromPoolReused() { + _m.Called() +} + +// ConnectionFromPoolUpdated provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) ConnectionFromPoolUpdated() { + _m.Called() +} + +// NewConnectionEstablished provides a mock function with given fields: +func (_m *GRPCConnectionPoolMetrics) NewConnectionEstablished() { + _m.Called() +} + +// TotalConnectionsInPool provides a mock function with given fields: connectionCount, connectionPoolSize +func (_m *GRPCConnectionPoolMetrics) TotalConnectionsInPool(connectionCount uint, connectionPoolSize uint) { + _m.Called(connectionCount, connectionPoolSize) +} + +type mockConstructorTestingTNewGRPCConnectionPoolMetrics interface { + mock.TestingT + Cleanup(func()) +} + +// NewGRPCConnectionPoolMetrics creates a new instance of GRPCConnectionPoolMetrics. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewGRPCConnectionPoolMetrics(t mockConstructorTestingTNewGRPCConnectionPoolMetrics) *GRPCConnectionPoolMetrics { + mock := &GRPCConnectionPoolMetrics{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index 2262046f289..64167ce9ed8 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -14,8 +14,6 @@ import ( network "github.com/onflow/flow-go/network" - peer "github.com/libp2p/go-libp2p/core/peer" - protocol "github.com/libp2p/go-libp2p/core/protocol" ) @@ -40,22 +38,6 @@ func (_m *Middleware) Done() <-chan struct{} { return r0 } -// GetAllDisallowedListCausesFor provides a mock function with given fields: _a0 -func (_m *Middleware) GetAllDisallowedListCausesFor(_a0 peer.ID) []network.DisallowListedCause { - ret := _m.Called(_a0) - - var r0 []network.DisallowListedCause - if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.DisallowListedCause) - } - } - - return r0 -} - // IsConnected provides a mock function with given fields: nodeID func (_m *Middleware) IsConnected(nodeID flow.Identifier) (bool, error) { ret := _m.Called(nodeID) diff --git a/network/p2p/mock/connection_gater.go b/network/p2p/mock/connection_gater.go index d5943e8efa9..c316e3f31d0 100644 --- a/network/p2p/mock/connection_gater.go +++ b/network/p2p/mock/connection_gater.go @@ -10,6 +10,8 @@ import ( network "github.com/libp2p/go-libp2p/core/network" + p2p "github.com/onflow/flow-go/network/p2p" + peer "github.com/libp2p/go-libp2p/core/peer" ) @@ -98,6 +100,11 @@ func (_m *ConnectionGater) InterceptUpgraded(_a0 network.Conn) (bool, control.Di return r0, r1 } +// SetDisallowListOracle provides a mock function with given fields: oracle +func (_m *ConnectionGater) SetDisallowListOracle(oracle p2p.DisallowListOracle) { + _m.Called(oracle) +} + type mockConstructorTestingTNewConnectionGater interface { mock.TestingT Cleanup(func()) diff --git a/network/p2p/mock/create_node_func.go b/network/p2p/mock/create_node_func.go index 3169c71cb1e..1a57772cbeb 100644 --- a/network/p2p/mock/create_node_func.go +++ b/network/p2p/mock/create_node_func.go @@ -16,13 +16,13 @@ type CreateNodeFunc struct { mock.Mock } -// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 host.Host, _a2 p2p.ProtocolPeerCache, _a3 p2p.PeerManager) p2p.LibP2PNode { - ret := _m.Called(_a0, _a1, _a2, _a3) +// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 +func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 host.Host, _a2 p2p.ProtocolPeerCache, _a3 p2p.PeerManager, _a4 *p2p.DisallowListCacheConfig) p2p.LibP2PNode { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4) var r0 p2p.LibP2PNode - if rf, ok := ret.Get(0).(func(zerolog.Logger, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager) p2p.LibP2PNode); ok { - r0 = rf(_a0, _a1, _a2, _a3) + if rf, ok := ret.Get(0).(func(zerolog.Logger, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager, *p2p.DisallowListCacheConfig) p2p.LibP2PNode); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(p2p.LibP2PNode) diff --git a/network/p2p/mock/disallow_list_cache.go b/network/p2p/mock/disallow_list_cache.go new file mode 100644 index 00000000000..6a0e206d7b6 --- /dev/null +++ b/network/p2p/mock/disallow_list_cache.go @@ -0,0 +1,88 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + network "github.com/onflow/flow-go/network" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" +) + +// DisallowListCache is an autogenerated mock type for the DisallowListCache type +type DisallowListCache struct { + mock.Mock +} + +// AllowFor provides a mock function with given fields: peerID, cause +func (_m *DisallowListCache) AllowFor(peerID peer.ID, cause network.DisallowListedCause) []network.DisallowListedCause { + ret := _m.Called(peerID, cause) + + var r0 []network.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID, network.DisallowListedCause) []network.DisallowListedCause); ok { + r0 = rf(peerID, cause) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + return r0 +} + +// DisallowFor provides a mock function with given fields: peerID, cause +func (_m *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) { + ret := _m.Called(peerID, cause) + + var r0 []network.DisallowListedCause + var r1 error + if rf, ok := ret.Get(0).(func(peer.ID, network.DisallowListedCause) ([]network.DisallowListedCause, error)); ok { + return rf(peerID, cause) + } + if rf, ok := ret.Get(0).(func(peer.ID, network.DisallowListedCause) []network.DisallowListedCause); ok { + r0 = rf(peerID, cause) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + if rf, ok := ret.Get(1).(func(peer.ID, network.DisallowListedCause) error); ok { + r1 = rf(peerID, cause) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAllDisallowedListCausesFor provides a mock function with given fields: peerID +func (_m *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause { + ret := _m.Called(peerID) + + var r0 []network.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { + r0 = rf(peerID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + return r0 +} + +type mockConstructorTestingTNewDisallowListCache interface { + mock.TestingT + Cleanup(func()) +} + +// NewDisallowListCache creates a new instance of DisallowListCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDisallowListCache(t mockConstructorTestingTNewDisallowListCache) *DisallowListCache { + mock := &DisallowListCache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/disallow_list_notification_consumer.go b/network/p2p/mock/disallow_list_notification_consumer.go new file mode 100644 index 00000000000..0d30cddea03 --- /dev/null +++ b/network/p2p/mock/disallow_list_notification_consumer.go @@ -0,0 +1,40 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + network "github.com/onflow/flow-go/network" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" +) + +// DisallowListNotificationConsumer is an autogenerated mock type for the DisallowListNotificationConsumer type +type DisallowListNotificationConsumer struct { + mock.Mock +} + +// OnAllowListNotification provides a mock function with given fields: id, cause +func (_m *DisallowListNotificationConsumer) OnAllowListNotification(id peer.ID, cause network.DisallowListedCause) { + _m.Called(id, cause) +} + +// OnDisallowListNotification provides a mock function with given fields: id, cause +func (_m *DisallowListNotificationConsumer) OnDisallowListNotification(id peer.ID, cause network.DisallowListedCause) { + _m.Called(id, cause) +} + +type mockConstructorTestingTNewDisallowListNotificationConsumer interface { + mock.TestingT + Cleanup(func()) +} + +// NewDisallowListNotificationConsumer creates a new instance of DisallowListNotificationConsumer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDisallowListNotificationConsumer(t mockConstructorTestingTNewDisallowListNotificationConsumer) *DisallowListNotificationConsumer { + mock := &DisallowListNotificationConsumer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/disallow_list_oracle.go b/network/p2p/mock/disallow_list_oracle.go new file mode 100644 index 00000000000..f85c82d75de --- /dev/null +++ b/network/p2p/mock/disallow_list_oracle.go @@ -0,0 +1,46 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + network "github.com/onflow/flow-go/network" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" +) + +// DisallowListOracle is an autogenerated mock type for the DisallowListOracle type +type DisallowListOracle struct { + mock.Mock +} + +// GetAllDisallowListedCauses provides a mock function with given fields: peerId +func (_m *DisallowListOracle) GetAllDisallowListedCauses(peerId peer.ID) []network.DisallowListedCause { + ret := _m.Called(peerId) + + var r0 []network.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { + r0 = rf(peerId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]network.DisallowListedCause) + } + } + + return r0 +} + +type mockConstructorTestingTNewDisallowListOracle interface { + mock.TestingT + Cleanup(func()) +} + +// NewDisallowListOracle creates a new instance of DisallowListOracle. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDisallowListOracle(t mockConstructorTestingTNewDisallowListOracle) *DisallowListOracle { + mock := &DisallowListOracle{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 326b2280eca..5d62ad73a77 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -8,6 +8,8 @@ import ( context "context" + flow_gonetwork "github.com/onflow/flow-go/network" + host "github.com/libp2p/go-libp2p/core/host" irrecoverable "github.com/onflow/flow-go/module/irrecoverable" @@ -90,6 +92,22 @@ func (_m *LibP2PNode) Done() <-chan struct{} { return r0 } +// GetAllDisallowListedCauses provides a mock function with given fields: peerId +func (_m *LibP2PNode) GetAllDisallowListedCauses(peerId peer.ID) []flow_gonetwork.DisallowListedCause { + ret := _m.Called(peerId) + + var r0 []flow_gonetwork.DisallowListedCause + if rf, ok := ret.Get(0).(func(peer.ID) []flow_gonetwork.DisallowListedCause); ok { + r0 = rf(peerId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]flow_gonetwork.DisallowListedCause) + } + } + + return r0 +} + // GetIPPort provides a mock function with given fields: func (_m *LibP2PNode) GetIPPort() (string, string, error) { ret := _m.Called() @@ -207,6 +225,16 @@ func (_m *LibP2PNode) ListPeers(topic string) []peer.ID { return r0 } +// OnAllowListNotification provides a mock function with given fields: id, cause +func (_m *LibP2PNode) OnAllowListNotification(id peer.ID, cause flow_gonetwork.DisallowListedCause) { + _m.Called(id, cause) +} + +// OnDisallowListNotification provides a mock function with given fields: id, cause +func (_m *LibP2PNode) OnDisallowListNotification(id peer.ID, cause flow_gonetwork.DisallowListedCause) { + _m.Called(id, cause) +} + // PeerManagerComponent provides a mock function with given fields: func (_m *LibP2PNode) PeerManagerComponent() component.Component { ret := _m.Called() diff --git a/network/p2p/mock/node_builder.go b/network/p2p/mock/node_builder.go index 70184e2ecaf..a14e07363ae 100644 --- a/network/p2p/mock/node_builder.go +++ b/network/p2p/mock/node_builder.go @@ -90,11 +90,11 @@ func (_m *NodeBuilder) SetBasicResolver(_a0 madns.BasicResolver) p2p.NodeBuilder } // SetConnectionGater provides a mock function with given fields: _a0 -func (_m *NodeBuilder) SetConnectionGater(_a0 connmgr.ConnectionGater) p2p.NodeBuilder { +func (_m *NodeBuilder) SetConnectionGater(_a0 p2p.ConnectionGater) p2p.NodeBuilder { ret := _m.Called(_a0) var r0 p2p.NodeBuilder - if rf, ok := ret.Get(0).(func(connmgr.ConnectionGater) p2p.NodeBuilder); ok { + if rf, ok := ret.Get(0).(func(p2p.ConnectionGater) p2p.NodeBuilder); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { From b76ab400fda0bc562e2d1b4da62a8c0c1c22581c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:23:08 -0700 Subject: [PATCH 077/815] lint fix --- network/internal/testutils/testUtil.go | 2 +- network/p2p/test/fixtures.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 611330bd31e..2073f0934a1 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -472,7 +472,7 @@ func withRateLimiterDistributor(distributor p2p.UnicastRateLimiterDistributor) n } } -func withConnectionGater(connectionGater connmgr.ConnectionGater) nodeBuilderOption { +func withConnectionGater(connectionGater p2p.ConnectionGater) nodeBuilderOption { return func(nb p2p.NodeBuilder) { nb.SetConnectionGater(connectionGater) } diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index cb48d8bc014..c9acf89cfc1 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -181,7 +181,7 @@ type NodeFixtureParameters struct { ConnectionPruning bool // peer manager parameter UpdateInterval time.Duration // peer manager parameter PeerProvider p2p.PeersProvider // peer manager parameter - ConnGater connmgr.ConnectionGater + ConnGater p2p.ConnectionGater ConnManager connmgr.ConnManager GossipSubFactory p2p.GossipSubFactoryFunc GossipSubConfig p2p.GossipSubAdapterConfigFunc From a32e12f3560996ee6248bc51089ef5cea5855389 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:24:34 -0700 Subject: [PATCH 078/815] lint fix --- network/internal/testutils/testUtil.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 2073f0934a1..7f0bb42d486 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -11,7 +11,6 @@ import ( "time" dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/host" p2pNetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -312,7 +311,7 @@ type optsConfig struct { networkMetrics module.NetworkMetrics peerManagerFilters []p2p.PeerFilter unicastRateLimiterDistributor p2p.UnicastRateLimiterDistributor - connectionGater connmgr.ConnectionGater + connectionGater p2p.ConnectionGater createStreamRetryInterval time.Duration } @@ -359,7 +358,7 @@ func WithUnicastRateLimiters(limiters *ratelimit.RateLimiters) func(*optsConfig) } } -func WithConnectionGater(connectionGater connmgr.ConnectionGater) func(*optsConfig) { +func WithConnectionGater(connectionGater p2p.ConnectionGater) func(*optsConfig) { return func(o *optsConfig) { o.connectionGater = connectionGater } @@ -591,7 +590,7 @@ func NewResourceManager(t *testing.T) p2pNetwork.ResourceManager { } // NewConnectionGater creates a new connection gater for testing with given allow listing filter. -func NewConnectionGater(idProvider module.IdentityProvider, allowListFilter p2p.PeerFilter) connmgr.ConnectionGater { +func NewConnectionGater(idProvider module.IdentityProvider, allowListFilter p2p.PeerFilter) p2p.ConnectionGater { filters := []p2p.PeerFilter{allowListFilter} return connection.NewConnGater(unittest.Logger(), idProvider, From 3c25286ea965b659261524a039f3a39fb6a8d749 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:33:27 -0700 Subject: [PATCH 079/815] lint fix --- network/p2p/test/fixtures.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index c9acf89cfc1..69bdca38098 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -256,7 +256,7 @@ func WithDHTOptions(opts ...dht.Option) NodeFixtureParameterOption { } } -func WithConnectionGater(connGater connmgr.ConnectionGater) NodeFixtureParameterOption { +func WithConnectionGater(connGater p2p.ConnectionGater) NodeFixtureParameterOption { return func(p *NodeFixtureParameters) { p.ConnGater = connGater } From aa36d191c4be42908f8a23e5d5fd50ec1ae53bf6 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 15:33:51 -0700 Subject: [PATCH 080/815] fix lint --- network/alsp/manager/manager_test.go | 60 +++++++++++++++++++--------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index fb63a2609c2..a667a235cf7 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -282,6 +282,7 @@ func TestReportCreation(t *testing.T) { // In other words, variation of input values do not cause a nil ALSP manager to be created or a panic. func TestNewMisbehaviorReportManager(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { @@ -291,25 +292,25 @@ func TestNewMisbehaviorReportManager(t *testing.T) { } t.Run("with default values", func(t *testing.T) { - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) assert.NotNil(t, m) }) t.Run("with a custom spam record cache", func(t *testing.T) { - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) assert.NotNil(t, m) }) t.Run("with ALSP module enabled", func(t *testing.T) { - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) assert.NotNil(t, m) }) t.Run("with ALSP module disabled", func(t *testing.T) { - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) assert.NotNil(t, m) }) @@ -319,10 +320,11 @@ func TestNewMisbehaviorReportManager(t *testing.T) { // It is a minimum viable test that ensures that a nil ALSP manager is created with invalid set of inputs. func TestMisbehaviorReportManager_InitializationError(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) t.Run("missing spam report queue size", func(t *testing.T) { cfg.SpamReportQueueSize = 0 - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.Error(t, err) require.ErrorIs(t, err, alspmgr.ErrSpamReportQueueSizeNotSet) assert.Nil(t, m) @@ -330,7 +332,7 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { t.Run("missing spam record cache size", func(t *testing.T) { cfg.SpamRecordCacheSize = 0 - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.Error(t, err) require.ErrorIs(t, err, alspmgr.ErrSpamRecordCacheSizeNotSet) assert.Nil(t, m) @@ -338,7 +340,7 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { t.Run("missing heartbeat intervals", func(t *testing.T) { cfg.HeartBeatInterval = 0 - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.Error(t, err) require.ErrorIs(t, err, alspmgr.ErrSpamRecordCacheSizeNotSet) assert.Nil(t, m) @@ -349,6 +351,8 @@ func TestMisbehaviorReportManager_InitializationError(t *testing.T) { // The test ensures that the misbehavior report is handled correctly and the penalty is applied to the peer in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) + // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -358,7 +362,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -402,6 +406,8 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { // The test ensures that the misbehavior is reported on metrics but the penalty is not applied to the peer in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) + cfg.DisablePenalty = true // disable penalty for misbehavior reports alspMetrics := mockmodule.NewAlspMetrics(t) cfg.AlspMetrics = alspMetrics @@ -414,7 +420,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -459,6 +465,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the peer in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentially(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache @@ -468,7 +475,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -520,6 +527,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the peer in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrently(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -528,7 +536,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -589,6 +597,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl // The test ensures that each misbehavior report is handled correctly and the penalties are applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -597,7 +606,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -648,6 +657,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential // The test ensures that each misbehavior report is handled correctly and the penalties are applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -656,7 +666,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -717,6 +727,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequentially(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -725,7 +736,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -798,6 +809,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti // The test ensures that each misbehavior report is handled correctly and the penalties are cumulatively applied to the corresponding peers in the cache. func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurrently(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -806,7 +818,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -872,6 +884,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre // is uniquely identifying a traffic violation, even though the description of the violation is the same. func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -880,7 +893,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -938,6 +951,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t // is decayed after a single heartbeat. The test guarantees waiting for at least one heartbeat by waiting for the first decay to happen. func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -946,7 +960,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -1026,6 +1040,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { // The test ensures that the misbehavior penalty is decayed with a linear progression within multiple heartbeats. func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -1034,7 +1049,7 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -1114,6 +1129,7 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { // The test ensures that the misbehavior penalty is decayed with a linear progression within multiple heartbeats. func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ @@ -1122,7 +1138,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { return cache }), } - m, err := alspmgr.NewMisbehaviorReportManager(cfg) + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) require.NoError(t, err) // start the ALSP manager @@ -1178,6 +1194,12 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { return true }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") + // eventually, we expect the ALSP manager to emit an allow list notification to the network layer when the penalty is decayed to zero. + consumer.On("OnAllowListNotification", &network.AllowListingUpdate{ + FlowIds: flow.IdentifierList{report.OriginId()}, + Cause: network.DisallowListedCauseAlsp, + }).Return(nil).Once() + // phase-2: default decay speed is 1000 and with 10 penalties in range of [-1, -10], the penalty should be decayed to zero in // a single heartbeat. time.Sleep(1 * time.Second) From 7d02c90d19cf2db8681fcfc78cfab6bb60d769b1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 5 Jun 2023 16:38:12 -0700 Subject: [PATCH 081/815] adds test for disallow list notification --- network/alsp/manager/manager.go | 29 ++++++---- network/alsp/manager/manager_test.go | 82 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 7f43368bd02..6d0c0d18752 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -317,6 +317,23 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { return record, fmt.Errorf("illegal state: spam record %x has non-positive decay %f", id, record.Decay) } + // TODO: this can be done in batch but at this stage let's send individual notifications. + if record.Penalty < model.DisallowListingThreshold { + // cutoff counter keeps track of how many times the penalty has been below the threshold. + record.CutoffCounter++ + + m.logger.Warn(). + Str("key", logging.KeySuspicious). + Hex("identifier", logging.ID(id)). + Uint64("cutoff_counter", record.CutoffCounter). + Msg("node penalty is below threshold, disallow listing") + m.disallowListingConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ + FlowIds: flow.IdentifierList{id}, + Cause: network.DisallowListedCauseAlsp, // sets the ALSP disallow listing cause on node + }) + + } + // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed // is a positive number. So the penalty is getting closer to zero. // We use math.Min() to make sure the penalty is never positive. @@ -330,18 +347,6 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { }) } - // TODO: this can be done in batch but at this stage let's send individual notifications. - if record.Penalty < model.DisallowListingThreshold { - m.logger.Warn(). - Str("key", logging.KeySuspicious). - Hex("identifier", logging.ID(id)). - Msg("node penalty is below threshold, disallow listing") - m.disallowListingConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ - FlowIds: flow.IdentifierList{id}, - Cause: network.DisallowListedCauseAlsp, // sets the ALSP disallow listing cause on node - }) - } - return record, nil }) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index a667a235cf7..5982d0b8c37 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -1215,6 +1215,88 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) } +// TestDisallowListNotification tests the emission of the allow list notification to the network layer when the misbehavior +// penalty of a node is dropped below the disallow-listing threshold. The test ensures that the disallow list notification is +// emitted to the network layer when the misbehavior penalty is dropped below the disallow-listing threshold and that the +// cutoff counter of the spam record for the misbehaving node is incremented indicating that the node is disallow-listed once. +func TestDisallowListNotification(t *testing.T) { + cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) + + var cache alsp.SpamRecordCache + cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + }), + } + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) + require.NoError(t, err) + + // start the ALSP manager + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + cancel() + unittest.RequireCloseBefore(t, m.Done(), 100*time.Millisecond, "ALSP manager did not stop") + }() + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + m.Start(signalerCtx) + unittest.RequireCloseBefore(t, m.Ready(), 100*time.Millisecond, "ALSP manager did not start") + + // creates a single misbehavior report + originId := unittest.IdentifierFixture() + report := misbehaviorReportFixtureWithDefaultPenalty(t, originId) + require.Less(t, report.Penalty(), float64(0)) // ensure the penalty is negative + + channel := channels.Channel("test-channel") + + // reporting the same misbehavior 120 times, should result in a single disallow list notification, since each + // misbehavior report is reported with the same penalty 0.01 * diallowlisting-threshold. We go over the threshold + // to ensure that the disallow list notification is emitted only once. + times := 120 + wg := sync.WaitGroup{} + wg.Add(times) + + // concurrently reports the same misbehavior report twice + for i := 0; i < times; i++ { + go func() { + defer wg.Done() + + m.HandleMisbehaviorReport(channel, report) + }() + } + + // at this point, we expect a single disallow list notification to be emitted to the network layer when all the misbehavior + // reports are processed by the ALSP manager (the notification is emitted when at the next heartbeat). + consumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ + FlowIds: flow.IdentifierList{report.OriginId()}, + Cause: network.DisallowListedCauseAlsp, + }).Return().Once() + + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + require.Eventually(t, func() bool { + // check if the misbehavior reports have been processed by verifying that the Adjust method was called on the cache + record, ok := cache.Get(originId) + if !ok { + return false + } + require.NotNil(t, record) + + // eventually, the penalty should be the accumulated penalty of all the duplicate misbehavior reports (with the default decay). + // the decay is added to the penalty as we allow for a single heartbeat before the disallow list notification is emitted. + if record.Penalty != report.Penalty()*float64(times)+record.Decay { + return false + } + // cuttoff counter should be incremented since the penalty is above the disallowlisting threshold. + require.Equal(t, uint64(1), record.CutoffCounter) + // the decay should be the default decay value. + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) + + return true + }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") +} + // misbehaviorReportFixture creates a mock misbehavior report for a single origin id. // Args: // - t: the testing.T instance From ed7938c23fa947e44532bc36e4e687c4cb710630 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 6 Jun 2023 16:34:20 +0300 Subject: [PATCH 082/815] Moved connection selection to separate module. --- Makefile | 1 + engine/access/rpc/backend/backend.go | 187 +------------ engine/access/rpc/backend/backend_accounts.go | 3 +- engine/access/rpc/backend/backend_events.go | 3 +- engine/access/rpc/backend/backend_scripts.go | 5 +- engine/access/rpc/backend/backend_test.go | 58 +++- .../rpc/backend/backend_transactions.go | 39 +-- ...ection_guard.go => connection_selector.go} | 259 ++++++++++++------ .../rpc/backend/mock/connection_selector.go | 82 ++++++ engine/access/rpc/engine.go | 3 + module/mock/finalized_header_cache.go | 44 +++ 11 files changed, 378 insertions(+), 306 deletions(-) rename engine/access/rpc/backend/{node_connection_guard.go => connection_selector.go} (57%) create mode 100644 engine/access/rpc/backend/mock/connection_selector.go create mode 100644 module/mock/finalized_header_cache.go diff --git a/Makefile b/Makefile index 5e55f9fe57b..9c9de19b91d 100644 --- a/Makefile +++ b/Makefile @@ -179,6 +179,7 @@ generate-mocks: install-mock-generators mockery --name 'API' --dir="./engine/protocol" --case=underscore --output="./engine/protocol/mock" --outpkg="mock" mockery --name 'API' --dir="./engine/access/state_stream" --case=underscore --output="./engine/access/state_stream/mock" --outpkg="mock" mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" + mockery --name 'ConnectionSelector' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" mockery --name 'IngestRPC' --dir="./engine/execution/ingestion" --case=underscore --tags relic --output="./engine/execution/ingestion/mock" --outpkg="mock" mockery --name '.*' --dir=model/fingerprint --case=underscore --output="./model/fingerprint/mock" --outpkg="mock" mockery --name 'ExecForkActor' --structname 'ExecForkActorMock' --dir=module/mempool/consensus/mock/ --case=underscore --output="./module/mempool/consensus/mock/" --outpkg="mock" diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 721b3b063c9..4832f0b7cde 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,8 +3,6 @@ package backend import ( "context" "fmt" - "time" - "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -17,7 +15,6 @@ import ( "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" @@ -76,6 +73,7 @@ type Backend struct { collections storage.Collections executionReceipts storage.ExecutionReceipts connFactory ConnectionFactory + connSelector ConnectionSelector } func New( @@ -91,6 +89,7 @@ func New( chainID flow.ChainID, transactionMetrics module.TransactionMetrics, connFactory ConnectionFactory, + connSelector ConnectionSelector, retryEnabled bool, maxHeightRange uint, preferredExecutionNodeIDs []string, @@ -116,6 +115,7 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, + connSelector: connSelector, state: state, log: log, metrics: transactionMetrics, @@ -134,6 +134,7 @@ func New( transactionMetrics: transactionMetrics, retry: retry, connFactory: connFactory, + connSelector: connSelector, previousAccessNodes: historicalAccessNodes, log: log, }, @@ -142,6 +143,7 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, + connSelector: connSelector, log: log, maxHeightRange: maxHeightRange, }, @@ -158,6 +160,7 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, + connSelector: connSelector, log: log, }, backendExecutionResults: backendExecutionResults{ @@ -171,6 +174,7 @@ func New( collections: collections, executionReceipts: executionReceipts, connFactory: connFactory, + connSelector: connSelector, chainID: chainID, } @@ -285,180 +289,3 @@ func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, err return convert.SnapshotToBytes(validSnapshot) } - -// executionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities -// which have executed the given block ID. -// If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func executionNodesForBlockID( - ctx context.Context, - blockID flow.Identifier, - executionReceipts storage.ExecutionReceipts, - state protocol.State, - log zerolog.Logger) (flow.IdentityList, error) { - - var executorIDs flow.IdentifierList - - // check if the block ID is of the root block. If it is then don't look for execution receipts since they - // will not be present for the root block. - rootBlock, err := state.Params().Root() - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - if rootBlock.ID() == blockID { - executorIdentities, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = executorIdentities.NodeIDs() - } else { - // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID - for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, executionReceipts, log) - if err != nil { - return nil, err - } - - if len(executorIDs) >= minExecutionNodesCnt { - break - } - - // log the attempt - log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). - Int("execution_receipts_found", len(executorIDs)). - Str("block_id", blockID.String()). - Msg("insufficient execution receipts") - - // if one or less execution receipts may have been received then re-query - // in the hope that more might have been received by now - - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-time.After(100 * time.Millisecond << time.Duration(attempt)): - //retry after an exponential backoff - } - } - - receiptCnt := len(executorIDs) - // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs - if receiptCnt < minExecutionNodesCnt { - newExecutorIDs, err := state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = newExecutorIDs.NodeIDs() - } - } - - // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(state, executorIDs) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - // randomly choose upto maxExecutionNodesCnt identities - executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) - - if len(executionIdentitiesRandom) == 0 { - return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) - } - - return executionIdentitiesRandom, nil -} - -// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the -// given blockID -func findAllExecutionNodes( - blockID flow.Identifier, - executionReceipts storage.ExecutionReceipts, - log zerolog.Logger) (flow.IdentifierList, error) { - - // lookup the receipt's storage with the block ID - allReceipts, err := executionReceipts.ByBlockID(blockID) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) - } - - executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) - for _, r := range allReceipts { - executionResultMetaList = append(executionResultMetaList, r.Meta()) - } - executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() - - // maximum number of matching receipts found so far for any execution result id - maxMatchedReceiptCnt := 0 - // execution result id key for the highest number of matching receipts in the identicalReceipts map - var maxMatchedReceiptResultID flow.Identifier - - // find the largest list of receipts which have the same result ID - for resultID, executionReceiptList := range executionResultGroupedMetaList { - currentMatchedReceiptCnt := executionReceiptList.Size() - if currentMatchedReceiptCnt > maxMatchedReceiptCnt { - maxMatchedReceiptCnt = currentMatchedReceiptCnt - maxMatchedReceiptResultID = resultID - } - } - - // if there are more than one execution result for the same block ID, log as error - if executionResultGroupedMetaList.NumberGroups() > 1 { - identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) - log.Error(). - Str("block_id", blockID.String()). - Str("execution_receipts", identicalReceiptsStr). - Msg("execution receipt mismatch") - } - - // pick the largest list of matching receipts - matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) - - metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() - - // collect all unique execution node ids from the receipts - var executorIDs flow.IdentifierList - for executorID := range metaReceiptGroupedByExecutorID { - executorIDs = append(executorIDs, executorID) - } - - return executorIDs, nil -} - -// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first -// choosing the preferred execution nodes which have executed the transaction. If no such preferred -// execution nodes are found, then the fixed execution nodes defined in the identity table are returned -// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. -// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} -// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs -func chooseExecutionNodes(state protocol.State, executorIDs flow.IdentifierList) (flow.IdentityList, error) { - - allENs, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive all execution IDs: %w", err) - } - - // first try and choose from the preferred EN IDs - var chosenIDs flow.IdentityList - if len(preferredENIdentifiers) > 0 { - // find the preferred execution node IDs which have executed the transaction - chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(preferredENIdentifiers...), - filter.HasNodeID(executorIDs...))) - if len(chosenIDs) > 0 { - return chosenIDs, nil - } - } - - // if no preferred EN ID is found, then choose from the fixed EN IDs - if len(fixedENIdentifiers) > 0 { - // choose fixed ENs which have executed the transaction - chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(fixedENIdentifiers...), filter.HasNodeID(executorIDs...))) - if len(chosenIDs) > 0 { - return chosenIDs, nil - } - // if no such ENs are found then just choose all fixed ENs - chosenIDs = allENs.Filter(filter.HasNodeID(fixedENIdentifiers...)) - return chosenIDs, nil - } - - // If no preferred or fixed ENs have been specified, then return all executor IDs i.e. no preference at all - return allENs.Filter(filter.HasNodeID(executorIDs...)), nil -} diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index a3a41053c61..4781e395d23 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -22,6 +22,7 @@ type backendAccounts struct { headers storage.Headers executionReceipts storage.ExecutionReceipts connFactory ConnectionFactory + connSelector ConnectionSelector log zerolog.Logger } @@ -82,7 +83,7 @@ func (b *backendAccounts) getAccountAtBlockID( BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) if err != nil { return nil, rpc.ConvertError(err, "failed to get account from the execution node", codes.Internal) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index e097843b933..83824e74733 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -25,6 +25,7 @@ type backendEvents struct { executionReceipts storage.ExecutionReceipts state protocol.State connFactory ConnectionFactory + connSelector ConnectionSelector log zerolog.Logger maxHeightRange uint } @@ -129,7 +130,7 @@ func (b *backendEvents) getBlockEventsFromExecutionNode( // choose the last block ID to find the list of execution nodes lastBlockID := blockIDs[len(blockIDs)-1] - execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, lastBlockID) if err != nil { b.log.Error().Err(err).Msg("failed to retrieve events from execution node") return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 9f4ec5dffb2..48df09d6f12 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -28,6 +28,7 @@ type backendScripts struct { executionReceipts storage.ExecutionReceipts state protocol.State connFactory ConnectionFactory + connSelector ConnectionSelector log zerolog.Logger metrics module.BackendScriptsMetrics loggedScripts *lru.Cache @@ -86,12 +87,12 @@ func (b *backendScripts) findScriptExecutors( ctx context.Context, blockID flow.Identifier, ) ([]string, error) { - // send script queries to archive nodes if archive addres is configured + // send script queries to archive nodes if archive address is configured if len(b.archiveAddressList) > 0 { return b.archiveAddressList, nil } - executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + executors, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) if err != nil { return nil, err } diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 11109130222..03294796633 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -47,6 +47,7 @@ type Suite struct { execClient *access.ExecutionAPIClient historicalAccessClient *access.AccessAPIClient connectionFactory *backendmock.ConnectionFactory + connSelector *backendmock.ConnectionSelector chainID flow.ChainID } @@ -74,6 +75,7 @@ func (suite *Suite) SetupTest() { suite.chainID = flow.Testnet suite.historicalAccessClient = new(access.AccessAPIClient) suite.connectionFactory = new(backendmock.ConnectionFactory) + suite.connSelector = new(backendmock.ConnectionSelector) } func (suite *Suite) TestPing() { @@ -98,6 +100,7 @@ func (suite *Suite) TestPing() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -133,6 +136,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -198,6 +202,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, 100, nil, @@ -270,6 +275,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, 100, nil, @@ -335,6 +341,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, 100, nil, @@ -411,6 +418,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, 100, nil, @@ -471,6 +479,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -509,6 +518,7 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -555,6 +565,7 @@ func (suite *Suite) TestGetTransaction() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -595,6 +606,7 @@ func (suite *Suite) TestGetCollection() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -636,6 +648,8 @@ func (suite *Suite) TestGetTransactionResultByIndex() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connSelector := new(backendmock.ConnectionSelector) + exeEventReq := &execproto.GetTransactionByIndexRequest{ BlockId: blockId[:], Index: index, @@ -658,6 +672,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -700,6 +715,8 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connSelector := new(backendmock.ConnectionSelector) + exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockId[:], } @@ -721,6 +738,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -790,6 +808,8 @@ func (suite *Suite) TestTransactionStatusTransition() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) + connSelector := new(backendmock.ConnectionSelector) + exeEventReq := &execproto.GetTransactionResultRequest{ BlockId: blockID[:], TransactionId: txID[:], @@ -812,6 +832,7 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -932,6 +953,7 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -1085,6 +1107,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { // create a mock connection factory connFactory := suite.setupConnectionFactory() + connSelector := new(backendmock.ConnectionSelector) backend := New( suite.state, @@ -1099,6 +1122,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, 100, nil, @@ -1157,6 +1181,7 @@ func (suite *Suite) TestTransactionResultUnknown() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -1211,6 +1236,7 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -1276,6 +1302,8 @@ func (suite *Suite) TestGetEventsForBlockIDs() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connSelector := new(backendmock.ConnectionSelector) + // create the expected results from execution node and access node exeResults := make([]*execproto.GetEventsForBlockIDsResponse_Result, len(blockHeaders)) @@ -1341,6 +1369,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1373,6 +1402,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1399,6 +1429,7 @@ func (suite *Suite) TestGetExecutionResultByID() { // create a mock connection factory connFactory := new(backendmock.ConnectionFactory) + connSelector := new(backendmock.ConnectionSelector) nonexistingID := unittest.IdentifierFixture() blockID := unittest.IdentifierFixture() @@ -1432,6 +1463,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1462,6 +1494,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1490,6 +1523,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { // create a mock connection factory connFactory := new(backendmock.ConnectionFactory) + connSelector := new(backendmock.ConnectionSelector) blockID := unittest.IdentifierFixture() executionResult := unittest.ExecutionResultFixture( @@ -1525,6 +1559,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1556,6 +1591,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1691,6 +1727,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { } connFactory := suite.setupConnectionFactory() + connSelector := new(backendmock.ConnectionSelector) suite.Run("invalid request max height < min height", func() { backend := New( @@ -1706,6 +1743,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1745,6 +1783,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1783,6 +1822,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1820,6 +1860,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, 1, // set maximum range to 1 nil, @@ -1857,6 +1898,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1920,6 +1962,8 @@ func (suite *Suite) TestGetAccount() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connSelector := new(backendmock.ConnectionSelector) + // create the handler with the mock backend := New( suite.state, @@ -1934,6 +1978,7 @@ func (suite *Suite) TestGetAccount() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -1983,6 +2028,8 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connSelector := new(backendmock.ConnectionSelector) + // create the expected execution API request blockID := h.ID() exeReq := &execproto.GetAccountAtBlockIDRequest{ @@ -2015,6 +2062,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { flow.Testnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, @@ -2054,6 +2102,7 @@ func (suite *Suite) TestGetNetworkParameters() { flow.Mainnet, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -2121,6 +2170,8 @@ func (suite *Suite) TestExecutionNodesForBlockID() { func(flow.IdentityFilter) error { return nil }) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() + connSelector := new(backendmock.ConnectionSelector) + testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) { if preferredENs != nil { @@ -2129,7 +2180,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { if fixedENs != nil { fixedENIdentifiers = fixedENs.NodeIDs() } - actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) + actualList, err := connSelector.GetExecutionNodesForBlockID(context.Background(), block.ID()) require.NoError(suite.T(), err) if expectedENs == nil { expectedENs = flow.IdentityList{} @@ -2149,7 +2200,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { attempt2Receipts = flow.ExecutionReceiptList{} attempt3Receipts = flow.ExecutionReceiptList{} suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) - actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) + actualList, err := connSelector.GetExecutionNodesForBlockID(context.Background(), block.ID()) require.NoError(suite.T(), err) require.Equal(suite.T(), len(actualList), maxExecutionNodesCnt) }) @@ -2219,6 +2270,8 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) + connSelector := new(backendmock.ConnectionSelector) + // create the handler with the mock backend := New( suite.state, @@ -2233,6 +2286,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { flow.Mainnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client + connSelector, false, DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 661fc3f90f8..286cd544aae 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -24,8 +24,6 @@ import ( "github.com/onflow/flow-go/storage" ) -const collectionNodesToTry uint = 3 - type backendTransactions struct { staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node transactions storage.Transactions @@ -38,6 +36,7 @@ type backendTransactions struct { transactionValidator *access.TransactionValidator retry *Retry connFactory ConnectionFactory + connSelector ConnectionSelector previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger @@ -86,7 +85,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T } // otherwise choose a random set of collections nodes to try - collAddrs, err := b.chooseCollectionNodes(tx, collectionNodesToTry) + collAddrs, err := b.connSelector.GetCollectionNodes(tx.ID()) if err != nil { return fmt.Errorf("failed to determine collection node for tx %x: %w", tx, err) } @@ -112,34 +111,6 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T return sendErrors.ErrorOrNil() } -// chooseCollectionNodes finds a random subset of size sampleSize of collection node addresses from the -// collection node cluster responsible for the given tx -func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sampleSize uint) ([]string, error) { - - // retrieve the set of collector clusters - clusters, err := b.state.Final().Epochs().Current().Clustering() - if err != nil { - return nil, fmt.Errorf("could not cluster collection nodes: %w", err) - } - - // get the cluster responsible for the transaction - txCluster, ok := clusters.ByTxID(tx.ID()) - if !ok { - return nil, fmt.Errorf("could not get local cluster by txID: %x", tx.ID()) - } - - // select a random subset of collection nodes from the cluster to be tried in order - targetNodes := txCluster.Sample(sampleSize) - - // collect the addresses of all the chosen collection nodes - var targetAddrs = make([]string, len(targetNodes)) - for i, id := range targetNodes { - targetAddrs[i] = id.Address - } - - return targetAddrs, nil -} - // sendTransactionToCollection sends the transaction to the given collection node via grpc func (b *backendTransactions) sendTransactionToCollector(ctx context.Context, tx *flow.TransactionBody, @@ -371,7 +342,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( req := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -491,7 +462,7 @@ func (b *backendTransactions) GetTransactionResultByIndex( BlockId: blockID[:], Index: index, } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -715,7 +686,7 @@ func (b *backendTransactions) getTransactionResultFromExecutionNode( TransactionId: transactionID, } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) if err != nil { // if no execution receipt were found, return a NotFound GRPC error if IsInsufficientExecutionReceipts(err) { diff --git a/engine/access/rpc/backend/node_connection_guard.go b/engine/access/rpc/backend/connection_selector.go similarity index 57% rename from engine/access/rpc/backend/node_connection_guard.go rename to engine/access/rpc/backend/connection_selector.go index a46029219a1..8a70e0af5e8 100644 --- a/engine/access/rpc/backend/node_connection_guard.go +++ b/engine/access/rpc/backend/connection_selector.go @@ -3,50 +3,56 @@ package backend import ( "context" "fmt" - "github.com/onflow/flow-go/storage" "time" - "github.com/rs/zerolog" - "github.com/sony/gobreaker" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/storage" + "github.com/rs/zerolog" ) -type NodeSelector interface { +const collectionNodesToTry uint = 3 + +type ConnectionSelector interface { GetExecutionNodesForBlockID(ctx context.Context, blockID flow.Identifier) (flow.IdentityList, error) GetCollectionNodes(txID flow.Identifier) ([]string, error) } -type NodeConnectionGuard struct { +type MainConnectionSelector struct { state protocol.State executionReceipts storage.ExecutionReceipts log zerolog.Logger - circuitBreaker *gobreaker.CircuitBreaker - connectionFactory ConnectionFactory } -var _ NodeSelector = (*NodeConnectionGuard)(nil) - -func NewNodeConnectionGuard(connectionFactory ConnectionFactory, state protocol.State, executionReceipts storage.ExecutionReceipts, log zerolog.Logger) NodeConnectionGuard { - return NodeConnectionGuard{ - state: state, - executionReceipts: executionReceipts, - log: log, - circuitBreaker: gobreaker.NewCircuitBreaker(gobreaker.Settings{}), - connectionFactory: connectionFactory, +type CircuitBreakerConnectionSelector MainConnectionSelector + +var _ ConnectionSelector = (*MainConnectionSelector)(nil) + +func NewConnectionSelector( + state protocol.State, + executionReceipts storage.ExecutionReceipts, + log zerolog.Logger, + isCircuitBreakerEnabled bool, +) ConnectionSelector { + if isCircuitBreakerEnabled { + return &CircuitBreakerConnectionSelector{ + state: state, + executionReceipts: executionReceipts, + log: log, + } + } else { + return &MainConnectionSelector{ + state: state, + executionReceipts: executionReceipts, + log: log, + } } } -func (ncg *NodeConnectionGuard) Invoke(req func() (interface{}, error)) (interface{}, error) { - result, err := ncg.circuitBreaker.Execute(req) - return result, err -} - -func (ncg *NodeConnectionGuard) GetCollectionNodes(txId flow.Identifier) ([]string, error) { +func (ncs *MainConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { // retrieve the set of collector clusters - clusters, err := ncg.state.Final().Epochs().Current().Clustering() + clusters, err := ncs.state.Final().Epochs().Current().Clustering() if err != nil { return nil, fmt.Errorf("could not cluster collection nodes: %w", err) } @@ -58,8 +64,7 @@ func (ncg *NodeConnectionGuard) GetCollectionNodes(txId flow.Identifier) ([]stri } // select a random subset of collection nodes from the cluster to be tried in order - //TODO: Change to cb selection of nodes. - targetNodes := txCluster.Sample(3) + targetNodes := txCluster.Sample(collectionNodesToTry) // collect the addresses of all the chosen collection nodes var targetAddrs = make([]string, len(targetNodes)) @@ -73,46 +78,46 @@ func (ncg *NodeConnectionGuard) GetCollectionNodes(txId flow.Identifier) ([]stri // GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities // which have executed the given block ID. // If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func (ncg *NodeConnectionGuard) GetExecutionNodesForBlockID( +func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( ctx context.Context, - blockID flow.Identifier) (flow.IdentityList, error) { + blockID flow.Identifier, +) (flow.IdentityList, error) { var executorIDs flow.IdentifierList // check if the block ID is of the root block. If it is then don't look for execution receipts since they // will not be present for the root block. - rootBlock, err := ncg.state.Params().Root() + rootBlock, err := ncs.state.Params().Root() if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } if rootBlock.ID() == blockID { - executorIdentities, err := ncg.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + executorIdentities, err := ncs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } executorIDs = executorIdentities.NodeIDs() } else { - // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = ncg.findAllExecutionNodes(blockID) + executorIDs, err = findAllExecutionNodes(blockID, ncs.executionReceipts, ncs.log) if err != nil { return nil, err } - if len(executorIDs) >= minExecutionNodesCnt { + if len(executorIDs) > 0 { break } // log the attempt - ncg.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + ncs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). Int("execution_receipts_found", len(executorIDs)). Str("block_id", blockID.String()). Msg("insufficient execution receipts") // if one or less execution receipts may have been received then re-query // in the hope that more might have been received by now - //TODO: Should be removed select { case <-ctx.Done(): return nil, ctx.Err() @@ -120,20 +125,10 @@ func (ncg *NodeConnectionGuard) GetExecutionNodesForBlockID( //retry after an exponential backoff } } - - receiptCnt := len(executorIDs) - // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs - if receiptCnt < minExecutionNodesCnt { - newExecutorIDs, err := ncg.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = newExecutorIDs.NodeIDs() - } } // choose from the preferred or fixed execution nodes - subsetENs, err := ncg.chooseExecutionNodes(executorIDs) + subsetENs, err := chooseExecutionNodes(ncs.state, executorIDs) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } @@ -148,58 +143,94 @@ func (ncg *NodeConnectionGuard) GetExecutionNodesForBlockID( return executionIdentitiesRandom, nil } -// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the -// given blockID -func (ncg *NodeConnectionGuard) findAllExecutionNodes( - blockID flow.Identifier) (flow.IdentifierList, error) { - - // lookup the receipt's storage with the block ID - allReceipts, err := ncg.executionReceipts.ByBlockID(blockID) +func (nccbs *CircuitBreakerConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { + // retrieve the set of collector clusters + clusters, err := nccbs.state.Final().Epochs().Current().Clustering() if err != nil { - return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) + return nil, fmt.Errorf("could not cluster collection nodes: %w", err) } - executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) - for _, r := range allReceipts { - executionResultMetaList = append(executionResultMetaList, r.Meta()) + // get the cluster responsible for the transaction + txCluster, ok := clusters.ByTxID(txId) + if !ok { + return nil, fmt.Errorf("could not get local cluster by txID: %x", txId) } - executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() - // maximum number of matching receipts found so far for any execution result id - maxMatchedReceiptCnt := 0 - // execution result id key for the highest number of matching receipts in the identicalReceipts map - var maxMatchedReceiptResultID flow.Identifier + // collect the addresses of all the chosen collection nodes + var targetAddress = make([]string, len(txCluster)) + for i, id := range txCluster { + targetAddress[i] = id.Address + } - // find the largest list of receipts which have the same result ID - for resultID, executionReceiptList := range executionResultGroupedMetaList { - currentMatchedReceiptCnt := executionReceiptList.Size() - if currentMatchedReceiptCnt > maxMatchedReceiptCnt { - maxMatchedReceiptCnt = currentMatchedReceiptCnt - maxMatchedReceiptResultID = resultID - } + return targetAddress, nil +} + +// GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities +// which have executed the given block ID. +// If no such execution node is found, an InsufficientExecutionReceipts error is returned. +func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( + ctx context.Context, + blockID flow.Identifier, +) (flow.IdentityList, error) { + + var executorIDs flow.IdentifierList + + // check if the block ID is of the root block. If it is then don't look for execution receipts since they + // will not be present for the root block. + rootBlock, err := nccbs.state.Params().Root() + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } - // if there are more than one execution result for the same block ID, log as error - if executionResultGroupedMetaList.NumberGroups() > 1 { - identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) - ncg.log.Error(). - Str("block_id", blockID.String()). - Str("execution_receipts", identicalReceiptsStr). - Msg("execution receipt mismatch") + if rootBlock.ID() == blockID { + executorIdentities, err := nccbs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = executorIdentities.NodeIDs() + } else { + // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { + executorIDs, err = findAllExecutionNodes(blockID, nccbs.executionReceipts, nccbs.log) + if err != nil { + return nil, err + } + + if len(executorIDs) > 0 { + break + } + + // log the attempt + nccbs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + Int("execution_receipts_found", len(executorIDs)). + Str("block_id", blockID.String()). + Msg("insufficient execution receipts") + + // if one or less execution receipts may have been received then re-query + // in the hope that more might have been received by now + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(100 * time.Millisecond << time.Duration(attempt)): + //retry after an exponential backoff + } + } } - // pick the largest list of matching receipts - matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) + // choose from the preferred or fixed execution nodes + subsetENs, err := chooseExecutionNodes(nccbs.state, executorIDs) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } - metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() + // randomly choose upto maxExecutionNodesCnt identities + executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) - // collect all unique execution node ids from the receipts - var executorIDs flow.IdentifierList - for executorID := range metaReceiptGroupedByExecutorID { - executorIDs = append(executorIDs, executorID) + if len(executionIdentitiesRandom) == 0 { + return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) } - return executorIDs, nil + return executionIdentitiesRandom, nil } // chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first @@ -208,9 +239,9 @@ func (ncg *NodeConnectionGuard) findAllExecutionNodes( // If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. // e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} // and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs -func (ncg *NodeConnectionGuard) chooseExecutionNodes(executorIDs flow.IdentifierList) (flow.IdentityList, error) { +func chooseExecutionNodes(state protocol.State, executorIDs flow.IdentifierList) (flow.IdentityList, error) { - allENs, err := ncg.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + allENs, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) if err != nil { return nil, fmt.Errorf("failed to retreive all execution IDs: %w", err) } @@ -241,3 +272,59 @@ func (ncg *NodeConnectionGuard) chooseExecutionNodes(executorIDs flow.Identifier // If no preferred or fixed ENs have been specified, then return all executor IDs i.e. no preference at all return allENs.Filter(filter.HasNodeID(executorIDs...)), nil } + +// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the +// given blockID +func findAllExecutionNodes( + blockID flow.Identifier, + executionReceipts storage.ExecutionReceipts, + log zerolog.Logger) (flow.IdentifierList, error) { + + // lookup the receipt's storage with the block ID + allReceipts, err := executionReceipts.ByBlockID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) + } + + executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) + for _, r := range allReceipts { + executionResultMetaList = append(executionResultMetaList, r.Meta()) + } + executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() + + // maximum number of matching receipts found so far for any execution result id + maxMatchedReceiptCnt := 0 + // execution result id key for the highest number of matching receipts in the identicalReceipts map + var maxMatchedReceiptResultID flow.Identifier + + // find the largest list of receipts which have the same result ID + for resultID, executionReceiptList := range executionResultGroupedMetaList { + currentMatchedReceiptCnt := executionReceiptList.Size() + if currentMatchedReceiptCnt > maxMatchedReceiptCnt { + maxMatchedReceiptCnt = currentMatchedReceiptCnt + maxMatchedReceiptResultID = resultID + } + } + + // if there are more than one execution result for the same block ID, log as error + if executionResultGroupedMetaList.NumberGroups() > 1 { + identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) + log.Error(). + Str("block_id", blockID.String()). + Str("execution_receipts", identicalReceiptsStr). + Msg("execution receipt mismatch") + } + + // pick the largest list of matching receipts + matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) + + metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() + + // collect all unique execution node ids from the receipts + var executorIDs flow.IdentifierList + for executorID := range metaReceiptGroupedByExecutorID { + executorIDs = append(executorIDs, executorID) + } + + return executorIDs, nil +} diff --git a/engine/access/rpc/backend/mock/connection_selector.go b/engine/access/rpc/backend/mock/connection_selector.go new file mode 100644 index 00000000000..6337683391f --- /dev/null +++ b/engine/access/rpc/backend/mock/connection_selector.go @@ -0,0 +1,82 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + context "context" + + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" +) + +// ConnectionSelector is an autogenerated mock type for the ConnectionSelector type +type ConnectionSelector struct { + mock.Mock +} + +// GetCollectionNodes provides a mock function with given fields: txID +func (_m *ConnectionSelector) GetCollectionNodes(txID flow.Identifier) ([]string, error) { + ret := _m.Called(txID) + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier) ([]string, error)); ok { + return rf(txID) + } + if rf, ok := ret.Get(0).(func(flow.Identifier) []string); ok { + r0 = rf(txID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { + r1 = rf(txID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetExecutionNodesForBlockID provides a mock function with given fields: ctx, blockID +func (_m *ConnectionSelector) GetExecutionNodesForBlockID(ctx context.Context, blockID flow.Identifier) (flow.IdentityList, error) { + ret := _m.Called(ctx, blockID) + + var r0 flow.IdentityList + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, flow.Identifier) (flow.IdentityList, error)); ok { + return rf(ctx, blockID) + } + if rf, ok := ret.Get(0).(func(context.Context, flow.Identifier) flow.IdentityList); ok { + r0 = rf(ctx, blockID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(flow.IdentityList) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, flow.Identifier) error); ok { + r1 = rf(ctx, blockID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewConnectionSelector interface { + mock.TestingT + Cleanup(func()) +} + +// NewConnectionSelector creates a new instance of ConnectionSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewConnectionSelector(t mockConstructorTestingTNewConnectionSelector) *ConnectionSelector { + mock := &ConnectionSelector{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index ae25ecfcb76..4db6d0ab14f 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -175,6 +175,8 @@ func NewBuilder(log zerolog.Logger, CircuitBreakerConfig: config.CircuitBreakerConfig, } + connectionSelector := backend.NewConnectionSelector(state, executionReceipts, log, config.CircuitBreakerConfig.Enabled) + backend := backend.New(state, collectionRPC, historicalAccessNodes, @@ -187,6 +189,7 @@ func NewBuilder(log zerolog.Logger, chainID, transactionMetrics, connectionFactory, + connectionSelector, retryEnabled, config.MaxHeightRange, config.PreferredExecutionNodeIDs, diff --git a/module/mock/finalized_header_cache.go b/module/mock/finalized_header_cache.go new file mode 100644 index 00000000000..018981fb347 --- /dev/null +++ b/module/mock/finalized_header_cache.go @@ -0,0 +1,44 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" +) + +// FinalizedHeaderCache is an autogenerated mock type for the FinalizedHeaderCache type +type FinalizedHeaderCache struct { + mock.Mock +} + +// Get provides a mock function with given fields: +func (_m *FinalizedHeaderCache) Get() *flow.Header { + ret := _m.Called() + + var r0 *flow.Header + if rf, ok := ret.Get(0).(func() *flow.Header); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.Header) + } + } + + return r0 +} + +type mockConstructorTestingTNewFinalizedHeaderCache interface { + mock.TestingT + Cleanup(func()) +} + +// NewFinalizedHeaderCache creates a new instance of FinalizedHeaderCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFinalizedHeaderCache(t mockConstructorTestingTNewFinalizedHeaderCache) *FinalizedHeaderCache { + mock := &FinalizedHeaderCache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 43d995f0657cb6fc02835bb6a648468c8db66ae9 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 7 Jun 2023 01:09:18 +0300 Subject: [PATCH 083/815] Fixed broken tests --- engine/access/access_test.go | 5 +++++ engine/access/rpc/backend/historical_access_test.go | 2 ++ engine/access/rpc/backend/retry_test.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/engine/access/access_test.go b/engine/access/access_test.go index 8aa301ba49b..677bd218882 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -150,6 +150,7 @@ func (suite *Suite) RunTest( suite.chainID, suite.metrics, nil, + nil, false, backend.DefaultMaxHeightRange, nil, @@ -322,6 +323,7 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { suite.chainID, metrics, connFactory, + nil, false, backend.DefaultMaxHeightRange, nil, @@ -648,6 +650,7 @@ func (suite *Suite) TestGetSealedTransaction() { suite.chainID, suite.metrics, connFactory, + nil, false, backend.DefaultMaxHeightRange, nil, @@ -787,6 +790,7 @@ func (suite *Suite) TestGetTransactionResult() { suite.chainID, suite.metrics, connFactory, + nil, false, backend.DefaultMaxHeightRange, nil, @@ -978,6 +982,7 @@ func (suite *Suite) TestExecuteScript() { suite.chainID, suite.metrics, connFactory, + nil, false, backend.DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index b66904f6604..3ba35c15d70 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -49,6 +49,7 @@ func (suite *Suite) TestHistoricalTransactionResult() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -107,6 +108,7 @@ func (suite *Suite) TestHistoricalTransaction() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index c10b66bbbc0..7a4ddf6b375 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -54,6 +54,7 @@ func (suite *Suite) TestTransactionRetry() { suite.chainID, metrics.NewNoopCollector(), nil, + nil, false, DefaultMaxHeightRange, nil, @@ -143,6 +144,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.chainID, metrics.NewNoopCollector(), connFactory, + nil, false, DefaultMaxHeightRange, nil, From 1fd72a8d870543ef0b2d051eb07fa8efa4ff2d91 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:21:38 -0700 Subject: [PATCH 084/815] renames a test fixture and add more --- insecure/corruptlibp2p/spam_test.go | 2 +- insecure/rpc_inspector/utils.go | 2 +- .../p2p/connection/connection_gater_test.go | 2 +- network/p2p/p2pnode/disallow_listing_test.go | 1 + network/p2p/test/fixtures.go | 59 +++++++++++++++++-- network/test/middleware_test.go | 2 +- 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 network/p2p/p2pnode/disallow_listing_test.go diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index f446e9f4c65..cd759fd52e9 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -71,7 +71,7 @@ func TestSpam_IHave(t *testing.T) { // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a prior connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. - p2ptest.EnsureConnected(t, ctx, nodes) + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic diff --git a/insecure/rpc_inspector/utils.go b/insecure/rpc_inspector/utils.go index 02cf9492f7c..164634236bc 100644 --- a/insecure/rpc_inspector/utils.go +++ b/insecure/rpc_inspector/utils.go @@ -19,7 +19,7 @@ func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContex // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a prior connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. - p2ptest.EnsureConnected(t, ctx, nodes) + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkID) return unittest.ProposalFixture(), blockTopic diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index e3f723ef71e..07c3f0e2115 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -396,7 +396,7 @@ func ensureCommunicationSilenceAmongGroups(t *testing.T, ctx context.Context, sp // ensureCommunicationOverAllProtocols ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. func ensureCommunicationOverAllProtocols(t *testing.T, ctx context.Context, sporkId flow.Identifier, nodes []p2p.LibP2PNode, inbounds []chan string) { - p2ptest.EnsureConnected(t, ctx, nodes) + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) p2ptest.EnsurePubsubMessageExchange(t, ctx, nodes, func() (interface{}, channels.Topic) { blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) return unittest.ProposalFixture(), blockTopic diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go new file mode 100644 index 00000000000..3abbbf1cd60 --- /dev/null +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -0,0 +1 @@ +package p2pnode diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 69bdca38098..556da6c3fd7 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -57,7 +57,7 @@ func NodeFixture( opts ...NodeFixtureParameterOption, ) (p2p.LibP2PNode, flow.Identity) { - logger := unittest.Logger().Level(zerolog.ErrorLevel) + logger := unittest.Logger().Level(zerolog.WarnLevel) rpcInspectorSuite, err := inspectorbuilder.NewGossipSubInspectorBuilder(logger, sporkID, inspectorbuilder.DefaultGossipSubRPCInspectorsConfig(), idProvider, metrics.NewNoopCollector()). Build() @@ -390,20 +390,71 @@ func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.Li } } -// EnsureConnected ensures that the given nodes are connected to each other. +// TryConnectionAndEnsureConnected tries connecting nodes to each other and ensures that the given nodes are connected to each other. // It fails the test if any of the nodes is not connected to any other node. -func EnsureConnected(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { +func TryConnectionAndEnsureConnected(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { for _, node := range nodes { for _, other := range nodes { if node == other { continue } require.NoError(t, node.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.Host().ID()))) + // the other node should be connected to this node require.Equal(t, node.Host().Network().Connectedness(other.Host().ID()), network.Connected) + // at least one connection should be established + require.True(t, len(node.Host().Network().ConnsToPeer(other.Host().ID())) > 0) } } } +// RequireConnectedEventually ensures eventually that the given nodes are already connected to each other. +// It fails the test if any of the nodes is not connected to any other node. +// Args: +// - nodes: the nodes to check +// - tick: the tick duration +// - timeout: the timeout duration +func RequireConnectedEventually(t *testing.T, nodes []p2p.LibP2PNode, tick time.Duration, timeout time.Duration) { + require.Eventually(t, func() bool { + for _, node := range nodes { + for _, other := range nodes { + if node == other { + continue + } + if node.Host().Network().Connectedness(other.Host().ID()) != network.Connected { + return false + } + if len(node.Host().Network().ConnsToPeer(other.Host().ID())) == 0 { + return false + } + } + } + return true + }, timeout, tick) +} + +// RequireEventuallyNotConnected ensures eventually that the given groups of nodes are not connected to each other. +// It fails the test if any of the nodes from groupA is connected to any of the nodes from groupB. +// Args: +// - groupA: the first group of nodes +// - groupB: the second group of nodes +// - tick: the tick duration +// - timeout: the timeout duration +func RequireEventuallyNotConnected(t *testing.T, groupA []p2p.LibP2PNode, groupB []p2p.LibP2PNode, tick time.Duration, timeout time.Duration) { + require.Eventually(t, func() bool { + for _, node := range groupA { + for _, other := range groupB { + if node.Host().Network().Connectedness(other.Host().ID()) == network.Connected { + return false + } + if len(node.Host().Network().ConnsToPeer(other.Host().ID())) > 0 { + return false + } + } + } + return true + }, timeout, tick) +} + // EnsureStreamCreationInBothDirections ensure that between each pair of nodes in the given list, a stream is created in both directions. func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode) { for _, this := range nodes { @@ -420,7 +471,7 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod } // EnsurePubsubMessageExchange ensures that the given connected nodes exchange the given message on the given channel through pubsub. -// Note: EnsureConnected() must be called to connect all nodes before calling this function. +// Note: TryConnectionAndEnsureConnected() must be called to connect all nodes before calling this function. func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p.LibP2PNode, messageFactory func() (interface{}, channels.Topic)) { _, topic := messageFactory() diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 3fe9ecc042f..a7b16badc90 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -317,7 +317,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // return true only if the node is a direct peer of the other, after rate limiting this direct // peer should be removed by the peer manager. p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - p2ptest.EnsureConnected(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}) + p2ptest.TryConnectionAndEnsureConnected(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}) // with the rate limit configured to 5 msg/sec we send 10 messages at once and expect the rate limiter // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream From 2983198dd65f37cf9b1a0835cb3e89239f902e50 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:21:53 -0700 Subject: [PATCH 085/815] decreases min backoff --- network/p2p/connection/connector_factory.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/p2p/connection/connector_factory.go b/network/p2p/connection/connector_factory.go index a5c8be29704..71a27a810c1 100644 --- a/network/p2p/connection/connector_factory.go +++ b/network/p2p/connection/connector_factory.go @@ -11,7 +11,10 @@ import ( const ( // minBackoff is the minimum backoff duration for the backoff connector. - minBackoff = time.Second * 10 + // We set it to 1 second as we want to let the LibP2PNode be in charge of connection establishment and can disconnect + // and reconnect to peers as soon as it needs. This is essential to ensure that the allow-listing and disallow-listing + // time intervals are working as expected. + minBackoff = 1 * time.Second // maxBackoff is the maximum backoff duration for the backoff connector. When the backoff duration reaches this value, // it will not increase any further. maxBackoff = time.Hour From 9f28a3b93b276e3b6f6ee5842c1ca73026135de0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:22:09 -0700 Subject: [PATCH 086/815] adds a new method to connector host --- network/p2p/connector.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/network/p2p/connector.go b/network/p2p/connector.go index 2bbf9f24dea..5ba291d7063 100644 --- a/network/p2p/connector.go +++ b/network/p2p/connector.go @@ -31,26 +31,33 @@ type ConnectorHost interface { // Connections returns all the connections of the underlying host. Connections() []network.Conn + // IsConnectedTo returns true if the given peer.ID is connected to the underlying host. + // Args: + // peerID: peer.ID for which the connection status is requested + // Returns: + // true if the given peer.ID is connected to the underlying host. + IsConnectedTo(peerId peer.ID) bool + // PeerInfo returns the peer.AddrInfo for the given peer.ID. // Args: // id: peer.ID for which the peer.AddrInfo is requested // Returns: // peer.AddrInfo for the given peer.ID - PeerInfo(id peer.ID) peer.AddrInfo + PeerInfo(peerId peer.ID) peer.AddrInfo // IsProtected returns true if the given peer.ID is protected from pruning. // Args: // id: peer.ID for which the protection status is requested // Returns: // true if the given peer.ID is protected from pruning - IsProtected(id peer.ID) bool + IsProtected(peerId peer.ID) bool // ClosePeer closes the connection to the given peer.ID. // Args: // id: peer.ID for which the connection is to be closed // Returns: // error if there is any error while closing the connection to the given peer.ID. All errors are benign. - ClosePeer(id peer.ID) error + ClosePeer(peerId peer.ID) error // ID returns the peer.ID of the underlying host. // Returns: From e2e01462ad95058ac592599807bb8c915d35462a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:23:10 -0700 Subject: [PATCH 087/815] adds disallow listing logic to libp2p node --- network/p2p/p2pnode/libp2pNode.go | 47 ++++++++++++++----------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 7d1527191f6..f05f3c38c63 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -360,24 +360,25 @@ func (n *Node) WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler func (n *Node) WithPeersProvider(peersProvider p2p.PeersProvider) { // TODO: chore: we should not allow overriding the peers provider if one is already set. if n.peerManager != nil { - n.peerManager.SetPeersProvider(func() peer.IDSlice { - authorizedPeersIds := peersProvider() - for i, id := range authorizedPeersIds { - // exclude the disallowed peers from the authorized peers list - causes := n.disallowListedCache.GetAllDisallowedListCausesFor(id) - if len(causes) > 0 { - n.logger.Warn(). - Str("peer_id", id.String()). - Str("causes", fmt.Sprintf("%v", causes)). - Msg("peer is disallowed for a cause, removing from authorized peers of peer manager") - - // exclude the peer from the authorized peers list - authorizedPeersIds = append(authorizedPeersIds[:i], authorizedPeersIds[i+1:]...) + n.peerManager.SetPeersProvider( + func() peer.IDSlice { + authorizedPeersIds := peersProvider() + for i, id := range authorizedPeersIds { + // exclude the disallowed peers from the authorized peers list + causes := n.disallowListedCache.GetAllDisallowedListCausesFor(id) + if len(causes) > 0 { + n.logger.Warn(). + Str("peer_id", id.String()). + Str("causes", fmt.Sprintf("%v", causes)). + Msg("peer is disallowed for a cause, removing from authorized peers of peer manager") + + // exclude the peer from the authorized peers list + authorizedPeersIds = append(authorizedPeersIds[:i], authorizedPeersIds[i+1:]...) + } } - } - return authorizedPeersIds - }) + return authorizedPeersIds + }) } } @@ -499,13 +500,6 @@ func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.Disallow Str("notification_cause", cause.String()). Str("causes", fmt.Sprintf("%v", causes)). Msg("peer added to disallow list cache") - - // TODO: technically, adding a peer to the disallow list should also remove its connection (through the peer manager) - // hence, this code part can be removed. - err = n.RemovePeer(peerId) - if err != nil { - n.logger.Error().Err(err).Str("peer_id", peerId.String()).Msg("failed to disconnect from blocklisted peer") - } } // OnAllowListNotification is called when a new allow list update notification is distributed. @@ -520,12 +514,13 @@ func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.Disallow // // none func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { - n.disallowListedCache.AllowFor(peerId, cause) + remainingCauses := n.disallowListedCache.AllowFor(peerId, cause) - n.logger.Debug(). + n.logger.Warn(). Str("peer_id", peerId.String()). Str("causes", fmt.Sprintf("%v", cause)). - Msg("peer added to disallow list cache") + Str("remaining_causes", fmt.Sprintf("%v", remainingCauses)). + Msg("peer is allow-listed for cause") } // GetAllDisallowListedCauses for a disallow-listed peer returns all disallow-listed causes. From c9ed4467b9bfb26bbe03c66f773ce816b302cbec Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:23:22 -0700 Subject: [PATCH 088/815] adds disallow listing test --- network/p2p/p2pnode/disallow_listing_test.go | 79 +++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index 3abbbf1cd60..a5604f951e9 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -1 +1,78 @@ -package p2pnode +package p2pnode_test + +import ( + "context" + "testing" + "time" + + "github.com/libp2p/go-libp2p/core/peer" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" + mockmodule "github.com/onflow/flow-go/module/mock" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/connection" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" +) + +// TestDisconnectingFromDisallowListedNode ensures that the node disconnects from a disallow listed node while the node is +// connected to other (allow listed) nodes. It also ensures that when a disallow-listed node is allow-listed again, the +// node reconnects to it. +func TestDisconnectingFromDisallowListedNode(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + sporkID := unittest.IdentifierFixture() + idProvider := mockmodule.NewIdentityProvider(t) + + peerIDSlice := peer.IDSlice{} + // node 1 is the node that will be disallow-listing another node (node 2). + node1, identity1 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider, p2ptest.WithPeerManagerEnabled(true, connection.DefaultPeerUpdateInterval, func() peer.IDSlice { + return peerIDSlice + })) + idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity1, true).Maybe() + peerIDSlice = append(peerIDSlice, node1.Host().ID()) + + // node 2 is the node that will be disallow-listed by node 1. + node2, identity2 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider) + idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity2, true).Maybe() + peerIDSlice = append(peerIDSlice, node2.Host().ID()) + + // node 3 is the node that will be connected to node 1 (to ensure that node 1 is still able to connect to other nodes + // after disallow-listing node 2). + node3, identity3 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider) + idProvider.On("ByPeerID", node3.Host().ID()).Return(&identity3, true).Maybe() + peerIDSlice = append(peerIDSlice, node3.Host().ID()) + + nodes := []p2p.LibP2PNode{node1, node2, node3} + ids := flow.IdentityList{&identity1, &identity2, &identity3} + + p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + + // initially all nodes should be connected to each other. + p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 2*time.Second) + + // phase-1: node 1 disallow-lists node 2. + node1.OnDisallowListNotification(node2.Host().ID(), network.DisallowListedCauseAlsp) + + // eventually node 1 should be disconnected from node 2 while other nodes should remain connected. + // we choose a timeout of 2 seconds because peer manager updates peers every 1 second. + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{node1}, []p2p.LibP2PNode{node2}, 100*time.Millisecond, 2*time.Second) + + // but nodes 1 and 3 should remain connected as well as nodes 2 and 3. + // we choose a short timeout because we expect the nodes to remain connected. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node1, node3}, 1*time.Millisecond, 100*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node2, node3}, 1*time.Millisecond, 100*time.Second) + + // phase-2: now we allow-list node 1 back + node1.OnAllowListNotification(node2.Host().ID(), network.DisallowListedCauseAlsp) + + // eventually node 1 should be connected to node 2 again, hence all nodes should be connected to each other. + // we choose a timeout of 2 seconds because peer manager updates peers every 1 second. + p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 2*time.Second) +} From 6098a7f38313541c2148b8b45f0482593ab65855 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:23:36 -0700 Subject: [PATCH 089/815] decreases time interval of peer manager --- network/p2p/connection/peerManager.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/p2p/connection/peerManager.go b/network/p2p/connection/peerManager.go index d82c5b779b6..a15c5a5a3ab 100644 --- a/network/p2p/connection/peerManager.go +++ b/network/p2p/connection/peerManager.go @@ -16,8 +16,9 @@ import ( "github.com/onflow/flow-go/utils/logging" ) -// DefaultPeerUpdateInterval is default duration for which the peer manager waits in between attempts to update peer connections -var DefaultPeerUpdateInterval = 10 * time.Minute +// DefaultPeerUpdateInterval is default duration for which the peer manager waits in between attempts to update peer connections. +// We set it to 1 second to be aligned with the heartbeat intervals of libp2p, alsp, and gossipsub. +var DefaultPeerUpdateInterval = 1 * time.Second var _ p2p.PeerManager = (*PeerManager)(nil) var _ component.Component = (*PeerManager)(nil) From 938a36ef0aa35bae0f85e5408473c51ac5655730 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:23:55 -0700 Subject: [PATCH 090/815] skips connecting to already connected peers --- network/p2p/connection/connector.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/network/p2p/connection/connector.go b/network/p2p/connection/connector.go index bfbba1e15d1..8b21e527f27 100644 --- a/network/p2p/connection/connector.go +++ b/network/p2p/connection/connector.go @@ -109,6 +109,10 @@ func (l *Libp2pConnector) connectToPeers(ctx context.Context, peerIDs peer.IDSli } for _, peerID := range peerIDs { + if l.host.IsConnectedTo(peerID) { + l.log.Debug().Str("peer_id", peerID.String()).Msg("already connected to peer, skipping connection") + continue + } peerCh <- peer.AddrInfo{ID: peerID} } @@ -152,6 +156,11 @@ func (l *Libp2pConnector) pruneAllConnectionsExcept(peerIDs peer.IDSlice) { if flowStream != nil { lg = lg.With().Str("flow_stream", string(flowStream.Protocol())).Logger() } + for _, stream := range conn.GetStreams() { + if err := stream.Close(); err != nil { + lg.Warn().Err(err).Msg("failed to close stream, when pruning connections") + } + } // close the connection with the peer if it is not part of the current fanout err := l.host.ClosePeer(peerID) From f77621cf89722bb8f85bd3d2723f8c268b0a2e98 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 16:24:06 -0700 Subject: [PATCH 091/815] adds is connected method --- network/p2p/connection/connector_host.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/network/p2p/connection/connector_host.go b/network/p2p/connection/connector_host.go index 6af6ecc4777..04cfd56b28a 100644 --- a/network/p2p/connection/connector_host.go +++ b/network/p2p/connection/connector_host.go @@ -27,6 +27,18 @@ func (c *ConnectorHost) Connections() []network.Conn { return c.h.Network().Conns() } +// IsConnectedTo returns true if the given peer.ID is connected to the underlying host. +// Args: +// +// peerID: peer.ID for which the connection status is requested +// +// Returns: +// +// true if the given peer.ID is connected to the underlying host. +func (c *ConnectorHost) IsConnectedTo(peerID peer.ID) bool { + return c.h.Network().Connectedness(peerID) == network.Connected && len(c.h.Network().ConnsToPeer(peerID)) > 0 +} + // PeerInfo returns the peer.AddrInfo for the given peer.ID. // Args: // From 0342aee9fd36099b153320738428dc0d76a28eb1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 18:39:49 -0700 Subject: [PATCH 092/815] reduces backup --- network/p2p/connection/connector_factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/connection/connector_factory.go b/network/p2p/connection/connector_factory.go index 71a27a810c1..c3ecfaeee9c 100644 --- a/network/p2p/connection/connector_factory.go +++ b/network/p2p/connection/connector_factory.go @@ -24,7 +24,7 @@ const ( timeUnit = time.Second // exponentialBackOffBase is the base for the exponential backoff. The backoff duration will be a multiple of the time unit // multiplied by the exponential base raised to the exponential offset, i.e., exponentialBase^(timeUnit*attempt). - exponentialBackOffBase = 5.0 + exponentialBackOffBase = 2.0 // exponentialBackOffOffset is the offset for the exponential backoff. It acts as a constant that is added result // of the exponential base raised to the exponential offset, i.e., exponentialBase^(timeUnit*attempt) + exponentialBackOffOffset. // This is used to ensure that the backoff duration is always greater than the time unit. We set this to 0 as we want the From 5bb3fc4ca988236af3d0a0f60beb2be81a3f265b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 18:40:22 -0700 Subject: [PATCH 093/815] fixes closure of authorized peers --- network/p2p/p2pnode/libp2pNode.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index f05f3c38c63..224a1be4542 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -363,21 +363,23 @@ func (n *Node) WithPeersProvider(peersProvider p2p.PeersProvider) { n.peerManager.SetPeersProvider( func() peer.IDSlice { authorizedPeersIds := peersProvider() - for i, id := range authorizedPeersIds { + allowListedPeerIds := peer.IDSlice{} // subset of authorizedPeersIds that are not disallowed + for _, peerId := range authorizedPeersIds { // exclude the disallowed peers from the authorized peers list - causes := n.disallowListedCache.GetAllDisallowedListCausesFor(id) + causes := n.disallowListedCache.GetAllDisallowedListCausesFor(peerId) if len(causes) > 0 { n.logger.Warn(). - Str("peer_id", id.String()). + Str("peer_id", peerId.String()). Str("causes", fmt.Sprintf("%v", causes)). Msg("peer is disallowed for a cause, removing from authorized peers of peer manager") // exclude the peer from the authorized peers list - authorizedPeersIds = append(authorizedPeersIds[:i], authorizedPeersIds[i+1:]...) + continue } + allowListedPeerIds = append(allowListedPeerIds, peerId) } - return authorizedPeersIds + return allowListedPeerIds }) } } From d5cf92a107fad6dd5eae5e92ecc5fb736c37fc49 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 6 Jun 2023 18:40:29 -0700 Subject: [PATCH 094/815] adds disallow listing test --- network/p2p/p2pnode/disallow_listing_test.go | 37 +++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index a5604f951e9..f31bd141248 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/connection" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -29,15 +30,26 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { peerIDSlice := peer.IDSlice{} // node 1 is the node that will be disallow-listing another node (node 2). - node1, identity1 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider, p2ptest.WithPeerManagerEnabled(true, connection.DefaultPeerUpdateInterval, func() peer.IDSlice { - return peerIDSlice - })) + node1, identity1 := p2ptest.NodeFixture(t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithPeerManagerEnabled(true, connection.DefaultPeerUpdateInterval, + func() peer.IDSlice { + return peerIDSlice + }), + p2ptest.WithConnectionGater(testutils.NewConnectionGater(idProvider, func(p peer.ID) error { + // allow all the connections, except for the ones that are disallow-listed, which are determined when + // this connection gater object queries the disallow listing oracle that will be provided to it by + // the libp2p node. So, here, we don't need to do anything except just enabling the connection gater. + return nil + }))) idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity1, true).Maybe() peerIDSlice = append(peerIDSlice, node1.Host().ID()) // node 2 is the node that will be disallow-listed by node 1. node2, identity2 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", node2.Host().ID()).Return(&identity2, true).Maybe() peerIDSlice = append(peerIDSlice, node2.Host().ID()) // node 3 is the node that will be connected to node 1 (to ensure that node 1 is still able to connect to other nodes @@ -66,13 +78,22 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { // but nodes 1 and 3 should remain connected as well as nodes 2 and 3. // we choose a short timeout because we expect the nodes to remain connected. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node1, node3}, 1*time.Millisecond, 100*time.Second) - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node2, node3}, 1*time.Millisecond, 100*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node1, node3}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{node2, node3}, 1*time.Millisecond, 100*time.Millisecond) + + // while node 2 is disallow-listed, it cannot connect to node 1. Also, node 1 cannot directly dial and connect to node 2, unless + // it is allow-listed again. + p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{node1}, []p2p.LibP2PNode{node2}) // phase-2: now we allow-list node 1 back node1.OnAllowListNotification(node2.Host().ID(), network.DisallowListedCauseAlsp) // eventually node 1 should be connected to node 2 again, hence all nodes should be connected to each other. - // we choose a timeout of 2 seconds because peer manager updates peers every 1 second. - p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 2*time.Second) + // we choose a timeout of 5 seconds because peer manager updates peers every 1 second and we need to wait for + // any potential random backoffs to expire (min 1 second). + p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 5*time.Second) +} + +func TestConnectionGatingDisallowListingNodes(t *testing.T) { + } From a1cd3164617453a1370895f780650b87b061d2a8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 09:47:50 -0700 Subject: [PATCH 095/815] adds godoc --- network/p2p/p2pnode/disallow_listing_test.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index f31bd141248..143da5e01b4 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -18,9 +18,9 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestDisconnectingFromDisallowListedNode ensures that the node disconnects from a disallow listed node while the node is -// connected to other (allow listed) nodes. It also ensures that when a disallow-listed node is allow-listed again, the -// node reconnects to it. +// TestDisconnectingFromDisallowListedNode ensures that (1) the node disconnects from a disallow listed node while the node is +// connected to other (allow listed) nodes. (2) new inbound or outbound connections to and from disallow-listed nodes are rejected. +// (3) When a disallow-listed node is allow-listed again, the node reconnects to it. func TestDisconnectingFromDisallowListedNode(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) @@ -93,7 +93,3 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { // any potential random backoffs to expire (min 1 second). p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 5*time.Second) } - -func TestConnectionGatingDisallowListingNodes(t *testing.T) { - -} From f3dd70d5ebbff3b48bc1ab84487fcef9da543275 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 13:33:17 -0700 Subject: [PATCH 096/815] separate the connection gater for each node in test util --- network/internal/testutils/testUtil.go | 16 +++++++------ network/test/middleware_test.go | 33 +++++++++++++++----------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 7f0bb42d486..ea25564c37a 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -137,9 +137,11 @@ func GenerateIDs(t *testing.T, logger zerolog.Logger, n int, opts ...func(*optsC o := &optsConfig{ peerUpdateInterval: connection.DefaultPeerUpdateInterval, unicastRateLimiterDistributor: ratelimit.NewUnicastRateLimiterDistributor(), - connectionGater: NewConnectionGater(idProvider, func(p peer.ID) error { - return nil - }), + connectionGaterFactory: func() p2p.ConnectionGater { + return NewConnectionGater(idProvider, func(p peer.ID) error { + return nil + }) + }, createStreamRetryInterval: unicast.DefaultRetryDelay, } for _, opt := range opts { @@ -163,7 +165,7 @@ func GenerateIDs(t *testing.T, logger zerolog.Logger, n int, opts ...func(*optsC opts = append(opts, withDHT(o.dhtPrefix, o.dhtOpts...)) opts = append(opts, withPeerManagerOptions(connection.PruningEnabled, o.peerUpdateInterval)) opts = append(opts, withRateLimiterDistributor(o.unicastRateLimiterDistributor)) - opts = append(opts, withConnectionGater(o.connectionGater)) + opts = append(opts, withConnectionGater(o.connectionGaterFactory())) opts = append(opts, withUnicastManagerOpts(o.createStreamRetryInterval)) libP2PNodes[i], tagObservables[i] = generateLibP2PNode(t, logger, key, idProvider, opts...) @@ -311,7 +313,7 @@ type optsConfig struct { networkMetrics module.NetworkMetrics peerManagerFilters []p2p.PeerFilter unicastRateLimiterDistributor p2p.UnicastRateLimiterDistributor - connectionGater p2p.ConnectionGater + connectionGaterFactory func() p2p.ConnectionGater createStreamRetryInterval time.Duration } @@ -358,9 +360,9 @@ func WithUnicastRateLimiters(limiters *ratelimit.RateLimiters) func(*optsConfig) } } -func WithConnectionGater(connectionGater p2p.ConnectionGater) func(*optsConfig) { +func WithConnectionGaterFactory(connectionGaterFactory func() p2p.ConnectionGater) func(*optsConfig) { return func(o *optsConfig) { - o.connectionGater = connectionGater + o.connectionGaterFactory = connectionGaterFactory } } diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index a7b16badc90..631808230cf 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -250,17 +250,20 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { idProvider := testutils.NewUpdatableIDProvider(m.ids) // create a new staked identity - connGater := testutils.NewConnectionGater(idProvider, func(pid peer.ID) error { - if messageRateLimiter.IsRateLimited(pid) { - return fmt.Errorf("rate-limited peer") - } - return nil - }) + connGaterFactory := func() p2p.ConnectionGater { + return testutils.NewConnectionGater(idProvider, func(pid peer.ID) error { + if messageRateLimiter.IsRateLimited(pid) { + return fmt.Errorf("rate-limited peer") + } + return nil + }) + } + ids, libP2PNodes, _ := testutils.GenerateIDs(m.T(), m.logger, 1, testutils.WithUnicastRateLimiterDistributor(distributor), - testutils.WithConnectionGater(connGater)) + testutils.WithConnectionGaterFactory(connGaterFactory)) idProvider.SetIdentities(append(m.ids, ids...)) // create middleware @@ -405,19 +408,21 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { idProvider := testutils.NewUpdatableIDProvider(m.ids) // create connection gater, connection gater will refuse connections from rate limited nodes - connGater := testutils.NewConnectionGater(idProvider, func(pid peer.ID) error { - if bandwidthRateLimiter.IsRateLimited(pid) { - return fmt.Errorf("rate-limited peer") - } + connGaterFactory := func() p2p.ConnectionGater { + return testutils.NewConnectionGater(idProvider, func(pid peer.ID) error { + if bandwidthRateLimiter.IsRateLimited(pid) { + return fmt.Errorf("rate-limited peer") + } - return nil - }) + return nil + }) + } // create a new staked identity ids, libP2PNodes, _ := testutils.GenerateIDs(m.T(), m.logger, 1, testutils.WithUnicastRateLimiterDistributor(distributor), - testutils.WithConnectionGater(connGater)) + testutils.WithConnectionGaterFactory(connGaterFactory)) idProvider.SetIdentities(append(m.ids, ids...)) // create middleware From 2a033dba5ac1cccee2deb7d631dd83c3ffb2a825 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 13:33:32 -0700 Subject: [PATCH 097/815] extends a documentation --- network/p2p/p2pnode/disallow_listing_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index 143da5e01b4..0249c3ee91f 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -18,8 +18,9 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestDisconnectingFromDisallowListedNode ensures that (1) the node disconnects from a disallow listed node while the node is -// connected to other (allow listed) nodes. (2) new inbound or outbound connections to and from disallow-listed nodes are rejected. +// TestDisconnectingFromDisallowListedNode ensures that: +// (1) the node disconnects from a disallow listed node while the node is connected to other (allow listed) nodes. +// (2) new inbound or outbound connections to and from disallow-listed nodes are rejected. // (3) When a disallow-listed node is allow-listed again, the node reconnects to it. func TestDisconnectingFromDisallowListedNode(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) From e8b7178d79e55e403c792d81c9bed5719c81fe60 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 13:34:49 -0700 Subject: [PATCH 098/815] adds misbehavior and disallowlisting test --- network/alsp/manager/manager.go | 2 - network/alsp/manager/manager_test.go | 92 +++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 6d0c0d18752..bc9c1246ce9 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -321,7 +321,6 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { if record.Penalty < model.DisallowListingThreshold { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ - m.logger.Warn(). Str("key", logging.KeySuspicious). Hex("identifier", logging.ID(id)). @@ -331,7 +330,6 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { FlowIds: flow.IdentifierList{id}, Cause: network.DisallowListedCauseAlsp, // sets the ALSP disallow listing cause on node }) - } // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 5982d0b8c37..2c6ecaca074 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -29,6 +29,7 @@ import ( "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" ) @@ -102,7 +103,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { // It prepares a set of misbehavior reports and reports them to the conduit on the test channel. // The test ensures that the MisbehaviorReportManager receives and handles all reported misbehavior // without any duplicate reports and within a specified time. -func TestHandleReportedMisbehavior_Integration(t *testing.T) { +func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { cfg := managerCfgFixture() // create a new MisbehaviorReportManager @@ -126,7 +127,6 @@ func TestHandleReportedMisbehavior_Integration(t *testing.T) { require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}, 100*time.Millisecond) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) @@ -188,6 +188,94 @@ func TestHandleReportedMisbehavior_Integration(t *testing.T) { }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") } +// TestHandleReportedMisbehavior_And_DisallowListing_Integration implements an end-to-end integration test for the +// handling of reported misbehavior and disallow listing. +// +// The test sets up 3 nodes, one victim, one honest, and one (alledged) spammer. +// Initially, the test ensures that all nodes are connected to each other. +// Then, test imitates that victim node reports the spammer node for spamming. +// The test generates enough spam reports to trigger the disallow listing of the victim node. +// The test ensures that the victim node is disconnected from the spammer node. +// The test ensures that despite attempting on connections, no inbound or outbound connections between the victim and +// the disallow-listed spammer node are established. +func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) { + cfg := managerCfgFixture() + + // create a new MisbehaviorReportManager + var victimSpamRecordCacheCache alsp.SpamRecordCache + cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return victimSpamRecordCacheCache + }), + } + + ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( + t, + 3, + unittest.Logger(), + unittest.NetworkCodec(), + unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector())) + sms := testutils.GenerateSubscriptionManagers(t, mws) + networkCfg := testutils.NetworkConfigFixture(t, unittest.Logger(), *ids[0], ids, mws[0], sms[0], p2p.WithAlspConfig(cfg)) + victimNetwork, err := p2p.NewNetwork(networkCfg) + require.NoError(t, err) + + // index of the victim node in the nodes slice. + victimIndex := 0 + // index of the spammer node in the nodes slice (the node that will be reported for misbehavior and disallow-listed by victim). + spammerIndex := 1 + // other node (not victim and not spammer) that we have to ensure is not affected by the disallow-listing of the spammer. + honestIndex := 2 + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}, 100*time.Millisecond) + defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) + defer cancel() + + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + // initially victim and spammer should be able to connect to each other. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + + e := mocknetwork.NewEngine(t) + con, err := victimNetwork.Register(channels.TestNetworkChannel, e) + require.NoError(t, err) + + // creates a misbehavior report for the spammer + report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) + + // imitates that the victim has detected the spammer on 120 times of violations and reports the misbehavior + // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is disallow-listed definitely. + reportCount := 120 + wg := sync.WaitGroup{} + for i := 0; i < reportCount; i++ { + wg.Add(1) + // reports the misbehavior + report := report // capture range variable + go func() { + defer wg.Done() + + con.ReportMisbehavior(report) + }() + } + + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // ensures that the spammer is disallow-listed by the victim + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + + // despite disallow-listing spammer, it ensures that (victim and spammer) and (honest and spammer) are still connected. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + // while node 2 is disallow-listed, it cannot connect to node 1. Also, node 1 cannot directly dial and connect to node 2, unless + // it is allow-listed again. + p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) +} + // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. // It checks that when a misbehavior report is received by the ALSP manager, the metrics are recorded. // It fails the test if the metrics are not recorded or if they are recorded incorrectly. From afde747ad901de07f2d318448bf168607bae9f0d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 13:45:22 -0700 Subject: [PATCH 099/815] updates mocks --- cmd/node_builder.go | 3 +++ network/mocknetwork/connector_host.go | 38 ++++++++++++++++++--------- network/p2p/mock/connector_host.go | 38 ++++++++++++++++++--------- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 42eb7eb40cb..36020c40d71 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -197,6 +197,9 @@ type NetworkConfig struct { // UnicastCreateStreamRetryDelay initial delay used in the exponential backoff for create stream retries UnicastCreateStreamRetryDelay time.Duration // size of the cache keeping the status of disallow-listed peers. Recommended to be 100 * number of authorized nodes. + // Note: this cache is meant to only keep the authorized (i.e., staked) but disallow-listed peers. There is no sybil + // vulnerability for this cache. However, the cache must be large enough to ensure it never runs out of space even if + // the node has not been re-configured for a long time. DisallowListCacheSize uint32 // UnicastRateLimitersConfig configuration for all unicast rate limiters. UnicastRateLimitersConfig *UnicastRateLimitersConfig diff --git a/network/mocknetwork/connector_host.go b/network/mocknetwork/connector_host.go index 51c7ac7b539..e656391a11f 100644 --- a/network/mocknetwork/connector_host.go +++ b/network/mocknetwork/connector_host.go @@ -14,13 +14,13 @@ type ConnectorHost struct { mock.Mock } -// ClosePeer provides a mock function with given fields: id -func (_m *ConnectorHost) ClosePeer(id peer.ID) error { - ret := _m.Called(id) +// ClosePeer provides a mock function with given fields: peerId +func (_m *ConnectorHost) ClosePeer(peerId peer.ID) error { + ret := _m.Called(peerId) var r0 error if rf, ok := ret.Get(0).(func(peer.ID) error); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Error(0) } @@ -58,13 +58,13 @@ func (_m *ConnectorHost) ID() peer.ID { return r0 } -// IsProtected provides a mock function with given fields: id -func (_m *ConnectorHost) IsProtected(id peer.ID) bool { - ret := _m.Called(id) +// IsConnectedTo provides a mock function with given fields: peerId +func (_m *ConnectorHost) IsConnectedTo(peerId peer.ID) bool { + ret := _m.Called(peerId) var r0 bool if rf, ok := ret.Get(0).(func(peer.ID) bool); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Get(0).(bool) } @@ -72,13 +72,27 @@ func (_m *ConnectorHost) IsProtected(id peer.ID) bool { return r0 } -// PeerInfo provides a mock function with given fields: id -func (_m *ConnectorHost) PeerInfo(id peer.ID) peer.AddrInfo { - ret := _m.Called(id) +// IsProtected provides a mock function with given fields: peerId +func (_m *ConnectorHost) IsProtected(peerId peer.ID) bool { + ret := _m.Called(peerId) + + var r0 bool + if rf, ok := ret.Get(0).(func(peer.ID) bool); ok { + r0 = rf(peerId) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// PeerInfo provides a mock function with given fields: peerId +func (_m *ConnectorHost) PeerInfo(peerId peer.ID) peer.AddrInfo { + ret := _m.Called(peerId) var r0 peer.AddrInfo if rf, ok := ret.Get(0).(func(peer.ID) peer.AddrInfo); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Get(0).(peer.AddrInfo) } diff --git a/network/p2p/mock/connector_host.go b/network/p2p/mock/connector_host.go index 549c013db28..5cb468884e8 100644 --- a/network/p2p/mock/connector_host.go +++ b/network/p2p/mock/connector_host.go @@ -14,13 +14,13 @@ type ConnectorHost struct { mock.Mock } -// ClosePeer provides a mock function with given fields: id -func (_m *ConnectorHost) ClosePeer(id peer.ID) error { - ret := _m.Called(id) +// ClosePeer provides a mock function with given fields: peerId +func (_m *ConnectorHost) ClosePeer(peerId peer.ID) error { + ret := _m.Called(peerId) var r0 error if rf, ok := ret.Get(0).(func(peer.ID) error); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Error(0) } @@ -58,13 +58,13 @@ func (_m *ConnectorHost) ID() peer.ID { return r0 } -// IsProtected provides a mock function with given fields: id -func (_m *ConnectorHost) IsProtected(id peer.ID) bool { - ret := _m.Called(id) +// IsConnectedTo provides a mock function with given fields: peerId +func (_m *ConnectorHost) IsConnectedTo(peerId peer.ID) bool { + ret := _m.Called(peerId) var r0 bool if rf, ok := ret.Get(0).(func(peer.ID) bool); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Get(0).(bool) } @@ -72,13 +72,27 @@ func (_m *ConnectorHost) IsProtected(id peer.ID) bool { return r0 } -// PeerInfo provides a mock function with given fields: id -func (_m *ConnectorHost) PeerInfo(id peer.ID) peer.AddrInfo { - ret := _m.Called(id) +// IsProtected provides a mock function with given fields: peerId +func (_m *ConnectorHost) IsProtected(peerId peer.ID) bool { + ret := _m.Called(peerId) + + var r0 bool + if rf, ok := ret.Get(0).(func(peer.ID) bool); ok { + r0 = rf(peerId) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// PeerInfo provides a mock function with given fields: peerId +func (_m *ConnectorHost) PeerInfo(peerId peer.ID) peer.AddrInfo { + ret := _m.Called(peerId) var r0 peer.AddrInfo if rf, ok := ret.Get(0).(func(peer.ID) peer.AddrInfo); ok { - r0 = rf(id) + r0 = rf(peerId) } else { r0 = ret.Get(0).(peer.AddrInfo) } From 276d3523c969aad314b2352e64b83d761dfa3334 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 13:56:26 -0700 Subject: [PATCH 100/815] fixes build issues --- cmd/access/node_builder/access_node_builder.go | 6 +++++- cmd/observer/node_builder/observer_builder.go | 6 +++++- follower/follower_builder.go | 6 +++++- network/alsp/manager/manager.go | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index bfd744ff172..2436bd1d72d 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1213,7 +1213,11 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri bindAddress, networkKey, builder.SporkID, - builder.LibP2PResourceManagerConfig). + builder.LibP2PResourceManagerConfig, + &p2p.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + }). SetBasicResolver(builder.Resolver). SetSubscriptionFilter( subscription.NewRoleBasedFilter( diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 6b41ed7f206..2c604b8e885 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -723,7 +723,11 @@ func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr builder.BaseConfig.BindAddr, networkKey, builder.SporkID, - builder.LibP2PResourceManagerConfig). + builder.LibP2PResourceManagerConfig, + &p2p.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + }). SetSubscriptionFilter( subscription.NewRoleBasedFilter( subscription.UnstakedRole, builder.IdentityProvider, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 4dad311456e..a58b8229d74 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -611,7 +611,11 @@ func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr builder.BaseConfig.BindAddr, networkKey, builder.SporkID, - builder.LibP2PResourceManagerConfig). + builder.LibP2PResourceManagerConfig, + &p2p.DisallowListCacheConfig{ + MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + }). SetSubscriptionFilter( subscription.NewRoleBasedFilter( subscription.UnstakedRole, builder.IdentityProvider, diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index bc9c1246ce9..98704386533 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -318,6 +318,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { } // TODO: this can be done in batch but at this stage let's send individual notifications. + // (it requires enabling the batch mode end-to-end including the cache in middleware). if record.Penalty < model.DisallowListingThreshold { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ @@ -338,6 +339,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { record.Penalty = math.Min(record.Penalty+record.Decay, 0) // TODO: this can be done in batch but at this stage let's send individual notifications. + // (it requires enabling the batch mode end-to-end including the cache in middleware). if record.Penalty == float64(0) { m.disallowListingConsumer.OnAllowListNotification(&network.AllowListingUpdate{ FlowIds: flow.IdentifierList{id}, From dd43a6334a42ecf32e6233e42d53ad78c89d9606 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 15:24:48 -0700 Subject: [PATCH 101/815] lint fix --- cmd/access/node_builder/access_node_builder.go | 4 ---- cmd/observer/node_builder/observer_builder.go | 4 ---- follower/follower_builder.go | 4 ---- 3 files changed, 12 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 2436bd1d72d..a094a28bab3 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1268,10 +1268,6 @@ func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(logger, networkMetrics), - DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ - MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 2c604b8e885..90db8f8deea 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -918,10 +918,6 @@ func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), SlashingViolationsConsumer: slashingViolationsConsumer, - DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ - MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index a58b8229d74..463c6447e7d 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -746,10 +746,6 @@ func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), SlashingViolationsConsumer: slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network), - DisallowListCacheConfig: &middleware.DisallowListCacheConfig{ - MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), - }, }, middleware.WithMessageValidators(validators...), ) From 14fe8db9a9929e6749bbaad71103b4b6e6b88889 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 15:35:32 -0700 Subject: [PATCH 102/815] renames block list to disallow list --- .../node_builder/access_node_builder.go | 4 +- cmd/scaffold.go | 4 +- network/p2p/cache/node_blocklist_wrapper.go | 64 +++++++++++-------- .../p2p/cache/node_blocklist_wrapper_test.go | 42 ++++++------ 4 files changed, 62 insertions(+), 52 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index a094a28bab3..a04f1501160 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -821,9 +821,9 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { // register the wrapper for dynamic configuration via admin command err = node.ConfigManager.RegisterIdentifierListConfig("network-id-provider-blocklist", - disallowListWrapper.GetBlocklist, disallowListWrapper.Update) + disallowListWrapper.GetDisallowList, disallowListWrapper.Update) if err != nil { - return fmt.Errorf("failed to register blocklist with config manager: %w", err) + return fmt.Errorf("failed to register disallow-list wrapper with config manager: %w", err) } builder.SyncEngineParticipantsProviderFactory = func() module.IdentifierProvider { diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 206ff3a85f7..0b3a4bfdd73 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -1062,9 +1062,9 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // register the disallow list wrapper for dynamic configuration via admin command err = node.ConfigManager.RegisterIdentifierListConfig("network-id-provider-blocklist", - disallowListWrapper.GetBlocklist, disallowListWrapper.Update) + disallowListWrapper.GetDisallowList, disallowListWrapper.Update) if err != nil { - return fmt.Errorf("failed to register blocklist with config manager: %w", err) + return fmt.Errorf("failed to register disallow-list wrapper with config manager: %w", err) } node.SyncEngineIdentifierProvider = id.NewIdentityFilterIdentifierProvider( diff --git a/network/p2p/cache/node_blocklist_wrapper.go b/network/p2p/cache/node_blocklist_wrapper.go index 728b40edbd5..b04e2211712 100644 --- a/network/p2p/cache/node_blocklist_wrapper.go +++ b/network/p2p/cache/node_blocklist_wrapper.go @@ -24,17 +24,19 @@ func (s IdentifierSet) Contains(id flow.Identifier) bool { return found } -// NodeBlocklistWrapper is a wrapper for an `module.IdentityProvider` instance, where the +// NodeDisallowListingWrapper is a wrapper for an `module.IdentityProvider` instance, where the // wrapper overrides the `Ejected` flag to true for all NodeIDs in a `disallowList`. // To avoid modifying the source of the identities, the wrapper creates shallow copies // of the identities (whenever necessary) and modifies the `Ejected` flag only in // the copy. -// The `NodeBlocklistWrapper` internally represents the `disallowList` as a map, to enable +// The `NodeDisallowListingWrapper` internally represents the `disallowList` as a map, to enable // performant lookup. However, the exported API works with `flow.IdentifierList` for // disallowList, as this is a broadly supported data structure which lends itself better // to config or command-line inputs. +// When a node is disallow-listed, the networking layer connection to that node is closed and no +// incoming or outgoing connections are established with that node. // TODO: terminology change - rename `blocklist` to `disallowList` everywhere to be consistent with the code. -type NodeBlocklistWrapper struct { +type NodeDisallowListingWrapper struct { m sync.RWMutex db *badger.DB @@ -45,21 +47,21 @@ type NodeBlocklistWrapper struct { updateConsumer network.DisallowListNotificationConsumer } -var _ module.IdentityProvider = (*NodeBlocklistWrapper)(nil) +var _ module.IdentityProvider = (*NodeDisallowListingWrapper)(nil) // NewNodeDisallowListWrapper wraps the given `IdentityProvider`. The disallow-list is // loaded from the database (or assumed to be empty if no database entry is present). func NewNodeDisallowListWrapper( identityProvider module.IdentityProvider, db *badger.DB, - updateConsumer network.DisallowListNotificationConsumer) (*NodeBlocklistWrapper, error) { + updateConsumer network.DisallowListNotificationConsumer) (*NodeDisallowListingWrapper, error) { - disallowList, err := retrieveBlocklist(db) + disallowList, err := retrieveDisallowList(db) if err != nil { return nil, fmt.Errorf("failed to read set of disallowed node IDs from data base: %w", err) } - return &NodeBlocklistWrapper{ + return &NodeDisallowListingWrapper{ db: db, identityProvider: identityProvider, disallowList: disallowList, @@ -68,37 +70,45 @@ func NewNodeDisallowListWrapper( } // Update sets the wrapper's internal set of blocked nodes to `disallowList`. Empty list and `nil` -// (equivalent to empty list) are accepted inputs. To avoid legacy entries in the data base, this +// (equivalent to empty list) are accepted inputs. To avoid legacy entries in the database, this // function purges the entire data base entry if `disallowList` is empty. -// This implementation is _eventually consistent_, where changes are written to the data base first +// This implementation is _eventually consistent_, where changes are written to the database first // and then (non-atomically!) the in-memory set of blocked nodes is updated. This strongly // benefits performance and modularity. No errors are expected during normal operations. -func (w *NodeBlocklistWrapper) Update(blocklist flow.IdentifierList) error { - b := blocklist.Lookup() // converts slice to map +// +// Args: +// - disallowList: list of node IDs to be disallow-listed from the networking layer, i.e., the existing connections +// to these nodes will be closed and no new connections will be established (neither incoming nor outgoing). +// +// Returns: +// - error: if the update fails, e.g., due to a database error. Any returned error is irrecoverable and the caller +// should abort the process. +func (w *NodeDisallowListingWrapper) Update(disallowList flow.IdentifierList) error { + b := disallowList.Lookup() // converts slice to map w.m.Lock() defer w.m.Unlock() - err := persistBlocklist(b, w.db) + err := persistDisallowList(b, w.db) if err != nil { return fmt.Errorf("failed to persist set of blocked nodes to the data base: %w", err) } w.disallowList = b w.updateConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ - FlowIds: blocklist, + FlowIds: disallowList, Cause: network.DisallowListedCauseAdmin, }) return nil } -// ClearBlocklist purges the set of blocked node IDs. Convenience function +// ClearDisallowList purges the set of blocked node IDs. Convenience function // equivalent to w.Update(nil). No errors are expected during normal operations. -func (w *NodeBlocklistWrapper) ClearBlocklist() error { +func (w *NodeDisallowListingWrapper) ClearDisallowList() error { return w.Update(nil) } -// GetBlocklist returns the set of blocked node IDs. -func (w *NodeBlocklistWrapper) GetBlocklist() flow.IdentifierList { +// GetDisallowList returns the set of blocked node IDs. +func (w *NodeDisallowListingWrapper) GetDisallowList() flow.IdentifierList { w.m.RLock() defer w.m.RUnlock() @@ -113,7 +123,7 @@ func (w *NodeBlocklistWrapper) GetBlocklist() flow.IdentifierList { // protocol that pass the provided filter. Caution, this includes ejected nodes. // Please check the `Ejected` flag in the returned identities (or provide a // filter for removing ejected nodes). -func (w *NodeBlocklistWrapper) Identities(filter flow.IdentityFilter) flow.IdentityList { +func (w *NodeDisallowListingWrapper) Identities(filter flow.IdentityFilter) flow.IdentityList { identities := w.identityProvider.Identities(filter) if len(identities) == 0 { return identities @@ -145,7 +155,7 @@ func (w *NodeBlocklistWrapper) Identities(filter flow.IdentityFilter) flow.Ident // true if and only if Identity has been found, i.e. `Identity` is not nil. // Caution: function returns include ejected nodes. Please check the `Ejected` // flag in the identity. -func (w *NodeBlocklistWrapper) ByNodeID(identifier flow.Identifier) (*flow.Identity, bool) { +func (w *NodeDisallowListingWrapper) ByNodeID(identifier flow.Identifier) (*flow.Identity, bool) { identity, b := w.identityProvider.ByNodeID(identifier) return w.setEjectedIfBlocked(identity), b } @@ -154,7 +164,7 @@ func (w *NodeBlocklistWrapper) ByNodeID(identifier flow.Identifier) (*flow.Ident // Shortcuts: // - If the node's identity is nil, there is nothing to do because we don't generate identities here. // - If the node is already ejected, we don't have to check the disallowList. -func (w *NodeBlocklistWrapper) setEjectedIfBlocked(identity *flow.Identity) *flow.Identity { +func (w *NodeDisallowListingWrapper) setEjectedIfBlocked(identity *flow.Identity) *flow.Identity { if identity == nil || identity.Ejected { return identity } @@ -180,25 +190,25 @@ func (w *NodeBlocklistWrapper) setEjectedIfBlocked(identity *flow.Identity) *flo // true if and only if Identity has been found, i.e. `Identity` is not nil. // Caution: function returns include ejected nodes. Please check the `Ejected` // flag in the identity. -func (w *NodeBlocklistWrapper) ByPeerID(p peer.ID) (*flow.Identity, bool) { +func (w *NodeDisallowListingWrapper) ByPeerID(p peer.ID) (*flow.Identity, bool) { identity, b := w.identityProvider.ByPeerID(p) return w.setEjectedIfBlocked(identity), b } -// persistBlocklist writes the given disallowList to the database. To avoid legacy +// persistDisallowList writes the given disallowList to the database. To avoid legacy // entries in the database, we prune the entire data base entry if `disallowList` is // empty. No errors are expected during normal operations. -func persistBlocklist(blocklist IdentifierSet, db *badger.DB) error { - if len(blocklist) == 0 { +func persistDisallowList(disallowList IdentifierSet, db *badger.DB) error { + if len(disallowList) == 0 { return db.Update(operation.PurgeBlocklist()) } - return db.Update(operation.PersistBlocklist(blocklist)) + return db.Update(operation.PersistBlocklist(disallowList)) } -// retrieveBlocklist reads the set of blocked nodes from the data base. +// retrieveDisallowList reads the set of blocked nodes from the data base. // In case no database entry exists, an empty set (nil map) is returned. // No errors are expected during normal operations. -func retrieveBlocklist(db *badger.DB) (IdentifierSet, error) { +func retrieveDisallowList(db *badger.DB) (IdentifierSet, error) { var blocklist map[flow.Identifier]struct{} err := db.View(operation.RetrieveBlocklist(&blocklist)) if err != nil && !errors.Is(err, storage.ErrNotFound) { diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index 7315de99f08..3e5a609daac 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -26,7 +26,7 @@ type NodeBlocklistWrapperTestSuite struct { DB *badger.DB provider *mocks.IdentityProvider - wrapper *cache.NodeBlocklistWrapper + wrapper *cache.NodeDisallowListingWrapper updateConsumer *mocknetwork.DisallowListNotificationConsumer } @@ -45,7 +45,7 @@ func TestNodeBlocklistWrapperTestSuite(t *testing.T) { } // TestHonestNode verifies: -// For nodes _not_ on the disallowList, the `cache.NodeBlocklistWrapper` should forward +// For nodes _not_ on the disallowList, the `cache.NodeDisallowListingWrapper` should forward // the identities from the wrapped `IdentityProvider` without modification. func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { s.Run("ByNodeID", func() { @@ -281,7 +281,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { // Note: // conceptually, the disallowList is a set, i.e. not order dependent. // The wrapper internally converts the list to a set and vice versa. Therefore -// the order is not preserved by `GetBlocklist`. Consequently, we compare +// the order is not preserved by `GetDisallowList`. Consequently, we compare // map-based representations here. func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { blocklist1 := unittest.IdentifierListFixture(8) @@ -294,7 +294,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { }).Return().Once() err := s.wrapper.Update(blocklist1) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist1.Lookup(), s.wrapper.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist1.Lookup(), s.wrapper.GetDisallowList().Lookup()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: blocklist2, @@ -302,15 +302,15 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { }).Return().Once() err = s.wrapper.Update(blocklist2) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), s.wrapper.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist2.Lookup(), s.wrapper.GetDisallowList().Lookup()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: nil, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.ClearBlocklist() + err = s.wrapper.ClearDisallowList() require.NoError(s.T(), err) - require.Empty(s.T(), s.wrapper.GetBlocklist()) + require.Empty(s.T(), s.wrapper.GetDisallowList()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: blocklist3, @@ -318,26 +318,26 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { }).Return().Once() err = s.wrapper.Update(blocklist3) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist3.Lookup(), s.wrapper.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist3.Lookup(), s.wrapper.GetDisallowList().Lookup()) } // TestDataBasePersist verifies database interactions of the wrapper with the data base. // This test verifies that the disallowList updates are persisted across restarts. // To decouple this test from the lower-level data base design, we proceed as follows: -// - We do data-base operation through the exported methods from `NodeBlocklistWrapper` -// - Then, we create a new `NodeBlocklistWrapper` backed by the same data base. Since it is a +// - We do data-base operation through the exported methods from `NodeDisallowListingWrapper` +// - Then, we create a new `NodeDisallowListingWrapper` backed by the same data base. Since it is a // new wrapper, it must read its state from the data base. Hence, if the new wrapper returns // the correct data, we have strong evidence that data-base interactions are correct. // // Note: The wrapper internally converts the list to a set and vice versa. Therefore -// the order is not preserved by `GetBlocklist`. Consequently, we compare +// the order is not preserved by `GetDisallowList`. Consequently, we compare // map-based representations here. func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { blocklist := unittest.IdentifierListFixture(8) blocklist2 := unittest.IdentifierListFixture(8) s.Run("Get disallowList from empty database", func() { - require.Empty(s.T(), s.wrapper.GetBlocklist()) + require.Empty(s.T(), s.wrapper.GetDisallowList()) }) s.Run("Clear disallowList on empty database", func() { @@ -345,14 +345,14 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { FlowIds: nil, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err := s.wrapper.ClearBlocklist() // No-op as data base does not contain any block list + err := s.wrapper.ClearDisallowList() // No-op as data base does not contain any block list require.NoError(s.T(), err) - require.Empty(s.T(), s.wrapper.GetBlocklist()) + require.Empty(s.T(), s.wrapper.GetDisallowList()) // newly created wrapper should read `disallowList` from data base during initialization w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Empty(s.T(), w.GetBlocklist()) + require.Empty(s.T(), w.GetDisallowList()) }) s.Run("Update disallowList and init new wrapper from database", func() { @@ -366,7 +366,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { // newly created wrapper should read `disallowList` from data base during initialization w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist.Lookup(), w.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist.Lookup(), w.GetDisallowList().Lookup()) }) s.Run("Update and overwrite disallowList and then init new wrapper from database", func() { @@ -387,7 +387,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { // newly created wrapper should read initial state from data base w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), w.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist2.Lookup(), w.GetDisallowList().Lookup()) }) s.Run("Update & clear & update and then init new wrapper from database", func() { @@ -402,7 +402,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist.Lookup(), w0.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist.Lookup(), w0.GetDisallowList().Lookup()) // clear disallowList -> // newly created wrapper should now read empty disallowList from data base during initialization @@ -410,12 +410,12 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { FlowIds: nil, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.ClearBlocklist() + err = s.wrapper.ClearDisallowList() require.NoError(s.T(), err) w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Empty(s.T(), w1.GetBlocklist()) + require.Empty(s.T(), w1.GetDisallowList()) // set blocklist2 -> // newly created wrapper should now read this list from data base during initialization @@ -428,6 +428,6 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), w2.GetBlocklist().Lookup()) + require.Equal(s.T(), blocklist2.Lookup(), w2.GetDisallowList().Lookup()) }) } From a2fd745a8b4dd3be8b2e1311bb7544f10080273e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 15:40:09 -0700 Subject: [PATCH 103/815] renames block list to disallow list --- .../p2p/cache/node_blocklist_wrapper_test.go | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index 3e5a609daac..9c0ca36f35f 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -21,7 +21,7 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -type NodeBlocklistWrapperTestSuite struct { +type NodeDisallowListWrapperTestSuite struct { suite.Suite DB *badger.DB provider *mocks.IdentityProvider @@ -30,7 +30,7 @@ type NodeBlocklistWrapperTestSuite struct { updateConsumer *mocknetwork.DisallowListNotificationConsumer } -func (s *NodeBlocklistWrapperTestSuite) SetupTest() { +func (s *NodeDisallowListWrapperTestSuite) SetupTest() { s.DB, _ = unittest.TempBadgerDB(s.T()) s.provider = new(mocks.IdentityProvider) @@ -40,14 +40,14 @@ func (s *NodeBlocklistWrapperTestSuite) SetupTest() { require.NoError(s.T(), err) } -func TestNodeBlocklistWrapperTestSuite(t *testing.T) { - suite.Run(t, new(NodeBlocklistWrapperTestSuite)) +func TestNodeDisallowListWrapperTestSuite(t *testing.T) { + suite.Run(t, new(NodeDisallowListWrapperTestSuite)) } // TestHonestNode verifies: // For nodes _not_ on the disallowList, the `cache.NodeDisallowListingWrapper` should forward // the identities from the wrapped `IdentityProvider` without modification. -func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { +func (s *NodeDisallowListWrapperTestSuite) TestHonestNode() { s.Run("ByNodeID", func() { identity := unittest.IdentityFixture() s.provider.On("ByNodeID", identity.NodeID).Return(identity, true) @@ -79,7 +79,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { }) } -// TestDenylistedNode tests proper handling of identities _on_ the disallowList: +// TestDisallowListNode tests proper handling of identities _on_ the disallowList: // - For any identity `i` with `i.NodeID ∈ disallowList`, the returned identity // should have `i.Ejected` set to `true` (irrespective of the `Ejected` // flag's initial returned by the wrapped `IdentityProvider`). @@ -92,7 +92,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestHonestNode() { // While returning (non-nil identity, false) is not a defined return value, // we expect the wrapper to nevertheless handle this case to increase its // generality. -func (s *NodeBlocklistWrapperTestSuite) TestDenylistedNode() { +func (s *NodeDisallowListWrapperTestSuite) TestDisallowListNode() { blocklist := unittest.IdentityListFixture(11) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: blocklist.NodeIDs(), @@ -192,7 +192,7 @@ func (s *NodeBlocklistWrapperTestSuite) TestDenylistedNode() { // TestUnknownNode verifies that the wrapper forwards nil identities // irrespective of the boolean return values. -func (s *NodeBlocklistWrapperTestSuite) TestUnknownNode() { +func (s *NodeDisallowListWrapperTestSuite) TestUnknownNode() { for _, b := range []bool{true, false} { s.Run(fmt.Sprintf("IdentityProvider.ByNodeID returning (nil, %v)", b), func() { id := unittest.IdentifierFixture() @@ -214,13 +214,13 @@ func (s *NodeBlocklistWrapperTestSuite) TestUnknownNode() { } } -// TestBlocklistAddRemove checks that adding and subsequently removing a node from the disallowList +// TestDisallowListAddRemove checks that adding and subsequently removing a node from the disallowList // it in combination a no-op. We test two scenarious // - Node whose original `Identity` has `Ejected = false`: // After adding the node to the disallowList and then removing it again, the `Ejected` should be false. // - Node whose original `Identity` has `Ejected = true`: // After adding the node to the disallowList and then removing it again, the `Ejected` should be still be true. -func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { +func (s *NodeDisallowListWrapperTestSuite) TestDisallowListAddRemove() { for _, originalEjected := range []bool{true, false} { s.Run(fmt.Sprintf("Add & remove node with Ejected = %v", originalEjected), func() { originalIdentity := unittest.IdentityFixture() @@ -283,26 +283,26 @@ func (s *NodeBlocklistWrapperTestSuite) TestBlocklistAddRemove() { // The wrapper internally converts the list to a set and vice versa. Therefore // the order is not preserved by `GetDisallowList`. Consequently, we compare // map-based representations here. -func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { - blocklist1 := unittest.IdentifierListFixture(8) - blocklist2 := unittest.IdentifierListFixture(11) - blocklist3 := unittest.IdentifierListFixture(5) +func (s *NodeDisallowListWrapperTestSuite) TestUpdate() { + disallowList1 := unittest.IdentifierListFixture(8) + disallowList2 := unittest.IdentifierListFixture(11) + disallowList3 := unittest.IdentifierListFixture(5) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist1, + FlowIds: disallowList1, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err := s.wrapper.Update(blocklist1) + err := s.wrapper.Update(disallowList1) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist1.Lookup(), s.wrapper.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList1.Lookup(), s.wrapper.GetDisallowList().Lookup()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist2, + FlowIds: disallowList2, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.Update(blocklist2) + err = s.wrapper.Update(disallowList2) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), s.wrapper.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList2.Lookup(), s.wrapper.GetDisallowList().Lookup()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: nil, @@ -313,12 +313,12 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { require.Empty(s.T(), s.wrapper.GetDisallowList()) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist3, + FlowIds: disallowList3, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.Update(blocklist3) + err = s.wrapper.Update(disallowList3) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist3.Lookup(), s.wrapper.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList3.Lookup(), s.wrapper.GetDisallowList().Lookup()) } // TestDataBasePersist verifies database interactions of the wrapper with the data base. @@ -332,15 +332,15 @@ func (s *NodeBlocklistWrapperTestSuite) TestUpdate() { // Note: The wrapper internally converts the list to a set and vice versa. Therefore // the order is not preserved by `GetDisallowList`. Consequently, we compare // map-based representations here. -func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { - blocklist := unittest.IdentifierListFixture(8) - blocklist2 := unittest.IdentifierListFixture(8) +func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { + disallowList1 := unittest.IdentifierListFixture(8) + disallowList2 := unittest.IdentifierListFixture(8) s.Run("Get disallowList from empty database", func() { require.Empty(s.T(), s.wrapper.GetDisallowList()) }) - s.Run("Clear disallowList on empty database", func() { + s.Run("Clear disallow-list on empty database", func() { s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ FlowIds: nil, Cause: network.DisallowListedCauseAdmin, @@ -357,52 +357,52 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { s.Run("Update disallowList and init new wrapper from database", func() { s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist, + FlowIds: disallowList1, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err := s.wrapper.Update(blocklist) + err := s.wrapper.Update(disallowList1) require.NoError(s.T(), err) // newly created wrapper should read `disallowList` from data base during initialization w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist.Lookup(), w.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList1.Lookup(), w.GetDisallowList().Lookup()) }) s.Run("Update and overwrite disallowList and then init new wrapper from database", func() { s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist, + FlowIds: disallowList1, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err := s.wrapper.Update(blocklist) + err := s.wrapper.Update(disallowList1) require.NoError(s.T(), err) s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist2, + FlowIds: disallowList2, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.Update(blocklist2) + err = s.wrapper.Update(disallowList2) require.NoError(s.T(), err) // newly created wrapper should read initial state from data base w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), w.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList2.Lookup(), w.GetDisallowList().Lookup()) }) s.Run("Update & clear & update and then init new wrapper from database", func() { // set disallowList -> // newly created wrapper should now read this list from data base during initialization s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist, + FlowIds: disallowList1, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err := s.wrapper.Update(blocklist) + err := s.wrapper.Update(disallowList1) require.NoError(s.T(), err) w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist.Lookup(), w0.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList1.Lookup(), w0.GetDisallowList().Lookup()) // clear disallowList -> // newly created wrapper should now read empty disallowList from data base during initialization @@ -417,17 +417,17 @@ func (s *NodeBlocklistWrapperTestSuite) TestDataBasePersist() { require.NoError(s.T(), err) require.Empty(s.T(), w1.GetDisallowList()) - // set blocklist2 -> + // set disallowList2 -> // newly created wrapper should now read this list from data base during initialization s.updateConsumer.On("OnDisallowListNotification", &network.DisallowListingUpdate{ - FlowIds: blocklist2, + FlowIds: disallowList2, Cause: network.DisallowListedCauseAdmin, }).Return().Once() - err = s.wrapper.Update(blocklist2) + err = s.wrapper.Update(disallowList2) require.NoError(s.T(), err) w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) require.NoError(s.T(), err) - require.Equal(s.T(), blocklist2.Lookup(), w2.GetDisallowList().Lookup()) + require.Equal(s.T(), disallowList2.Lookup(), w2.GetDisallowList().Lookup()) }) } From f5c8bd3836160f64866383d78030b76ae8dad57e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 16:05:55 -0700 Subject: [PATCH 104/815] fuxes herocache metrics issue --- cmd/observer/node_builder/observer_builder.go | 2 +- follower/follower_builder.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 90db8f8deea..5d059f06af1 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -726,7 +726,7 @@ func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr builder.LibP2PResourceManagerConfig, &p2p.DisallowListCacheConfig{ MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), }). SetSubscriptionFilter( subscription.NewRoleBasedFilter( diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 463c6447e7d..0cf0b58ef0c 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -614,7 +614,7 @@ func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr builder.LibP2PResourceManagerConfig, &p2p.DisallowListCacheConfig{ MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), }). SetSubscriptionFilter( subscription.NewRoleBasedFilter( From 1da07ede1f894d7c98f335a8d7e543bc5251b631 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 16:52:08 -0700 Subject: [PATCH 105/815] fixes nil dependecy issue --- .../node_builder/access_node_builder.go | 4 ++- cmd/observer/node_builder/observer_builder.go | 4 ++- cmd/scaffold.go | 2 +- follower/follower_builder.go | 4 ++- network/p2p/cache/node_blocklist_wrapper.go | 21 ++++++++------ .../p2p/cache/node_blocklist_wrapper_test.go | 28 ++++++++++++++----- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index a04f1501160..5f11489d4f5 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -813,7 +813,9 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { + return builder.Middleware + }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 5d059f06af1..2c70f33f048 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -582,7 +582,9 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { // The following wrapper allows to black-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) + builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { + return builder.Middleware + }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 0b3a4bfdd73..813fffee2a8 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -1054,7 +1054,7 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, fnb.Middleware) + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(node.IdentityProvider, node.DB, fnb.Middleware) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 0cf0b58ef0c..7087a329685 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -477,7 +477,9 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of the disallow-listed nodes to true - builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, builder.Middleware) + builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { + return builder.Middleware + }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } diff --git a/network/p2p/cache/node_blocklist_wrapper.go b/network/p2p/cache/node_blocklist_wrapper.go index b04e2211712..f655215178a 100644 --- a/network/p2p/cache/node_blocklist_wrapper.go +++ b/network/p2p/cache/node_blocklist_wrapper.go @@ -43,8 +43,13 @@ type NodeDisallowListingWrapper struct { identityProvider module.IdentityProvider disallowList IdentifierSet // `IdentifierSet` is a map, hence efficient O(1) lookup - // updateConsumer is called whenever the disallow-list is updated. - updateConsumer network.DisallowListNotificationConsumer + // updateConsumerOracle is called whenever the disallow-list is updated. + // Note that we do not use the `updateConsumer` directly due to the circular dependency between the + // middleware (i.e., updateConsumer), and the wrapper (i.e., NodeDisallowListingWrapper). + // Middleware needs identity provider to be initialized, and identity provider needs this wrapper to be initialized. + // Hence, if we pass the updateConsumer by the interface value, it will be nil at the time of initialization. + // Instead, we use the oracle function to get the updateConsumer whenever we need it. + updateConsumerOracle func() network.DisallowListNotificationConsumer } var _ module.IdentityProvider = (*NodeDisallowListingWrapper)(nil) @@ -54,7 +59,7 @@ var _ module.IdentityProvider = (*NodeDisallowListingWrapper)(nil) func NewNodeDisallowListWrapper( identityProvider module.IdentityProvider, db *badger.DB, - updateConsumer network.DisallowListNotificationConsumer) (*NodeDisallowListingWrapper, error) { + updateConsumerOracle func() network.DisallowListNotificationConsumer) (*NodeDisallowListingWrapper, error) { disallowList, err := retrieveDisallowList(db) if err != nil { @@ -62,10 +67,10 @@ func NewNodeDisallowListWrapper( } return &NodeDisallowListingWrapper{ - db: db, - identityProvider: identityProvider, - disallowList: disallowList, - updateConsumer: updateConsumer, + db: db, + identityProvider: identityProvider, + disallowList: disallowList, + updateConsumerOracle: updateConsumerOracle, }, nil } @@ -93,7 +98,7 @@ func (w *NodeDisallowListingWrapper) Update(disallowList flow.IdentifierList) er return fmt.Errorf("failed to persist set of blocked nodes to the data base: %w", err) } w.disallowList = b - w.updateConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ + w.updateConsumerOracle().OnDisallowListNotification(&network.DisallowListingUpdate{ FlowIds: disallowList, Cause: network.DisallowListedCauseAdmin, }) diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index 9c0ca36f35f..01e23e5a276 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -36,7 +36,9 @@ func (s *NodeDisallowListWrapperTestSuite) SetupTest() { var err error s.updateConsumer = mocknetwork.NewDisallowListNotificationConsumer(s.T()) - s.wrapper, err = cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + s.wrapper, err = cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) } @@ -350,7 +352,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { require.Empty(s.T(), s.wrapper.GetDisallowList()) // newly created wrapper should read `disallowList` from data base during initialization - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Empty(s.T(), w.GetDisallowList()) }) @@ -364,7 +368,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { require.NoError(s.T(), err) // newly created wrapper should read `disallowList` from data base during initialization - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Equal(s.T(), disallowList1.Lookup(), w.GetDisallowList().Lookup()) }) @@ -385,7 +391,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { require.NoError(s.T(), err) // newly created wrapper should read initial state from data base - w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Equal(s.T(), disallowList2.Lookup(), w.GetDisallowList().Lookup()) }) @@ -400,7 +408,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { err := s.wrapper.Update(disallowList1) require.NoError(s.T(), err) - w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w0, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Equal(s.T(), disallowList1.Lookup(), w0.GetDisallowList().Lookup()) @@ -413,7 +423,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { err = s.wrapper.ClearDisallowList() require.NoError(s.T(), err) - w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w1, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Empty(s.T(), w1.GetDisallowList()) @@ -426,7 +438,9 @@ func (s *NodeDisallowListWrapperTestSuite) TestDataBasePersist() { err = s.wrapper.Update(disallowList2) require.NoError(s.T(), err) - w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, s.updateConsumer) + w2, err := cache.NewNodeDisallowListWrapper(s.provider, s.DB, func() network.DisallowListNotificationConsumer { + return s.updateConsumer + }) require.NoError(s.T(), err) require.Equal(s.T(), disallowList2.Lookup(), w2.GetDisallowList().Lookup()) }) From 1445fbdc970b939d1b3e7c9e713a48c61f284572 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 17:25:54 -0700 Subject: [PATCH 106/815] lint fix --- cmd/scaffold.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 813fffee2a8..fa1df0fe735 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -1054,7 +1054,9 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeDisallowListWrapper(node.IdentityProvider, node.DB, fnb.Middleware) + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(node.IdentityProvider, node.DB, func() network.DisallowListNotificationConsumer { + return fnb.Middleware + }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) } From c1ac0a429a59790da23d0d6e3b294707877d0338 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 7 Jun 2023 18:40:14 -0700 Subject: [PATCH 107/815] fixes nil pointer exception --- cmd/scaffold.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index fa1df0fe735..89748e3a817 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -1054,7 +1054,7 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true - disallowListWrapper, err := cache.NewNodeDisallowListWrapper(node.IdentityProvider, node.DB, func() network.DisallowListNotificationConsumer { + disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { return fnb.Middleware }) if err != nil { From 065494d309e12e2b2aafaeaa481d5abe64cfe435 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 8 Jun 2023 12:56:17 +0300 Subject: [PATCH 108/815] Added tests for circuit breaker. --- .../export_report.json | 6 + engine/access/rpc/backend/backend_test.go | 75 ++++++------ .../access/rpc/backend/connection_factory.go | 2 +- .../rpc/backend/connection_factory_test.go | 112 ++++++++++++++++++ .../access/rpc/backend/connection_selector.go | 4 +- go.mod | 2 +- insecure/go.mod | 1 + insecure/go.sum | 2 + 8 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json new file mode 100644 index 00000000000..72af21af279 --- /dev/null +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -0,0 +1,6 @@ +{ + "EpochCounter": 0, + "PreviousStateCommitment": "53749b13e1f99759abb35a7ab7d7a4f180d8f6bc24e5ef6b29f3565d459765f0", + "CurrentStateCommitment": "53749b13e1f99759abb35a7ab7d7a4f180d8f6bc24e5ef6b29f3565d459765f0", + "ReportSucceeded": true +} \ No newline at end of file diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 31834efc190..a2d34eca298 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -3,8 +3,6 @@ package backend import ( "context" "fmt" - "testing" - "github.com/dgraph-io/badger/v2" accessproto "github.com/onflow/flow/protobuf/go/flow/access" entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" @@ -16,6 +14,7 @@ import ( "github.com/stretchr/testify/suite" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "testing" access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" @@ -27,6 +26,7 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/rand" "github.com/onflow/flow-go/utils/unittest" ) @@ -47,7 +47,7 @@ type Suite struct { execClient *access.ExecutionAPIClient historicalAccessClient *access.AccessAPIClient connectionFactory *backendmock.ConnectionFactory - connSelector *backendmock.ConnectionSelector + nil *backendmock.ConnectionSelector chainID flow.ChainID } @@ -75,7 +75,7 @@ func (suite *Suite) SetupTest() { suite.chainID = flow.Testnet suite.historicalAccessClient = new(access.AccessAPIClient) suite.connectionFactory = new(backendmock.ConnectionFactory) - suite.connSelector = new(backendmock.ConnectionSelector) + suite.nil = new(backendmock.ConnectionSelector) } func (suite *Suite) TestPing() { @@ -648,8 +648,6 @@ func (suite *Suite) TestGetTransactionResultByIndex() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - connSelector := new(backendmock.ConnectionSelector) - exeEventReq := &execproto.GetTransactionByIndexRequest{ BlockId: blockId[:], Index: index, @@ -672,7 +670,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -715,8 +713,6 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - connSelector := new(backendmock.ConnectionSelector) - exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockId[:], } @@ -738,7 +734,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -808,8 +804,6 @@ func (suite *Suite) TestTransactionStatusTransition() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) - connSelector := new(backendmock.ConnectionSelector) - exeEventReq := &execproto.GetTransactionResultRequest{ BlockId: blockID[:], TransactionId: txID[:], @@ -832,7 +826,7 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1107,7 +1101,6 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { // create a mock connection factory connFactory := suite.setupConnectionFactory() - connSelector := new(backendmock.ConnectionSelector) backend := New( suite.state, @@ -1122,7 +1115,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, 100, nil, @@ -1302,8 +1295,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - connSelector := new(backendmock.ConnectionSelector) - // create the expected results from execution node and access node exeResults := make([]*execproto.GetEventsForBlockIDsResponse_Result, len(blockHeaders)) @@ -1369,7 +1360,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1402,7 +1393,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1429,7 +1420,6 @@ func (suite *Suite) TestGetExecutionResultByID() { // create a mock connection factory connFactory := new(backendmock.ConnectionFactory) - connSelector := new(backendmock.ConnectionSelector) nonexistingID := unittest.IdentifierFixture() blockID := unittest.IdentifierFixture() @@ -1463,7 +1453,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1494,7 +1484,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1523,7 +1513,6 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { // create a mock connection factory connFactory := new(backendmock.ConnectionFactory) - connSelector := new(backendmock.ConnectionSelector) blockID := unittest.IdentifierFixture() executionResult := unittest.ExecutionResultFixture( @@ -1559,7 +1548,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1591,7 +1580,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1727,7 +1716,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { } connFactory := suite.setupConnectionFactory() - connSelector := new(backendmock.ConnectionSelector) suite.Run("invalid request max height < min height", func() { backend := New( @@ -1743,7 +1731,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1783,7 +1771,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1822,7 +1810,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1860,7 +1848,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, 1, // set maximum range to 1 nil, @@ -1898,7 +1886,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -1962,8 +1950,6 @@ func (suite *Suite) TestGetAccount() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - connSelector := new(backendmock.ConnectionSelector) - // create the handler with the mock backend := New( suite.state, @@ -1978,7 +1964,7 @@ func (suite *Suite) TestGetAccount() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -2028,8 +2014,6 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - connSelector := new(backendmock.ConnectionSelector) - // create the expected execution API request blockID := h.ID() exeReq := &execproto.GetAccountAtBlockIDRequest{ @@ -2062,7 +2046,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { flow.Testnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, @@ -2171,6 +2155,19 @@ func (suite *Suite) TestExecutionNodesForBlockID() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() connSelector := new(backendmock.ConnectionSelector) + connSelector.On("GetExecutionNodesForBlockID").Return(func() flow.IdentityList { + randomItems := make(flow.IdentityList, 0, maxExecutionNodesCnt) + + for i := 0; i < maxExecutionNodesCnt; i++ { + // Generate a random index within the range of the array + randomIndex, err := rand.Uintn(uint(len(allExecutionNodes))) + require.NoError(suite.T(), err) + // Append the item at the random index to the new slice + randomItems = append(randomItems, allExecutionNodes[randomIndex]) + } + + return randomItems + }) testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) { @@ -2270,8 +2267,6 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) - connSelector := new(backendmock.ConnectionSelector) - // create the handler with the mock backend := New( suite.state, @@ -2286,7 +2281,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { flow.Mainnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - connSelector, + nil, false, DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index bb4f5e3548f..cf6940f006e 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -265,7 +265,7 @@ func WithClientUnaryInterceptor(timeout time.Duration, circuitBreakerConfig Circ circuitBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{ Timeout: circuitBreakerConfig.RestoreTimeout, ReadyToTrip: func(counts gobreaker.Counts) bool { - return counts.ConsecutiveFailures > circuitBreakerConfig.MaxRequestToBreak + return counts.ConsecutiveFailures >= circuitBreakerConfig.MaxRequestToBreak }, }) } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index fa4801a5897..24953bb22a1 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -3,6 +3,7 @@ package backend import ( "context" "fmt" + "github.com/sony/gobreaker" "net" "strconv" "strings" @@ -407,6 +408,117 @@ func TestConnectionPoolStale(t *testing.T) { assert.Equal(t, resp, expected) } +// TestCircuitBreakerExecutionNode +func TestCircuitBreakerExecutionNode(t *testing.T) { + timeout := time.Second + + // create an execution node + en := new(executionNode) + en.start(t) + defer en.stop(t) + + // setup the handler mock to not respond within the timeout + req := &execution.PingRequest{} + resp := &execution.PingResponse{} + en.handler.On("Ping", testifymock.Anything, req).After(timeout+time.Second).Return(resp, nil) + + // create the factory + connectionFactory := new(ConnectionFactoryImpl) + // set the execution grpc port + connectionFactory.ExecutionGRPCPort = en.port + // set the execution grpc client timeout + connectionFactory.ExecutionNodeGRPCTimeout = timeout + // set the connection pool cache size + cacheSize := 5 + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ + Enabled: true, + MaxRequestToBreak: 1, + RestoreTimeout: 10 * time.Second, + } + + // create the execution API client + client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) + assert.NoError(t, err) + + ctx := context.Background() + callAndMeasurePingDuration := func() (time.Duration, error) { + start := time.Now() + + // make the call to the execution node + _, err = client.Ping(ctx, req) + + return time.Since(start), err + } + + duration, err := callAndMeasurePingDuration() + // assert that the client timed out + assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) + assert.LessOrEqual(t, timeout, duration) + + duration, err = callAndMeasurePingDuration() + assert.Equal(t, gobreaker.ErrOpenState, err) + assert.Greater(t, timeout, duration) +} + +// TestCircuitBreakerCollectionNode +func TestCircuitBreakerCollectionNode(t *testing.T) { + timeout := time.Second + + // create a collection node + cn := new(collectionNode) + cn.start(t) + defer cn.stop(t) + + // set up the handler mock to not respond within the timeout + req := &access.PingRequest{} + resp := &access.PingResponse{} + cn.handler.On("Ping", testifymock.Anything, req).After(timeout+time.Second).Return(resp, nil) + + // create the factory + connectionFactory := new(ConnectionFactoryImpl) + // set the collection grpc port + connectionFactory.CollectionGRPCPort = cn.port + // set the collection grpc client timeout + connectionFactory.CollectionNodeGRPCTimeout = timeout + // set the connection pool cache size + cacheSize := 5 + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() + + connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ + Enabled: true, + MaxRequestToBreak: 1, + RestoreTimeout: 10 * time.Second, + } + + // create the collection API client + client, _, err := connectionFactory.GetAccessAPIClient(cn.listener.Addr().String()) + assert.NoError(t, err) + + ctx := context.Background() + callAndMeasurePingDuration := func() (time.Duration, error) { + start := time.Now() + + // make the call to the collection node + _, err = client.Ping(ctx, req) + + return time.Since(start), err + } + + duration, err := callAndMeasurePingDuration() + // assert that the client timed out + assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) + assert.LessOrEqual(t, timeout, duration) + + duration, err = callAndMeasurePingDuration() + assert.Equal(t, gobreaker.ErrOpenState, err) + assert.Greater(t, timeout, duration) +} + // node mocks a flow node that runs a GRPC server type node struct { server *grpc.Server diff --git a/engine/access/rpc/backend/connection_selector.go b/engine/access/rpc/backend/connection_selector.go index 8a70e0af5e8..fe351fdb5b2 100644 --- a/engine/access/rpc/backend/connection_selector.go +++ b/engine/access/rpc/backend/connection_selector.go @@ -87,7 +87,7 @@ func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( // check if the block ID is of the root block. If it is then don't look for execution receipts since they // will not be present for the root block. - rootBlock, err := ncs.state.Params().Root() + rootBlock, err := ncs.state.Params().FinalizedRoot() if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } @@ -177,7 +177,7 @@ func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( // check if the block ID is of the root block. If it is then don't look for execution receipts since they // will not be present for the root block. - rootBlock, err := nccbs.state.Params().Root() + rootBlock, err := nccbs.state.Params().FinalizedRoot() if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } diff --git a/go.mod b/go.mod index 4413c64fd11..22b92538103 100644 --- a/go.mod +++ b/go.mod @@ -101,6 +101,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/onflow/wal v0.0.0-20230529184820-bc9f8244608d github.com/slok/go-http-metrics v0.10.0 + github.com/sony/gobreaker v0.5.0 gonum.org/v1/gonum v0.8.2 ) @@ -242,7 +243,6 @@ require ( github.com/psiemens/sconfig v0.1.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/sony/gobreaker v0.5.0 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.0 // indirect diff --git a/insecure/go.mod b/insecure/go.mod index 008b173cd61..a068e1efdab 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -212,6 +212,7 @@ require ( github.com/sethvargo/go-retry v0.2.3 // indirect github.com/shirou/gopsutil/v3 v3.22.2 // indirect github.com/slok/go-http-metrics v0.10.0 // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.0 // indirect diff --git a/insecure/go.sum b/insecure/go.sum index 5b525b5d7da..a6b14f91090 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1368,6 +1368,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= From ab604f9dab01a187d7a476017bc9e3769d2779c0 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Mon, 12 Jun 2023 14:09:33 +0300 Subject: [PATCH 109/815] Added additional checks to test. --- .../rpc/backend/connection_factory_test.go | 89 ++++++++++++------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 24953bb22a1..96dba28889b 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -410,34 +410,39 @@ func TestConnectionPoolStale(t *testing.T) { // TestCircuitBreakerExecutionNode func TestCircuitBreakerExecutionNode(t *testing.T) { - timeout := time.Second - + requestTimeout := 1 * time.Second + circuitBreakerRestoreTimeout := 3 * time.Second // create an execution node en := new(executionNode) en.start(t) defer en.stop(t) - // setup the handler mock to not respond within the timeout + // setup the handler mock to not respond within the requestTimeout req := &execution.PingRequest{} resp := &execution.PingResponse{} - en.handler.On("Ping", testifymock.Anything, req).After(timeout+time.Second).Return(resp, nil) + en.handler.On("Ping", testifymock.Anything, req).After(2*requestTimeout).Return(resp, nil) // create the factory connectionFactory := new(ConnectionFactoryImpl) // set the execution grpc port connectionFactory.ExecutionGRPCPort = en.port - // set the execution grpc client timeout - connectionFactory.ExecutionNodeGRPCTimeout = timeout - // set the connection pool cache size - cacheSize := 5 - connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting - connectionFactory.AccessMetrics = metrics.NewNoopCollector() + // set the execution grpc client requestTimeout + connectionFactory.ExecutionNodeGRPCTimeout = requestTimeout + // set the configuration for circuit breaker connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ Enabled: true, MaxRequestToBreak: 1, - RestoreTimeout: 10 * time.Second, + RestoreTimeout: circuitBreakerRestoreTimeout, } + // set the connection pool cache size + cacheSize := 1 + cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { + evictedValue.(*CachedClient).Close() + }) + connectionFactory.ConnectionsCache = cache + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() // create the execution API client client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) @@ -454,46 +459,59 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { } duration, err := callAndMeasurePingDuration() - // assert that the client timed out assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) - assert.LessOrEqual(t, timeout, duration) + assert.LessOrEqual(t, requestTimeout, duration) duration, err = callAndMeasurePingDuration() assert.Equal(t, gobreaker.ErrOpenState, err) - assert.Greater(t, timeout, duration) + assert.Greater(t, requestTimeout, duration) + + //Wait until Circuit breaker go to Half-open state + time.Sleep(circuitBreakerRestoreTimeout + time.Second) + + en.handler.On("Ping", testifymock.Anything, req).Unset() + en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) + + duration, err = callAndMeasurePingDuration() + assert.Greater(t, requestTimeout, duration) + assert.Equal(t, nil, err) } // TestCircuitBreakerCollectionNode func TestCircuitBreakerCollectionNode(t *testing.T) { - timeout := time.Second - + requestTimeout := 1 * time.Second + circuitBreakerRestoreTimeout := 3 * time.Second // create a collection node cn := new(collectionNode) cn.start(t) defer cn.stop(t) - // set up the handler mock to not respond within the timeout + // set up the handler mock to not respond within the requestTimeout req := &access.PingRequest{} resp := &access.PingResponse{} - cn.handler.On("Ping", testifymock.Anything, req).After(timeout+time.Second).Return(resp, nil) + cn.handler.On("Ping", testifymock.Anything, req).After(2*requestTimeout).Return(resp, nil) // create the factory connectionFactory := new(ConnectionFactoryImpl) // set the collection grpc port connectionFactory.CollectionGRPCPort = cn.port - // set the collection grpc client timeout - connectionFactory.CollectionNodeGRPCTimeout = timeout - // set the connection pool cache size - cacheSize := 5 - connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting - connectionFactory.AccessMetrics = metrics.NewNoopCollector() - + // set the collection grpc client requestTimeout + connectionFactory.CollectionNodeGRPCTimeout = requestTimeout + // set the configuration for circuit breaker connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ Enabled: true, MaxRequestToBreak: 1, - RestoreTimeout: 10 * time.Second, + RestoreTimeout: circuitBreakerRestoreTimeout, } + // set the connection pool cache size + cacheSize := 1 + cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { + evictedValue.(*CachedClient).Close() + }) + connectionFactory.ConnectionsCache = cache + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() // create the collection API client client, _, err := connectionFactory.GetAccessAPIClient(cn.listener.Addr().String()) @@ -510,13 +528,22 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { } duration, err := callAndMeasurePingDuration() - // assert that the client timed out assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) - assert.LessOrEqual(t, timeout, duration) + assert.LessOrEqual(t, requestTimeout, duration) duration, err = callAndMeasurePingDuration() assert.Equal(t, gobreaker.ErrOpenState, err) - assert.Greater(t, timeout, duration) + assert.Greater(t, requestTimeout, duration) + + //Wait until Circuit breaker go to Half-open state + time.Sleep(circuitBreakerRestoreTimeout + time.Second) + + cn.handler.On("Ping", testifymock.Anything, req).Unset() + cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) + + duration, err = callAndMeasurePingDuration() + assert.Greater(t, requestTimeout, duration) + assert.Equal(t, nil, err) } // node mocks a flow node that runs a GRPC server From bd510e948d0dd3a803e90b9e6e1e6132211fc313 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Mon, 12 Jun 2023 14:56:43 +0300 Subject: [PATCH 110/815] Rename circuit breaker configurations. --- cmd/access/node_builder/access_node_builder.go | 10 +++++----- engine/access/rpc/backend/connection_factory.go | 9 ++++----- engine/access/rpc/backend/connection_factory_test.go | 12 ++++++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 42d5ad29a4c..d24fe4537bd 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -162,9 +162,9 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { ArchiveAddressList: nil, MaxMsgSize: grpcutils.DefaultMaxMsgSize, CircuitBreakerConfig: backend.CircuitBreakerConfig{ - Enabled: false, - RestoreTimeout: time.Duration(60) * time.Second, - MaxRequestToBreak: 5, + Enabled: false, + RestoreTimeout: time.Duration(60) * time.Second, + MaxFailures: 5, }, }, stateStreamConf: state_stream.Config{ @@ -688,7 +688,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringVar(&builder.PublicNetworkConfig.BindAddress, "public-network-address", defaultConfig.PublicNetworkConfig.BindAddress, "staked access node's public network bind address") flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "whether to enable the circuit breaker for collection and execution node connections") flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "initial timeout for circuit breaker to try connect again. Default value is 60s") - flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "circuit-breaker-max-request-to-break", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRequestToBreak, "number of consecutive failures to break connection. Default value is 5") + flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxFailures, "circuit-breaker-max-failures", defaultConfig.rpcConf.CircuitBreakerConfig.MaxFailures, "number of consecutive failures to break connection. Default value is 5") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") @@ -754,7 +754,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { } } if builder.rpcConf.CircuitBreakerConfig.Enabled { - if builder.rpcConf.CircuitBreakerConfig.MaxRequestToBreak == 0 { + if builder.rpcConf.CircuitBreakerConfig.MaxFailures == 0 { return errors.New("circuit-breaker-max-request-to-break must be greater than 0") } } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index cf6940f006e..37cf965c372 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -65,11 +65,10 @@ type ConnectionFactoryImpl struct { CircuitBreakerConfig CircuitBreakerConfig } -// TODO: describe type CircuitBreakerConfig struct { - Enabled bool - RestoreTimeout time.Duration - MaxRequestToBreak uint32 + Enabled bool + RestoreTimeout time.Duration + MaxFailures uint32 } type CachedClient struct { @@ -265,7 +264,7 @@ func WithClientUnaryInterceptor(timeout time.Duration, circuitBreakerConfig Circ circuitBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{ Timeout: circuitBreakerConfig.RestoreTimeout, ReadyToTrip: func(counts gobreaker.Counts) bool { - return counts.ConsecutiveFailures >= circuitBreakerConfig.MaxRequestToBreak + return counts.ConsecutiveFailures >= circuitBreakerConfig.MaxFailures }, }) } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 96dba28889b..3c96b9c124a 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -430,9 +430,9 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { connectionFactory.ExecutionNodeGRPCTimeout = requestTimeout // set the configuration for circuit breaker connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ - Enabled: true, - MaxRequestToBreak: 1, - RestoreTimeout: circuitBreakerRestoreTimeout, + Enabled: true, + MaxFailures: 1, + RestoreTimeout: circuitBreakerRestoreTimeout, } // set the connection pool cache size cacheSize := 1 @@ -499,9 +499,9 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { connectionFactory.CollectionNodeGRPCTimeout = requestTimeout // set the configuration for circuit breaker connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ - Enabled: true, - MaxRequestToBreak: 1, - RestoreTimeout: circuitBreakerRestoreTimeout, + Enabled: true, + MaxFailures: 1, + RestoreTimeout: circuitBreakerRestoreTimeout, } // set the connection pool cache size cacheSize := 1 From 6435656fa99b45387322a417e283a7e95bbb13fe Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:41:27 -0700 Subject: [PATCH 111/815] adds a comment --- network/alsp/manager/manager.go | 1 + 1 file changed, 1 insertion(+) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 98704386533..3f747616e2e 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -341,6 +341,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). if record.Penalty == float64(0) { + // Penalty has fully decayed to zero and the node can be back in the allow list. m.disallowListingConsumer.OnAllowListNotification(&network.AllowListingUpdate{ FlowIds: flow.IdentifierList{id}, Cause: network.DisallowListedCauseAlsp, // clears the ALSP disallow listing cause from node From 1f77da8171987f7d0298a0e21cf259b44919edb3 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:42:16 -0700 Subject: [PATCH 112/815] Update network/disallow.go Co-authored-by: Misha --- network/disallow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/disallow.go b/network/disallow.go index 092fbd1e4c1..af332f7d123 100644 --- a/network/disallow.go +++ b/network/disallow.go @@ -38,7 +38,7 @@ type AllowListingUpdate struct { // DisallowListNotificationConsumer is an interface for consuming disallow/allow list update notifications. type DisallowListNotificationConsumer interface { // OnDisallowListNotification is called when a new disallow list update notification is distributed. - // Any error on consuming event must handle internally. + // Any error on consuming an event must be handled internally. // The implementation must be concurrency safe. OnDisallowListNotification(*DisallowListingUpdate) From ae12a36718f03593fd45517995836f86a62d18e4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:42:44 -0700 Subject: [PATCH 113/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 355be3dd7ce..b624b552c05 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -136,8 +136,8 @@ func (d *DisallowListCache) disallowListFor(peerID peer.ID, cause network.Disall dEntity := mustBeDisallowListEntity(adjustedEntity) updatedCauses := make([]network.DisallowListedCause, 0, len(dEntity.causes)) - for cause := range dEntity.causes { - updatedCauses = append(updatedCauses, cause) + for c := range dEntity.causes { + updatedCauses = append(updatedCauses, c) } return updatedCauses, nil From 0a996e550431863e01bf54cf84ef29f73da1f21a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:43:12 -0700 Subject: [PATCH 114/815] Update network/alsp/manager/manager.go Co-authored-by: Misha --- network/alsp/manager/manager.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 3f747616e2e..d89cc0d58cb 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -319,6 +319,9 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). + // as long as record.Penalty is NOT below model.DisallowListingThreshold, + // the node is considered allow-listed and can conduct inbound and outbound connections. + // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. if record.Penalty < model.DisallowListingThreshold { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ From c3c81e7af4d56009801aaade51682324f7d5e8fb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:43:46 -0700 Subject: [PATCH 115/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index b624b552c05..4c6c145caaa 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -166,8 +166,8 @@ func (d *DisallowListCache) AllowFor(peerID peer.ID, cause network.DisallowListe dEntity := mustBeDisallowListEntity(adjustedEntity) // returning a deep copy of causes (to avoid being mutated externally). causes := make([]network.DisallowListedCause, 0, len(dEntity.causes)) - for cause := range dEntity.causes { - causes = append(causes, cause) + for c := range dEntity.causes { + causes = append(causes, c) } return causes } From 3b43e0b7923be1e4fa315ad775171d3a97749c34 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:43:59 -0700 Subject: [PATCH 116/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 4c6c145caaa..f7cca905fc6 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -71,8 +71,8 @@ func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []netw // Args: // - peerID: the peerID of the peer to be disallow-listed. // Returns: -// - true if the entity is successfully added to the cache. -// - false if the entity already exists in the cache. +// - bool: true if the entity is successfully added to the cache. +// false if the entity already exists in the cache. func (d *DisallowListCache) init(peerID peer.ID) bool { return d.c.Add(&disallowListCacheEntity{ peerID: peerID, From 8e5d412148f6d1d41516844ef4dd997c8e182071 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:47:47 -0700 Subject: [PATCH 117/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index f7cca905fc6..bbed5fa6a13 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -91,7 +91,6 @@ func (d *DisallowListCache) init(peerID peer.ID) bool { func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) { // first, we try to optimistically add the peer to the disallow list. causes, err := d.disallowListFor(peerID, cause) - switch { case err == nil: return causes, nil From c5b48af1fbed211f1b45e112d416b4346d66f993 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:49:20 -0700 Subject: [PATCH 118/815] Update module/metrics/labels.go Co-authored-by: Khalil Claybon --- module/metrics/labels.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/metrics/labels.go b/module/metrics/labels.go index 57c905b4b92..10d8e42200e 100644 --- a/module/metrics/labels.go +++ b/module/metrics/labels.go @@ -91,7 +91,7 @@ const ( ResourceNetworkingApplicationLayerSpamRecordCache = "application_layer_spam_record_cache" ResourceNetworkingApplicationLayerSpamReportQueue = "application_layer_spam_report_queue" ResourceNetworkingRpcClusterPrefixReceivedCache = "rpc_cluster_prefixed_received_cache" - ResourceNetworkingDisallowListCache = "networking_disallow_list_cache" + ResourceNetworkingDisallowListCache = "disallow_list_cache" ResourceFollowerPendingBlocksCache = "follower_pending_block_cache" // follower engine ResourceClusterBlockProposalQueue = "cluster_compliance_proposal_queue" // collection node, compliance engine From 8c1cd974995fc83a5797da041d40b68a21e85e5f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:49:42 -0700 Subject: [PATCH 119/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 2c6ecaca074..551575d638e 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -271,7 +271,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // while node 2 is disallow-listed, it cannot connect to node 1. Also, node 1 cannot directly dial and connect to node 2, unless + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) } From 5cd885539960f063ba2bee1422a440352fe58a37 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:49:55 -0700 Subject: [PATCH 120/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 551575d638e..42030c55c06 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -267,7 +267,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // ensures that the spammer is disallow-listed by the victim p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) - // despite disallow-listing spammer, it ensures that (victim and spammer) and (honest and spammer) are still connected. + // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) From 282ef12432a3a3b72fc592e03371e004f4d10424 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:50:40 -0700 Subject: [PATCH 121/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 42030c55c06..7cb309b0594 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -194,7 +194,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { // The test sets up 3 nodes, one victim, one honest, and one (alledged) spammer. // Initially, the test ensures that all nodes are connected to each other. // Then, test imitates that victim node reports the spammer node for spamming. -// The test generates enough spam reports to trigger the disallow listing of the victim node. +// The test generates enough spam reports to trigger the disallow-listing of the victim node. // The test ensures that the victim node is disconnected from the spammer node. // The test ensures that despite attempting on connections, no inbound or outbound connections between the victim and // the disallow-listed spammer node are established. From aa6228a2a240c357197e27b126e199d08bb94db2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 10:51:07 -0700 Subject: [PATCH 122/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index bbed5fa6a13..2d80b405002 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -86,8 +86,8 @@ func (d *DisallowListCache) init(peerID peer.ID) bool { // - peerID: the peerID of the peer to be disallow-listed. // - cause: the cause for disallow-listing the peer. // Returns: -// - the list of causes for which the peer is disallow-listed. -// - error if the operation fails, error is irrecoverable. +// - []network.DisallowListedCause: the list of causes for which the peer is disallow-listed. +// - error: if the operation fails, error is irrecoverable. func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) { // first, we try to optimistically add the peer to the disallow list. causes, err := d.disallowListFor(peerID, cause) From 088dbb340f5ab47bae4d175aad3260ef07738c3b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:02:20 -0700 Subject: [PATCH 123/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 2d80b405002..5b7cca0c628 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -51,7 +51,7 @@ func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector mod // Args: // - peerID: the peer to check. // Returns: -// - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, +// - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, // an empty list is returned. func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause { causes := make([]network.DisallowListedCause, 0) From 58f40908264756bb568661a7a4526f6a1f286fc9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:02:49 -0700 Subject: [PATCH 124/815] Update network/p2p/p2pnode/internal/cache.go Co-authored-by: Khalil Claybon --- network/p2p/p2pnode/internal/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 5b7cca0c628..1045cb7459c 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -30,7 +30,7 @@ type DisallowListCache struct { // - logger: the logger used by the cache. // - collector: the metrics collector used by the cache. // Returns: -// - *DisallowListCache, the created cache. +// - *DisallowListCache: the created cache. func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector module.HeroCacheMetrics) *DisallowListCache { backData := herocache.NewCache(sizeLimit, herocache.DefaultOversizeFactor, From f1d581feb4e5e05cd09492bacfa7be6fab2450ef Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:25:56 -0700 Subject: [PATCH 125/815] Update network/disallow.go Co-authored-by: Misha --- network/disallow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/disallow.go b/network/disallow.go index af332f7d123..feaa6d2b27b 100644 --- a/network/disallow.go +++ b/network/disallow.go @@ -43,7 +43,7 @@ type DisallowListNotificationConsumer interface { OnDisallowListNotification(*DisallowListingUpdate) // OnAllowListNotification is called when a new allow list update notification is distributed. - // Any error on consuming event must handle internally. + // Any error on consuming an event must be handled internally. // The implementation must be concurrency safe. OnAllowListNotification(*AllowListingUpdate) } From 14f7d60c522240d04e171f01090779426111dd6a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:30:49 -0700 Subject: [PATCH 126/815] adds documentation for integration testing --- network/alsp/manager/manager_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 42030c55c06..c8f43a6e88a 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -106,7 +106,9 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { cfg := managerCfgFixture() - // create a new MisbehaviorReportManager + // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute + // of the network, we need to configure the ALSP manager via the network configuration, and let the network create + // the ALSP manager. var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { @@ -201,7 +203,9 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) { cfg := managerCfgFixture() - // create a new MisbehaviorReportManager + // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute + // of the network, we need to configure the ALSP manager via the network configuration, and let the network create + // the ALSP manager. var victimSpamRecordCacheCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { @@ -281,6 +285,10 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // It fails the test if the metrics are not recorded or if they are recorded incorrectly. func TestMisbehaviorReportMetrics(t *testing.T) { cfg := managerCfgFixture() + + // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute + // of the network, we need to configure the ALSP manager via the network configuration, and let the network create + // the ALSP manager. alspMetrics := mockmodule.NewAlspMetrics(t) cfg.AlspMetrics = alspMetrics From 8a6439fd9f9e5b3b44a0aa0985d51a1b388c30b6 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Mon, 12 Jun 2023 21:32:04 +0300 Subject: [PATCH 127/815] Added missing part of code. --- .../access/rpc/backend/connection_selector.go | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/engine/access/rpc/backend/connection_selector.go b/engine/access/rpc/backend/connection_selector.go index fe351fdb5b2..c3bbfcf722c 100644 --- a/engine/access/rpc/backend/connection_selector.go +++ b/engine/access/rpc/backend/connection_selector.go @@ -50,9 +50,9 @@ func NewConnectionSelector( } } -func (ncs *MainConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { +func (mcs *MainConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { // retrieve the set of collector clusters - clusters, err := ncs.state.Final().Epochs().Current().Clustering() + clusters, err := mcs.state.Final().Epochs().Current().Clustering() if err != nil { return nil, fmt.Errorf("could not cluster collection nodes: %w", err) } @@ -78,7 +78,7 @@ func (ncs *MainConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]s // GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities // which have executed the given block ID. // If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( +func (mcs *MainConnectionSelector) GetExecutionNodesForBlockID( ctx context.Context, blockID flow.Identifier, ) (flow.IdentityList, error) { @@ -87,37 +87,38 @@ func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( // check if the block ID is of the root block. If it is then don't look for execution receipts since they // will not be present for the root block. - rootBlock, err := ncs.state.Params().FinalizedRoot() + rootBlock, err := mcs.state.Params().FinalizedRoot() if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } if rootBlock.ID() == blockID { - executorIdentities, err := ncs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + executorIdentities, err := mcs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } executorIDs = executorIdentities.NodeIDs() } else { - // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, ncs.executionReceipts, ncs.log) + executorIDs, err = findAllExecutionNodes(blockID, mcs.executionReceipts, mcs.log) if err != nil { return nil, err } - if len(executorIDs) > 0 { + if len(executorIDs) >= minExecutionNodesCnt { break } // log the attempt - ncs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + mcs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). Int("execution_receipts_found", len(executorIDs)). Str("block_id", blockID.String()). Msg("insufficient execution receipts") // if one or less execution receipts may have been received then re-query // in the hope that more might have been received by now + select { case <-ctx.Done(): return nil, ctx.Err() @@ -125,10 +126,20 @@ func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( //retry after an exponential backoff } } + + receiptCnt := len(executorIDs) + // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs + if receiptCnt < minExecutionNodesCnt { + newExecutorIDs, err := mcs.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = newExecutorIDs.NodeIDs() + } } // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(ncs.state, executorIDs) + subsetENs, err := chooseExecutionNodes(mcs.state, executorIDs) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } @@ -143,9 +154,9 @@ func (ncs *MainConnectionSelector) GetExecutionNodesForBlockID( return executionIdentitiesRandom, nil } -func (nccbs *CircuitBreakerConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { +func (cbcs *CircuitBreakerConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { // retrieve the set of collector clusters - clusters, err := nccbs.state.Final().Epochs().Current().Clustering() + clusters, err := cbcs.state.Final().Epochs().Current().Clustering() if err != nil { return nil, fmt.Errorf("could not cluster collection nodes: %w", err) } @@ -168,7 +179,7 @@ func (nccbs *CircuitBreakerConnectionSelector) GetCollectionNodes(txId flow.Iden // GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities // which have executed the given block ID. // If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( +func (cbcs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( ctx context.Context, blockID flow.Identifier, ) (flow.IdentityList, error) { @@ -177,13 +188,13 @@ func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( // check if the block ID is of the root block. If it is then don't look for execution receipts since they // will not be present for the root block. - rootBlock, err := nccbs.state.Params().FinalizedRoot() + rootBlock, err := cbcs.state.Params().FinalizedRoot() if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } if rootBlock.ID() == blockID { - executorIdentities, err := nccbs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) + executorIdentities, err := cbcs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } @@ -191,7 +202,7 @@ func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( } else { // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, nccbs.executionReceipts, nccbs.log) + executorIDs, err = findAllExecutionNodes(blockID, cbcs.executionReceipts, cbcs.log) if err != nil { return nil, err } @@ -201,7 +212,7 @@ func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( } // log the attempt - nccbs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + cbcs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). Int("execution_receipts_found", len(executorIDs)). Str("block_id", blockID.String()). Msg("insufficient execution receipts") @@ -215,22 +226,29 @@ func (nccbs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( //retry after an exponential backoff } } + + receiptCnt := len(executorIDs) + // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs + if receiptCnt < minExecutionNodesCnt { + newExecutorIDs, err := cbcs.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = newExecutorIDs.NodeIDs() + } } // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(nccbs.state, executorIDs) + subsetENs, err := chooseExecutionNodes(cbcs.state, executorIDs) if err != nil { return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } - // randomly choose upto maxExecutionNodesCnt identities - executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) - - if len(executionIdentitiesRandom) == 0 { + if len(subsetENs) == 0 { return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) } - return executionIdentitiesRandom, nil + return subsetENs, nil } // chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first From 850310e4a4470670386838ff0141591828891ddf Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:32:57 -0700 Subject: [PATCH 128/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index f10a0f317e8..d12473124f0 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -252,7 +252,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // imitates that the victim has detected the spammer on 120 times of violations and reports the misbehavior // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - // the spammer is disallow-listed definitely. + // the spammer is definitely disallow-listed. reportCount := 120 wg := sync.WaitGroup{} for i := 0; i < reportCount; i++ { From 37bedd3d691e5fb0ebed609bfcaebf2979171e93 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:37:13 -0700 Subject: [PATCH 129/815] resolves shadow variables --- network/alsp/manager/manager_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index f10a0f317e8..22ab84fa272 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -154,11 +154,11 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { wg.Add(len(reports)) // reports the misbehavior for _, report := range reports { - report := report // capture range variable + r := report // capture range variable go func() { defer wg.Done() - con.ReportMisbehavior(report) + con.ReportMisbehavior(r) }() } } @@ -258,11 +258,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) for i := 0; i < reportCount; i++ { wg.Add(1) // reports the misbehavior - report := report // capture range variable + r := report // capture range variable go func() { defer wg.Done() - con.ReportMisbehavior(report) + con.ReportMisbehavior(r) }() } @@ -656,12 +656,12 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl // handle the misbehavior reports totalPenalty := float64(0) for _, report := range reports { - report := report // capture range variable + r := report // capture range variable totalPenalty += report.Penalty() go func() { defer wg.Done() - m.HandleMisbehaviorReport(channel, report) + m.HandleMisbehaviorReport(channel, r) }() } @@ -786,12 +786,12 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent // handle the misbehavior reports totalPenalty := float64(0) for _, report := range reports { - report := report // capture range variable + r := report // capture range variable totalPenalty += report.Penalty() go func() { defer wg.Done() - m.HandleMisbehaviorReport(channel, report) + m.HandleMisbehaviorReport(channel, r) }() } @@ -863,11 +863,11 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti for _, reports := range peersReports { wg.Add(len(reports)) for _, report := range reports { - report := report // capture range variable + r := report // capture range variable go func() { defer wg.Done() - m.HandleMisbehaviorReport(channel, report) + m.HandleMisbehaviorReport(channel, r) }() } } From eefca22bb917e3cb7ebde099b5495494277f827a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:39:30 -0700 Subject: [PATCH 130/815] Update network/alsp/manager/manager.go Co-authored-by: Khalil Claybon --- network/alsp/manager/manager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index d89cc0d58cb..aa76155f95a 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -163,7 +163,6 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana // perform the lower-level disallow-listing action at the networking layer. // All connections to the disallow-listed node are closed and the node is removed from the overlay, and // no further connections are established to the disallow-listed node, either inbound or outbound. -// Note: A consumer must be set before the manager is started. The manager panics if the consumer is not set. // // Returns: // From 3e3cd10a6493cc5d0c49fd767a20ade4fd291149 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:40:23 -0700 Subject: [PATCH 131/815] Update network/alsp/manager/manager.go Co-authored-by: Khalil Claybon --- network/alsp/manager/manager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index aa76155f95a..4144adae230 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -78,7 +78,6 @@ type MisbehaviorReportManager struct { // disallowListingConsumer is the consumer for the disallow-listing notifications. // It is notified when a node is disallow-listed by this manager. - // Exactly one consumer must be set. The code should panic if the consumer is not set or set more than once. disallowListingConsumer network.DisallowListNotificationConsumer // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. From 9264afb62a17058c566ebd8606907c5c3b7039eb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:40:51 -0700 Subject: [PATCH 132/815] Update network/p2p/connection/peerManager.go Co-authored-by: Khalil Claybon --- network/p2p/connection/peerManager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/connection/peerManager.go b/network/p2p/connection/peerManager.go index a15c5a5a3ab..05cc7c47129 100644 --- a/network/p2p/connection/peerManager.go +++ b/network/p2p/connection/peerManager.go @@ -18,7 +18,7 @@ import ( // DefaultPeerUpdateInterval is default duration for which the peer manager waits in between attempts to update peer connections. // We set it to 1 second to be aligned with the heartbeat intervals of libp2p, alsp, and gossipsub. -var DefaultPeerUpdateInterval = 1 * time.Second +var DefaultPeerUpdateInterval = time.Second var _ p2p.PeerManager = (*PeerManager)(nil) var _ component.Component = (*PeerManager)(nil) From 554c7d5b21c671a5488d9b18e4c0774a693432dc Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:54:25 -0700 Subject: [PATCH 133/815] renames and refactors a cache and libp2p method --- network/p2p/connection/connection_gater.go | 8 ++--- network/p2p/disallowListCache.go | 9 ++--- network/p2p/libp2pNode.go | 12 +++---- network/p2p/mock/disallow_list_cache.go | 16 +++++++-- network/p2p/mock/disallow_list_oracle.go | 16 +++++++-- network/p2p/mock/lib_p2_p_node.go | 42 +++++++++++++--------- network/p2p/p2pnode/internal/cache.go | 25 +++++++------ network/p2p/p2pnode/internal/cache_test.go | 18 ++++++---- network/p2p/p2pnode/libp2pNode.go | 21 +++++------ 9 files changed, 103 insertions(+), 64 deletions(-) diff --git a/network/p2p/connection/connection_gater.go b/network/p2p/connection/connection_gater.go index 0d6682ff664..3603d15d227 100644 --- a/network/p2p/connection/connection_gater.go +++ b/network/p2p/connection/connection_gater.go @@ -73,8 +73,8 @@ func NewConnGater(log zerolog.Logger, identityProvider module.IdentityProvider, func (c *ConnGater) InterceptPeerDial(p peer.ID) bool { lg := c.log.With().Str("peer_id", p.String()).Logger() - disallowListCauses := c.disallowListOracle.GetAllDisallowListedCauses(p) - if len(disallowListCauses) > 0 { + disallowListCauses, disallowListed := c.disallowListOracle.IsDisallowListed(p) + if disallowListed { lg.Warn(). Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). Msg("outbound connection attempt to disallow listed peer is rejected") @@ -132,8 +132,8 @@ func (c *ConnGater) InterceptSecured(dir network.Direction, p peer.ID, addr netw Str("remote_address", addr.RemoteMultiaddr().String()). Logger() - disallowListCauses := c.disallowListOracle.GetAllDisallowListedCauses(p) - if len(disallowListCauses) > 0 { + disallowListCauses, disallowListed := c.disallowListOracle.IsDisallowListed(p) + if disallowListed { lg.Warn(). Str("disallow_list_causes", fmt.Sprintf("%v", disallowListCauses)). Msg("inbound connection attempt to disallow listed peer is rejected") diff --git a/network/p2p/disallowListCache.go b/network/p2p/disallowListCache.go index 5fea5292f8f..b153084b6cf 100644 --- a/network/p2p/disallowListCache.go +++ b/network/p2p/disallowListCache.go @@ -10,13 +10,14 @@ import ( // DisallowListCache is an interface for a cache that keeps the list of disallow-listed peers. // It is designed to present a centralized interface for keeping track of disallow-listed peers for different reasons. type DisallowListCache interface { - // GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. + // IsDisallowListed determines whether the given peer is disallow-listed for any reason. // Args: // - peerID: the peer to check. // Returns: - // - the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, - // an empty list is returned. - GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause + // - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, + // a nil slice is returned. + // - bool: true if the peer is disallow-listed for any reason, false otherwise. + IsDisallowListed(peerID peer.ID) ([]network.DisallowListedCause, bool) // DisallowFor disallow-lists a peer for a cause. // Args: diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 74297c1b00d..a5a92d5cc70 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -141,12 +141,12 @@ type DisallowListNotificationConsumer interface { // DisallowListOracle is an interface for querying disallow-listed peers. type DisallowListOracle interface { - // GetAllDisallowListedCauses for a disallow-listed peer returns all disallow-listed causes. - // If the peer is not disallow-listed, returns an empty slice (not nil). - // The implementation must be concurrency safe. + // IsDisallowListed determines whether the given peer is disallow-listed for any reason. // Args: - // none + // - peerID: the peer to check. // Returns: - // []network.DisallowListedCause: list of disallow-listed causes for the peer or empty slice if the peer is not disallow-listed. - GetAllDisallowListedCauses(peerId peer.ID) []network.DisallowListedCause + // - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, + // a nil slice is returned. + // - bool: true if the peer is disallow-listed for any reason, false otherwise. + IsDisallowListed(peerId peer.ID) ([]network.DisallowListedCause, bool) } diff --git a/network/p2p/mock/disallow_list_cache.go b/network/p2p/mock/disallow_list_cache.go index 6a0e206d7b6..54d7fcf0d3c 100644 --- a/network/p2p/mock/disallow_list_cache.go +++ b/network/p2p/mock/disallow_list_cache.go @@ -56,11 +56,15 @@ func (_m *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowL return r0, r1 } -// GetAllDisallowedListCausesFor provides a mock function with given fields: peerID -func (_m *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause { +// IsDisallowListed provides a mock function with given fields: peerID +func (_m *DisallowListCache) IsDisallowListed(peerID peer.ID) ([]network.DisallowListedCause, bool) { ret := _m.Called(peerID) var r0 []network.DisallowListedCause + var r1 bool + if rf, ok := ret.Get(0).(func(peer.ID) ([]network.DisallowListedCause, bool)); ok { + return rf(peerID) + } if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { r0 = rf(peerID) } else { @@ -69,7 +73,13 @@ func (_m *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []net } } - return r0 + if rf, ok := ret.Get(1).(func(peer.ID) bool); ok { + r1 = rf(peerID) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 } type mockConstructorTestingTNewDisallowListCache interface { diff --git a/network/p2p/mock/disallow_list_oracle.go b/network/p2p/mock/disallow_list_oracle.go index f85c82d75de..8779bce7186 100644 --- a/network/p2p/mock/disallow_list_oracle.go +++ b/network/p2p/mock/disallow_list_oracle.go @@ -14,11 +14,15 @@ type DisallowListOracle struct { mock.Mock } -// GetAllDisallowListedCauses provides a mock function with given fields: peerId -func (_m *DisallowListOracle) GetAllDisallowListedCauses(peerId peer.ID) []network.DisallowListedCause { +// IsDisallowListed provides a mock function with given fields: peerId +func (_m *DisallowListOracle) IsDisallowListed(peerId peer.ID) ([]network.DisallowListedCause, bool) { ret := _m.Called(peerId) var r0 []network.DisallowListedCause + var r1 bool + if rf, ok := ret.Get(0).(func(peer.ID) ([]network.DisallowListedCause, bool)); ok { + return rf(peerId) + } if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { r0 = rf(peerId) } else { @@ -27,7 +31,13 @@ func (_m *DisallowListOracle) GetAllDisallowListedCauses(peerId peer.ID) []netwo } } - return r0 + if rf, ok := ret.Get(1).(func(peer.ID) bool); ok { + r1 = rf(peerId) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 } type mockConstructorTestingTNewDisallowListOracle interface { diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 5d62ad73a77..6665b1a9cb2 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -92,22 +92,6 @@ func (_m *LibP2PNode) Done() <-chan struct{} { return r0 } -// GetAllDisallowListedCauses provides a mock function with given fields: peerId -func (_m *LibP2PNode) GetAllDisallowListedCauses(peerId peer.ID) []flow_gonetwork.DisallowListedCause { - ret := _m.Called(peerId) - - var r0 []flow_gonetwork.DisallowListedCause - if rf, ok := ret.Get(0).(func(peer.ID) []flow_gonetwork.DisallowListedCause); ok { - r0 = rf(peerId) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]flow_gonetwork.DisallowListedCause) - } - } - - return r0 -} - // GetIPPort provides a mock function with given fields: func (_m *LibP2PNode) GetIPPort() (string, string, error) { ret := _m.Called() @@ -209,6 +193,32 @@ func (_m *LibP2PNode) IsConnected(peerID peer.ID) (bool, error) { return r0, r1 } +// IsDisallowListed provides a mock function with given fields: peerId +func (_m *LibP2PNode) IsDisallowListed(peerId peer.ID) ([]flow_gonetwork.DisallowListedCause, bool) { + ret := _m.Called(peerId) + + var r0 []flow_gonetwork.DisallowListedCause + var r1 bool + if rf, ok := ret.Get(0).(func(peer.ID) ([]flow_gonetwork.DisallowListedCause, bool)); ok { + return rf(peerId) + } + if rf, ok := ret.Get(0).(func(peer.ID) []flow_gonetwork.DisallowListedCause); ok { + r0 = rf(peerId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]flow_gonetwork.DisallowListedCause) + } + } + + if rf, ok := ret.Get(1).(func(peer.ID) bool); ok { + r1 = rf(peerId) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + // ListPeers provides a mock function with given fields: topic func (_m *LibP2PNode) ListPeers(topic string) []peer.ID { ret := _m.Called(topic) diff --git a/network/p2p/p2pnode/internal/cache.go b/network/p2p/p2pnode/internal/cache.go index 1045cb7459c..6d8952e6628 100644 --- a/network/p2p/p2pnode/internal/cache.go +++ b/network/p2p/p2pnode/internal/cache.go @@ -47,32 +47,37 @@ func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector mod } } -// GetAllDisallowedListCausesFor returns the list of causes for which the given peer is disallow-listed. +// IsDisallowListed determines whether the given peer is disallow-listed for any reason. // Args: // - peerID: the peer to check. // Returns: // - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, -// an empty list is returned. -func (d *DisallowListCache) GetAllDisallowedListCausesFor(peerID peer.ID) []network.DisallowListedCause { - causes := make([]network.DisallowListedCause, 0) +// a nil slice is returned. +// - bool: true if the peer is disallow-listed for any reason, false otherwise. +func (d *DisallowListCache) IsDisallowListed(peerID peer.ID) ([]network.DisallowListedCause, bool) { entity, exists := d.c.ByID(makeId(peerID)) if !exists { - return causes + return nil, false } dEntity := mustBeDisallowListEntity(entity) - for cause := range dEntity.causes { - causes = append(causes, cause) + if len(dEntity.causes) == 0 { + return nil, false } - return causes + + causes := make([]network.DisallowListedCause, 0, len(dEntity.causes)) + for c := range dEntity.causes { + causes = append(causes, c) + } + return causes, true } // init initializes the disallow-list cache entity for the peerID. // Args: // - peerID: the peerID of the peer to be disallow-listed. // Returns: -// - bool: true if the entity is successfully added to the cache. -// false if the entity already exists in the cache. +// - bool: true if the entity is successfully added to the cache. +// false if the entity already exists in the cache. func (d *DisallowListCache) init(peerID peer.ID) bool { return d.c.Add(&disallowListCacheEntity{ peerID: peerID, diff --git a/network/p2p/p2pnode/internal/cache_test.go b/network/p2p/p2pnode/internal/cache_test.go index 365ce66cc2c..d4ab02d5c9b 100644 --- a/network/p2p/p2pnode/internal/cache_test.go +++ b/network/p2p/p2pnode/internal/cache_test.go @@ -74,7 +74,8 @@ func TestDisallowFor_MultiplePeers(t *testing.T) { for i := 0; i <= 10; i++ { // getting the disallow-listed causes for a peerID - causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i))) + require.True(t, disallowListed) require.Len(t, causes, 2) require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp}) } @@ -108,7 +109,8 @@ func TestAllowFor_SinglePeer(t *testing.T) { require.Contains(t, causes, network.DisallowListedCauseAdmin) // getting the disallow-listed causes for the peerID - causes = disallowListCache.GetAllDisallowedListCausesFor(peerID) + causes, disallowListed := disallowListCache.IsDisallowListed(peerID) + require.True(t, disallowListed) require.Len(t, causes, 1) require.Contains(t, causes, network.DisallowListedCauseAdmin) @@ -118,7 +120,8 @@ func TestAllowFor_SinglePeer(t *testing.T) { require.Len(t, causes, 0) // getting the disallow-listed causes for the peerID - causes = disallowListCache.GetAllDisallowedListCausesFor(peerID) + causes, disallowListed = disallowListCache.IsDisallowListed(peerID) + require.False(t, disallowListed) require.Len(t, causes, 0) // disallowing the peerID for a cause @@ -179,7 +182,8 @@ func TestAllowFor_MultiplePeers_Sequentially(t *testing.T) { for i := 0; i <= 10; i++ { // getting the disallow-listed causes for a peerID - causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i))) + require.True(t, disallowListed) require.Len(t, causes, 1) require.Contains(t, causes, network.DisallowListedCauseAlsp) } @@ -265,8 +269,9 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // getting the disallow-listed causes for a peerID - causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i))) require.Len(t, causes, 1) + require.True(t, disallowListed) require.Contains(t, causes, network.DisallowListedCauseAlsp) }(i) } @@ -330,7 +335,8 @@ func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) { defer wg.Done() // getting the disallow-listed causes for a peerID - causes := disallowListCache.GetAllDisallowedListCausesFor(peer.ID(fmt.Sprintf("peer-%d", i))) + causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i))) + require.False(t, disallowListed) require.Len(t, causes, 0) }(i) } diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 224a1be4542..8a2ec5b107f 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -366,8 +366,8 @@ func (n *Node) WithPeersProvider(peersProvider p2p.PeersProvider) { allowListedPeerIds := peer.IDSlice{} // subset of authorizedPeersIds that are not disallowed for _, peerId := range authorizedPeersIds { // exclude the disallowed peers from the authorized peers list - causes := n.disallowListedCache.GetAllDisallowedListCausesFor(peerId) - if len(causes) > 0 { + causes, disallowListed := n.disallowListedCache.IsDisallowListed(peerId) + if disallowListed { n.logger.Warn(). Str("peer_id", peerId.String()). Str("causes", fmt.Sprintf("%v", causes)). @@ -525,16 +525,13 @@ func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowLis Msg("peer is allow-listed for cause") } -// GetAllDisallowListedCauses for a disallow-listed peer returns all disallow-listed causes. -// If the peer is not disallow-listed, returns an empty slice (not nil). -// The implementation must be concurrency safe. +// IsDisallowListed determines whether the given peer is disallow-listed for any reason. // Args: -// -// none -// +// - peerID: the peer to check. // Returns: -// -// []network.DisallowListedCause: list of disallow-listed causes for the peer or empty slice if the peer is not disallow-listed. -func (n *Node) GetAllDisallowListedCauses(peerId peer.ID) []flownet.DisallowListedCause { - return n.disallowListedCache.GetAllDisallowedListCausesFor(peerId) +// - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason, +// a nil slice is returned. +// - bool: true if the peer is disallow-listed for any reason, false otherwise. +func (n *Node) IsDisallowListed(peerId peer.ID) ([]flownet.DisallowListedCause, bool) { + return n.disallowListedCache.IsDisallowListed(peerId) } From 0c60366a10269149cd62acbc48c88275b4823e4d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 11:56:19 -0700 Subject: [PATCH 134/815] lint fix --- network/alsp/manager/manager.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 4144adae230..1781dfac906 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -317,9 +317,9 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). - // as long as record.Penalty is NOT below model.DisallowListingThreshold, - // the node is considered allow-listed and can conduct inbound and outbound connections. - // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. + // as long as record.Penalty is NOT below model.DisallowListingThreshold, + // the node is considered allow-listed and can conduct inbound and outbound connections. + // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. if record.Penalty < model.DisallowListingThreshold { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ From 473153ba4144829cb90f98b45cda26fe426c0f2e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 12 Jun 2023 15:34:51 -0700 Subject: [PATCH 135/815] adds readme --- network/alsp/manager/README.md | 82 ++++++++++++++++++++++++++ network/alsp/manager/alsp-manager.png | Bin 0 -> 988056 bytes network/alsp/manager/manager.go | 7 +-- 3 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 network/alsp/manager/README.md create mode 100644 network/alsp/manager/alsp-manager.png diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md new file mode 100644 index 00000000000..ef98775d140 --- /dev/null +++ b/network/alsp/manager/README.md @@ -0,0 +1,82 @@ +# Application Layer Spam Prevention (ASLP) Manager +Implementation of ALSP manager is available here: [manager.go](manager.go) +Note that this readme is primarily focusing on the ALSP manager. For more details regarding the ALSP system please refer to [readme.md](..%2Freadme.md). +--- +## Architectural Overview +### Reporting Misbehavior and Managing Node Penalties +Figure below illustrates the ALSP manager’s role in the reporting of misbehavior and the management of node penalties as +well as the interactions between the ALSP manager and the `LibP2PNode`, `ConnectionGater`, and `PeerManager` components for +the disallow listing and allow listing processes. + +#### Reporting Misbehavior +In the event that an engine detects misbehavior within a channel, +it is imperative to report this finding to the ALSP manager. +This is achieved by invoking the `ReportMisbehavior` method on the conduit corresponding to the engine. + +#### Managing Penalties +The ALSP manager is responsible for maintaining records of misbehavior reports associated with +remote nodes and for calculating their accumulated misbehavior penalties. +Should a node’s misbehavior penalty surpass a certain threshold +(referred to as `DisallowListingThreshold`), the ALSP manager initiates the disallow listing process. When a remote node is disallow-listed, +it is effectively isolated from the network by the `ConnectionGater` and `PeerManager` components, i.e., the exisitng +connections to that remote node are closed and no new connections are allowed to be established. + +##### Disallow Listing Process +1. The ALSP manager communicates with the `LibP2PNode` by calling its `OnDisallowListNotification` method to indicate that a particular remote node has been disallow-listed. +2. In response, the `LibP2PNode` takes two important actions: + a. It alerts the `PeerManager`, instructing it to sever the connection with the disallow-listed node. + b. It notifies the `ConnectionGater` to block any incoming or outgoing connections to and from the disallow-listed node. +This ensures that the disallow-listed node is effectively isolated from the network. + +##### Penalty Decay and Allow Listing Process +The ALSP manager also includes a penalty decay mechanism, which gradually reduces the penalties of nodes over time upon regular hearbeat intervals (default is evey one second). +Once a disallow-listed node's penalty decays back to zero, the node can be reintegrated into the network through the allow listing process. The allow-listing process involves allowing +the `ConnectionGater` to lift the block on the disallow-listed node and instructing the `PeerManager` to initiate an outbound connection with the allow-listed node. + +1. The ALSP manager calls the `OnAllowListNotification` method on the `LibP2PNode` to signify that a previously disallow-listed node is now allow-listed. +2. The `LibP2PNode` responds by: + a. Instructing the `ConnectionGater` to lift the block, thereby permitting connections with the now allow-listed node. + b. Requesting the `PeerManager` to initiate an outbound connection with the allow-listed node. + +This series of actions allows the rehabilitated node to be reintegrated and actively participate in the network once again. +![alsp-manager.png](alsp-manager.png) +--- + + + +## Developer Guidelines +The ALSP (Application Layer Spam Prevention) Manager handles application layer spamming misbehavior reports and penalizes misbehaving nodes. It also disallow-lists nodes whose penalties drop below a threshold. + + +- **Misbehavior Reports**: When a local engine detects a spamming misbehavior of a remote node, it sends a report to the ALSP manager, by invoking the `HandleMisbehaviorReport` method of the corresponding +conduit on which the misbehavior was detected. The manager handles the report in a thread-safe and non-blocking manner, using worker pools. + +```go +func (m *MisbehaviorReportManager) HandleMisbehaviorReport(channel channels.Channel, report network.MisbehaviorReport) { + // Handle the report +} +``` + +- **Penalties**: Misbehaving nodes are penalized by the manager. +The manager keeps a cache of records with penalties for each node. +The penalties are decayed over time through periodic heartbeats. + +- **Disallow-listing**: Nodes whose penalties drop below a threshold are disallow-listed. + +- **Heartbeats**: Periodic heartbeats allow the manager to perform recurring tasks, such as decaying the penalties of misbehaving nodes. +```go +func (m *MisbehaviorReportManager) heartbeatLoop(ctx irrecoverable.SignalerContext, interval time.Duration) { + // Handle heartbeats +} +``` + +- **Disallow-list Notification Consumer**: is the interface of the consumer of disallow-list notifications, which is +responsible for taking actions when a node is disallow-listed, i.e., closing exisitng connections with the remote disallow-listed +node and blocking any incoming or outgoing connections to that node. The consumer is passed to the manager when it is created. +In the current implementation the consumer is the instance of the `LibP2PNode` component of the node. +```go +disallowListingConsumer network.DisallowListNotificationConsumer +``` + +### Configuration +The configuration includes settings like cache size, heartbeat intervals, and network type. \ No newline at end of file diff --git a/network/alsp/manager/alsp-manager.png b/network/alsp/manager/alsp-manager.png new file mode 100644 index 0000000000000000000000000000000000000000..97e111e532b3e0af7b044c452b67f3e1b6c2bbdb GIT binary patch literal 988056 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⁢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%0D^l~{Wj^-{p(I4#IU%I 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 ^TwSTex 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
xr0TTi>xazlURIQN+erSz>+KL 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�w#4WZz7Ey&=`h>rI9qJ>N zwX_u4tv+FJXcID>Tq|ERt2k*-s>4hk(SyY@Y~3<&?W)r3p~iYs0E&Q(8Mr4oK1xYd zzWo-*kJfbH1c*lPH1dVc->nvz8u>7tRL9&piLI<}ZtvlXUf3p))4*_^4(=`h|wZqz7haV@0 zOhN|n3MvTU^H@>3pT6(zV~5q6e=@u`VMp$ey0;#KK?9vP_zzAO3^Gn4&t&ppyC zjtuNUmsQ)=+_Y)3Q*RI7f!AxxTj*{5_S+h5)1F@xjQ$;!|d)pP?zd|0i2VsLn01n-L%yanuPk*lyvCO!uyo!sXx&Q!OK3Dz>u2;kkzpsqTp{t? zZ^T6dv9-BoBfWE-{o1b*UQ@wc_`_6vBS4H_+t{^mazBYb|F7)Y9R*<{qrS0SbHb|8 z;{f~5_0`n`;PL4wU)XYaf4JIVnk z$}hyH59oVL_4huYqTNTn66wqkK_+8enbHF(8%wG#RX8=yfrVgNEo&F5{huIJQuc}PdO^&cT#f%p!1mE51@5Iicqe&=@&{bDx^BssqJg}H^hY!UP zCi!cEwf0`{wY0t@3cP`R^r-NX?vPzg@`EfJ;DUdQUp%ts<;8kao=pyoP=p8v=`}J$ zoH+2R^|E^eLRE1HSbpxszHX^8r9!HG3RWX~fv5HU(*x>#uj;?)aU_qXBl-sQv!(gQ z_4~PBN03O2ZbCwu7V=T`)FM!4lh`Wp7`6j$nbVS-XcSEuGf(c0Y`kpkjtBA)0(ex@ zO|$E)<*C%I5(Dz*c93+{>1l4;m^SD?WEpTL$XeZ_Pj`u&H!X_Mo|-N1RB{-0wcSDd zU6NNAEB_F5`z_IFgO_g7uMUpCac>n?U)5mhq_nj|>aQ(x`dq_unSY~Kdjt$iTvG`l z^OogWQ^~9liS9)*Q>T3ZTm+4(-hW0PW>MoUx5h|U>r$27f&YM2<+1h1B9qthlR|DB#Vuj`q4WcyiyklV(?an*b864+44_NEYBr3 zpHz_n|336Li`~%v_}Tng@wbjab|^~!^~u+AzbLIul#9AGqdzxIK*3pkRZh+JHlBLu zZ)W$@RZF$1M5hF^GQ)f%e3I3~ z_{Jo9D?trcr;A zz`#v!i6^YB*WxvDN0ipD;fN0$na{oErpn_zk^d7z)Tej98S7pJ9s<(kLDQlgoPNik z_9NyHV5;OTS=uhu!3G(x5$z?Y9UlW1(*zw(1<@o{@LEy0M~>rQ=Ve&#)2ZyxSfa z)N<+sV*+K{l$tW~Z@QRm6@}f|0v-lKA>kKEGq!?&wgzy{!fclC{@jN-pP{d&p%Rd%&=1?f{xvU_Nd=U zX|w%^ZMd77tuZDJq$L%(6ETO&l{lunu)tq1Beec+UNvq)m38}vBrRpVv$;11m~~&# zW?#Z*Tg{5U(Bp~MduLpZ*ZU(Gw?B4}Tf5}vcQrn^B6FpAlXLGO-v7< zT>35HWuszMtk5)j`^hUFC)E)q-C63z{nFQC!>XkIZ3p zdI9kh3$|2y)|4Rk209DbsGrVTA<&UWR&}DAb05OA!4!+wiAzZ_dE(FMZH?2z+iUWOYxt0Q#iR`WIl*g@Da~+?BWpxDf z+&&|w3r9Cgh7!^3p`76HtSRXHa%D=ut+G75)FNtM<>4J9kRZ@@Wump)BpH>K zNlfT+96ljawOm>CAbluxj#%1B`v*qp>Kg_2UF8ocNf~^d9f1rV8@l3)YH4pKUkwK? z9_=hWd{8O0g%VCk)O50W6*}+i6(L+RfxQ&|FUe?ZIARygxI5$Le$;G{x*ri+IsKlw za-nzxpj}uVE!JN?x>=Zy&uT#P`AjA4R5mrRQPp+*R+bv{AfLllH5M^^DpJ0C!9-3oTG0Y zh__x3M@j~~9pn0v9f%#`tkEDrziY)pBlB9T^MohK_A;VI|1q_z3@&K>>J5166Z5-c zz{8JI?FGH(n{flO8+2^R@z~goa25p!v6Q8;0L`t|L`bcTU5bg6x6D&C-K>hhG`X2w z3$;oHv+z@b*3^o+nwbG3rIJ^ewN~Mu2Tca92VkQ{& zKwZ%}!;-T-S)Fc;{2C>KUHNcl39)c3&Xr5)dJ!m_%1rGUA1b&5$lE$a(8F@4yK+x{ zx)E8lPzl7Y?jDZ#=P?Ettja_rgFAO?r^#gQ7P>LJAJVqhTm6kAW;4Js+}u;WdY~_c zQsaCC!nB|p04F9(1ebenmsQq|nM%JcY&+pa0>pnAFiplzzlU{Hp=`*>KMOraBFjql zeH+%b$n8(UF*4f8G2?03zf05J>ZU|+Vq^yGuF4jO9ms)^fN{``VwzG-sz+u`>lN|_ z{%EB4{x1o=Q|EEwYa8`D@0zxQo~9YU=69MMYjQl1*r~Fg7o&NXTq|>w9 z2xs3d?$c*lf9`-?%0-DkDXQRCSJ?p5)Ng^O&y9T8MD3t_G3~_SN8fX2egD)Zk}0!X zP&3ZkirA+aZg5ngT(T8p!mN&ucC1M*{|ZhPo8-jXf9>IgOSws>Yk8|uW?tTAZ}B+o z-}`qrc4*6NuX+f+x=)CCaa#Vf)hL~`eLLcs67sQ5yo<4Qw$9*611T;?u$Yzrd#^uC z*WXVl`L}W3l^Wj?JBPAD-z>u93Unzo{%-1Yq)+`kyz1J80U=*kh*kG$oelEO{-67r z#FMGeygtR`MBRl+du#;Mc3bp;1$EQ^LD}xE!kNiZB#h;ETz46mITSnur!0lF{)<*WTALnb zF}_W3hIdYjF}CkxQ!wId3LRyU|ELJ$YH~T)(|rj@P*6U%W@lKBlD1FF%(M7ySSK5* zax(|U~pwKy@1G|)mD8}o6ZwPcsbV z3TTY!!=jCz=E*xt>e`-7-52f#v$bTbVGP6I+&&w>dr9rj=Ia?4NbC5$^C3xxNmzf;U zKQzY6%seN!D*>QwHEINCMpV}yv15j}Jg|-c3jYGhIy`>V;un2l+0l7bBmsl; z3SAP4G_+Q;owkD4l@s~^r@R{-wHVqIo8&N_i3(#x0;sj~@$X!~OutG?%lB(Wgfmg< zSlp=xNg$b@Ns1Zu=a2>oU$&_CGa8S~BEtuU4CNI-^Y0Q~y_9zOB3H8qyCpZQ2a6w1 zrocv)pW&`4H-M;F%7ol19*?KRKL0-3^H^&%g_P1FgWZuh9w~3+!B1@h%b2h03#RTI z5BmX7I;~-EO^BP|i@rf<^%CVdaSIsJbF7R zbzea=oPXCW6oQd-YQ-0TT_J0uTGp$;n_Bpwt?>aqJwf&r zkzLDccP*%b^Tlg`+;=1uc1toWO>hj#a#5@^NuCXp>2{b4v6#1rnhQh6Q+)wBHRHW` zW$Sm1lYZ^u2KL5+`1}BbO-XLL4k!xKvE}(_@e>um8)g6WsqAb{g=$Q&g@IMMKlsp` z_dET0biI%`d<6=%GKZ5&nwOowB~ez%au+X~f_t}>1AnXv(GaDv_p505J>VZ4`M52j zp3=W&D#HoRu50w{@)b1jqYNBO>DtkoP8AMOa>frlg;BFS)XBSDU<8 z2fi1?SJ-eZH4Sc#QW?AtU0p|1r4EBGOtp{nBt>1MQ5ufMDL|4KE4ZNdrHU@ zI(}4E-W+U*#459vJ|r{W^jSN@VgGm}#ad$Jsu9?W7p?Y1(Ica=WNK|D$1%3OpFg>K zazzYbQgO+QGZ+>rQ`r0CxQ>~EkMfEZ_T7uQfLYxDm%W?O1i`*R zm~T92f_fXTA_83tqZ(>8QoSGCY-=3_?}r4ru#M#h+TAf)BWhc69f>!}woqx{7NNpx#>fkwr^Oc|5=W^WNpfWM z2GzZbVO>u%s-L=V@}FWP;oBm=eQYgnm{izJP&(%-H{s#@BW5iKwZ=#$bjoru*Jcw8 zEXvTRBl^ECh6xn5iv9tr6Kh}TuXEmjaEw(+ z%QlOHhg6&+VRajK2^H z^yTf}hUhL#+C!LO;KPk9T0)9nAQ)IFS=d|lun?pGEf`hmp&)r86pBO7$R-*e0eG-i z@6)F>j&5JpAJ*P=&TsbfRod|$G=yGq;7DTE47W2^y$b%G{(O+Oo{LeSj^OgCi6%-W#AMzhnid_A z`~Hwnihygq|C&j+m;g^p1(_3or4Sq*HtZ1<=f)y%=A>n*%<}M_*Or%XPLzo3;}6m; zqNv#6-hN*%(AyvENZfKth9_zravOpqpU{6%xh`nl#AgtiIF_tDFlhbwwoFQtbgvXA z*u1ZwsnV2@3;qwQ>xymZT~O^jM!PcKcCaOZ|4O4q;(2}@EUx2nJm(Vu|7&#e1Sjhh zmYJyJna8UHRPZtkB~wWuso0bO`A_K3a9pwInzr36BB zeQ5QYM&T%qC$uk_6umiXN4&nwPYP|{(-PXA&cHeaPrq>n96#%(ajpP|R9Ai&$oU%U z@>pJ9_hUQn(s*R>M8YR_aJgG}^@;0RAI?@)JsGBx0%5ViEAiI-h*AyO`iE-hwCVI>vcSk|wF-Zr@sDZ=^pB zh%Vb#u&P7T^?DG#5}%CFopg^ptX1V|3^cXT!n24OH<-hJ$*bF!d00k4@?qmwbkfd8 zw0!ranMCp~7*KEpemZOw$%f9INUjOaS%V))Y>z2Rr#VOx9yu|iKaFAT?8dYNnCU<) zk^v5@f&xp>3_CC!W~#*WwodE1tL`8b^V~kj^S)&(#6`DoL18}}xdkY06(t?trVZwAD!AOtB~i2wP9^`W)@6aT8AQ|}v+qErje zd-}X2G(}f44DdsygNUar6Ll;@q=wZ8dZwjr9uyCBrB1F%{Y9&pCYq|kU>hx!z~Lz7 z_A`-J-%!5~Ac2~%$a2{<#A71JmgW0RswB^LB^S!DWEc(-vEC*|)cm4Ph0bl9wOFL! z4;SMHDhj?!p=%OeXtMtdIL8ZGc7OyXJ>3Kc_Jw3kcD$@(6N*p0`Sk4vg~hO{`O`}q z+_ltJ+pL)mXCLKLkqyjj&bdBf{$`@c>XLBwF8~#UfCNc|9?BdL5mQj$DcSui&w5dS zAIOs06?%EuIrMVO$`t_1 zA4-Ova(U70BAENs3|+Z%soK8oR~3#oI;U~L2SWSPA~{NQZa?t}?u4Q9*>X)SMu@gw z@Y|-m2@S{-NDK0ijXcJrDenxRk$|a)wd10<(S#kR%thlE3-ATLd0S-?iw-%)`GU z(w0fT0Jhv1Wt|{a&sp|UHD1HczM|jZJ2G; zeFfF+MUd^SX!l;7HfUvaFT?Zs7|6aQ5?zO}m!qQzA95G-Ztq(QhOU<{z0dua}bLhZ9Pw=(OMSv-2jCBo3_We{#6F4>{M(+9{;J21|Dg-L{ zI4r^2*ntxuJz6PHntNni z%8D)!-D$D04P6t0cz{f^pWGm_l)_-wT+UxWEUJ!)Wni{lDna1q)bESx`yp|N1In{e z)F%{7g`?A0-gvg>L;c(0NXXv~^iG+28%wvmT-RHx4+=@ks-wNx{@h16tdDURGEfwv z{IqyZsk@?^$moz31qME(zGr|4F0FHz+o=p1wp2?OeQp0$=vKBT{GB2_?R0 zXR`<5F9<7Qb2aoiADGGSQ4a z97E9ba7AjUj_a}GI@l_+ehzXEscnwct=>6rH=NihuWw)Q+E5UCoYA_yi*cSFkI(-2 zO}?WFwF)vwZC+XKLcwiLoXu}nzJ0GhMI&o<>8mfi?aO9Y1=RdQx*tud7R+c7@~cPH z?E{Wo0=4?2eAI|#O!btO!oAhqr3X#TWP4P~8Gl5T#(n3D=gqe!DQ^T+MW-`mCB)?E| zx>0VAm!f55tg;L53e(P zK|-V?hVBk&Nog2jsHyXe_iz25wdUolc`@gl@7ep>*Zy2zRq-K{sruz4T$59l?J5Wm z(6nI5!NmsTbh@y$3F5z9+tA4BtUVX>gPX9Pl%z)~sbDKF5AiHiUHvz_woTEskO^bj z9UX0W+l@gBhEP5aIZE$ImQ6hB+UM<>r#wL^JWgn4tNS6>gWT`bLRSI56iC0p2ZYBb zXk5vKLxjx`7}iCyia-QnQO8SGx}^aeNt)v0)l}zl-ysduWbZw zKR)Cy_G#^E3E_UW3Yq=vH+2H8raD%`hsiR5)6!N3HXfkkw4b3WYhnW`(ucUlxl%ve z2D`=mjqiOw+0AYtdVeKZ=P)QM(>#P~q(wZ%Ywzu=Raix?)4eixP`$?s@L~mA9J9Rl z%9gkG*Sb#)bjfvkif>ryqVe2f`FSW^)1!lUOH|JH=D{r5OFu-twQupiSxWC|t1pMzQkeVmhm_B54BWvZqq>PUx)Y@{ z&Ak)n%4>L;`;5Rxh2)1;@tXX9%J5V$i|2tKl+|tY)D!!)4pv$@A9Nu~OO8FQh*cyd zH#TMvJ^V`}ua>-7DyZ8)K4bC>0$Cp${eCR* zrzlZNaT&LnRwIDS}9tB^&}e9N&qG$ zR9~l-B^kLX^ebqixdrW1W8AhdE<~E;F>qIrdZ1iRUF{{s?^t`SI~z+ogzdy{EqRVI zTFTI8cYr!C#XT*M*aw6j^{PQk33n%qx`f*Co-C^3@FjHj-U77ctbC2+`hA5JJA{Ci z^x{}T{B0GnkZGYpuY5YL-Jg*1^$j73Wp`%5Mr8Bk9#SvKa>wcBGj2f~MKFx2%>W-` z&ugD-Kd|xW41V2buNNYPX_GRcw0$?Kx_e2XGM0vp6E2_UM0<*&KKxS6Ud#dfw|iDK zyapf#%TTf>K6_L z<;K}2Jub}r;5i!C`F88XHQ9Gvk^^hIBzDtcb%0KUgqNpdGU?qo&08|m*J9DtkMv)( zV8p3|poLp{sy`t9@HTzOQ3-=OylR`_KH0#jMVTK6dnRoZG$=wVF=J*Tv25& zPHL{|y)YvS7GGC`aj-e{YSWf#Z{z*A%z*mYXvSw>`^aY|D&xA2WgLc(-piA0* zqdQoFDRQ0eM14koJej{lT59^O~F!%15!9t}pcC7`83^J;?73 zuJp#+7whrMkT`nJwi=Fd*w-(Dlg%o3E7yk$8NZO%N9()z+Jx$(ACwb1`J9f+TZ<89pl(q-Et6|js({8Lz@e|`{63SWZe@S{b}>JAkAu?{v-UM$ zpX{Kmxs4>)P!dcJife9bYARTS17c;X391ZYyTG?ic14 zJ9kGBFk2WL{oWD|F0f)7?9&Pg+;_Nr2kHbf9Mb(v+iPAcRM;DEJ60-Ca@R~bD&ex} zs{1_fk_Vy`a5Fd}y0iLk7$4MlD{mAt4f50NoiY7$D`QbUEaCg8f~_Qr9!iAFLRsn; zCpG%|-h83R+2vca+2bia=*~g-7{$*kY=z_VR#6>Q(2(;+CAE5c=4VbIZkPx+&eX?=b&N38%NE$LLw3*2|JCZxrzL>@co)W>*n}VAU(!*Uz|_U zU0wlGPOvd_Sb*(5Z3Km!r->?toQ%e#((2O5op5iwcpu$y{2bF6Da3=IJ*X-NvqX3` zvYqJa8J2X~2prYyn&k1gTX>vMy3nyG4oQRTD9#0ZNsl4Xx7(I~c!!`hT5pNj%=_k7 zo?ALis(yj`nl(cQv&1h=>VDU4Y)0?!&6ZPF95%k-ozA`g8P_R4y=x#m zn)U#KTR9s*bZU&jJN+M6c@)I;&?Zo(odmlt`J13qKL4>TT5c;j5zF>xN2k9Z)2Po5 zrbzqvYl%=_yaOJNa z);;JMbK0z4^nu&OazOoS`~4SwwURTnwlLuCvx#RTPsSieK&zD_fKDiO;e1Rlzvoi3 z;9%hh@bj4l;+(m1!lUi#D@S_PjZX8Yor?Uf<#mk%;UJ{I@VuN3& zC&lh2sQqBKR+!!5eiMj7Jep2Ge z{NmnaT>Ja(pReV2F2dguWA60JTwrk1pmM`!@pWUP3kXo9iTc^odsvEhaoB!**X&nB zGAij!_qT0=p7juzdTL;zyaP8u6V4q08;ra^ABIDJQ#)*wGD%3Ew}q;>KW&Q&&R6Gn%Z}v;$-N($zLjU_%@qI!%1qD;|wq)8q~+lFqp<=zgM= zovq{>RIF7Ig|};nmlSAa_`NAOf88PsIZqd^W*>6hf>;wkYFL=% z6{5dlR8nTxDS6r?v5ve+>~3abqk9yrt1D~dqU5hbQll9w??2T>5R<@JI6e_EKGFsq zyeZ`6v?{6$C}St%t)QO|>z#GHnE~wcM!4jb)9)_Pl#n9Q@0_f0feVCB5SIAB>v`0K> zTrGuOQ+M|(SxZEyty6cu%@SRu5cG%%*7qd6uMHg?gjx*S(}3tkV*g@CcWecNx|dKb z96O^CJ{^`6IPzCte9>cHocix%G5Pm}Tbxh?CG7Iw$OVJ>YzhCC%R{?Qq9<}hDk1tf z|C1=~WzCGsIJAVJLD(O%31!HjfSWet0k`8iCoMpYJMIOF&F6og3Q64$EDfEft7N)a z?7z@hF=qMh$wGUUx>~SC@w(s=-7HE{n|kKOFVgqw>asMKn)li-yKIYSp23Y2o(^ad zcO8W#HEP1|b`Zfj18y)H33eBw5#}gl0}kYKVXj0B@|HuEOSr_6!X6zH60Prb-TV6B zK_)Hyl1Hi6msSvd+G4o9DM<0(1Iz7r9zP~L%hkIt9OeHW-A)pvMxJBCf2K4Qt|x%%5^Es# zuM;UQ<1q$`e5;S^uW-R~hUc%&F1JIW52rfJ)Et*v_2tFFY=wOPL>=l7Re7MOib9{R z{Jn4+Ot<1)keX!-e7c%6lYB|{EQEdig5|}1R8C7IpoafYAn8-4di@W`h9`YaIg2+$H~=d-DRyx?3*@Ez3%wq)khB{nwD1{jT;3U7^Ye;ZybM#N4w?^; zk5vJE_>cD{`*DAhroVhXHy_jIZC+2BKQ<1+5!I;O^7->D;Y?%r#Hq9(^#uloL@%G{ zm^Fns-zE^cG?FU?ea-`zaH*l!#f491?(npyYqjGGcE8{RbI)gV?mpa}O@w@;CJ7xi z!+Q4snbl7j!U&E3-3VvZ%hmf|iu~UOeRw3TA?-=Al+EJjVx9rCtxUo(Bo!AW)onuDg>R(MKaU9L~=-?pBXKm|t zj6V-{oy%S3VonC9~azOD=v z5h~|JU{(d0LyAY(p%T20jKKB3`y<-~#|8IdL{`0lrojnTH9G4YQv^xVdpZtBTdeLIyuKP)>!=XV^MN74Qq zX^QMVk57#}7xT?3o8#W`8ZjWd#uA)GM(Io2omZi2FSdm3?)hzz;wV!RMN&!YQZQ0l znl)xaUmNOQ;;YN*gAZQ1K|d7(e7j8c@UOeD-PZ5ee>RDHqi%eMZYvFr&NmO%f~)xm^nt%Sr7Fryg17&$3Lm!)qe} zQJ3~;e$Nb{TUXRM#QG?VqZta~zG{y84^PnF%!HX7-(kUI$?>*iv#$qW!>#Q+SH}(S z_2{u(X+-;Odh2?V=5mPbaDJWdlIj`fz7K;6-7*V#mVnx_Y};=m9d=Li4Z5;-K%i^q zR%IN#|9;Xpq@;Z|bmn%C#pYpbEwmJBdtE>a2R^Wzhj`@mL2iqmpTj#zk@tEXu&bc) z`9dCXJY~e=I5*qtWijFhZPJiTaRypGG5q;o9mC+>wU8m|DWW27hU8(+(CuWwzi)dW zvO&K^*n#8+R2PNdXR)n?%p5Hdjv}(MFhkGVL0#L9^%ESgt_k!lJO*@TEL?RIj?=f@ zs*a0)ndV$`yg7u%vl}+?tn) z(E^Ar{eflozwjd-!0!~~p@xZfr9KRW>a=8rAB(2*NQJhp1%5Klr6Ngu1O zzR~aUO)$oA^-Lj|+yB9=4KzoMV5rv14~d9a&GC=N@x=GQakp;{q|1Oe4h75Dhf%QM zXE8$lHJ{%wm=G;F4=4~W#uNLjf$(Ss-cMC2aO7{~k@3go6d3Z>E#gKM#9y7ut~+Gc zfxEmbQLK!MS{@&`JimP^<%QZj8D#JfyzKEEEF+86`X;3q7^aff2Wlj9jS+9pVG<<& zpf92mkBz-6^0X^wg?(A1lEE$o5&Hrt7@k{O1!-e!`@&)0Yg>z~VS+#8xccbu!WKUs z`>%27$~}6x7I_0*`9`xyJOuGEnh{I#95nvo@FN`ZzA7up`2Dz`RF9w;>4admSc)r4 zP&Xr8k69_s7y^b4oY59S|IEq6S$In=?1qDCzt>%{Drw7sDw;MQ1Q3|MFRrM1hYzPa@cO5OB>L8pi zA?4_c4ah>5EC}l;-~AO~iYNaFt0?t_jk3HuZOO%srEcwQI)Zq7@3Sm(W8$d#k|OA*63nl!2AI{x;hqK_ z8S;S@ZB&Ch4!fFW;a#;hP?G@FD`HbN|1iM~jRMJZoL;d&315fAN%UfOlPMmH5pNA4 zj(i1TpbzE*fzTCpw|Mc*<=S#3&%fPRFUb=~NFg=$i;QMBP7FgSG4S2|KyCn(wI9DE_XT|l7M}$niz-+IGl4lm{^7j zYR7w&hnWpRb@<^gn~3)y!w)BCRj9!Qi&$brZ^gm_yrPNZ$VEdk$wvnb3y4x2F}WhoYDj*u27L+#vC5(7 zv?5z)4KLx39cRz$|Icag_Pqxi8S;Gkw8(>Nop2A_Jx91)xtpJ9pdPKm9I%2O9i$x( zeb#|IPB#!;HNRI89MJ>?{O;w_1YKwZe-Y$p`yDd5&LmfmIok)}K+`Ds@gl0E?bu-j z?3YQrel8neRE}(Wbo)GF)o~S?f-q}huXR>X1KL8>E#q+gGP?WV3_UcQpQ7LHBfj^g z-B(;?E5jDK`ZAv+k+W@1y*bBWNt~F$-KWi*WLGbm#a2?pBw027$%-X4nF;mX#?%cqh-my-o)-k zL9!f&yD7}C?8mR=pVNKsTw1@M#lDp-t1)gBGLVI z1Q8zA6YjzMCcd>dpvOS;apEIlS@Bsy2;wzz+81P8ZB@s&*Wd)}frvC^(B^jD=#7C{ zE%2Vu~TSx7+K zhV6GHSM@QjsIozmI+37>eGQNvO^|@_*vBMUgWrBhgUHJ4R{9c+{AJI=BY3mWNt*K) zl)xM7ambc>nYc*Fny*eXC;3r-n~)&a5^EgpO{4KXoxd{Gn=c)I?`RGC?LtkHwmv>G zwxh;n3p2_CL1nM_ZldQ_Nz(0-NOsn?M`zDE@AJS&-mb9Q^>+s53f=INDRz~Kk}#oZ zy`My%5h;SoiH-yqI33jyjwy%&Xg6M0Bo5~-Kyb9dQ=Z*4w#3lxLuDUvmf%5GvVmhk zI~~kVR;Mf7wG|5#UxZU1k-BNOOTYrzpkfLA%KMpxwhE37K}0#_B3C6|wACMQJa6$g z>4CEj`Ncd`3sWh+Kt?8rmwAp0rKLa!l?+61qQDAB!{;~a@NLmX_dj>l&F*&oO3Mjs zJw`k4X8sUM2EgwI*Jp^7oYd)BQ%T-@n(tgrn#_Wm$vrQrW!(6HL z%aYIFZ~-#hbZqA})TU&w2uNeczoyv7T#+yTVs~-UeWE&h3$Z8~XeMuCix{BmV=)mKCUB z|7~S=gcqdoqf)%Q%)+{DQwJ`#BCe;czkyGA%L%Bv5LVGV(6~>!@`-J5goH_Cu+6g` z#x5p&{X`*rdxJ`Lx{sQ)k*`_so*(E?nUg@N9X5vZ@yIp1zWoz z&UgfT6}Kg%2bau&`gFX6j$$O$`JTbD+AZ_NTb7=(A8*hyXTSMu?DBW|_lrw{t!PP9 z2AQV0tp~NYvS$EJzYQ)U@bE={g3*qJWo`gzyp%t!?EuBEOP*K1mHxqq0N(Zr#*IKN z6<1dv*grSxM!DqYw|wVEq?V5q>j;SjRT`jx^zItjt?XV}xqTUmsTxR7Emu5N>J%C{ znf9zyQP<-qS!K)c5ikYJ9qf8LLHtTJ;Po~DstK8^nT zQ@qjjNh*)Xx%AEli!VUN2HNtTdLi4_AmnkhaB?U5-Qqs@UHmunkEt)z9tFr!|FF~p zzv5*{CJUbxDdNdC}Pab=mnwY)o~hk;s}5HbsHWGT+=b> ziE++>RS75hG`iHblX1BxO{L1;tk3TNI-tl7e#Y-sUU9i9Jci%f`e(@FvED~i#7FO5 zj3XQvls0(M?p(ZU&|;xqwZC`cW-7YzhSV3?>@Dk$T@MC1WPf~(X!=)@bVWo&%-G*e zV^L`ww9-%L^ygA%z9NM^*u2*J<7)x*Lyis9OiD$ijmh6U&C~y86T_$p0>d$KDptHr zkp9HM4Owc0P3dhrw1$8r??!I6C5evzzi%YH*n;G4Oh(%mKV>^!ix9Rb6<5$1>rHr5 zFxvkq{$gbVy^!?hF2K=DNTx25N5qtC%~+Pvz; z^5p$iqk|Ub1ucmDs6+M}0kJmFrF*@6_WN_C|F6Y-9)d^wn<~vVF=Mjc`?vn;S-$)Cdp+be zQyq1$>D8rh4}6;|`T>6Yk}*WF7aG0`EkNEs4!i8r;n)e}Cm!H^DfMY=Qf{Z-ys6V) z{el9J$Ub@Sl5aHOiJ!zQ({y~R#1JvBMm|X4*~wHN)SYrC60lPF-`T!#{-c&7RKrCX~ID1TvnVjJlI_)z3*SfaDj5Wo_Y{6!Glz4~_3sMD+rF4BP zFG&-{q~pxZwo>f`#Kr>A98PsxxelWcI$ByiykIf9{>AC<-@tEZ=0SDzG-$TRrG9Cs zs_?{o6tJ`|*-DbnG+%hbQtINCf#>uTyPO}8NWU&kyaQa<&_iC{J-e3ZeRXHu=OJdj zwm?aFCtjr*uv6`XI#SqlNDvch`^(Cu&C;sQL+DXTSw*#*nN+%_kp}SY;}PF;Nfq6v>QcUF`?+hUoGgbjCzbb8)8#MZ(s%>5NbO1nH&N9^ zjrvczf3<#bxc0>MNbp6KB2JjHPoZfjgYV37gdZp6~jw!qCmc0Yd=Ca>RsJuW60Jfr&%5# z5?IkXtuYA}zx(D*dU)^&@{t3b;_JHIZ)4$xq*E>n2SZYec~rt~F_I@O!k5rF`>Obo ztaj1)`QRHhhSmxT%k3M;2*zQqXoYv7oe2^$kPCdlbV-iGG*7g0ZuJV!yLA_+!4|w= zL^$Jnt$G?UGn>9{rIfHA$XdBUa35N+si9j#urEJK)tM(Z<+q0Gs z*!x7~u-9Hk-Y0xXC(61q1rrxP`5(?R@hxgdFzus=t)=?o<)jx*J5AeG_L z(*ec|7GZ=vD4qQd6m1D~Sa4me(9C*S<7q4n%m&Xup7|!%EFr_5xQ{h z7%z8LosaxP5~Wpyi{=Ap_ZB%*@y{u*z$jBfC<*vZ@A~%|1VNU=ssWaS@&ryU(~WPo zU$>mM1Oe?^o%9jW4nT&RSuf#w-PDFXG)T}*(3S5&0Hc4riRNE29mo=^xKZUiV&L`) zsVfVwfVJrd&Ptm?^n$HS+R=k3`eO(46A^DQu_@=9htC5yq-?=Zyya#6Czxzk~UcDG28rf z@s+e)vwDmroEj($WcqLYOy}o^#IwlYhw3DAG_a33FSlKQ!eF(wkn-7O8)$o|`p-CA z&f>Y@XkzWHaE8<+aRa>9&HBa<=oNl3=KLf+%E+%CyKfxi4_-^1EDyx9DkRNB{pK$L z$$Q9f{*?XSkdP zI1NFkmU9tLpI**aEhpdR_!et>tFJyD!EI%EPNF@^Bwgw9-ajHTFyDZDoaP$|BgMb1 z`Lr4XQk>ssYo3zuy8Qw!hAtQQkvfJi70*{DtSZ4C{F;=Ww|h9T6+9{6wE%>?N>)iQ zNi_kge?3LXKv=efD&jTDkf95GqK3a(Pw_x5_wwAHLhN`l|I0mi(Dn&hH z7*N)##x!G7ePV5FMtVcu>CZ`qk9H!3Izk(2eVUfCiUm4PIug!56xgoWz~a(nl=i) z>iee~>?xMD-Ybu1REIt}fgHaIv<(m(y?m!zfK9oM>=6XQcDaEMVHN|>|Hd2OU9*2X z4F&hT5O{)x_{ED&_(T17e-4(=^~ONpk4?+hJrU)Qs=PBv*n;K#a1ytS>oTWjhV=-D z5uDjinLf*dk)@Rf;%OPu7k|Ok>-?CZtS0ff#T9O0ptjG;L+FPVcS8m@9z%b!JN>VG zRcj9$nHUl3njQ2?v?54fih7|R`3HG>o68$7u-JR#EVxt!W{m#6&cplN_-9%eVZp%D zu^Mqp*IaDo*1bfvf+%4jxA#t^Dj&^JEE00@8cOu{tt1-euFN-RJ#ieq#2eR~lb^_^}9jtmc=$dZPxTA;l|oXFHW=akH;& zd)BsG>91W45+G6yy}@jcef6M;kmRe#A0tfdUokHMr2#&BgQm_@Ag0kwr`6T{#hAwsmp>8sOa}|wuBWMd*Pn! zoz=a@-UO9;3n}b)GUgkkxelMCm@b#h&0WO7g6KT*m&NQ(5)kV;>&wad_fFt19_XQr zZ);*xj8r4EeRo0UQ9=@OyX=WxSv_UDuVy)sFLmj>3HaZaH}h47Uju)O1c^v?$ColF z#x*Po=pIKTq1HzWsft0C+}wI~7`{h9rm(2?{f3nvh=rxX+}bA4du^z%;$`y^Zl%U& zMs;*y_(I7+V)Q|o33hgN+-K%MI-m3{maZB38EDxCiWN=bxx2VM%hUU& z9Gxu%X}ovQ&6JdBGnVz};;iyk?Dew4Uz7VwzC=YTM3MH8R&$GWR-?0=boG>rneYqN zl1C!HHL;*;mt~d2$jWXbrGAs^=Gzc_!!&Cz;Rn8>!9K~yMtdd1gy5hMw_u;w*v6Xk zDE4k!%(5wQq5khr%SOE;3uw~j`^Sr?4u&>EhpmEb{vWpy6s&dBen$1dJN9LMI>Gf9 zaN7uv=}V)t-h(4QU|3e9ni!LF=vSf(Nf z9pkp}dj=SGZ>>0;;^CF5(ICcTcm8&3UPb#sBNDeGC8tZj3QGU6@)5qgLP&pFR)Nb_ z1KUm8%bxMm?w-0_{NV}%@TU3O79=|EV8vC%M^kT$GxD&ET%J97g;Rc``V^Fp=AtU5 z$M)ia@^hEprCG(^U;CiF?9(*ZRx=vmsZN_Oe03aHW!wd&+1GBuR&&mT^mMLvg@@!9 z;_~tOps)lsyD2L{Exv31gp}K4`Ca9$xp;8okQBabjlzLWVY`I5P4veB=gt<6B{Ssd zNkXfls#2cMn*Zl0g@w${qk>Me0#-2d->axSk3g(8wv zON(MO9;dY0dQUaiO=`TVyaYcxW5jENl=3Bsfh|5z6IKKlOVmC z=?n~u%779P@rjw?0B9rn_f$8f_`u6+k4o-J%Z_1HB zC+8mqVdj4ZslK0opdy zvOM}hSG+MofQRVNh9w8=1at{AMTd7mX_em8!v6PG{ZGjI8IiLE+9V8Izas{)g*$aY zxWVY0EA-Z+6z~o4g15RCtY{46ulXI&*|}m75Y0tZ6X=ug;jV>!(CWwBqrL~{3Kfu+ zRWpSh0U~LckD&!d3yiYmw#j0YUrQI>p+jBb*f=7UWONCF4R|-opm5vLj>6U>Jk18m z%h(lX1MOr}Q(1d={mY%=%qtj?zk3X&;l5i2ka}N8{AwS}C8^;MP8svJ2%Du$bi|uB zdrn@e@-<+`{?)KFh(iMUZq^*}rU%ow6>9q@Cz1+Kb>|YH**g6YZ`$(P!~wVZ6T6Bx z-?1$zAC{<}etO@!PSmG`mSg*@L)52|emg+F#^+nWz!^oUn4ZM2UES1&K2?oeU&pV% z9`}m1GFuKSHO(Da%N)M9FLNN<52aY^x0vlKTyCzIY!JA2q$)c5`~a&lpjAopzG73#ecIeP~?k zA$^p}W2u0^XU@&kshOhJ*$14BYRAcuE}HMcE!p@X#7FUrD(vLg|AL{(|-bNBTy*fEbQ9r;W6P?^AcSLFExi4`qhbDN z^~2q9sNcsz{(nzIs3UJNGI<7ml`zNs)4t_wXftQV#q=nf);Y4+wRVP1Y}4HgkGA{B z4R=#UHrKp7ElA8W{oYbH+2cq_Vhz)lrLmnIy*I&ANdexd@}ie}FY~$bT%7eGrP%A< z2dc*+Xyc(Ig(p~2R^{RCBQr|d`YV=UTBTW+y1DOcw7>l33Qcaj4LBlJkqzpz8TJs^ zSEgvf>VxvLn0wl|B63OItdM2@b?1J^B4-aJ9M><~Ml;5v;2dOgTYN|KMytny&EV6K zEtH}8^f&*_&}`Tdi&e7Q3~t(E@)dm&xKDTUCIg@a2d~iQf|bSgYtsg21=!cgi2@Et z=X$8=DL6t}!6X?=Z7x^LdCtkVRhpx!=Y!>Ep#5!Pb9d?xT#|A0yLM`SQb1X6$*&eb zKBb_ZK=U+6a6A{-YPKF>EA={g(#VVQQj8ARtwH|YmdaDgI7w(rOY`~D?XRO~Lg6V%hxY7=DF&)I z>%@Aj#OvNF=DgA!Mi42nRFh^4ymwk{NH4-oatiUKY)f#SA!(Yee&Ws@Bu0dqTsEi| zhCCwh-)^m5ER7y4SX6;j*s6j~O_S~WaP=3ImoH!Ybd8Sp@Vt7LZLGrK6mQ881U?z! z0KYTHFU{JmZ?8Rqpc*8d++}$3l5iIVDj)^#&-|h~WI*VR6Bf?D^U+EEiRqGF4F(Nq zUh4SphHXP_OZO)>DX?ip-yayyW#O)tgNW1hL=J@hBiCf;4COXd8}3aKgm>q@5xa#$ zK7)tQ1iiFP3J&u6GK$xEgJ#KrN|{9bUI%`$yFRc$v%6K(H1S;SRkko7zg@AF?Lgx1 z$-8y+`*bM>EUZ!-I7fpTV4OZjy z8QN2~0ma+JqJo+2-Z)(Bs~rtBA{yr?1Nc{5ZxnqE-SsDSx=+@^0tNK7*v$;lM6{ZU zgo~0-Smc6G#>7X(t7Vxrwn_fb-vGbXVTTi3Lg1@Yx}`YdZhSILB5my5p}XhH#(MiY z+21;|bIM)PkJ#t5?sVfDw6EE-rQvci#cVj3MemEMDlVm_#a2_vC=2nhl89XI9R%Jc z9z1bRXr(HdaVqPOZOWUwF=?u3ZYkvb3f9zAlKX_VC{CWkt)YG2QKN75j^JJ8#u~P! znd$Pk2-*_n>~4fSg?tFocWZ*TRx>{-`$It`{=Qi+g_YRf=f`q`Mej>VcZZT)Z4iP!M5L%`(dC?ltf;`5g99l#&!$b9Bh2%8+|@+Tt;p*R??;IWxqUszF$;u`Tj zd|c6o;iK(kJoa8xexA+;n<^z;15O4r61kjx&W+u_!DDl(;coK{m_tG?<(HRF*Sg7% zIx0vBUXfhZp}Icp9-~=1X;r z<*&dBhb8u_GQ@<$;@;Q#x%qGL;LxF#+jg$WbEwJNbyKq2J?zGbITy@1-KN3gn2~i{ zqcHv*ygp7L)0+Ia^qTY)d5Lvo8qv>nzr)6%-zugJm@K#qWnw!$`LyyqZL@KgL{@7b z^czpFSIfBE>R8&fS_jTI4ge>bt)LQ?yWVco;i6sr$n=h!q z90q0!^tmD;eg6!%@93bfTzB=O6B;&DWw#DC@?KB$+aY~$)Gb8%n!4wQ4U}q-#jB0( z+Aaj|l%S?&vSn%I(|eF7Ya~C`_CWYRX@{9dItTbteMolV9{!& z@b95!JewmFDT0DAfJ>@NuTk2kzgF^G?9OK~HRW=Q-VlU?t$YDa_4y7B)wT07HxFz+ zLQXauob7V6mli$9NF(MWiK;IM$DcZ{JWSCa5^WmV@3OpEMv={?Y^dWc>va#lRYAnD zz0;ko0Qnt{T356^DG;gZUrS*F5zVhK0G~kC29tWX&?&Jrz6)1$b+y$ms}%o21bGrk ztEy-Z+{`bI<#$2bTKvipkQtd`D(T^;>A^;~xOGRMo}m1Yj)Y}l}+RZ3NAnI?(?Rm z0MEW@(!4~UI#;^kjz4TtgO#F5Q*-P0Dq3zJjjWZ9Gs7IdmsMOo)zla%GR@*TwsqJC_7sG}i{!WFyhpm0l^`I^P zRY+vJ|9H!PX~B3}Pzw=q!;{ z_dBW?+%m5;avElOG>9DSBcxN4{!6Fc z=-4mi`~l^@0}!@ryH~)^%O~;?%MlVUNFZ-?F?f ztlB3?G~hYpkbsfOp#4IdKJeLSV)q@C&T(7}4E*sO9+9Wh?vrQ^CLtTRy%JKOA2FgX z!&Y1Pp?3ROBI=@ZM17OfGyNq(ad3az7AxZ<2g?l{*uQXB;CcAEbL~~5v9b#U)`4%Q zBg74qsLwRYr-#z^Lb06kwp&q!2ekWK_)=tp;wEcvkUmS2Du=jt!MC;02D1fG*z`C~TSt+$8iW*{H?AXE&*hdCbtqez z+LpdiPBX8Fa~^GoXFEz-8fgZSwt4hk*Jhb;4B$?kzP7S3?S7KPx$b?99!%6lMK$gJ zv2nHBN|JMFOIega874SV3rU0J`q*=Z7DIyvN?*O(Z%9q=HA(GV>WAc<$()#Kw?gxl zw@9RQJZ8zk9LajG9mUqQEomGnR0vKVvUO^5%%Sc1I*8gbNaj-5=<#CgAl}ng+WweK zhQ861bL81Uf@9Ej*sa~g(9vKHUyh{j2(}UpT@BrNa4PW{{s@GySm3;LgT?F z>g~79PxYx%l-_BW!r7QMR#A zjmT>#Feb#Fq_?i_l=Voa}G%A(j{W%s;-~yeP4T^@nGNt#ixTGS0>~pES@E-cf zj;Khmj1Une%0^dl0(Lu)IFe{P{Gg_Oqw&2v(|)&g9~}ppx!$MF(93f4gT_*nO}@v{)0#`Ey{!<4 zJj*CKHV$pdQG#4+JKh=;W_Md0bL9L9juOSeuElYR9SfWzuJyQ)F%s(ow&Yee0F}kI z{AAzfHdtS2f@G{-b;8*9zf_6q#N5_b&7H9MuGwBTWxx)it6d_OABiE2(cT zHw3$)uEAg#u;m1KhqCz-& zf)+Nl?p_S3Bo}Ggzb=(7crNxB->dC_h5yPN_`PJs7@8tXm)Oi=xCLAnu5{J+z~yI; zwbSC@_M66Ux0aw*5czW^({l;o`Kh21@+iv60YAVc8MjfjzF)p<8SBdwTXP%ODS_wm zKYS?R&P9ca4O`cF2BN^1(Sg=3 z@Jx99zJExRaC)+)kvA5T`u{vm6H*_qG=3eIW$YJ6)o^zhh`~C~}eZ{`k-s`*Eci*jbiO{l1@|RHwKBcq@Lip)Z zyfN?DqQRO?436U(n?LsuR1jYrQS^T+b2Po^eSh*|c+CVw;0F~q9w$J?6{gcaQ145rf?EXVA@Nt{>wlgl)&g4cOi=P==!*h4Mso`FgcjK zIxD@;@1Aty#OyA2{$WTNRlR8&M#vEw!Wmq(lKyUKdb}sJ|4bTw2WRfl^`sa6NIk=8 z;g2VYOFZcx+mk)02Z8h5hPxW2R&6m44`K+^tLG;kJ=Db1ZO5GIKe%iZP4=}=oDA+~ z44wpkht=D{0Zp_C<5f*z$~mDjTZ}qCJiI^OG^b8eI{cM2e)X@nq*8)De3_%; zjzq@JQWMw$Tz$idr*Z+s`0VkDP2%d@PehAYMafg_f=@W>7CcVPOL3yT{y~MavT`9; z^S8M&6aLD1A2!JR(j7=AA15&G1nt)**c@l>dOm1U_SFdCVLRjXSye!#Tda|m@|<=1 zlLTGkGY)e-{GxSTnn*9@hDUm()3&91>Xy$hi49r|V_bH!5wl@XymVIz36dMaVy!I# z@SW??NJe~a*9RZ`u$deg+9M&DRv=ig{mZ95A+fGu;k!w7z(2r!2#Co+sT)-p1J1ab z1cudEbJ+_N+6K?)jyY{Y<1LM=V~ZfiI;H3?B#wSSR?KDc57!o2#)8g8Is$AJAfCkzp&;mTaf!8G>)tkvLXxNH0AcAGy85Nl%r>oP`%Z z%R^B?Kq>1nDI_(Q#O5|`Zt(m1?zn?4`f7EvUo2<}6+orEht5V8$FAmK-VM+oL0I7} zH|N14G-djEX}o+EwZA9_QX5WqfO{%Gv^}o81;Bq~ z4zQumvWe;9DJgAVhI)P8r>0es2};>g?tWpRFr=c_?hc@YNnp}dU7DTK@{oIu zGq24D2GO`=QUj>qWiB5mE4oImGxCb$9v9`>G~IM!k=XtL&4zq{GOxX-Ok@cI4kWg3 z-6VEe$(%Hn-a^iIO_p2e%^lJ0p7lu7*3rqgY4rO&bGP_ak8!Ba&cmCf3`lL*Rn`O3 z88ARO}!BiRACW#79eVE31V^@5K0Tj?>X?kq?HI*lmn#3I8RM&Sn!%WVjCL zDRlcn&V)BehH(KbFR0v5u_SVwHGWrYy)ylqUcZ+g$;GzLSQ_?MTGFSQOKNgYcdtFv zx`8toF&j5;OlXv({oKn#ME0_J(5r`x?UB&{e6A^jq4AS{%oRiPk>QKWy3y2;&BS*E zT93V4HoxQPOYP9?N&4E0HuU;PnlrUkNxo~=1#C2pKmAiay+`JFS+}Ko97b4XE=F;_rfgY?Zs!*&0s7pUYGEeJSyLH+7E=sq&tG$!hS5{%As~kzNEO~PD)5F z5|3VTlp?0#mZMw`jbAUIe^Z*8g5_K|&P?-@#pY3}x%ipP?sM{M*1WwpQUbPMlFSdQ!nn- z3?%6kNys9@?BcbhH1r02r|H)NOJyY`?-f+bOYm_&V=pIo_>pDo>TcJD?;i|*0+RYTL?Id|v<3mtU^&QOf-5c3v%L-GQ}a5p z-xdIt$=(WFCpPQKrEtDd0gDWd+&&?rt){R%FM#-I^c`)!yw_s*TVQ7!NW2%tl`F6H zS4Teg{16ktkrNJtdDYDt*2RDybXkmMf(3MjqHKRFyrqZ^{*i1WEr;E-2#(?(P}uTo z*I?ZN_M|A_Y5ji6C1fZr1aO7JM``y)+Cg$BAlXlBxI!)}rV1l@93wR4A&E}ct{|&T zHX82ID$A&@5IXt&*b7$LBq^+;XqW~%KppsmZ!%gTjeLb4FR)-LTAukvw+Kk$w43WW z;;h%tlMUSj-dlubAa`?n;Ws(dr`4M_s@Kblu_~wcK-lZLF1+A@Ia5bm6Q7o^&bDKf zTd53u2zLY*Qp#;=C~g|c{ZzNlhI7B}qYHX~8wAKWKc(K5`#~kWPnvCXrwz}j1epwg zFR+8@;A5vf=z?rjQ~_=7;1Id#V>i?j_+F~u0{!DPik8;U7L2$^+mC8hpt-*%Zh)|? zo#hnTASvg;nXYN4yEw0j2yiAJ)j(9}-MPwC$*u89Y4-gMMo5MK%sf~<5=5eiX_FX* z{m}3Jve;8i@`xNpE5-dg=u96Kp|+I(HW6;RvU(zahW>mVeZ__5OK7$tO^Q-9j*wpr zr(m4Pem$bp2ovx~^rqOVv@*x23j9OyLvswpIy=1l7wQBJZn%s@$%d~7x6(#LE<9{t zZ?=VMKCeWz#|b~sPpIVteb6D1ND8I!@ArE8f4?nnhn$W3>ywXy2;D3$mFzxNZQOyh z3v0_}*N7MTti$Jr-3pI5+7S`VVdx*Ttc)Cl5B~PiSXnB>K_g|R=*@eioZ`(EW*_i> z^9f&ih{^rK+wslVp>a6^vTDG{-#2_Y_nLqP45Pxs$`>cHjIm@ws5vi0rd*P7Jdv+~zn|a_7{7rH>U@CgaSnKRX z#woTc>2R!s9!GZ^4OY{v!9q(=nR)tz@XpF;Akb&QJV^iZ8>_RPzh^+o%Uk;1`OcW) z3&R;R5@eTkJV-0mnQqXL4wK;h~nhT zkCS&@CqKC?br^BLiLJM$7EYWaDy87#`dp_? zOpO9t`X*bN^1+-b-qhASxyiv5%98P{B3s6rBg-H3M1#xo!A={$?wMVxnZPU?d+Z;B zrNenl*dbq7srjT&vKYwxcXgO|m+!@|7eH5mJ&Xl~67y!7#mjqS?`48XD)up|Tjzh2@h&kq3v^#3s!-OQlq z{!6R3>$d_y|1~zY!y`q|N=)@pyKtGq7w@%};lN*znsGA?1Lk#jn=b^2v&ul(!bCst z>gMU|=(LX>OY;y)bLy7CHcfFX}~-jbv8%96CBJn55UJSfT)8_ zcIaMP-B0`-A0}@lbdE$Q#cw+>o<&*MTwaG9g{-O&zrEbeF|Zu08hGTpp{6?g%s_O6 zeenI&>S-o&i&yHNFSriX3>_tS=+$1;Zgstme)mwyHQDZYA)BmGJG7LF@9#5<56Ls> z*=-@<(DrBSz-0X|Y66>%v%u@LP&xab-Uq;s{Nz6lBPPfUlJn0@E-O}NY;+&s5^@j| z1-?`C_L^51Y*zsuU;cdwoKT1;@kw<#CV!>c2UVdhmhh{lU-ee!{j(G}pO=u)uIzT7 z+9gYFNLDi-P1E1FkNwL&J}lV&)fDiu;XAq>Lg`u$+RPXDiino~OAui3LG(SnnK_kP zq{;*^&Hm9{)`ODM4#S#Hs&GU&!H$36r7_8cDd_ax%?Y}Et&wek0vR$=xnIdV4hX{~*jljVY4{=4qtZE|S_ zA*zqYd{)Z822a`O>@8gSX*~K2B{%lx&YR21l81E0yYxpd}owua)g!r&f)_l9C2R! z3AaCF5jX91-}51>|(x1oH*bVo{_;#u;RThM>!f?cSQe<-(;n6kGZC<}>E2n#tn?QUdhaytozjcKlv>K-9VGv26ATwy&b4leTyZk_2e&36ly^G%=bD8 zJpLV9FOSbe7?K%`*YZoU}EWCz_Xf{YgjGFNyW`1u7nR4#9+l`t8aS;2_?l|wOp~4kM$;7%EKzz|8=#20WWpyC&Z9|30R2`n_+W8w5X##>DpF`7W;o?BlE)`< zlSDd&!iLzmfRo~TfU3pR+QXoEywXU1Ws;yqk;`?G#h3NJB#=UvzxCY<|J-5(hl)q1 zY5X1^sfK~;;RioJ?wube{|ki0(7iMy&~%t-YS%2#^YZ#%AbDr^5+kF05L# z!w8fVYZ|D%56_v6;EK(fmr{9=ewYyx_I({^=f-Xv1WNH{Y0&6K6X+FcClGY9 z0$lZTx151Zqu;Sw?|PF-sr*7mLwD{njNWc1UZm#R@mTV~>HB-d$`$~>HIfonS|24l zXlzsTqrPQ5jONwIiWGnLN_U{*!{3~j+wOmTy*?1j`7l`Va2nF70~|4fQzzTv(l!)0 zpSW>UcOp~p*qwm)E!1+J;iqp_Z0PIqe5QO$j=raN3p)F*cozb4wjP-*9X`hnQ7O{4 zBhAxWApXc-74C`+`hB?SJLrC6@hrzqUa}`yjpAK!0BiMCj%!w>%7PG%54j{*;(3m* zdEF|6mN8;JM&Ehma+&V5Q4}7T9!=M&_%SEcoCyBd%-(AYXwFV?>`;0u5x1BTzp1l5 zW<}SP{Ock_dDg)JhEm6gBd+dyV*oRm_adq~_urH1{B1goJ?KF}kNj#d=Q0Jy{wxxC zyNx`%p8=)uf*6lK+DKkA)QLsj`7SJ?Dw}FQWfg7^?vnvrXnhYrWLk^kmDqb+xnZ(j z-f6WdsOvsP=k!WRy~mcKa_p?=HfxZ~D(KweONREBK5;d(e+b7`8Cg0aly*>TzZ>(d zX7+VHg`Qci>2peMV3}|JV8JHj-_9OW%5PR^0c~=phh#+FpSF_-cHKU;yjpU5D%g;= zD7BZ_X%Re`i5)W5Mg}aJU-&(VNOzJ>9%SB_y=snGqE`ld_MC8F%kCAcdU< z7O$2W*O+1H=A*4bjmpIxKgo}$8Ytv)`|G(jG{C;fOmN^filZb^jvM&#^Xr$X%;L)H z4B_I;=v?_foX8irt=~Qrk!Ko}NJ4=Ful5te;ye%>>Ib5P$yxwc6_vsoQVBNf-f<8a zQu($2=HHV%-!BP&*<0$rcaOKN2!4_gfjh~c%zwsxG(P(?$&PJ(%*S+bNc4K7M9`}c za||;3u<%dzxydmR>clUt@a~pe z(#*K0jlu@O!+LnS?5emMdB3Oh%;oB1mnJeoetycLp~v0nfl06X17k5Da}QR4AYbxZ zuetJPwil0!0m~7l>U(Zx$J&nu4r`voEZ>fM;9zMM{K)kx9Z>?-K0A*v<#JuR8gI$^ z^&&0QBk%IL|xU~DO?X8xy`?tRUep(t|KE>x&H%49SsZy zDjlSKd0#rH^jg2D-5Rq)3o*)hvd>iPsy|U1&>bCuYgzsHK@!VS4~1XfkAz0 z9My8?ODS&^_twEqY&NaLn_9`}fwNkI;e*?b-9a6HrTtXDu&k(_yrTa`b^h)_f5Dh3 zhDiSTTqD)2-@Gx-JHfc+e{T^y;k!1Bl*+Fn+kVjO_zO`iV z;KRc@DUoV-b+t8Uy_Xm@Fw!+T`NCYe^~;>V;6NsRQ1(#P_|>;-yronR@5f)gZs^9;_?$UZ0oN1;AXUbxwaVONV})uOl?9(GVsFM_q{CJu;ZtmY}oGi((#t`61tR@LcK0My*T z9;QYgq=ly%)!$AjI>{VRlk18KT_+DJL5)5J?8%ANs}2rTH6_T*FXWV!It)B?SiL;> zLlr(TR`vqG(dgOgPzmn^1lnEo7I(o!tLs=XCfrX^h8!#hKtG5N&~TyKRlT?7MYA=9 zYppO#gKHMj9weIkw4l>%79@2`P|7mKa@80!xFM5!b|vnXHh|SxrsYHqOD=3DqP{;> zwOwCX^|jD*ac%F88`PvO8GmblJaM%}-+UQ``;F(!Ts34`7wVS}XroZBsK9di4Y&=L3qZv@VsEE>2*=_o(a4j%IVeQ!$TJK1q za-D0&A>X-{^H0UrWr>4KKO_PnCOlXL!CRn^*HCRn z+su;0!RDW4dW2a9CX2ds&vtP?b-f{I3?-$wZ9N)A-DwYXdC}Rh$Af-YXsWK+U|S!{ z5?@9z@908G9^6$>VGg0F-JJcSm({WFSt0r^Lb8eBO%3S+T>=MO-%x{reTO_G4)j88 zeNK}c!AyiU2XU0$f=V}Z%JRyXjz>|Txo`1ymb7yQ4!PFbABpnA7SAl@hBS+`R}IVy zAiZP>(7=UA3&niPSnrklvQn89uZ<^vb^#%${fQ|Z>1!-LMmZ1{TfMh!d^VF%dvG}i$pa{J~Q20D;UgLx8Q+s)A zDWM5!(XjLj$!*?vWrBNppinn=xj81*QxFsDsqin>Q})~A5P3?kqSNNKDc>p1{dzsm zTzMBtqcNblb=-5lR66$~%c3pJNF+dH%L>QR?e0_yg*UHvN58}ynkGf%@D-RnqJ{9C zJmy131aB8tp`UAg;6E{LLq0)7Hq+b{`|;8sahWkF<Ri%su4^J2c zBwHx<9fXfWe>*i&l5yADmuP2qeSkK(Q{3dg*n3y(h-tBqX2iah=5(d;Z3LOOB-dcV zgw2yAFadoR#s3`mq9n@rSs$)Te*&YlO{ld5CO8#?2*u>1N<75h3p^1o`yR)rEr-2# z;%FO2kD)fQG>kEwLQZ8E?D8WVyPLH>L<20xP9OGC(z_=bzC)Nek3>oL=BK-H87LG zFOk}P((HBLeO9-hMO38!@Sww&gB`rr$Zf$-msM6Bsy)6#?lf(alWKMx zc?*NR{_XSTDChkO@VtQg78#w-GuXCfVQb}`us4M zr*35Mso8OLWZdX;J-x{Gt@CBcPb*tl4GYN@&{P82MB+p!^}%*ul=lcjdChb?)5x{< z{7XQ8N*}#NPgB$6cm(}tE`Nf#kNZbt^uw%qXVX>{6jY3jn`ZSgjam3kLKaL!h?mu; z?ao)rr$)5$-bokHN!CO34_*+~T0n;a8{%z9rOuca9*(1KP9rnk?cQ7vQElccfXs_4 z8Awe+GZb*%lAx0fyDdjnuLxF$lv5ZRzrv}`)nqIFJRjs4Qh3O?FNvBEmI__IOAOwR zAQ{$X!xGB*y63A7zM7ezcUS6RFNfpMGY{x7`#qIi0)yzykUSP{q(Sk*F@YRR_`%nq zB4Ougby7?H?vv#D{-0H^Sj_Q^o(_rb;9PK+QW7A3^|`Wrw`jPaxZtppSSGzqZ>#~L zBjk!$4?oCYRS6d(MyLkjo(s#1-2nz_Y!IP(U;>VD0>KyiG_4ant4-`YqVGGqNNCG) zq1wzbft`L+?ubya>+cc4>0qKrl^y`elE{(^{`Yn=e1C(&^%2l3CqK!7?~V6Wx4PRN zF`oHiBbxn8=l)b0L?L0sIwTT_U{B>AC({x_MG}NsJvk-B7n8h%t-4!o?6h^ z4PwFMk*vk)10hY0-ao9qX-|5nd8gK`76O}(@PlRym}MpmJzo$V&g;S1%`nxSU_>Q>U-fs@z4va-hCo!w>g6g-pfRrEr#5nCui8az5Vni z{i~C)f&|YbTgK$Z4@f&C7p(go4nEqqp?9zQFNtVE9`*J^+jkK2-if;)^7zU7OZF>|Gdv zjj{8kwFa<06twoxAdWi$*H_HkfRetQLsQB|u)T|8s{K)x$HvT~uHPE3aoiDK2O?zB z?uwMLw7fa@k~c4tLvUN(J)U0GLW03#cjEAG#Eh2|+lfLT6~e%M=0BcUn;qmbSQqDZ z*fv;Nmh#>7yHWWz$-x7ObZ`V)02^memxR4+oe8``yN%&MJur>=;Jtmug)+KWAgWsU z@ssp(=5ygHYP9Y`8t$_T2*5mM!-BM$F|fNd6d&2=dI1dorru1RRrYkMbOaVbEyDR( zb(!*jdc49O^kARj;-_Ti2~?Y{VP97zH#;=03$zGasLz!@NV0W1E4R_?)mwece+Ir< zNMA5s<>#jyJ9_4s3kBS3%WC$ASO4b2NZD0Eq=7$v_s7LsBD(uNfYKh*e4vs`Y8)-pa9&zU%ebNGOE*J*187j6F;_!6vim2uoqmtfhf8c z0*sO~XIEmK+wC>-)V^Q=q%l4-EiwaUv6t&#ni#8(K1m;!sCW82Mc?wcWp{7>-Ypfj zH5|V6L|E}&2yqa~$lG2B2`4!3*^8Xi=KU&r#b}xg65)^FMC*#DRO78}n5zmJwyV9s z3EaQ7P+FVftbds`D(3wpEQjcBFpKlJRq6DeoKSYDH}B*lj}E2DL)Uzu3e+R&ncIhQ zX1AqS79qFejhApO`XsCyV@CYG--K=z`)Fk*3a>dw6s#5X9$TSjw21hdkTf=Nbdfh| z`ZI80krXE`Og`s(2`!xZIp#kmRli0RA-Jm4FX+n?lu@j5SO*Ik;n0`cL^Hj&8c}o@ zLpBMT0|0Hn*rC6v=N7cqVS+S1IK&_^$jA|(4t^yB7!MP&O>IUo%)`M|cT<@us;i?w z>g)U7gTW`S3^bqL|68w4R zJMT7f)f0LMl}5CMtOfAj-(4$b9a23=`>HD2^_=5!QC^mXhpB^w{k6&{OIoyxA-oK| z_Fz`%GU)yDd^uKh51Q^SwkkyB?x+Q!_dx~*?)hz<1kDIyuZ0H{6(_s*^fhA=rBBN+ zb?m=HNOHZB@x82n1B?snBIvZcc@Xy5ypj=!ebxav?!ht`MxxbzXp02Tt(z_rk#D)o z0Y^{C2cICmg>E=Ndc2;f`vy~EE3M3Y5PnC`TbF4wl7|C<;Bc!H9lHOIaE*CKXOnx} z3)G!7E?3oOvR#-~ZKa!*W8_Sn7FG*fo9mDy_ zf}bO>q%xNosM?(jc7UOL8UVZ>7sG>;C7-HJx=H~5iI%&FhD#Wq6oYr@62w=zAF zOBm%k|4%}-_)J9n^nyq1n+9MWp?8_+^Z*8my!%f==N|;Fb#@g9cmk`U1FTj7n@OV! z4UbOGktnPw7Ku{HUwaP4&(~|75x(bY*6Q&ie%#Z$5A%&XBw6GCzD`h4`euOFQ%TsW zq$hmpMlF30!1T2TH1sAJ$oF;FU4$ zTv|lX&GD1o!^;4tXYR_S<%05zt?8ny>`Ff7FBu@GB@@Z-v0pS}DKmZQn0BOxN1)Tf zIYG(9irPao2d^g~D^GV;qFG`Lc(WO51lwr-s}IYgU_$h;YSoV_HAH4^EaK%6phdwLBS0vIX!Tz)|D!_~1<`g9L5 zqrLR1=ttE3sKBQuLcoA&=6fFMD3b4;hDWYUBoWtFb_IZ!)-?5e2GNTG?k=EuachDd z3Rb>x3$ZtyAM`~oz)wH4%7XqZrg-vr4BgQz^p7-7=%VoS{?&w&z@ML+llb3FV5ah7 zkfZ%Yn9OXoR!Ja^;ZhcLf&3wiocy(i_udQN>FCQ1=4VtQbz|AS#~|aU-(`V5s7emjkB9=mcNyyj2s(LchlDQ_E*$!%Y&R{+Dgdq?rHRvr8Bi@LJ| zihTd=&}H+!M77FS%F&c)d3H|Z3}}PA@<vzexV16|G-N$#+uR zJ()v4!K66`b=vfHL;nbMoN-B@pvRyjnIMAh$nWzP+JzoWm2h>h)dRpLHwNa_Yw zzMpbzL4M-;KWL|S4Dvfm-&Ka>R4pnlHU8*T76>!#PQ}JY*@FqB_Xun=0acfGFrw>G z>4gUer-?rBTaA`<5v^VWlkSbp=J!#VdaluVQi`Nm;HO^mU>h>g{wy z%@81y@~Sqz_#cZ&<5AZaOmlHqIDZ*~sxdxzhK}{c@Z=r>b`fD!d|xKyvCka5-uidq z42ST3{{G2)>QlTvrMh29w%3n%-ftcXgZlengNb<=Q$Szf_iD)6bHW!YcT&7(GIbWpC2JFjyR=A zM3-(tSa2U0nzlzh+fojcC)aPwA`~H}yV3g2G2+qfRe-S{3N1x|B$x9+*TyFHll>9D z&ut{Lwo~VSE3Ds552Lj?k)gQswRYg30n}NW9`FqmXpU-ob^s$3H0sCzl`7s%TA!X@ z1G}kfCwx0d;1V3yJm+<1fQl$`!Otwc3quLR?YavTX@e^N{-GcNwJ4g$|34hg1wa;! zi{iZWDc47k12Pq<&UXl$U;iip`-GXxk8nq$*aa*O{eJd}RfG+-zW|p#X5AHB$V?i> zX5|>BqOCW>*6m9^^w&5NGf-vOl$qA1`M;0;=YJnvo)+mSCMAIRZOsR~7+@u4wMdVm z9lkt)xz_2p>xtmc0}doV>5d7sJx0x~syPf5dF-9F&~8uLdd0|8F|>yC<7oagc1WLD z)K*Bb<9=yL+b&ZZqGcPrzg|kZQ9a&J|0_~Z5`A4AxnX+G( z%Q;;oDsTjuEN)rA$E|(v{c`F_{Jce-{~qgm{>pl3O2T%|RKe6(`LYHW=;bx&#G0#==Cksx^G@G1#p0>bH^AZfWj9RcJRT@Hj5TUkccWIH8ZLZ!@vA@$-7dG zK>AV7k1!h1AA}ypSuB!$MVP^4Mh6*ongg1GMENa1b=jI-a3BhG2G8DYn=<4Ol=DZG zVUp#=zE&30eG3NHr`K+3Ve1Z}ZobG{hJKqE&&=n>#Kl?)OoiTag5EOMVyMPkWLp}+ z2hm8bz#3pBlfkhzmufD+q$TX$3e;DC$^R)s_K)jld1eSb_Eb${h&kEZs{Y#>&6S)H zAiYJJ*%sx|$W6NaAjmyF@PBdD{RJw_TUo(|$K)*-oL;Wm$=M(>@J1O{iF_mG(#spq1-uki9cK^og6INmVLs>&%p2X9du;_QgnBA z{B@77P>_oRf4&4BqqLvEVdO?#>Btc~cZ7rU9$1DG>P~ofpLq@nya42b6|_xJL!QlH zN<#dSP&4!p@$CWjyuBfXDRaiZRNpth(5%M*ftMJ9Au#*)zYIf@U4Vs6`dyRV5lA3T z<`>#VZ%`CHb@(xe?YIg}@V+FmCmfC9Aj1>BJc$*`waYd5~j%|%u zsY1HE|CjLn2SIJCmN<;asq1Yy*D%3Y&;yBt3rpXQ6pLfX)w$fP^~g;_Tnb(Mk*I8z z$es==2jaO98hEJl2c6z&S>s!6I@-I~sZAW}Q<1z2oO8a%te7;X8em$~YlEU_!E1>h z55&GtmdP3)3mIQ-E?C6ug~PzkUq;kVo2Rfjl6xdhWT1<3axQv8j&zT7ySRkAF1j-j z;T5F>+#znUuiwJhv9LX`%hX$O>`X-9uWjLjbV+XX@E)4|R0Vo?*DIx>WGuX)3OYER zRS-&e@gZ`OJh3(s5KN(9o8wZ`;PxT-O;yF4r8Es~;1xddVpnVN9}D2fr8)YAx&Hx} zUF}rD`i}B1BsM1zvX*vGiJ+Dr23VnrpQ_%?H~a2G%-_+o;8#!Zj6Xng2&keyB(FA$ z692|Bd3OxF8h!W^R=y90Rbbo#GymHzuODG%uiK?jSD;LgH7n!72uf?_G|_%k>`z1{Qv}+6 zSp(F4loH3Ay!cj#qRMLONa1tZ(EC`VHy!Ogvq}UG6*sAS`qS&kr`cZDb{~ZeKWEfG zBUNfA$8#^weY%{}O`4#?>j!;UCkJs9zjrb~Z@rM6x$^iaqxX3$lQWr(;5Fv478*aG z?4iR-kL)EA4S*Zh^#*?w|Ks#C6`s6lncqauei#zwa||MQW)2UhJrY3)pZ;na)dG$> zjf#ynF1=JKKmpiOMI+jb6?Tk-){<@qnxgUJDGH(FOVw%H$$NauYn036N(VJ@y zx7Pli@qT4x1k^ka8Z+ek@WSH)lq@O_zsL_W z_|ZuEzNt&x*3$x?94?^UC(uV0Ley*6B&79)U}Nm0he+7CTyMTV{K-9BD`4TMs`I0v znNJ}yU|UXH9o^y-TiI#*>S;~=KJfN5;N&w9F=yW0v_o%Z^~1US&OrQB7AA%uzeIbF zuln>hWGsRJ%#N7ohQ?9yY|MgW^ogv(>P&87`88MH^Q((z_3l%= z?gIguKq!{@cmjlzq3mo^V7i zDDk|`%LsjcVZCzXx{!10@LmItEsmn=FATj>A7#>0Syg#WUzxF%iTsqbO_t zHA6 zHgNUlqHW#bP;vX%5A}+7b^>)}rNS6YO5qom-Ha1UKuw$^x^;2my=XC6OEQ>M|u!h+=X9gb)(FodBGTFW;n*U7bGPd)KzBoAiUg zu>H30d1B%XJ~u^OCVB63N|{2H>DZ`^*i*J|{{b4p2OaB!6q6PQcDXR+&``TH&sN z-lAb#n&byN!Icf^Ke8eke8J{>!AoR63j z-~AXlS+&{Cg3J|q;OJK^GPs&QcSnf0RGEooYi}uPTD#U$=(c=RcyNUY?YkB<@3ZPc z(ss2Ak*IchQ4zlVv%HF!2l0ssz3kH3eU0vJs&2n?wFj$1q_3adU*7~f$^5;}3n`sz zTGK|(CSNapHK?Obkndv)CUV@OXuKb2n*1W=Jl3+?7>tB3#|p{2S_4>Z{&W!wbu%U+ zl83e#Kw?#d7eZHfi96NU#~1*j01AGofW8g+PQH|SnW!JJ?oNYw zz++xPsDD^Yj1Y#Y$e+Son~unT@znpYteB_(!LC51SK9ADjzOPElCFG^t6$}r3r#CS zGq>P8f|HlbVe|yg`5t>G9_M!6sBj-bioZV#hYOTpl@9%}My^e0XC_BoMTjJ8M#sI; z$~jO1n?@n{5vMH(`n%$7b{ga>I9Kqax>TxU%@`9Qj70>1so~MT{*hz@T2aG$0FXhB zF`WQsp@DlS^yQps!_=RRtT>{e!VIZ2d_vR2#Qt@tL1OUCG02rTAc1qv&YT3PSHp~8 zmNAyQ2u}B-efh~G>I$kPI#8(evEbv@R078wPap|WVQ}-&+rQiY#{y8UeDZSLb zo8^&Rk7g9ED}yRZwR2GN?F+221?kYKwJ;;_ z8T31PJJnX^)XArhGJ$R02vQUlc_Xaso0=Ic2pZp{iF_zgefY{kT;`gg9OYBFXi?R0 z7;ceW+1a|NNLYlQn%oJ`xLFr1F+$%d5e^hRu%x7ck8CnPjYy-I;i2kEyH# z{^|<$Q3oT5Sfgts$nlgr;TX4uww!b0pD}}2$WJ+&145Majy6_M0#ssXyjOW#Hv7>9 z;am3=>*Iy)CSq3f#BkyR^g&-f!yP&#Fyu4I!xXDmy==ls@KZ8B!FabYG+?7g7Of@X z7$DE3RBrZS+J9(P8$OUI!LU(*D;ut!;)@*%l(go+CHN%+*NXgVt=UuehCgUGNxm=_ zLwPt5P2`omIK;D-$4F|4pX|R)jEe2a;?elYAT8MZlR9qx-ZyglKtPV;NGF5#sJk(l z(*@s~g}DUaAw>@Hjm5Hy#InntD>P`t;kd;PTw}o+WYlNt+HnPJVziJ`dvC57ReT;WM%DA`evc9%l0=6Uw*g79P4!0)PMgZ^nEV{7K zI!@Z2Jj@$bWL z>>e=rfmTN&27OM=n(kVN8PTWhCy%8zBA+&f2YI)dIrr<&RXdb|*%+jhf+|30so7a! zzsV4edyH#3l>l^HpVaipLeV_3oo2d zyz4PRo8;PE)4yv_II-^zqpsVaoXtQJxo+`0ftK4xoZ~T=-4CJpjMO}G-7S_D#(Vlw z<;=Yjg1jN*ueEi=?51{rbtFz8mX4+=159Z4&qTkNzTRQ=xpdjS-6K7jOvC+HlIFh2 z=-m1J&BuLsBpgZu?uj+@xShSXgRODaK_w*U{s_8Xk{Ai=ch_>!)+)Ih}zE;QV z7*P_J|9AIyv(KF9p(-m-KfWo`FG&c_ynHBZedgGM`nE1CZxC?*RQ`ki(3G%RAF?fzP^$pF)Kp-W}EOw09I3_lYDhH9L3BiwYwsPqhH$V=rOCBMg=lkb8u0@BsKP1!}ROT$6 z)q{t=I>*dWsR2HyKfY-0J3o4`j0<<`F)ZEOp%}96SSMfJhEUt?YAV4sP-k^cz3~;T zFtYM{EyvkJ5UKj9n|zIU+t!0R=;kf*9_S|8vR`btZ;*His=WkU0zgXExn%MqAZS1%)yo-=a5RY#a;Fy{@%TgE2LvFt{7!Di&mz%^gp?-Q45)o8E8Z_iEPqO3C+5hrj^gDxcHTu~o{SyTVdx)r zp-_Zx#G> zXG{yds@jN%DK}nsfEOtuESCAoAJ#P(noO<1*U?Vf$eSfo*cMShmt!-<_%rB;&vBL7 z0#on2E?8pWHugGV^UtD;Ylri^_07G3#Uh)|S7!+9?{$N-Z=<4p7y(5Kn7K3GX8`Pb z?Qp#TIi)l2S@Y^$*Lk-*?|WMkgzx>r^|qCHQMz2nL0dV|F4tYn#OA6Y$?>`znoyl# z5%`H>8!(=eBb{~O1E?jK?OuwIwm5b zA|N8&A|c%kBLt*TN+d?tNa-BiFjBf>bdDNq`|bVx9KXN!*N)@az30AOuj@RW;_6JZ ze6vp9qJt9d*<=tk5*yd4o4ogXANxceJs}t{#x(8x#mL>`m{2ptE>U^i`@IGSyegW; z_lyJ4J;Ee8aJl;@1)yo5pw>sJP^=;$@IOI9w%>ni;Cxq%L*w=6^~aN;m%3@7p?lU@ zfKiQ0zzGyo^)%Sws1dEAFj^5p%l>aQ?Oyp~-=-0!Vf@;_;NWG_yTVx*^p$-7!%8DN zm#dh!Y2fWM%J+)I;gPu~ybBe~0v_)_|B?7dj9yeS*-nW=MBtC`=9MV({C8!-n6Az ziQ*qct+qmsB=X@wVuW#XC57kvBoTSjm(~5`@TxzfVwE$|0xZarpsphVa*b4!?5X#u zkT3H!**P54K!RITe+6`i0?*f@HH9vd}oQXz75#S$t-wB__GV{llt~*0^(lzA1 z+PMz z83dbR{xV?Rn|Vq;x2|i>Q%~*!ogY1=Hl}Z#u1vBUI{KS%5QrM@`M#`mmU13&($*1d z?5vbpP1>?P2NpyGi`VU3L%oRPczPj~qK0mx;iB<5s~)n6w1n!N5qSyMY2_)c&`9>>8#%nVfx0J*{bC+5b+5*uDu6oMowNeh6nFfp z1hPCM-mi#x`~hbQ<_eC`F+s|9p3GS1;)iz_gqR~>z;yqCeI#D|!@i2QXT&Yz)f5xV z5`Ii%yXj|%L|C`5n+Y=c(r(x4(u~QkK3E-05b@Y^Vpi%~HcM;5_2#490WD`__nY+$ zvoAJ>08`%|ls953za60LLrP391`cb64cp*e-;d6`1pk zr_5neOf1*%FC;{>wK@IYr;BB z9oZW%1Oo!|cHZG_IUPb9I-WNG87hhm3Y!~bw3SO+)OX&M=y4#JWnl2 z!>?CxoxrZh3D08Aa}voTly1{srDuu2o#Kg{V%3LdR`=G)I}+1BoS(DBqXzrIb-)J* z%d+{8?irq}F!Qs#XYTAYP{Lt9!7`=+juR%avj2YXVtx@OiZ-WML} zF5zhjhVw%ZDmQeV&`Rce*)DjNpNZc+)WU4XJTwe<`~&AdF6{1>=(!D9@V{cB zlLq*y!1Wvk7ykj*=)d|aP-HfLbNGiFH zaQh`zdKpO)Rt{B7)hNBS`z*J$XRAbaP5HKdd%y}N2KJ&bqa zvr89y=yzof`fq+vS={?Q15FZo1Z8%B@eEgx8_JA(N?Q|}I7;AI-9E`xX>NZ&&aOM&zMfVfX*5ZeGTJy@qi{M6Cp z)%a>iYi>LkmZ(&82_j<}q9r)>X{L8^ll+O8|MmKLeeSr%^Z&wq9tVdPaR!r1^r(Yf zP*h?5lQ_>O-%^Kl+*q@@GsG66S>x!6o3JL?d#B`7&e!a@#IwX1pGF;u>DG9tz2G(9 z8$-FeZl~XNwcy`%ut8F0#{CbPeh;MEEZ1ApEOlzQsUq z>lzG}-d0bMuQGTsnTVlH-G9oe&97ccd<5}J9aYJHI30b$3Q5>QZOjb62H!B~6HwYn zT?{gwR*B9BY44-qTozyHKE@JGKZqe~uRe{X`b}Fg*CyPnB1K!Q2F|8csn>&YviM9GOeK zc+@h3GjQ@Kf^<@dH}6A-IZu<3oq$x;*_S~y@Z47#v)&=bp{d{pghWt?8CueN*#Nw= z=kHD;B&;_9B~_SyaCXanv|+8xS~3|u> zFBojrR{U3P5z>x8K6bb1HEz0EMTJwRVpWxb!QrsYXnIm_STd8a@J@gs&iB?YtR zM-iiXr06xUZ@R?%W2v=X&J&f!{oW{T-R{g(ZES_P{;2-A*f@X!@(5nvMVc+$bJ)kK zt=bqtRpYBQ#D>>5xT6C6#!V?r&YaW6XiUB`P8m{kK$ulOANF2`+<2AAowJ+%>!?H! z#k>hz;BZG^je?WQtQb!A|=yoqyQ zW@ffg?s;`1bfm+xXDllqZl*SL(LV9-&8OE-mNS+;5`9D18<0KSeuwGT1q;?r{dWhe zH_@;BtDo$)zJt&Ez0~rw0Wza6s-V01E(AcD41Tr5-|*3cHyG{!JWmoDL#?&O*3prp ze`pD91z*H``81)1H}vlt{`mmRpxPMoA<#?Pn_4n~nWHIU--P8hUW5vWnw;^xfn|vSyQ*iMY%-5jkXUJ;|cC?x`&S^e>43LQ_$Pvv!`uOJjBMRdLo- zQJxX5&E2|XnI|$g@^9L$Xo~L|;_eze49SVD85fk&Rz!5)bCe$$?$If)U?Ps8ut1kx zbUj=UV{N+Z>Tow%XtzKnw5H6My=5^o_dnx)Jk-SPd_`1^ z`w3ALNvh+k8#U=iVf-AC^?ilrC)e*gFgz`@C1AC3>#qHKd%C}up_R3#we`+bThlT< zDi3t$R+eP?5Ja{(H2ggGX5KohkV-adGmf{I@t)^Atpn@v#`cWFegB)?eZ@S5UtncY&#kVp ziV}yau6eNvY{dlox02|CUamw1pKD@-3kKof&`gY-AOTw_81opJ0UDrmcBB`xvD#6@ z@d-bSf?*{&iGi;oSrjZTN;#~fnME_A)MZc^dS+OO?{zB!I+5(ZF<}$iY3av9>^9Wl zNxlMR=z?dD&T;$ppw*jy2cRW&huQ}J>74ke%0+9bfAJ|vt3yI}YFOgP>A2O}b>K$W z4sZBToP2uhoD>ov|Wio7CLCt=xIu zQm6Zt2bt~^H#6p4;nEw6cvT^Md8r?b&ws=T$~ZZ; z%*5QboWwE4>q$e0)q{wR6V-p!#G^`djBt-K$JkFn_ZI)^dp3s!Bk-R|yG*a@Pps0F zb3t{&YG|!ZXY~sB0#8ZL`GSdQ_bA-DK@1!O*7J>6vrqRki|&3>&k+}_pNfL$_^)%g z!;{DDY7Duv#s{ECB607JKVggh_;QVSpT~8MCwj5uR;hX|@jxS&?#ngFJ_evGL3jO& z*o$ycm35gI3Z&&lT*wsZn6Xqp|M_N|>`48>q^=h})m{FdR$k74Ly>mv`@@>_0527c znUbhSy57rQmrqhRz47Cyw!ZMiP$~FlkL-oUFMBH0oNflDW(H3z_Imm*Ixm|A3{WH8 zCzNeQ`RYu#*_`2aL?%x;%7qKcuujM^~CB*QME0Wp%g=xXt>~hc1866l-U&Ec>9!qKx72d_308 z81tA>7mpc7dPmoYGSe95lk#;=T@DxUbIFRHy~wBumVC^X?y)wGUc{0{D%b2Ac(R1= z$TCLnZNk(I5IJZZ&%$xNj@ye{al|L+h9FMs-u?l+{3Y1o5QCA?eHI zGN~&>@nLQBg`~8+eT0*K7`xO~ReZIaGbrZCn0Ha$pR4VEpb&=Qqg{wXSkTwtF?`x( zWdF<(PtHl+8%0X9e&-JnnVu|L_v%vT4KMs~01V?3RpYfCrIzn9%h4> z@MsO~=GTGN&Cv!jaDP_=l=wWnaI__^4V=`fMlqB2PHbS!B4(%Ers$MDJS!d)bb< zz1%S=;COaIpDka^!V9($F5M>m7Z#GI8}&lcA;C313BUyEik;0W42SIisX zum6Vg8u*zg((#t;F^O=_$Ub36_QcE90tBDL1fm{_e+1kD5Otq3?*VY} zo71t~X)sN9f+7Wmek&nO*!i{zO_uonlBn~~=m%egA!sbME0@g9-GvbPVEWJTGroE8 zlPLg!fZ&>#WJNH_YvH-e0%hW4+c+54uu{eQDy`@)Tf=^$D@6gC!r)?Uqy*h?V?dog z+j#tTx_*_)t1dAPQ{F(+D;E2EzOd)^OsIhBsq&)(DDuhUOcX$Y{EDY8B7=JgrT4S_ zvd|72_O0uOw3ZZR4XqFv`=_;Z^O}u+!p!RV$+zr~;8aYv&-UAcjHQ_hdoL2Uuv89?OCW9yUefv4iUEH_icQA3kr29w9-dJ-Gy{|D(Qcl`7NTX(pCx1HW^e+bozRU83I$wuB<&4Bw7`9q>PI#pNG_&7EAs8&?aP0XD_d?a?WgSBgETd-k?r$# zm7O$_bu&Ec_aox#`Eu865GWa(0j4*Guj3rG5_p#uaN_r@gz~;5BV1f}rMsHTnn7n~ z*11F1`=^m}|D3B*yRX-P;<}#Hzd&h)RkYz%vp{KNz2u=~}97=*UMsVKMWt2b3?rdRM})OW~$0 zoM~V()UaX|!C`wTZc+A&P9fIXHd<#AO(NI9q^;Zeu1?M;NY?w~=y&>W))>P3cGdfO zyNb^JJ;Jh`Qk~Ph9fo~-EhNpRT1>cai~XR++9uiB4IZ1n`_~%GqEkFQe3vJl(ECvH zxmJ3j9$V^{tWwA9M9S0E+j{L5twL%}E%AE(BDtuptbquvN;GL7X3iq$@x-bdmDms! zyY(tEWkyRQ?UV;ihkeY!QlZ9_nq@C4IrDtD+342Js^9h|HyI65st zesI#kGo9#luiP#L8~pmC+#bmu3S$M1wa7K4|Gr)xOhVX9IZJ0>RKfQ}C29?1R?^TE*GzxF<{PUqH1wv43(^vNo5pOp!KKW{%@@{ zdiNEj*7&8&cD>8t1eNc0QVd5bpm8Fx${t!Vg!|x(uVl(B@M)_0@}HoI{>9tTN74Ok zv*c}KvevR!K%B8@rzR9Gt&6+C9G4UZk}RDvutg+!dg9-EIFQ8?M>OIpLSwRpz@50U#lW6Pll` zpV4oAFrf+ie3XAxvA(_|@icU9`3UgF(J5|_D{ok$4Im6yF#Xp{Nhj{PSbX#I6Wz3} zr$J8m%4bOMs&m(@-jRu=JK@Va)xd!<_9EZXg*oZ{3Cb~5SK6@L>^E*7*7UwSVE1=# zor+74f@SsVO+#`kXScM(YDVzOvdKe*T#0 z`z+&_$B=?YN@T$ew%N_IX0b-eHuBx^pKe#Xn_s{Xz_XP4xrbX3`zV(Cp?(vtpxQY+ z?sJybtWhNY6^rk<$MM{?^2c#m4`b?=E`I;s~%?M%!<^Y&OVSihl5+K&E}5E@ri;=*Fr_}{THteZi@p4sGT9mLgj->l`6G!S=-RB z8^OJ-LQ}=ngI{`wYMjSjX8=o_dAn4H_1B=KH?W2v)J>4-0*}J&w>8M&VU2p7yytzm z^Y@RLmplveq(xP&t6ATt(38!Zlvk1Run0w#8?@kK*6=q<_J0{R01(?q4_bvp?_EYODV)R?6VDwiPhB>leTA$6)p`8Olug ze`BvSX}b>}tAQ`NI^%TSe3ec2mlwZm>AhI~TYn{vqow}7e*3?X*wm2DThoXay?+ZpH9%AexLB@9M z2(Q-p8yBmk2Og2VX$Snh^Xdc>TI9@%(823Z#?`-4DQ~Rqed;A(CMxEaJz8DkU+{C8 zogEMMtm|87#`*Z+B14U_(vE#Kg^@v!tJ_ZeJ&Um7Oy>~o$W@>8xA{3Rye#Qhk@E1I zI8v8&&l*685_wMG#=D|CN?so7jx^bLv(j!*WIXQJ8-+mhTDd;Gs4`rH?GO2kf*&Nt zWn$HV3Q|NcY7L%wwxzHQ4tN-PU)bk9j0H4c$<`tS<3@vOr~SevWyAcL^W=IZh8Ns% zE>hPXOM0u5teo^P$m~9Jx0qPAj|WWoVr?|W0mJ@SlhgI<8i?)SqeE*o+v#%}onygZ z`n^)mDN`3Gn$tY`0B2h3Dj_mBPv0BkfbS^c$Bh~q%TjqiqiyH6e;H+y!_A`q4GS3( zE`QYXb3iy{oV4O(O=FEW_5)CNhKcmEU`kv`6fM<_B{{A`x6isb`%Fn1We-=3n9}0C zMDPPLM%XbW(oYGRRAMj=^!FFu{){y(G_e2_shGI7BCwVhvAFk>z%N_<<0|u+L8Z1@w2GLV)Ge%Ne<9GXd7jp7%B%J4NQo z5=_bDf{fmI=}FG_?T^W_-w&H!`QF^*Y*aiTx9%#u=&p70%(yZFyHgX300V_$bSdF*qMNHbOI8}kuH%~|oKNlt;_A*Zt*`2g$8Cn2@WL*gRRl(^_K&oI=>N3Gm zxknR|It@=P* z8&Lm_uLhq0H+@NeW7;^lH#|(d*QHx7og{}{Kr7pznv2Sth)mZ$WzVD4zJB<{)gzT>Lvz}h|b9L*}2YBIVJ`CzOTEF zN5c{C+^RN#pOI`;okPI>3dvht2oWEa4uL(*fU}+rQ8Zrs)9EO_|3PY+X3YGBFQ8Yq zhFQRqLO2yXk;xEU!0KRma-pHGqcbj-_%G#?qN6ZE7PEhG9Z_5mov`)_IhazEuEj~8 zB5hn4^0%K(IOm~d^n%v8yNV9tLVW=}N!8^?s12bVv?uI`z-WXpk! zsEs7uDa83;reho{XBSlMj9apw2+`BK0$WW{7Qg05@m&oFipNA#xPWRp>F-$hReF!= zyQPPneLF~WT@LoBU{3TpK#3~bAH^QE@1?2M$n6~c#yZ@7G}WE&YNGp-(;vUGmfQor zSaF$b8lgrsA(JTi2u#@r7|2@A7gUD|xP3}%Wn_>rhe-XfF}v`)b@x{ZVh+>1MM6oO z@8O~iqSo1=#MUN|N@r?avXP?0aP$jj+d{ULXB%qEGt8%XDhBedNzC7z>3P1LE{(tD z5B8iS`tb?@CXsqW0gu;p*D>$a?=43+tSlUaSP8$jP(q00JSGQW9SBB4N~Gx-Ly_i!&8+!sEs zh*|ObJj>88MHB!NF?3e!$4F&{P2es)IZ-g72=ITMa|1GUoDdEbRW$x&o(mqUNj9Qp zQIUMSA!QmUQlpvo2fDuz{gDxZ8a-kRf3lQlsMMVs%bpVQ|f{0T$zY}`O781AoO>o;^kPPN({gDnA$1q8uoaAsJ(X$~c1G$>xjyU6~O0l+pP z=u7FQMg96Q_71#NPm`neuJ;0+$h+8)Z8!j&%)v+p{0y|7w1)7MV9LlJ-;xP$1qF<3 z*;>N$WVcE7zxBOM*PSV^l39UN{aplvY`{#PdHK1I^`Kj2d0;bK_CAKKY%;O9m?1f(-fR|J#+SPjk zZiRB~xs!B_>%jPY(uNj`-CX`&uF^0fBdH$cF+-V0Rg}-VK&dv7F4%^7Fm_i1!+M}| zXe!D z!H^a5oLEO0txqnT2ltC5#$ zLy^w}nF|PN4prz=7_els%_(llkMtyyyn{*b)0rs(FIC^+os)aY7=Z)(Z1*X24j^cI z=hqhe7LNkfO;?02!O}rY_$t&eeh^^*YL2$hf}^Szgo1FFA*r_52oiLDb>||A{bd_ z>@y$cc}}8Y&K=LXmIR(WZo3owAZ|iaO2WkRFP#$J=yWx6bm(O>+(>lXP)(H|?>hGt zhk8d0Y`hXDw;z9lwzplphb%6dqTRXLFPi(W=6q!DE-NXqap4{3NZ-NDzhe%+^;`I% zo$R4KnGkY9@YVu`de3)Cmv_=;7M-brpA34HNED0K z>nTh|$B_vbEJh-|S_>Z7)e$NM64TJ-Jn>x4WSo6m~b~xtm zLu;})E;K$cl<40tkFVaFUJ3bDF8cUxu}V}KTk2R~$qWq=KjQ+KsbLW&7AtC3 z*4S(+Y`H)IU?;8hdtDj5cB1oR?ZJ6lg$3QGN?cNZVcG<(PvHdJ^sBOgv#`^h+g&;9 zo87$8MhCG3ZG$69J^LqrY`teDE=Z$S!Yv5%bTsfdom!@dm>Rs^6r)}EEB??PILh_k z0&k{XGfUQ&kwqZE)E?s}oR1GpBo{8bpl6!6CQE|CKr;GL6^`!t#dtpq91Me*@J!B* zH?+7|$?H6pM1XzzHmer^{3vDcp4!A}MRFNe5Es0B>VniE4#@KtZIS*}_KwZITb^)i z6`k%hqrnD3=8XmG7@l^&{Gbe?xv-Aq5?Bf6wYnZcjkA64(ZLqZ{A|^gO*XfiXCKoN ztVglFPpF8K*V!C(n1oowxgZJTPvjuSjorblxxCa}pHp4RQtH^fy3$5h#btLn2d0m$ z5=!=k{pUn{lS18|JlA~#i#R(g)j8wEx8GEIC;^Q(1fOgCo*OfuXM4ASLyBKQntb*5 z1G>o%{|@Nt2=UO7-Z%t(HZKpyP?mC?kymR37{JvQVN_ywV-0ngz9zFi=?SqMu?&ym z1bXCE-m>kjHd4sjxXj{Asu%^IIU)(Z?sDeGa5ykdGNfyB8(W8645uRniMW>LB_{yV zlV_+zndqPqfIc7+|iWV2Z%2iIE!PU;3e2`XlB-lgq7qAhS()mw3%HkoG<&0%cA(2}0{H_W(8;Q4&%SXTh|udNYL|?)9TT#xv<%52q_u z&{F+al!a`BE5HJZ=+GH8qY4!4$W=R?%GN0DZzI!0SMfJ*LD$Qlp^CGwla?om?^6HH zl$7CO4>R$-*t2po>SkdenB(dnCzR+@&_znMLDk5lvgTczDzDsSUp|9PG?g| zy!VYSu~aXn363RK;^vPJty1rL)zz#+rPkyb;#>EyON0Y0|7Jj4Vh~H;*M57@x3vQX zXfchqTOAPRs(PT+*%<|Q+bmjtmvJD^#;l>N)L1)o`}izmVy*WJ@N5 z7a>}eDA|4MLdIpJDCl|mY=vTK{{r&`EwO!CGM%!EN&4+YjSJOg4v(k3-Ro3gylX`5 z@UeB|J$r7_S>9kV#ghKnjrm~Q;qT1P!gVJ%ip#N_2 ziW&ymd{;%o6%X$cW1L0l9sOO7Hl`Nm(dPo?@p&ALYK(FrJa~7LG}+Y4io!#%Y&R)B znjyrSw7Q?2M?qkJqvB`nk;b3^6-Y#2soZ9aItDN|DUBEHZ6e-~!;=NL@4<l3xSh zoOcp)IJYC#Mv<^vvJ68n=@iE5$s`2&vh~3*B)5W#GS?0PzMZI}WYX9b=gB((6E5y| zH}#*k$){`I7`f=3wDYqm{J#9+(NM1s0@U~JF!wyZn*wiO;ap@qODle|aoI4mU^x0$ z9~EfLo9&hEF~_+5-DpO&1$MD`(!R3o_b2KDH#9Jf&C+NlioP)ymb}x?wn;xJRlK;O zRs?W~a9wJBKZL6mC^kNrJocp?(`s}1;GlE;N=S(84J0nl0f`~du@c2HpK)kWWSsm| za=SqY_ZOX;YOFAz20*6xH>6(jt!S_IXc9u-y8Fg|&h8}gEEqp7XTy=C-fPns zx6B)gJly+W4jY+`wbcZoA)u}I+He#6rBM_zFRN-?WT@mWa%mEF~up8LVxD%Fxa$T4-_^={G5^H>Bl3_ z?B_W%aM}qFo_Fu-)t^KhMH>9Xflr%P)RTF!HbH|dx|eS~rWid;a1y69ZsV{o!R*fR zd$Kzzf$``f0F~BqVVMhtWJ)pCZL8)w=*@*1#o4p9a`bvmBOj> z-2^(~;KNm{#4_o_R+M#y>>Hm0M*`Hgo)v7>6n*@V)omERe)HImEaM*RkwP`+6eo{= zOSLd)ZS+NV#XB`K;LOya7gp{ldGE%yAxUxp)&SD1swza$@$NK$Ij)Ed&9`S9F2!eS z)Eegc0?!r`ouA4bQMA{5i1T;#@nut~zSf{>I6#Z62sR@nWYM)`heC|YIIUERB1IW? zWP(vLpYSr0YK~K*vA-vh8gsHcApg~whJeFV-!M&_)lAh!BFaJNq=l6`a z9#)r(PcS?b`n~pQOKzhsT4Vc`DC^-}`W81y|6&h%Qhcw|I+f!jOBL}2;MSqDa+{Z2 zLXL}@7l^}Ue9|m7<_caVUtC>ifI9RE3^42HRhPH#=2oZ@0c^3RuiaKBRm1j04bTHK zPMIsFjBk_?qq9)QtC>puQTlBxZ-N}sLhrmInz^#D;qy&OVb*|*GM})#{S@z^N+{Fl zhWZ4*)GZZ+3;0F#;(vv$??_lp2U4_?@kE1F!h1t3UND|oo7$w z=P<_g2||*D3dT+%9B!*OjN=iOsf6v7X=CI-s<@YgIFE{>ytMYl$jorYComqf7s<$_ zPqLd{9=NxeczZQ8am!+%ovNUJQ&3FiyzhbjWv|ee^z*v1sF^&Y@|=zoLR3=Su%iwv z+W5h%LLGDl<8x?;)|X_|wXR8kOrN#+07*ZQs}&D8sSj?J1ep!d8X4quFe(_m0MApN zQ&S%AS9Q0NLn0HR*JhKLN9`wa8%?~}rR*QY3qfPFkDNcAhT>Md+P+$^ftlBLOKP_f zpNmt{sTKC3t9LWl2IO8F^>Ai?q&7;2s)^>^Q=^D|1`1RYK z-?Wan&QaiP)=s{++7eTW2_-Iin%Bm+Cp-Z~bgMyl+gy{RjC9^jdn6CI@N5 zClBOK#^*=ZYyk4_<0hljkF<3|1I?B(o4b+y+j!%rn9OUQt7M@mkQgKEEDa6*=hBF)KGFk!G-zpr!c?r2r zKO=csu;3v}B03GmySElZT3#&E0sDYQt%hqSyhX!Q9@u(WGy9*f@`r4Q;xFm>DpD;s z1p72wg5G-%+b|QvSEf`izm5k(C*C9kQ?*!t0S3bUAfe^Y1;9J};{75FkDly~6ofG% zgMf-*qXtJVXoOf}yx;rApMZHu8sD?$^! zYQCH>$9#%(RHPzNR*9X~q^)hb%=MOdX(F&Cj)W6-7!V-Y980ygX;T{Cg$a`%=LhX^%I`unjxX z>H4j3yzvZpDLGj5hx|6$396@yQ_QaJWeOZs=t37oR&@MDKZ}Y!&W{JRP4W=%drk4o zf1YLy*q{&SB54s#;!>EHAuLeJ2D16+`_55vFO_I%Ii@n ztxAM-hB?8sldqG-DIh`1%<|sGU$|~p1sG0`sCu%7`P>NmzG*e^XBViOCKi8xa*TOc z1?RR(BeaeERe_FY5{VYLs@C{2d_(ob=;QG9=vOTbF^ZS`e&{B6PZh|V_0}33_z4m5 z^Z7^l%W1kRw)!aRFP|6Q6{VvyZho9>stQ_Om}g1Pe6G4RMhs%EW0}mSf}BP_C!kYkx`GR-V62xDV)farR^oVL`)>cmY`o~(6mdknQe?B^ zc2%#PKUPw&xT}W#oGpXrJn$bjoo#m%Yk151rI^|b27a#oAIFGH9EZ+X00zLAGeAKr zmoU>9J@-JC=hjGzwt&hHeFpq`D61l&= zO^SBcVfomofrrXhqWJp?YOeeRGKuAUSt+9p&TK5OJ%DH(iTlB&^P*Uir#PJwm@qcx z*S~r*%S+3Q@@pm;zEy(+;y*pNFzzerftr2!FPqy_Cnk8kEnf!mZJ4u(P6{P|C#|q) zKSg1dL}d1%|MW|!VcYGT&HrZsd>2i>9)IibC`!)0n^}KG$Iwuo-b`I~^pFwSvEOm# z{HokRJhL*`Q&vhgW04%wy>u;j z8mT5-Fu$$4R<;wLhGMlA1G~jl2FmZ6L1u%eq8~Zfz4xvY!s3eOZ0qp*v;uomib}JO z^wPr6Zeb{W>p!t9+lG^-z9p-BQqY}ekaZl(HL6r!L#apC%l*0&E9ck?s!5=~1@?fL%F>>uy*i!3^Ilp!-5ar141S<)k@q_xYZ}NYUSs<{ zsh7mETl~E7O?Umsr4Z${jC)z{&6MJ;zSNq;O3(1d`-pBi?&Q+F$6qmnC;XKi`@;e* zW8w2I8y!R-o@6mR+M|o9b6s;?xJinWE5c{$o;8rI-I2d`mPe!ceHW zKWLM(s+CW*`^h@?+d@d8LK*#Z2B9%3>7E!$ZAWw9jzLHFZP%B9hL?jv4=guU((k9t z-8y~Hj|g#LQe*%%Hgj>^3pZ8Jd9x()Rfgc*F#awZ6j|x@N>@4b)5muVt;~_Dyj|i@RN55?)i*9I|t}1bu%3J{Y1! zC#s$yfX4*i$Nnwt;VvfWHmo>a0(iSs?ns^dZ90?pUq^&`j#e~IVvQBcd;w?5QY&i! z=il8q$O|Qs_27PewvlS=lE;$o-GWHfb_rpU64&rvB{XZs`jwbvaU3vQPo zM|<|BpQLALxxY}0H@f->_*ZwV_P)ozV3QSF18p_ygDH)c!xgIAKOD`SRK#)e3wJ8C zQldF4_FB7rpwYR)noh?3)rG;Q+xPvngr~J8X1GNfvfd*T zYkN}uJ1S_se<7*%q#f@ z-}@ySRR~d4Y&-s|HyHl(P3f?s(%am$O|Hd)9Usgd^=AKDLx->rJ>OtC=|kys%}s-; zt%Uht_s&}xn%juc(r>bbe;zD$0j(}QEP^ zzhK`@!RpK)!}bPTi^!FyzX-*VqUYfc;7R!*`ZE^DLf5N?kCl<^zV^cCGg+3ss74dv zF1sj#8_@kTJguSZe~In;-aZsX*-`jeGt9ZJ>Hwzj z*Q+8|*&3hRhaLn3KcZ7hvqv-_7aATNYt79Ooz%2AM#zsLF;?SV3Z>RgHkecs$0ut= zVrb??nm2o@7XQY`tQ6;$(2{F+o$SdXg7Z3tcdj=GC+Kw z`BALQHmn5`egznqsf<#0eEKTuN&iZa<1NqQOxjYG(~+;gjn2V;l#=pcBD9WcvrPZN zT^=@Yfht^;F*eWsTLhmCGy4QL-M-Ch!qMKBzr8Qupm0WOa1@Bkv@L>j&}ixn_w`W0 z+-5HnI&meB69M=+u#ZM1miqZ*AE8mnHsTHMa)d~1g?$KOweCn!J@GFLI^zXBDUjDr z3Rui@tl$fK0W5_szKa^Ea|njKInC2dddPPLUH@UYuP6tKMhRR=zN3m`4=%_d@ZdAB z)=3%4jV16@X>3lR{95+hHspK_C)a`puRf%n2+%P<;QTr^i)bu?IWNEFUA#h!Ly&3D z(CvhCNKT-xVXQS>Jo~}e!wftAe!oz@Xt_JK3=55d@}!@b)meWsdzryzXHWNwC*vG_ z^?5)a+y>u33aPHh#Gjv%_)Dvd8%Jd;D;p05sf;j6TkS2V=ieN);y*%fa<%h$iu7^* z;((T}e3$gRjyQ{zk@%yy4Rvw z7`Vj&+S0IsSckx`QI1G5K|AgpgSUv+AQ}Www>Wyf<*Q#n(_RF8W)=AL^6FFH3l!_#NTnm zJM@dM_8vTg$Bh-gS>9@zyHJ!Y3KN4GVmf(_vT~}w77)BdP6HijmX01$F16NxjObx# zO%n>dVD@tZe8{C)8T3r`EwwRSj|>^?T??703v=HBux%J2K%raMF$K}3*} z25A@?1pxs83F#0N>5>?_q!p1II;2Hu7`j`K?(Q05hMBp3enMGX}24I6C3s|hu!qVKvUA=7ELO*%F`*L!WbX9L)NhRoW49}2REB%uhV*yG$5iaIvh1<14e32FAb2~!M+{CW>eGpUguqVW}c3<;7{P4|MV0Ss;% zYX0EP)Pm@dcotDJ?nT3G?EE?u3p8~}_{dp8G~f8wgW=piAx}>jL&x}Z_>q3hZ-dtx zH$&S*213;7jcveKIyJ`X0^36~C1*>GQmY!S(qj2nG02 z2P`GuYMD(3LGndYHSQ;SrDfJxVCX#fD$KN#zz56dAH;>Hb73OOUyor`f{3nV9gO~0 z2JE5Pveqhk+|q&?Xm4NECU;g_Aw#$r0^_%vG1pP-6n!@M<2ic_C*5Z*>9mIR+1E>p zbZ;z^B?^I6L?5U3eDCh!321Ir95}en1+EzZ^ey;rYNGhXZk$`6F6Z4BAZBpY5+;ZN zk#iA}0FG3jk^e!APvPpd(14mt#zP%)bH<`8Tkdhz5oSV3VDt^qSet(ih$}lFa(WBp z0WPK`d!g=szEbaqqn!Yk*gl=Fm54@X?`|~c5ZP-xC7r9z&grZ+9yYudI3m{hZ<`6@ zn5UL??Uf7L8yV6w7QXuPJ{fsA_b0BVG6NHQi`O6dk#l+jxzq zyGrBoQ7Xy}Kk*k&K_vPa4zv!?cduhx$%ZyP zf4Jg1Bu=OT${wBAIJqAeD%1--3-{t~zuE_>@$dbuvmAbnd{$)UHVl23_ZjW!T1Z8# zyh0!?(j@(?U`9FojS5_?kum+FhG?q0lAm`LzeWFycZ&g&Lke z3AS*-ML5a!5j-c%R8S{->S+y7{Yt^JBYW^U9$3& z;gYN6uK1Gqq4w=!wfxLU6V-ktrY@Z3k}j#y!c%ORD!`>NY$S*gygGFcU83;A{e za8GJoW{(<}(M`DS*az==m2e?~S$~%1b?30Q?8 zwLqJcJyQjbev6h6k0Fop+;U<#7BT-GEAS%b z6CE4xm!L!>w~DdKi_FDI2V>poE&QrpX6+7U^?=5Sd9A`X=_38?0vC@8iA5JxTXKgm z{Cthdzm4{OiX11p*gevH5E%+Syk@zzS0Muq57+AxuEO3taJ1H<>I^Pd?3XBwQop*OX9^EH|IWv=L!`*5 zR1hhkqheiU^Gviva!2;05gitei$ujmLCt4*9?PgWCYkcr<(6p?@2j2P#R3(@!2`AZ zR)onQt3#BRS7gBNsogW{6x?nhVwkRs3B8Z?Ygxp7A3AQ>3b@j6+?m`eg1dW3CG)u& zv{wdOER4^>y;tVV&ZKiUe|tv&h25cBQoP)qRT#FqPZ2jXrjr}u{)BLa9 zz&)L)%RT!-8pB}erw;4u#h%NQ1q%iVB)h{Q!*t%Q|*fdWwv{X*LkC{;?=P{HA65(9eO?qElu@@m|a^y`^#GZJ`Cz{+Imgd?^>M&SVuhu?wx%!e z&iX{vPz63B{)hRcQ7N=_O}$i}4)}rQb04Uq?E#Jc5`A{HdIFo=;-rE5=<*_TUfkSk z<-SgoC9VpV&=PyxhEW!DPPwM&rt@w|S`>WjdvA+Bp8nvx3&1{uQ0OxozpAWZPVj0p zcp0CTu(7SDxfzBRAQr}Maqva^=RptkXoBq2hecp4$R++X`6{GRZPxk8ahT37 zyjG-=D>P1R_BQItB;{vYgZqsK@I(d!%Ddr>CG-}Im|@#|hAkpDMy(LKin0G@0WvG4 zPvf*njTOH%QL%~ys`J0xM%979rtNfSi48X55CIzad(Wy=>OHpG`_R+gIyKM7Q&DIM zG6BRt+A9dX@+jwho=g?Tr@w4ZbNo~R6LXq7(f#Y$hJv(;Cscnb>D}J{%y@3oM@a4H z#J+*c@LNCTBRLJpWkA=CovzQ?O|kgnh@h7+(e_wH@-&km31cnWwU^_MG8dw0(Ddkr z#1;<5=4X0(TY^V#yHhw*9A`QTw47B|^iX$KE;UMbp5>UzKJd(7a0uQ~gH+t(VzW8Cz2&EZR z(PdOGv}j9RanfHUw10KajZ8e6^C@{A@Hrp|{5s3hCksGzrqCiEFNIRo&Z`BfQ5XyX zLa63`O5I&GYYnHOxG()pYaEJ~o8Wa0iunN=$-pCe+&FO8HbKbB#D7`?iU6n^Fzx3Q zC0bONaURQtD66dFONz@3y!35vuAl!7jF?c9+FJ(35#(iMJ(MfkQ+mO4ox5De>i}Hp zmjN_ru;TCM>;o7%>bSgb+uo`hI)|9;qcV|I0-Z4nG{W-di7Y7LTH{GGJ`M2}-1rmG zFBvD?$~S-42KQ+Y0_u$YOy9(kBR#@m#C}upAYGc4cy`ItIu>YLer60<&aHHa|NFKe zGKyH6?M39r_^-Kq<6Q5|zx|-t_&cpuIADq|EBsE_UT%cRm#JU!y(3e=I&n+sG1A<$c0d4U9S{ODibjWhI`X%S<8;sm2VtFe#a**_KKPm>-0^W_7$e!1_*X8{cg zUkb%_DBgMeSLnP$!-2Kr((cy0K+GlvSyw-$k;C+C=tv#ZlbPO9QH-0jJ7^J5Hvi0K zq#hnQ<1z9PlS&(CnL7Y}-`}VeM@7zCT&N{je!qUW^~o0I2>eFzx>B*6`)U<4%gxq2 zO{5?D%G}Y&6ah=m>PT~XVZTks+;?S7#yxXHvOUyw8AS^Vm7h__T7DF%I7vS!zxPM8 zN&yj_q@WG*QW7IYb%BXOZP{WpQ$~8ys-iLNvM2HGAcV!Oj=;YeVM1Z3nKfE?Yn8R|l=2;f zRX9dLly)|ul;jf_poN0)sy`?7B$OpLoVtmg@r-yqJAwW^FqO63(Avt;w5P{hRT1S4ZUDHhh&S z`b7?SdmPe7yb3Q3W!lm4yN|(SZVY%fe77&sEpBOlV04+8o^4+@;)H?Q@(T{6cLCZ# zH2>O(#^0^MU42uA6P8M29M!_fjw-91m$J z#%F+QUNl;C+o8~DS;ke$JT@+8kWJZ)>ia?mgwKTdl-UgtKD~^eEt~TTj4)? z`s9TL6Mr%e{lx6>vcEElB{7o(_D@am!AvnW(FhZThg<(`HGp(JRDK(r;W*NML0hLO_!|*YYkzlSQ$L|(=vumRO%ws>J5rPQU77p zt(^p?Yj#T%%;Y5$z=S(oKe_d%y>q1Ky>``YZbv=3V4H9Nm=Mtz_N2tARA9En0%hRF z{q}H6Ys_YgaXfK??0jS(FhSM55OveIs_1=?j(7s)EU0 zkIXR7-+zPdv8d!1g};w{&={SCANuRA3vfxP?byLipuL(2SDzBn^4H6{OvJ2WOkO&W zyVA3>`oV01D}7~;(C-_fmFR{>EF#vnc^BppKyj$Bjf!ey6R5B40?f<@va&F|^IVxK zl^1u-DXKts33&UEj@L4~BKZ-UnP-KND0BD>D(pr(H#`mEcJW+^#hQI397OcWx;qec~T zt3Jn-d8>ufoEa%)ooRk`^U|P6$|zc>gH=l zQE9FpkrDKYy|4ud6<&Y&kg24rM33N?mTgT=pE3{A+dx1-^Cq<>MCTS3N&)(N**scE zbi8#-C;)aQ(1`ioycg#ImBzYKm71tY*n2caduiTl3i)#rsyL-1CbK7D=DLBJ^g-(= z0i=O$xo03^oR42mV=r{%rUKV4bY2`80kD(I=I75}x!ZLSiZBKq82^XU#b}%`qpO{` zIbe+4GY7N{|4gi6aS0v+-I`);qeekur)&v|a}aC*{T#N)sPo6nu82qX%L|kdc#c23 zo|zbpR*JlkCT+HyT)jXW$g&B_yy;!)!8kfa#7DZtTK_H>w`PKVF{|3z=Iq{07g&V-%hTQmgUZJL zfOIcZm>#Itx-u%h%$tKuo!yO%ctAU`%&^vr3Mk`i9Sp#(*4_@#Q^AnJDxffTDp*kl=_6FG*2Xe;&R73uwo{j z%=?E!1#J}!Kxl09$60Y;R~VH%m!>|ZD1~AIB?i9Z^5Ao^7`Qt45**k;YO}D4ba)2^ zWd*%Vw@nqJJQ$eNPqgFRNTmwJ9JchhT2GR|fEG!hxJZN&sIP{xqKC22pl8_jAz&$g z5&>ZTmr-D9s@j(ex)|swYpl}0H@WPS1E14nACYfrDm~vG=b$T-t$OMIN|*PNH(`*L zUWg*;B>DMeY=ZT-h}?ftH35qh)|mKV?Wc<7IlFXlcAZ%C3r_dD?GIEg40e0QBPPA* zS3$8T!EuP99Jn}h=9hiaookEcIo~`1S@)Qh zgI{@AD107e$-7SRaRSLy0>1I$ozB=?O6aPgV<(#@c>S_f{x@Ei?qi1`__oyH-_@bG z_`%zY4$gQpSJ7b7*Yn0k@&pWY;b=B(Gsj2YAJgjDypox#c^&20#;j#mHi=GFP)hyr z^Aok5oR$6q>|236=NgJL~9V)RifdcPS+7(dw0Qe4;+$7`$LQj-| z0AJqy9|ZC2YPt~>hDiX^7~5Y;l#MKA82yWp{MIj zi#NXna{pvEo!_ySS38+E^sa6DomlQuE3c8R&|wx}8%6!AGj)(wMI+_C$<(MCxV2&r zVoKx2z6)`XShD`s>Rn?kHJzOVy^AoJQUC;xF<6Cv zGY~vOuJ~X9VVl|3lD=qwD}Ga;rL}IWx*Bc6e7v|?Z?Y={4cnq*ze$(^+HWBo6TKHK zJ%GN>Z>Mtp3MP+He@(yzUOs=ln3?Tuk_;O7^{}Z?Gl*;U(x<9v^m<|Vs7hu;KD=_D z3pw6U4n-4`Lz}YanFqqy+nd*9C7X4{X%NdTZ=O>U7K2xIF|JojpcA`yzf4KYu=72q z#t8>`l&EG|q`V%oI-q?*=`GcI;n9qZN$rX{LUWsbUcER)(rBTB)7fh4f@ z_ZjQdtF1&{T^J>kqAsS^eVOaEC)Uan5kQG4PK+K?k2r3X*y$ziLSlz5)%m_u?du(l zn+gG)geQ4@NkiT$DX?a--Ff-fWeg1r(%YW&oI>qbSJf=ZingNo!?}ILBO4Ho45YRw)6{nn6f*k5s=s+X+6Aq@Pbu6X`d%+ zC?Ioib?X3pu&8py>M932&AA)hN$)2U&qJJ+cX9kF(44Z{7Lkv+E|EaE6d~UR#LNrN z$s_G0i&ixe<7hpHG1qaj)=jm~XU9`~>!2@A6I9G|!BGwLD zT9O>j(uT+S?(#hb+ZeW~H@kq#NdxKxp_J)A78_QJ2S`?UAPE*%pTGfrs;~fk%|RX!?Kumotn~ zcQIazvvjyO1bjZ1*(nP=`_Xs?ACEZCViVH&GdhB%MBb?9>)6|M?y>~Y)>iQ4!;#AI zmIO@Qlp0J3Ji`KN0RwwkMVMk&-5oa##nd_a=?kWjSKR-GussDnVTvx=ya%uE^?gQf zTPJ&PNlu?tv?O5>VO$ooU@(_1x^?8W@#cB7A=WpLa! zs+CE5i)_yczp(v8R+kxN%FUw%!ipeThrcOHmV2AqzD{}4K*AyT&vG2VQgBOf36(&E zt%DB#D0T)fs=$GkRjb|5%F&NdxgWQ;lYUCg=OudPD`hC1msv#0Z;q1YZ%clRh59%F zZC8Qjtlf0u7C`e1;n8XxKpFq%@BA;~>sXofRnI$hDHQKEO?f00=TlUF+o^X4_Eg%# z&DA{BFOEoXRN~kAk5>YH+s;qGM@~pp+|^pds%P%%faw_ESn}KHkJhL_^TfgVW)0yj z+MZOSet3H{1{iN@V5{2wfmyJU?*&i_O!wXo$A4lR$+c9>A$zL+taRmm$UO>(= zitqw0rU!XXUm&bYKb^LEQn0VYefDTPgAV*3t%QFZa)Io{{Sy0Xht&>U)TEydcKLo>-$xXd2o!AVyO#hnF@wj|2n_=4=$gOgY^=IB3>;HcK z@7n7=Ne!D@epDPRRrucB;o>Bc&a_Fos9eBKl6@iz~2e!FSbGEX>a z2_S~~HlOB>%P;<5T-|L9J5bm!F*#CzIcT(SOvW<_i@pLzYmvF<#Yf7r>ExD)$ZSt8bT|N!0%9*%Yf9b$fAc% zoy=Ltv{^JjId=S6K&0iIlbQBRYqcw941PH#>W{kWQCZ@gy^ONSpHP_+vid6dYF|hG zs1OL(_T&Q zQ*?w^fBjzefs;1Nj%lt7OrmFv4`SlML=tp(MXByEB0();$?d)^IT!Hi;W%UpY(yAz zwNsCsm?{G7!_jPWG?dF1$UyZm&^y$FyTmtpAfFkgSmX_Gr`)r^Q^h4yy)UJbn~Pa3 zE-aiKU!HHc;&x~S<@?R$!KQ2PFp|pM)QEk-HUE;;SMR6@i(z+~gG#GSdbbu^g8P`m zvk*<&(r3)A8fMmn19uz#;5x4XKE7y!p8TL*UA*({Ej4R<_<{Kj@}2~Q#Gb@zQx!_T~n6cdH2$G zoHw##G3!d4QOa6gPwSg|caz(vnUxyEPmVHI2ImLEs1xI4KRw&j#oRCK4(&W*fUyR? zmSHC1vWpg~E?lOl`SCM|dHbD_rbQ(I(-Mz5-aDoc_s= z_(x#2Fh$TB1iWG=)F*s9UODE+96ILgbH8~7M2F$PUIIn*Z?8Xw9ku1af))IL^ghXj zUV6;jL*UL0U1*SJnoy$6kpQ&o2yr-St{uP$Tx!>WCTADdxc!fRx6(vl-O|@zL3ho| z2VA=JDU=Lyz!clQ%MKSVO2p*NI{w%j3a7);C zet*_MFk;10_1Gp6hb;IAcU$Ra$hqZrcc!}Wz_BB0*3)_Lw=X+d5}9xPie{QtO1rta z0*5&4qS=lJ`;!9W(Yr{W=Yn9G=I1Qxr8!Y3>%~$~nZJ3*5JNTt<$TgKdJjxdtZP_NnT?-tXD2Z;ffQ2Tbc8ZR|l+_CYwG z2p5^0ataKn-w4s%-Cby&)U+W<$|VKl?vb`!0;y>zxX0z_8ZJvSnee0igv>1n0zCWn z=V1ysxxe{aKAZg4tpW+{N2Eyhq8pGY|FSLb8ZO0%?N5xx5RDf=Gn#s4Dsb_a5NWiC{*bN9j<}^N*vef89SAbGmLj zC0611Yc_m%$ZRa8n;wJa0@+=!Q+9{AC$IU%$P<$#EI7g5)g@wq!~53?GknbJ1cN3I zSWn-JUyf+|oc9L5SM(X+nA^D{md^BE?fx)EY23E(!=4qzz2VVOMoj2sR4N#%?|RZy zfw}p{bXqDO^BFCnSI3vuAD~u0%A5k{{XQ2MJm4h7+4~lkdcGAeBbUYM#9yC;VRme1 zPIeauDDvuj#sg42x>CMAoDpH3M=pMMWN*>gIAJxQsO@qVX2j$p!bcuId0xN$&2Utx zpq{jz)(DRRMsG?ui)0864hQ2=^uTvSO+B>gy#5N`jW|@8dwEs(E)_WX-@Mk))<~c? zRQ`JzN0-<$PPH~Ujvn{Jn%XxdeIV&Ya*dW!`v2)We@nsi|#=81+Q`v|cp0BTgjp zUX3?fpG{8F&-Q$|IjMXEG;Ct_9?3-hm;g~=81sTUPZa6R#C(VGv06H+#vr^1%^!}g ztv-bmCC>+^F12A1UPL}AlOaX#RS!Oe=x`sV(OS+RBs>Fahhs zrLg8bkPxhQ|87$9`U zOW314mFTopY6x19p#CrbD7K?geVw#_Bz>It^XXVH*4Jybt*a7!+ER^61QPI+E@-<_ z`dq(xgF5{UBPKUBpZqo+dX6}9uo|A7jv{^LOzkW+??%CV`QlW?GSj&t2lo@9ROKxd ziIO^yF3f#LdP{FrSHEo4!t)|=%i-^YZF&pUGZEF{(r_{ehAO7a`kF+r-3a4nmN=^h0- zN!jDFKC~B|TB;8ns;uPM-BjAh7_{SP{d}$pjWvQx=U87}UB8NLL-ZCoQ~jZM);vU% zsS4}LpBq`S^2oJJLl)p9yOh0PnY4fRrmD77h!5G{kvy_T+P?efr6n$$6|6Fu0IG-= zL6qpkE#N$z>_3#ur!L+Dxoya?teG6)blQcQU$pt`zkf#!WWVoZ6s93wx?=?XcPU-U zUM2URd?E)_MT(($UqVCx?#t?%4Wx(VC0KjJ9|FFk3`Oq(8I{-PcVTwQ0E&GiYpDn> z=)d`Gc~NzXm{UO}g+j)7kE<1P(FRosaaT8_foo>0H*m1K`BuE2Pg=e3-fh`FBN)8r&AQ`5S&B(0AwVOwmLMF~v0jq=E}kI@k_OS#j0vakQmI zjN_u>(v(JKgGZOH2P=3EH^f}uk6S;>%)X(1`m3CfRVb4}@FFduTQx*-eu8Fd`0q~9 z)1MC>gwJHt8u^alsvrIwhKn~+F1Zk>r@Zx3eRL+ntamLcISFb_G8hh{%ObeyrZiQJ zlFcR*?NU@}PBXWjIoKoI?_$#``Rx|Lm(^Xrc=9GZ%E)9d>;{bEAM)w;Gp9tnjX38a z2{)zs9)--CXHR@1Qp27ZHW+TrgfLbImp?voT4Q8!X1xurMr@OKoh43-1;yPHC-Rw4 z-yntD%nTrXgU9;T^}E&yqGSDI#D=)KaTn~z3m zZ+DK_126VO*Qk8h9wmCOlK+ad%JR8b=nUHfEi4bPZiXs_Cv<9w3*QYxDnAj6{Fjir-&?2+Ge!Dw#a&QFgNcX?TJeFA?FJwm0 zUXAUSJd$4xQL}Jqq1|+2w53b%B#S!6OMfB^(>-2YH#O7|ju-}k+#5HB)Eu53@F~YJ zT9Vhqk4%MR!dgO}ocunw>M}@j2=c7uVfL7PyX!pX;NpV^-d#NK^x9#Zm~?If-YaPD zJ|GX2UESWj7_Kk@2V&&pKY5@KLbC2D$d$D4!zBI`1Iw*T3s(f&<%i4pU4{BQnO~H@ z$p7f>uqetAY)TL~Z}pQ73y|K0h)=XTcMG?Qu>YL)kEr!H-Grh)O@Ib865Ewoqgk{jZ&~~} z`fPo7Zzr*bC>h0q-VuIi(*3KJ*rm>1gYEiZ(GmRXpi0KXbc`~0j`d@;PMb3Lho4&W zN*SmScEcly*^?j*drV9mQ=FAZX33crJn2Osy8X*On26QosHsJM?S9y89`#sCgWKlF$M(UhACZgOtZ(Lic+k&YDqnNG zIU-Z#-E`FdD@0%P%fk0STY=4&|82DOmYbcJaLtHa1jFTd|K7zP)q${)2Zt9r;)+zP z;33k>@7wgtP13R2{Dh}3R#S0PKjl@|Yf#L0sX*SS{#_h}hd*!M-T!hb7uNS0X>Gg? zZosrMHBBsFiH17$97q2;H;f-MF=_O1$gLmdzH$1q2ykX>oDOc2*@c`7F%KX?a$GPx z)g_0m=J#mIFyY%(R$_9nUUX;He#3XEwD^)|5UuPyEg70A;#Qs*q8+)AezzJdzfUKA zo)?oo@l@GLpxTyy7ZFoUYQq2Qs<`RTt#ywCX>HtPG1`QZ^rv5UPff3p5nRc?yw26c zs3j9LL`8mv;6(GKs>!YBcXFk9Mv)m>{Uv1maJ|Xet?1K)fhgN8g*JYI)!J*96q%_= z^Jf)$TE6wgwyX~R<7>CmHHkyW(7j_CEPv*4=$ONmspNIQ6CEPX5WuroZQt8+ zjE&J#nHKf>v9|O|pgRqnGyDyt1GDM z^~HGzV|m35o=|SBNz+5ozN4D%HN56IrwTHM(reH?(_-iWa;v0=q$_9RL$FDV|JUv{ z6}oH<&pC6~5Cd1qg|h7>pqsabe}uh8pSkj7zq^w28GgZEWCPd@Vn3aF z3yXos=Gr6$N+KA_!HM#JCbbDyH+8Bzr?<%V@ca!RouI_i$-QIRGUwD!IjZ2yt=%o|xX!XB4?%StOj(kD{r)#XXYd{Empr11sh_zAM-=(LH!VlMv^Qv&ISNn!OC zRIk*=459rcyGV_O2@B`PA*^nRc8VOuNMlx1IUE~1R4pE+xNwckx%dEu+>YJ*Dje{p z=q>SWmNY)1AZgy>uV$VRx?d8jcoj`bDez9SS?rCRQt+eeheekY67H7W1@K@hwzgq(p%Ti0CgLg0K4FZ|dwc%Z=0o+CdaGmvau*Us; zkxrTQWWv_CChBbX$o6HFaCL9mP2gmS_H&8wB1!}~=c5e|%x{;AQqTV-HQ?~I_U0aH zdVku#NDmk!6$(AtXiGc)U+^QbFBFWKhrbHePGTY3_X#(3XGr-6?Z+Hqhmi#%P_c#! zsnW$kpJSxxD65qI#URHidgN#i{0W$p19EPxK)rZ-(8DXwUOPJ=?oe z$_^5aHKGSsZJ-e=^E>2a*4NDY6_DA>BlNyZq!J{68|f4CLIk7jhMJYcqMr~Gi-v6# z&O15)_O+T%&t_4Yiq5{T zjK7h1V>d{@Jd))WZr~9xP%aN|{846~!SUC>)2+wglq_JOB0lYi%=1EMM5fxn%yCG= z%x=7+`AzEJNXA(A*VI*2iznZic9*dqGP+7ike53hmaVN>J&I4aSHtU2G{q0*;&ar& zC#2~%b9J>cC>C-n5>`SXN#E2)iAJY)h|ieR{=k`7^^&NN&Az{A2P&Zb&4O~tsZq%% zX?^r7o=#TKQ^@_!nV?;z@4(8Szy2;J+tA~^*Xzo^Z)Y3M2^F3Nkobsfe#bH(U+Q4o zEWt_XQO6~D;Id^Z%Fc!`GuG~x!u8>N_3{3zvm$w73XtSU0Sh_s!Xnjm9li%{n};VJ z&p9)wKTb_i@1{;XfeJe9txTULYa+~fn;Y>+W%dXYOQJ5R%KgjB6r!;E38pQ!B=i5S zqIdWl9tbCBU=%CO>AzW@COZ$k+r;gen2PdHcYGE+Go|_R1LacvYRC`9M>-`CiXg3H z)UGzg(%&qvC;rh(u5Ii+3HNCeJuKG_J_HG+^d;NeLz1@lx9PFI+SSao(!6eZ{T2fZ zZAVGSX*ry0fmNAdbF*vIf6VZ!+vVTW8v6Q#w_S%aSN-72fecexsn9#S?3K9HwgtVL z87lYn60y$y9f)N0Q+Zr?Z4sE7R!x?Ltg%~qV}X4)naB&>aC(z7gU0cU+nuNj{7T;& zOmn<7h9`a+pv~G#K8}Hx;?pF($5AnZDep)uF^E7*Ywo{p;ZfL$AT4)W3`Q)~qvJ%+ zLy{L0GZrD6VH962eInGCF&o7_M7WJ_&d;P<;h(NGDq2$h&(eF&<}!DF9#qD zV1dm@m$xULZ~pMfU0Yb{+mXlFt;1R4PDFM%N`CMgdiF79{CIbMmDIPZVq7IZ3|WgA^z#9 z6hN^Xa`Z}6ip`!E58uA(P&$1Ed)&qz{g^i&TpJwmgZoX#%ARU$X>@R~9)2PF<4_{v z-009pZBK51b{FsKlb}=lDR#e>!Ini4nK~I6(T1DME;G>9NQACt5r@(9Nn0Z z=u@T`YRnvdOHI^yZ_+?4VEQ|O=MYzOZR5KrW?im3i$7>lt*^|^uOEDsA68s7QWMD| z$zRrlfL!7FJd=dMd(^5wq+joUe%~1FutsPziWKyXm0={vl|}RMV#I`LULe~igL<04 zXG~$FeftpQ~ndh1_CmIb|G1sYI~SLUVpQT#7#jNOt40ZFdLx0dfyJl zJ8rxqOXIhCgt|ukUIAsx=juK?@V9&G_F_P9ibutGi7O|l&uEVx%>_?=ZC5)?W#`~W zJo$b*5~&@HbO^xEmXNgY_rG|{-zNMLtHi$i{$6(y+7s3Dvqri43d8Z^8q}+WekXlw z8=}CB#xa*^5-^p!uU12oAB0phcpJ6c>WS6e>FDX*?r4vp@&=ehy|C}1nOW}4*2@

`ZD>pn_`K`p>U&J*xOsyaPKNf%scFe&WW^8|z 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 z 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“t|6MMg2z??w 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

{@A?XGHX8#l|Y-x0}E;?T|m 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

>a)`XR})}K+_q8R3Aj5pY`vWWLi*IZ9@|0B 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

  • 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
    a0p4*MOh9zEjpVT0G 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

    XX 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 RMmqSTb5&n6|B6-U{{p3(zLEd{ literal 0 HcmV?d00001 diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 1781dfac906..aa83955621b 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -54,11 +54,8 @@ func defaultSpamRecordCacheFactory() SpamRecordCacheFactory { } } -// MisbehaviorReportManager is responsible for handling misbehavior reports. -// The current version is at the minimum viable product stage and only logs the reports. -// TODO: the mature version should be able to handle the reports and take actions accordingly, i.e., penalize the misbehaving node -// -// and report the node to be disallow-listed if the overall penalty of the misbehaving node drops below the disallow-listing threshold. +// MisbehaviorReportManager is responsible for handling misbehavior reports, i.e., penalizing the misbehaving node +// and report the node to be disallow-listed if the overall penalty of the misbehaving node drops below the disallow-listing threshold. type MisbehaviorReportManager struct { component.Component logger zerolog.Logger From e6a2f3892afd0bb46fc03ff8242ad377081ee83e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 13 Jun 2023 09:51:27 -0700 Subject: [PATCH 136/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index e8727919464..e9acafd3ef5 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -249,7 +249,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // creates a misbehavior report for the spammer report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) - // imitates that the victim has detected the spammer on 120 times of violations and reports the misbehavior + // simulates the victim node reporting the spammer node misbehavior 120 times``` // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that // the spammer is definitely disallow-listed. From 5290e7f0a0a3511d2ebb74c6e5183b1f0baa563b Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 00:02:45 +0300 Subject: [PATCH 137/815] Added chained interceptor --- Makefile | 1 - apiproxy/access_api_proxy.go | 8 +- .../node_builder/access_node_builder.go | 2 +- engine/access/apiproxy/access_api_proxy.go | 8 +- engine/access/rpc/backend/backend.go | 186 +++++++++- engine/access/rpc/backend/backend_accounts.go | 3 +- engine/access/rpc/backend/backend_events.go | 3 +- engine/access/rpc/backend/backend_scripts.go | 3 +- engine/access/rpc/backend/backend_test.go | 32 -- .../rpc/backend/backend_transactions.go | 39 +- .../access/rpc/backend/connection_factory.go | 64 ++-- .../access/rpc/backend/connection_selector.go | 348 ------------------ .../rpc/backend/mock/connection_selector.go | 82 ----- engine/access/rpc/engine.go | 3 - 14 files changed, 259 insertions(+), 523 deletions(-) delete mode 100644 engine/access/rpc/backend/connection_selector.go delete mode 100644 engine/access/rpc/backend/mock/connection_selector.go diff --git a/Makefile b/Makefile index 3e4c5ac62ec..f6726da1395 100644 --- a/Makefile +++ b/Makefile @@ -180,7 +180,6 @@ generate-mocks: install-mock-generators mockery --name 'API' --dir="./engine/protocol" --case=underscore --output="./engine/protocol/mock" --outpkg="mock" mockery --name '.*' --dir="./engine/access/state_stream" --case=underscore --output="./engine/access/state_stream/mock" --outpkg="mock" mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" - mockery --name 'ConnectionSelector' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" mockery --name 'IngestRPC' --dir="./engine/execution/ingestion" --case=underscore --tags relic --output="./engine/execution/ingestion/mock" --outpkg="mock" mockery --name '.*' --dir=model/fingerprint --case=underscore --output="./model/fingerprint/mock" --outpkg="mock" mockery --name 'ExecForkActor' --structname 'ExecForkActorMock' --dir=module/mempool/consensus/mock/ --case=underscore --output="./module/mempool/consensus/mock/" --outpkg="mock" diff --git a/apiproxy/access_api_proxy.go b/apiproxy/access_api_proxy.go index d54b1dab483..dfe610f5857 100644 --- a/apiproxy/access_api_proxy.go +++ b/apiproxy/access_api_proxy.go @@ -86,9 +86,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithInsecure(), //nolint:staticcheck - backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ - Enabled: false, - })) + grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) if err != nil { return err } @@ -102,9 +100,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ - Enabled: false, - })) + grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index d24fe4537bd..cdf66e66bf0 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -926,7 +926,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.rpcConf.CollectionAddr, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout, builder.rpcConf.CircuitBreakerConfig)) + grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout))) if err != nil { return err } diff --git a/engine/access/apiproxy/access_api_proxy.go b/engine/access/apiproxy/access_api_proxy.go index 7123411cc2b..ce95c1cca28 100644 --- a/engine/access/apiproxy/access_api_proxy.go +++ b/engine/access/apiproxy/access_api_proxy.go @@ -65,9 +65,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ - Enabled: false, - })) + grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) if err != nil { return err } @@ -81,9 +79,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientUnaryInterceptor(timeout, backend.CircuitBreakerConfig{ - Enabled: false, - })) + grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 19996285231..868858c8876 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,8 +3,10 @@ package backend import ( "context" "fmt" + "github.com/onflow/flow-go/model/flow/filter" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "time" lru "github.com/hashicorp/golang-lru" accessproto "github.com/onflow/flow/protobuf/go/flow/access" @@ -73,7 +75,6 @@ type Backend struct { collections storage.Collections executionReceipts storage.ExecutionReceipts connFactory ConnectionFactory - connSelector ConnectionSelector } func New( @@ -89,7 +90,6 @@ func New( chainID flow.ChainID, accessMetrics module.AccessMetrics, connFactory ConnectionFactory, - connSelector ConnectionSelector, retryEnabled bool, maxHeightRange uint, preferredExecutionNodeIDs []string, @@ -115,7 +115,6 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, - connSelector: connSelector, state: state, log: log, metrics: accessMetrics, @@ -134,7 +133,6 @@ func New( transactionMetrics: accessMetrics, retry: retry, connFactory: connFactory, - connSelector: connSelector, previousAccessNodes: historicalAccessNodes, log: log, }, @@ -143,7 +141,6 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, - connSelector: connSelector, log: log, maxHeightRange: maxHeightRange, }, @@ -160,7 +157,6 @@ func New( headers: headers, executionReceipts: executionReceipts, connFactory: connFactory, - connSelector: connSelector, log: log, }, backendExecutionResults: backendExecutionResults{ @@ -174,7 +170,6 @@ func New( collections: collections, executionReceipts: executionReceipts, connFactory: connFactory, - connSelector: connSelector, chainID: chainID, } @@ -289,3 +284,180 @@ func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, err return convert.SnapshotToBytes(validSnapshot) } + +// executionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities +// which have executed the given block ID. +// If no such execution node is found, an InsufficientExecutionReceipts error is returned. +func executionNodesForBlockID( + ctx context.Context, + blockID flow.Identifier, + executionReceipts storage.ExecutionReceipts, + state protocol.State, + log zerolog.Logger) (flow.IdentityList, error) { + + var executorIDs flow.IdentifierList + + // check if the block ID is of the root block. If it is then don't look for execution receipts since they + // will not be present for the root block. + rootBlock, err := state.Params().FinalizedRoot() + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + + if rootBlock.ID() == blockID { + executorIdentities, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = executorIdentities.NodeIDs() + } else { + // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { + executorIDs, err = findAllExecutionNodes(blockID, executionReceipts, log) + if err != nil { + return nil, err + } + + if len(executorIDs) >= minExecutionNodesCnt { + break + } + + // log the attempt + log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + Int("execution_receipts_found", len(executorIDs)). + Str("block_id", blockID.String()). + Msg("insufficient execution receipts") + + // if one or less execution receipts may have been received then re-query + // in the hope that more might have been received by now + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(100 * time.Millisecond << time.Duration(attempt)): + //retry after an exponential backoff + } + } + + receiptCnt := len(executorIDs) + // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs + if receiptCnt < minExecutionNodesCnt { + newExecutorIDs, err := state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = newExecutorIDs.NodeIDs() + } + } + + // choose from the preferred or fixed execution nodes + subsetENs, err := chooseExecutionNodes(state, executorIDs) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + + // randomly choose upto maxExecutionNodesCnt identities + executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) + + if len(executionIdentitiesRandom) == 0 { + return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) + } + + return executionIdentitiesRandom, nil +} + +// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the +// given blockID +func findAllExecutionNodes( + blockID flow.Identifier, + executionReceipts storage.ExecutionReceipts, + log zerolog.Logger) (flow.IdentifierList, error) { + + // lookup the receipt's storage with the block ID + allReceipts, err := executionReceipts.ByBlockID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) + } + + executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) + for _, r := range allReceipts { + executionResultMetaList = append(executionResultMetaList, r.Meta()) + } + executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() + + // maximum number of matching receipts found so far for any execution result id + maxMatchedReceiptCnt := 0 + // execution result id key for the highest number of matching receipts in the identicalReceipts map + var maxMatchedReceiptResultID flow.Identifier + + // find the largest list of receipts which have the same result ID + for resultID, executionReceiptList := range executionResultGroupedMetaList { + currentMatchedReceiptCnt := executionReceiptList.Size() + if currentMatchedReceiptCnt > maxMatchedReceiptCnt { + maxMatchedReceiptCnt = currentMatchedReceiptCnt + maxMatchedReceiptResultID = resultID + } + } + + // if there are more than one execution result for the same block ID, log as error + if executionResultGroupedMetaList.NumberGroups() > 1 { + identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) + log.Error(). + Str("block_id", blockID.String()). + Str("execution_receipts", identicalReceiptsStr). + Msg("execution receipt mismatch") + } + + // pick the largest list of matching receipts + matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) + + metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() + + // collect all unique execution node ids from the receipts + var executorIDs flow.IdentifierList + for executorID := range metaReceiptGroupedByExecutorID { + executorIDs = append(executorIDs, executorID) + } + + return executorIDs, nil +} + +// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first +// choosing the preferred execution nodes which have executed the transaction. If no such preferred +// execution nodes are found, then the fixed execution nodes defined in the identity table are returned +// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. +// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} +// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs +func chooseExecutionNodes(state protocol.State, executorIDs flow.IdentifierList) (flow.IdentityList, error) { + + allENs, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive all execution IDs: %w", err) + } + + // first try and choose from the preferred EN IDs + var chosenIDs flow.IdentityList + if len(preferredENIdentifiers) > 0 { + // find the preferred execution node IDs which have executed the transaction + chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(preferredENIdentifiers...), + filter.HasNodeID(executorIDs...))) + if len(chosenIDs) > 0 { + return chosenIDs, nil + } + } + + // if no preferred EN ID is found, then choose from the fixed EN IDs + if len(fixedENIdentifiers) > 0 { + // choose fixed ENs which have executed the transaction + chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(fixedENIdentifiers...), filter.HasNodeID(executorIDs...))) + if len(chosenIDs) > 0 { + return chosenIDs, nil + } + // if no such ENs are found then just choose all fixed ENs + chosenIDs = allENs.Filter(filter.HasNodeID(fixedENIdentifiers...)) + return chosenIDs, nil + } + + // If no preferred or fixed ENs have been specified, then return all executor IDs i.e. no preference at all + return allENs.Filter(filter.HasNodeID(executorIDs...)), nil +} diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index 4781e395d23..a3a41053c61 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -22,7 +22,6 @@ type backendAccounts struct { headers storage.Headers executionReceipts storage.ExecutionReceipts connFactory ConnectionFactory - connSelector ConnectionSelector log zerolog.Logger } @@ -83,7 +82,7 @@ func (b *backendAccounts) getAccountAtBlockID( BlockId: blockID[:], } - execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { return nil, rpc.ConvertError(err, "failed to get account from the execution node", codes.Internal) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index f2481b61288..f48ba395947 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -25,7 +25,6 @@ type backendEvents struct { executionReceipts storage.ExecutionReceipts state protocol.State connFactory ConnectionFactory - connSelector ConnectionSelector log zerolog.Logger maxHeightRange uint } @@ -130,7 +129,7 @@ func (b *backendEvents) getBlockEventsFromExecutionNode( // choose the last block ID to find the list of execution nodes lastBlockID := blockIDs[len(blockIDs)-1] - execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, lastBlockID) + execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) if err != nil { b.log.Error().Err(err).Msg("failed to retrieve events from execution node") return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 48df09d6f12..e6da62a5b7b 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -28,7 +28,6 @@ type backendScripts struct { executionReceipts storage.ExecutionReceipts state protocol.State connFactory ConnectionFactory - connSelector ConnectionSelector log zerolog.Logger metrics module.BackendScriptsMetrics loggedScripts *lru.Cache @@ -92,7 +91,7 @@ func (b *backendScripts) findScriptExecutors( return b.archiveAddressList, nil } - executors, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) + executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { return nil, err } diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index a2d34eca298..9751f64b865 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -100,7 +100,6 @@ func (suite *Suite) TestPing() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -136,7 +135,6 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -202,7 +200,6 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, 100, nil, @@ -275,7 +272,6 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, 100, nil, @@ -341,7 +337,6 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, 100, nil, @@ -418,7 +413,6 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, 100, nil, @@ -479,7 +473,6 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -518,7 +511,6 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -565,7 +557,6 @@ func (suite *Suite) TestGetTransaction() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -606,7 +597,6 @@ func (suite *Suite) TestGetCollection() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -670,7 +660,6 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -734,7 +723,6 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -826,7 +814,6 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -947,7 +934,6 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -1115,7 +1101,6 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, 100, nil, @@ -1174,7 +1159,6 @@ func (suite *Suite) TestTransactionResultUnknown() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -1229,7 +1213,6 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -1360,7 +1343,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1393,7 +1375,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1453,7 +1434,6 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1484,7 +1464,6 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1548,7 +1527,6 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1580,7 +1558,6 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1731,7 +1708,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1771,7 +1747,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1810,7 +1785,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1848,7 +1822,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, 1, // set maximum range to 1 nil, @@ -1886,7 +1859,6 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -1964,7 +1936,6 @@ func (suite *Suite) TestGetAccount() { suite.chainID, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -2046,7 +2017,6 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { flow.Testnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, @@ -2086,7 +2056,6 @@ func (suite *Suite) TestGetNetworkParameters() { flow.Mainnet, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -2281,7 +2250,6 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { flow.Mainnet, metrics.NewNoopCollector(), connFactory, // the connection factory should be used to get the execution node client - nil, false, DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 535387b63f2..5bc31162a07 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -24,6 +24,8 @@ import ( "github.com/onflow/flow-go/storage" ) +const collectionNodesToTry uint = 3 + type backendTransactions struct { staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node transactions storage.Transactions @@ -36,7 +38,6 @@ type backendTransactions struct { transactionValidator *access.TransactionValidator retry *Retry connFactory ConnectionFactory - connSelector ConnectionSelector previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger @@ -85,7 +86,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T } // otherwise choose a random set of collections nodes to try - collAddrs, err := b.connSelector.GetCollectionNodes(tx.ID()) + collAddrs, err := b.chooseCollectionNodes(tx, collectionNodesToTry) if err != nil { return fmt.Errorf("failed to determine collection node for tx %x: %w", tx, err) } @@ -111,6 +112,34 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T return sendErrors.ErrorOrNil() } +// chooseCollectionNodes finds a random subset of size sampleSize of collection node addresses from the +// collection node cluster responsible for the given tx +func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sampleSize uint) ([]string, error) { + + // retrieve the set of collector clusters + clusters, err := b.state.Final().Epochs().Current().Clustering() + if err != nil { + return nil, fmt.Errorf("could not cluster collection nodes: %w", err) + } + + // get the cluster responsible for the transaction + txCluster, ok := clusters.ByTxID(tx.ID()) + if !ok { + return nil, fmt.Errorf("could not get local cluster by txID: %x", tx.ID()) + } + + // select a random subset of collection nodes from the cluster to be tried in order + targetNodes := txCluster.Sample(sampleSize) + + // collect the addresses of all the chosen collection nodes + var targetAddrs = make([]string, len(targetNodes)) + for i, id := range targetNodes { + targetAddrs[i] = id.Address + } + + return targetAddrs, nil +} + // sendTransactionToCollection sends the transaction to the given collection node via grpc func (b *backendTransactions) sendTransactionToCollector(ctx context.Context, tx *flow.TransactionBody, @@ -342,7 +371,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( req := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockID[:], } - execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -473,7 +502,7 @@ func (b *backendTransactions) GetTransactionResultByIndex( BlockId: blockID[:], Index: index, } - execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -702,7 +731,7 @@ func (b *backendTransactions) getTransactionResultFromExecutionNode( TransactionId: transactionID, } - execNodes, err := b.connSelector.GetExecutionNodesForBlockID(ctx, blockID) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { // if no execution receipt were found, return a NotFound GRPC error if IsInsufficientExecutionReceipts(err) { diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 37cf965c372..a5f63ab9eb1 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -101,7 +101,8 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(cf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithKeepaliveParams(keepaliveParams), - WithClientUnaryInterceptor(timeout, cf.CircuitBreakerConfig)) + cf.withChainUnaryInterceptor(timeout), + ) if err != nil { return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) } @@ -257,18 +258,42 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func WithClientUnaryInterceptor(timeout time.Duration, circuitBreakerConfig CircuitBreakerConfig) grpc.DialOption { - var circuitBreaker *gobreaker.CircuitBreaker +func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration) grpc.DialOption { + var clientInterceptors []grpc.UnaryClientInterceptor - if circuitBreakerConfig.Enabled { - circuitBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{ - Timeout: circuitBreakerConfig.RestoreTimeout, + if cf.CircuitBreakerConfig.Enabled { + circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ + Timeout: cf.CircuitBreakerConfig.RestoreTimeout, ReadyToTrip: func(counts gobreaker.Counts) bool { - return counts.ConsecutiveFailures >= circuitBreakerConfig.MaxFailures + return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, }) + + interceptor := func( + ctx context.Context, + method string, + req interface{}, + reply interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, + ) error { + _, err := circuitBreaker.Execute(func() (interface{}, error) { + err := invoker(ctx, method, req, reply, cc, opts...) + + return nil, err + }) + return err + } + clientInterceptors = append(clientInterceptors, interceptor) } + clientInterceptors = append(clientInterceptors, WithClientUnaryInterceptor(timeout)) + + return grpc.WithChainUnaryInterceptor(clientInterceptors...) +} + +func WithClientUnaryInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -278,29 +303,16 @@ func WithClientUnaryInterceptor(timeout time.Duration, circuitBreakerConfig Circ invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - exec := func() (interface{}, error) { - // create a context that expires after timeout - ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - - defer cancel() + // create a context that expires after timeout + ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - // call the remote GRPC using the short context - err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) + defer cancel() - //TODO: As invoker do not return any results, for now nil returned - return nil, err - } - - var err error - - if circuitBreakerConfig.Enabled { - _, err = circuitBreaker.Execute(exec) - } else { - _, err = exec() - } + // call the remote GRPC using the short context + err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) return err } - return grpc.WithUnaryInterceptor(clientTimeoutInterceptor) + return clientTimeoutInterceptor } diff --git a/engine/access/rpc/backend/connection_selector.go b/engine/access/rpc/backend/connection_selector.go deleted file mode 100644 index c3bbfcf722c..00000000000 --- a/engine/access/rpc/backend/connection_selector.go +++ /dev/null @@ -1,348 +0,0 @@ -package backend - -import ( - "context" - "fmt" - "time" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/state/protocol" - "github.com/onflow/flow-go/storage" - "github.com/rs/zerolog" -) - -const collectionNodesToTry uint = 3 - -type ConnectionSelector interface { - GetExecutionNodesForBlockID(ctx context.Context, blockID flow.Identifier) (flow.IdentityList, error) - GetCollectionNodes(txID flow.Identifier) ([]string, error) -} - -type MainConnectionSelector struct { - state protocol.State - executionReceipts storage.ExecutionReceipts - log zerolog.Logger -} - -type CircuitBreakerConnectionSelector MainConnectionSelector - -var _ ConnectionSelector = (*MainConnectionSelector)(nil) - -func NewConnectionSelector( - state protocol.State, - executionReceipts storage.ExecutionReceipts, - log zerolog.Logger, - isCircuitBreakerEnabled bool, -) ConnectionSelector { - if isCircuitBreakerEnabled { - return &CircuitBreakerConnectionSelector{ - state: state, - executionReceipts: executionReceipts, - log: log, - } - } else { - return &MainConnectionSelector{ - state: state, - executionReceipts: executionReceipts, - log: log, - } - } -} - -func (mcs *MainConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { - // retrieve the set of collector clusters - clusters, err := mcs.state.Final().Epochs().Current().Clustering() - if err != nil { - return nil, fmt.Errorf("could not cluster collection nodes: %w", err) - } - - // get the cluster responsible for the transaction - txCluster, ok := clusters.ByTxID(txId) - if !ok { - return nil, fmt.Errorf("could not get local cluster by txID: %x", txId) - } - - // select a random subset of collection nodes from the cluster to be tried in order - targetNodes := txCluster.Sample(collectionNodesToTry) - - // collect the addresses of all the chosen collection nodes - var targetAddrs = make([]string, len(targetNodes)) - for i, id := range targetNodes { - targetAddrs[i] = id.Address - } - - return targetAddrs, nil -} - -// GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities -// which have executed the given block ID. -// If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func (mcs *MainConnectionSelector) GetExecutionNodesForBlockID( - ctx context.Context, - blockID flow.Identifier, -) (flow.IdentityList, error) { - - var executorIDs flow.IdentifierList - - // check if the block ID is of the root block. If it is then don't look for execution receipts since they - // will not be present for the root block. - rootBlock, err := mcs.state.Params().FinalizedRoot() - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - if rootBlock.ID() == blockID { - executorIdentities, err := mcs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = executorIdentities.NodeIDs() - } else { - // try to find atleast minExecutionNodesCnt execution node ids from the execution receipts for the given blockID - for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, mcs.executionReceipts, mcs.log) - if err != nil { - return nil, err - } - - if len(executorIDs) >= minExecutionNodesCnt { - break - } - - // log the attempt - mcs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). - Int("execution_receipts_found", len(executorIDs)). - Str("block_id", blockID.String()). - Msg("insufficient execution receipts") - - // if one or less execution receipts may have been received then re-query - // in the hope that more might have been received by now - - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-time.After(100 * time.Millisecond << time.Duration(attempt)): - //retry after an exponential backoff - } - } - - receiptCnt := len(executorIDs) - // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs - if receiptCnt < minExecutionNodesCnt { - newExecutorIDs, err := mcs.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = newExecutorIDs.NodeIDs() - } - } - - // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(mcs.state, executorIDs) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - // randomly choose upto maxExecutionNodesCnt identities - executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) - - if len(executionIdentitiesRandom) == 0 { - return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) - } - - return executionIdentitiesRandom, nil -} - -func (cbcs *CircuitBreakerConnectionSelector) GetCollectionNodes(txId flow.Identifier) ([]string, error) { - // retrieve the set of collector clusters - clusters, err := cbcs.state.Final().Epochs().Current().Clustering() - if err != nil { - return nil, fmt.Errorf("could not cluster collection nodes: %w", err) - } - - // get the cluster responsible for the transaction - txCluster, ok := clusters.ByTxID(txId) - if !ok { - return nil, fmt.Errorf("could not get local cluster by txID: %x", txId) - } - - // collect the addresses of all the chosen collection nodes - var targetAddress = make([]string, len(txCluster)) - for i, id := range txCluster { - targetAddress[i] = id.Address - } - - return targetAddress, nil -} - -// GetExecutionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities -// which have executed the given block ID. -// If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func (cbcs *CircuitBreakerConnectionSelector) GetExecutionNodesForBlockID( - ctx context.Context, - blockID flow.Identifier, -) (flow.IdentityList, error) { - - var executorIDs flow.IdentifierList - - // check if the block ID is of the root block. If it is then don't look for execution receipts since they - // will not be present for the root block. - rootBlock, err := cbcs.state.Params().FinalizedRoot() - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - if rootBlock.ID() == blockID { - executorIdentities, err := cbcs.state.Final().Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = executorIdentities.NodeIDs() - } else { - // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID - for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, cbcs.executionReceipts, cbcs.log) - if err != nil { - return nil, err - } - - if len(executorIDs) > 0 { - break - } - - // log the attempt - cbcs.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). - Int("execution_receipts_found", len(executorIDs)). - Str("block_id", blockID.String()). - Msg("insufficient execution receipts") - - // if one or less execution receipts may have been received then re-query - // in the hope that more might have been received by now - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-time.After(100 * time.Millisecond << time.Duration(attempt)): - //retry after an exponential backoff - } - } - - receiptCnt := len(executorIDs) - // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs - if receiptCnt < minExecutionNodesCnt { - newExecutorIDs, err := cbcs.state.AtBlockID(blockID).Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = newExecutorIDs.NodeIDs() - } - } - - // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(cbcs.state, executorIDs) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - if len(subsetENs) == 0 { - return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) - } - - return subsetENs, nil -} - -// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first -// choosing the preferred execution nodes which have executed the transaction. If no such preferred -// execution nodes are found, then the fixed execution nodes defined in the identity table are returned -// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. -// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} -// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs -func chooseExecutionNodes(state protocol.State, executorIDs flow.IdentifierList) (flow.IdentityList, error) { - - allENs, err := state.Final().Identities(filter.HasRole(flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive all execution IDs: %w", err) - } - - // first try and choose from the preferred EN IDs - var chosenIDs flow.IdentityList - if len(preferredENIdentifiers) > 0 { - // find the preferred execution node IDs which have executed the transaction - chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(preferredENIdentifiers...), - filter.HasNodeID(executorIDs...))) - if len(chosenIDs) > 0 { - return chosenIDs, nil - } - } - - // if no preferred EN ID is found, then choose from the fixed EN IDs - if len(fixedENIdentifiers) > 0 { - // choose fixed ENs which have executed the transaction - chosenIDs = allENs.Filter(filter.And(filter.HasNodeID(fixedENIdentifiers...), filter.HasNodeID(executorIDs...))) - if len(chosenIDs) > 0 { - return chosenIDs, nil - } - // if no such ENs are found then just choose all fixed ENs - chosenIDs = allENs.Filter(filter.HasNodeID(fixedENIdentifiers...)) - return chosenIDs, nil - } - - // If no preferred or fixed ENs have been specified, then return all executor IDs i.e. no preference at all - return allENs.Filter(filter.HasNodeID(executorIDs...)), nil -} - -// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the -// given blockID -func findAllExecutionNodes( - blockID flow.Identifier, - executionReceipts storage.ExecutionReceipts, - log zerolog.Logger) (flow.IdentifierList, error) { - - // lookup the receipt's storage with the block ID - allReceipts, err := executionReceipts.ByBlockID(blockID) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) - } - - executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) - for _, r := range allReceipts { - executionResultMetaList = append(executionResultMetaList, r.Meta()) - } - executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() - - // maximum number of matching receipts found so far for any execution result id - maxMatchedReceiptCnt := 0 - // execution result id key for the highest number of matching receipts in the identicalReceipts map - var maxMatchedReceiptResultID flow.Identifier - - // find the largest list of receipts which have the same result ID - for resultID, executionReceiptList := range executionResultGroupedMetaList { - currentMatchedReceiptCnt := executionReceiptList.Size() - if currentMatchedReceiptCnt > maxMatchedReceiptCnt { - maxMatchedReceiptCnt = currentMatchedReceiptCnt - maxMatchedReceiptResultID = resultID - } - } - - // if there are more than one execution result for the same block ID, log as error - if executionResultGroupedMetaList.NumberGroups() > 1 { - identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) - log.Error(). - Str("block_id", blockID.String()). - Str("execution_receipts", identicalReceiptsStr). - Msg("execution receipt mismatch") - } - - // pick the largest list of matching receipts - matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) - - metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() - - // collect all unique execution node ids from the receipts - var executorIDs flow.IdentifierList - for executorID := range metaReceiptGroupedByExecutorID { - executorIDs = append(executorIDs, executorID) - } - - return executorIDs, nil -} diff --git a/engine/access/rpc/backend/mock/connection_selector.go b/engine/access/rpc/backend/mock/connection_selector.go deleted file mode 100644 index 6337683391f..00000000000 --- a/engine/access/rpc/backend/mock/connection_selector.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mock - -import ( - context "context" - - flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" -) - -// ConnectionSelector is an autogenerated mock type for the ConnectionSelector type -type ConnectionSelector struct { - mock.Mock -} - -// GetCollectionNodes provides a mock function with given fields: txID -func (_m *ConnectionSelector) GetCollectionNodes(txID flow.Identifier) ([]string, error) { - ret := _m.Called(txID) - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) ([]string, error)); ok { - return rf(txID) - } - if rf, ok := ret.Get(0).(func(flow.Identifier) []string); ok { - r0 = rf(txID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { - r1 = rf(txID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetExecutionNodesForBlockID provides a mock function with given fields: ctx, blockID -func (_m *ConnectionSelector) GetExecutionNodesForBlockID(ctx context.Context, blockID flow.Identifier) (flow.IdentityList, error) { - ret := _m.Called(ctx, blockID) - - var r0 flow.IdentityList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, flow.Identifier) (flow.IdentityList, error)); ok { - return rf(ctx, blockID) - } - if rf, ok := ret.Get(0).(func(context.Context, flow.Identifier) flow.IdentityList); ok { - r0 = rf(ctx, blockID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(flow.IdentityList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, flow.Identifier) error); ok { - r1 = rf(ctx, blockID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -type mockConstructorTestingTNewConnectionSelector interface { - mock.TestingT - Cleanup(func()) -} - -// NewConnectionSelector creates a new instance of ConnectionSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewConnectionSelector(t mockConstructorTestingTNewConnectionSelector) *ConnectionSelector { - mock := &ConnectionSelector{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 10916425aa6..5947ee6cd7d 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -175,8 +175,6 @@ func NewBuilder(log zerolog.Logger, CircuitBreakerConfig: config.CircuitBreakerConfig, } - connectionSelector := backend.NewConnectionSelector(state, executionReceipts, log, config.CircuitBreakerConfig.Enabled) - backend := backend.New(state, collectionRPC, historicalAccessNodes, @@ -189,7 +187,6 @@ func NewBuilder(log zerolog.Logger, chainID, accessMetrics, connectionFactory, - connectionSelector, retryEnabled, config.MaxHeightRange, config.PreferredExecutionNodeIDs, From d95f01e26223cfdcb67efe9d8a9d56e593f1bd27 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 00:40:17 +0300 Subject: [PATCH 138/815] fixed tests --- engine/access/access_test.go | 5 ---- engine/access/rpc/backend/backend.go | 6 ++--- engine/access/rpc/backend/backend_test.go | 25 +++---------------- .../rpc/backend/connection_factory_test.go | 2 +- .../rpc/backend/historical_access_test.go | 2 -- engine/access/rpc/backend/retry_test.go | 2 -- 6 files changed, 8 insertions(+), 34 deletions(-) diff --git a/engine/access/access_test.go b/engine/access/access_test.go index 3dccd604980..63d7af2d76c 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -150,7 +150,6 @@ func (suite *Suite) RunTest( suite.chainID, suite.metrics, nil, - nil, false, backend.DefaultMaxHeightRange, nil, @@ -323,7 +322,6 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { suite.chainID, metrics, connFactory, - nil, false, backend.DefaultMaxHeightRange, nil, @@ -650,7 +648,6 @@ func (suite *Suite) TestGetSealedTransaction() { suite.chainID, suite.metrics, connFactory, - nil, false, backend.DefaultMaxHeightRange, nil, @@ -790,7 +787,6 @@ func (suite *Suite) TestGetTransactionResult() { suite.chainID, suite.metrics, connFactory, - nil, false, backend.DefaultMaxHeightRange, nil, @@ -982,7 +978,6 @@ func (suite *Suite) TestExecuteScript() { suite.chainID, suite.metrics, connFactory, - nil, false, backend.DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 868858c8876..954a04981c3 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,20 +3,20 @@ package backend import ( "context" "fmt" - "github.com/onflow/flow-go/model/flow/filter" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "time" lru "github.com/hashicorp/golang-lru" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 9751f64b865..923af00cacc 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -3,6 +3,8 @@ package backend import ( "context" "fmt" + "testing" + "github.com/dgraph-io/badger/v2" accessproto "github.com/onflow/flow/protobuf/go/flow/access" entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" @@ -14,7 +16,6 @@ import ( "github.com/stretchr/testify/suite" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "testing" access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" @@ -26,7 +27,6 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" - "github.com/onflow/flow-go/utils/rand" "github.com/onflow/flow-go/utils/unittest" ) @@ -47,7 +47,6 @@ type Suite struct { execClient *access.ExecutionAPIClient historicalAccessClient *access.AccessAPIClient connectionFactory *backendmock.ConnectionFactory - nil *backendmock.ConnectionSelector chainID flow.ChainID } @@ -75,7 +74,6 @@ func (suite *Suite) SetupTest() { suite.chainID = flow.Testnet suite.historicalAccessClient = new(access.AccessAPIClient) suite.connectionFactory = new(backendmock.ConnectionFactory) - suite.nil = new(backendmock.ConnectionSelector) } func (suite *Suite) TestPing() { @@ -2123,21 +2121,6 @@ func (suite *Suite) TestExecutionNodesForBlockID() { func(flow.IdentityFilter) error { return nil }) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() - connSelector := new(backendmock.ConnectionSelector) - connSelector.On("GetExecutionNodesForBlockID").Return(func() flow.IdentityList { - randomItems := make(flow.IdentityList, 0, maxExecutionNodesCnt) - - for i := 0; i < maxExecutionNodesCnt; i++ { - // Generate a random index within the range of the array - randomIndex, err := rand.Uintn(uint(len(allExecutionNodes))) - require.NoError(suite.T(), err) - // Append the item at the random index to the new slice - randomItems = append(randomItems, allExecutionNodes[randomIndex]) - } - - return randomItems - }) - testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) { if preferredENs != nil { @@ -2146,7 +2129,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { if fixedENs != nil { fixedENIdentifiers = fixedENs.NodeIDs() } - actualList, err := connSelector.GetExecutionNodesForBlockID(context.Background(), block.ID()) + actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) if expectedENs == nil { expectedENs = flow.IdentityList{} @@ -2166,7 +2149,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { attempt2Receipts = flow.ExecutionReceiptList{} attempt3Receipts = flow.ExecutionReceiptList{} suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) - actualList, err := connSelector.GetExecutionNodesForBlockID(context.Background(), block.ID()) + actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) require.Equal(suite.T(), len(actualList), maxExecutionNodesCnt) }) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 3c96b9c124a..d1fc6238df5 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -3,7 +3,6 @@ package backend import ( "context" "fmt" - "github.com/sony/gobreaker" "net" "strconv" "strings" @@ -14,6 +13,7 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/sony/gobreaker" "github.com/stretchr/testify/assert" testifymock "github.com/stretchr/testify/mock" "google.golang.org/grpc" diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 3ba35c15d70..b66904f6604 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -49,7 +49,6 @@ func (suite *Suite) TestHistoricalTransactionResult() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -108,7 +107,6 @@ func (suite *Suite) TestHistoricalTransaction() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 7a4ddf6b375..c10b66bbbc0 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -54,7 +54,6 @@ func (suite *Suite) TestTransactionRetry() { suite.chainID, metrics.NewNoopCollector(), nil, - nil, false, DefaultMaxHeightRange, nil, @@ -144,7 +143,6 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.chainID, metrics.NewNoopCollector(), connFactory, - nil, false, DefaultMaxHeightRange, nil, From dda2d3a22d3758bbcc90a7b2527ac08fbd2042f5 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 02:04:35 +0300 Subject: [PATCH 139/815] Added assert call. --- .../access/rpc/backend/connection_factory_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index d1fc6238df5..99bbad2e05f 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -421,7 +421,6 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { req := &execution.PingRequest{} resp := &execution.PingResponse{} en.handler.On("Ping", testifymock.Anything, req).After(2*requestTimeout).Return(resp, nil) - // create the factory connectionFactory := new(ConnectionFactoryImpl) // set the execution grpc port @@ -454,6 +453,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { // make the call to the execution node _, err = client.Ping(ctx, req) + en.handler.AssertCalled(t, "Ping", testifymock.Anything, req) return time.Since(start), err } @@ -466,12 +466,12 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) - //Wait until Circuit breaker go to Half-open state - time.Sleep(circuitBreakerRestoreTimeout + time.Second) - en.handler.On("Ping", testifymock.Anything, req).Unset() en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) + //Wait until Circuit breaker go to Half-open state + time.Sleep(circuitBreakerRestoreTimeout + time.Second) + duration, err = callAndMeasurePingDuration() assert.Greater(t, requestTimeout, duration) assert.Equal(t, nil, err) @@ -523,6 +523,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { // make the call to the collection node _, err = client.Ping(ctx, req) + cn.handler.AssertCalled(t, "Ping", testifymock.Anything, req) return time.Since(start), err } @@ -535,12 +536,12 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) - //Wait until Circuit breaker go to Half-open state - time.Sleep(circuitBreakerRestoreTimeout + time.Second) - cn.handler.On("Ping", testifymock.Anything, req).Unset() cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) + //Wait until Circuit breaker go to Half-open state + time.Sleep(circuitBreakerRestoreTimeout + time.Second) + duration, err = callAndMeasurePingDuration() assert.Greater(t, requestTimeout, duration) assert.Equal(t, nil, err) From d95aed3f25cd17fe7b8ac69431d353b6f680e5a4 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 11:00:24 +0300 Subject: [PATCH 140/815] Return all exec nodes that matches criteria. --- engine/access/access_test.go | 5 ++ engine/access/rpc/backend/backend.go | 49 +++++++-------- engine/access/rpc/backend/backend_accounts.go | 22 +++++-- engine/access/rpc/backend/backend_events.go | 23 ++++--- engine/access/rpc/backend/backend_scripts.go | 26 +++++--- engine/access/rpc/backend/backend_test.go | 35 +++++++++++ .../rpc/backend/backend_transactions.go | 62 +++++++++++++------ .../rpc/backend/historical_access_test.go | 2 + engine/access/rpc/backend/retry_test.go | 2 + engine/access/rpc/engine.go | 3 +- 10 files changed, 161 insertions(+), 68 deletions(-) diff --git a/engine/access/access_test.go b/engine/access/access_test.go index 63d7af2d76c..b6d4051e9ba 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -157,6 +157,7 @@ func (suite *Suite) RunTest( suite.log, backend.DefaultSnapshotHistoryLimit, nil, + false, ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me, access.WithBlockSignerDecoder(suite.signerIndicesDecoder)) f(handler, db, all) @@ -329,6 +330,7 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -655,6 +657,7 @@ func (suite *Suite) TestGetSealedTransaction() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -794,6 +797,7 @@ func (suite *Suite) TestGetTransactionResult() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -985,6 +989,7 @@ func (suite *Suite) TestExecuteScript() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, + false, ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 954a04981c3..8676631b7ae 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,13 +3,12 @@ package backend import ( "context" "fmt" - "time" - lru "github.com/hashicorp/golang-lru" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "time" "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" @@ -97,6 +96,7 @@ func New( log zerolog.Logger, snapshotHistoryLimit int, archiveAddressList []string, + circuitBreakerEnabled bool, ) *Backend { retry := newRetry() if retryEnabled { @@ -122,19 +122,20 @@ func New( archiveAddressList: archiveAddressList, }, backendTransactions: backendTransactions{ - staticCollectionRPC: collectionRPC, - state: state, - chainID: chainID, - collections: collections, - blocks: blocks, - transactions: transactions, - executionReceipts: executionReceipts, - transactionValidator: configureTransactionValidator(state, chainID), - transactionMetrics: accessMetrics, - retry: retry, - connFactory: connFactory, - previousAccessNodes: historicalAccessNodes, - log: log, + staticCollectionRPC: collectionRPC, + state: state, + chainID: chainID, + collections: collections, + blocks: blocks, + transactions: transactions, + executionReceipts: executionReceipts, + transactionValidator: configureTransactionValidator(state, chainID), + transactionMetrics: accessMetrics, + retry: retry, + connFactory: connFactory, + previousAccessNodes: historicalAccessNodes, + log: log, + circuitBreakerEnabled: circuitBreakerEnabled, }, backendEvents: backendEvents{ state: state, @@ -153,11 +154,12 @@ func New( state: state, }, backendAccounts: backendAccounts{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + circuitBreakerEnabled: circuitBreakerEnabled, }, backendExecutionResults: backendExecutionResults{ executionResults: executionResults, @@ -356,14 +358,11 @@ func executionNodesForBlockID( return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) } - // randomly choose upto maxExecutionNodesCnt identities - executionIdentitiesRandom := subsetENs.Sample(maxExecutionNodesCnt) - - if len(executionIdentitiesRandom) == 0 { + if len(subsetENs) == 0 { return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) } - return executionIdentitiesRandom, nil + return subsetENs, nil } // findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index a3a41053c61..c5864b3fd86 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -18,11 +18,12 @@ import ( ) type backendAccounts struct { - state protocol.State - headers storage.Headers - executionReceipts storage.ExecutionReceipts - connFactory ConnectionFactory - log zerolog.Logger + state protocol.State + headers storage.Headers + executionReceipts storage.ExecutionReceipts + connFactory ConnectionFactory + log zerolog.Logger + circuitBreakerEnabled bool } func (b *backendAccounts) GetAccount(ctx context.Context, address flow.Address) (*flow.Account, error) { @@ -82,7 +83,11 @@ func (b *backendAccounts) getAccountAtBlockID( BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + var execNodes flow.IdentityList + var err error + + execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + if err != nil { return nil, rpc.ConvertError(err, "failed to get account from the execution node", codes.Internal) } @@ -107,7 +112,12 @@ func (b *backendAccounts) getAccountAtBlockID( // other ENs are logged and swallowed. If all ENs fail to return a valid response, then an // error aggregating all failures is returned. func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { + if !b.circuitBreakerEnabled { + execNodes = execNodes.Sample(maxExecutionNodesCnt) + } + var errors *multierror.Error + for _, execNode := range execNodes { // TODO: use the GRPC Client interceptor start := time.Now() diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index f48ba395947..b58d19c8b6d 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -21,12 +21,13 @@ import ( ) type backendEvents struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - maxHeightRange uint + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + maxHeightRange uint + circuitBreakerEnabled bool } // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and @@ -129,7 +130,11 @@ func (b *backendEvents) getBlockEventsFromExecutionNode( // choose the last block ID to find the list of execution nodes lastBlockID := blockIDs[len(blockIDs)-1] - execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) + var execNodes flow.IdentityList + var err error + + execNodes, err = executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) + if err != nil { b.log.Error().Err(err).Msg("failed to retrieve events from execution node") return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) @@ -209,6 +214,10 @@ func verifyAndConvertToAccessEvents( func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { + if !b.circuitBreakerEnabled { + execNodes = execNodes.Sample(maxExecutionNodesCnt) + } + var errors *multierror.Error // try to get events from one of the execution nodes for _, execNode := range execNodes { diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index e6da62a5b7b..80b611d243c 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -24,14 +24,15 @@ import ( const uniqueScriptLoggingTimeWindow = 10 * time.Minute type backendScripts struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - metrics module.BackendScriptsMetrics - loggedScripts *lru.Cache - archiveAddressList []string + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + metrics module.BackendScriptsMetrics + loggedScripts *lru.Cache + archiveAddressList []string + circuitBreakerEnabled bool } func (b *backendScripts) ExecuteScriptAtLatestBlock( @@ -91,11 +92,18 @@ func (b *backendScripts) findScriptExecutors( return b.archiveAddressList, nil } - executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + var executors flow.IdentityList + var err error + + executors, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { return nil, err } + if !b.circuitBreakerEnabled { + executors = executors.Sample(maxExecutionNodesCnt) + } + executorAddrs := make([]string, 0, len(executors)) for _, executor := range executors { executorAddrs = append(executorAddrs, executor.Address) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 923af00cacc..8f572daf172 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -105,6 +105,7 @@ func (suite *Suite) TestPing() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) err := backend.Ping(context.Background()) @@ -140,6 +141,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized block @@ -205,6 +207,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized snapshot @@ -277,6 +280,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized snapshot @@ -342,6 +346,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized snapshot @@ -418,6 +423,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized snapshot @@ -478,6 +484,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { suite.log, snapshotHistoryLimit, nil, + false, ) // the handler should return a snapshot history limit error @@ -516,6 +523,7 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest sealed block @@ -562,6 +570,7 @@ func (suite *Suite) TestGetTransaction() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) actual, err := backend.GetTransaction(context.Background(), transaction.ID()) @@ -602,6 +611,7 @@ func (suite *Suite) TestGetCollection() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) actual, err := backend.GetCollectionByID(context.Background(), expected.ID()) @@ -665,6 +675,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) suite.execClient. On("GetTransactionResultByIndex", ctx, exeEventReq). @@ -728,6 +739,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) suite.execClient. On("GetTransactionResultsByBlockID", ctx, exeEventReq). @@ -819,6 +831,7 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // Successfully return empty event list @@ -939,6 +952,7 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // should return pending status when we have not observed an expiry block @@ -1106,6 +1120,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -1164,6 +1179,7 @@ func (suite *Suite) TestTransactionResultUnknown() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx @@ -1218,6 +1234,7 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // query the handler for the latest finalized header @@ -1348,6 +1365,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1380,6 +1398,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request with an empty block id list and expect an empty list of events and no error @@ -1439,6 +1458,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1469,6 +1489,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1532,6 +1553,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1563,6 +1585,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1713,6 +1736,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), maxHeight, minHeight) @@ -1752,6 +1776,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // execute request @@ -1790,6 +1815,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) actualResp, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1827,6 +1853,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, minHeight+1) @@ -1864,6 +1891,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1941,6 +1969,7 @@ func (suite *Suite) TestGetAccount() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2022,6 +2051,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2061,6 +2091,7 @@ func (suite *Suite) TestGetNetworkParameters() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) params := backend.GetNetworkParameters(context.Background()) @@ -2131,6 +2162,8 @@ func (suite *Suite) TestExecutionNodesForBlockID() { } actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) + actualList = actualList.Sample(maxExecutionNodesCnt) + if expectedENs == nil { expectedENs = flow.IdentityList{} } @@ -2150,6 +2183,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { attempt3Receipts = flow.ExecutionReceiptList{} suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) + actualList = actualList.Sample(maxExecutionNodesCnt) require.NoError(suite.T(), err) require.Equal(suite.T(), len(actualList), maxExecutionNodesCnt) }) @@ -2240,6 +2274,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // mock parameters diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 5bc31162a07..c92cf0212dc 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -27,20 +27,20 @@ import ( const collectionNodesToTry uint = 3 type backendTransactions struct { - staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node - transactions storage.Transactions - executionReceipts storage.ExecutionReceipts - collections storage.Collections - blocks storage.Blocks - state protocol.State - chainID flow.ChainID - transactionMetrics module.TransactionMetrics - transactionValidator *access.TransactionValidator - retry *Retry - connFactory ConnectionFactory - - previousAccessNodes []accessproto.AccessAPIClient - log zerolog.Logger + staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node + transactions storage.Transactions + executionReceipts storage.ExecutionReceipts + collections storage.Collections + blocks storage.Blocks + state protocol.State + chainID flow.ChainID + transactionMetrics module.TransactionMetrics + transactionValidator *access.TransactionValidator + retry *Retry + connFactory ConnectionFactory + previousAccessNodes []accessproto.AccessAPIClient + log zerolog.Logger + circuitBreakerEnabled bool } // SendTransaction forwards the transaction to the collection node @@ -123,13 +123,15 @@ func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sa } // get the cluster responsible for the transaction - txCluster, ok := clusters.ByTxID(tx.ID()) + targetNodes, ok := clusters.ByTxID(tx.ID()) if !ok { return nil, fmt.Errorf("could not get local cluster by txID: %x", tx.ID()) } - // select a random subset of collection nodes from the cluster to be tried in order - targetNodes := txCluster.Sample(sampleSize) + if !b.circuitBreakerEnabled { + // select a random subset of collection nodes from the cluster to be tried in order + targetNodes = targetNodes.Sample(sampleSize) + } // collect the addresses of all the chosen collection nodes var targetAddrs = make([]string, len(targetNodes)) @@ -371,7 +373,10 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( req := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + + var execNodes flow.IdentityList + + execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -502,7 +507,9 @@ func (b *backendTransactions) GetTransactionResultByIndex( BlockId: blockID[:], Index: index, } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + var execNodes flow.IdentityList + + execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -731,7 +738,10 @@ func (b *backendTransactions) getTransactionResultFromExecutionNode( TransactionId: transactionID, } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + var execNodes flow.IdentityList + var err error + + execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { // if no execution receipt were found, return a NotFound GRPC error if IsInsufficientExecutionReceipts(err) { @@ -762,6 +772,10 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionResultRequest, ) (*execproto.GetTransactionResultResponse, error) { + if !b.circuitBreakerEnabled { + execNodes = execNodes.Sample(maxExecutionNodesCnt) + } + var errs *multierror.Error logAnyError := func() { errToReturn := errs.ErrorOrNil() @@ -817,6 +831,10 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionsByBlockIDRequest, ) (*execproto.GetTransactionResultsResponse, error) { + if !b.circuitBreakerEnabled { + execNodes = execNodes.Sample(maxExecutionNodesCnt) + } + var errs *multierror.Error defer func() { @@ -876,6 +894,10 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionByIndexRequest, ) (*execproto.GetTransactionResultResponse, error) { + if !b.circuitBreakerEnabled { + execNodes = execNodes.Sample(maxExecutionNodesCnt) + } + var errs *multierror.Error logAnyError := func() { errToReturn := errs.ErrorOrNil() diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index b66904f6604..42dd829dbbc 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -56,6 +56,7 @@ func (suite *Suite) TestHistoricalTransactionResult() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // Successfully return the transaction from the historical node @@ -114,6 +115,7 @@ func (suite *Suite) TestHistoricalTransaction() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) // Successfully return the transaction from the historical node diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index c10b66bbbc0..2189223118a 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -61,6 +61,7 @@ func (suite *Suite) TestTransactionRetry() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry @@ -150,6 +151,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.log, DefaultSnapshotHistoryLimit, nil, + false, ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 5947ee6cd7d..c4b36c263b6 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -48,7 +48,7 @@ type Config struct { PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node - CircuitBreakerConfig backend.CircuitBreakerConfig //TODO: + CircuitBreakerConfig backend.CircuitBreakerConfig // the configuration for circuit breaker } // Engine exposes the server with a simplified version of the Access API. @@ -194,6 +194,7 @@ func NewBuilder(log zerolog.Logger, log, backend.DefaultSnapshotHistoryLimit, config.ArchiveAddressList, + config.CircuitBreakerConfig.Enabled, ) finalizedCache, finalizedCacheWorker, err := events.NewFinalizedHeaderCache(state) From 3ab2573d67cb3dbf70ec41bf0b60cf9dc0c820d4 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 11:01:27 +0300 Subject: [PATCH 141/815] linted --- engine/access/rpc/backend/backend.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 8676631b7ae..7e4e5ef0137 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,12 +3,13 @@ package backend import ( "context" "fmt" + "time" + lru "github.com/hashicorp/golang-lru" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "time" "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" From cda4d37a141e790166e2404598b895c958a54af2 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 14 Jun 2023 11:37:36 +0300 Subject: [PATCH 142/815] Remove unnecessary code --- .../export_report.json | 4 +-- engine/access/rpc/backend/backend.go | 30 ++++++++++--------- engine/access/rpc/backend/backend_accounts.go | 6 +--- engine/access/rpc/backend/backend_events.go | 6 +--- .../rpc/backend/backend_transactions.go | 12 ++------ 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 72af21af279..35f744445ee 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "53749b13e1f99759abb35a7ab7d7a4f180d8f6bc24e5ef6b29f3565d459765f0", - "CurrentStateCommitment": "53749b13e1f99759abb35a7ab7d7a4f180d8f6bc24e5ef6b29f3565d459765f0", + "PreviousStateCommitment": "6d2eb6ae0aa575274a45362f9bd175ec6f030f79552c5fed27ada030d3799a53", + "CurrentStateCommitment": "6d2eb6ae0aa575274a45362f9bd175ec6f030f79552c5fed27ada030d3799a53", "ReportSucceeded": true } \ No newline at end of file diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 7e4e5ef0137..6c63ca1d35f 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -113,14 +113,15 @@ func New( state: state, // create the sub-backends backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, - loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + state: state, + log: log, + metrics: accessMetrics, + loggedScripts: loggedScripts, + archiveAddressList: archiveAddressList, + circuitBreakerEnabled: circuitBreakerEnabled, }, backendTransactions: backendTransactions{ staticCollectionRPC: collectionRPC, @@ -139,12 +140,13 @@ func New( circuitBreakerEnabled: circuitBreakerEnabled, }, backendEvents: backendEvents{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - maxHeightRange: maxHeightRange, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + maxHeightRange: maxHeightRange, + circuitBreakerEnabled: circuitBreakerEnabled, }, backendBlockHeaders: backendBlockHeaders{ headers: headers, diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index c5864b3fd86..205fa0f3ab8 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -83,11 +83,7 @@ func (b *backendAccounts) getAccountAtBlockID( BlockId: blockID[:], } - var execNodes flow.IdentityList - var err error - - execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) - + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { return nil, rpc.ConvertError(err, "failed to get account from the execution node", codes.Internal) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index b58d19c8b6d..fc163459518 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -130,11 +130,7 @@ func (b *backendEvents) getBlockEventsFromExecutionNode( // choose the last block ID to find the list of execution nodes lastBlockID := blockIDs[len(blockIDs)-1] - var execNodes flow.IdentityList - var err error - - execNodes, err = executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) - + execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) if err != nil { b.log.Error().Err(err).Msg("failed to retrieve events from execution node") return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index c92cf0212dc..a9ba3002d40 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -374,9 +374,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( BlockId: blockID[:], } - var execNodes flow.IdentityList - - execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -507,9 +505,8 @@ func (b *backendTransactions) GetTransactionResultByIndex( BlockId: blockID[:], Index: index, } - var execNodes flow.IdentityList - execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -738,10 +735,7 @@ func (b *backendTransactions) getTransactionResultFromExecutionNode( TransactionId: transactionID, } - var execNodes flow.IdentityList - var err error - - execNodes, err = executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { // if no execution receipt were found, return a NotFound GRPC error if IsInsufficientExecutionReceipts(err) { From 520c2ef97fa06b00b03fa35956e2663601f0e94b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:48:34 -0700 Subject: [PATCH 143/815] Update network/alsp/manager/README.md Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/alsp/manager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index ef98775d140..64197d89414 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -18,7 +18,7 @@ The ALSP manager is responsible for maintaining records of misbehavior reports a remote nodes and for calculating their accumulated misbehavior penalties. Should a node’s misbehavior penalty surpass a certain threshold (referred to as `DisallowListingThreshold`), the ALSP manager initiates the disallow listing process. When a remote node is disallow-listed, -it is effectively isolated from the network by the `ConnectionGater` and `PeerManager` components, i.e., the exisitng +it is effectively isolated from the network by the `ConnectionGater` and `PeerManager` components, i.e., the existing connections to that remote node are closed and no new connections are allowed to be established. ##### Disallow Listing Process From c9e5140de733c4bd5553ad514d741474dd1e543f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:48:42 -0700 Subject: [PATCH 144/815] Update network/alsp/manager/README.md Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/alsp/manager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index 64197d89414..f7b6b0362c8 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -19,7 +19,7 @@ remote nodes and for calculating their accumulated misbehavior penalties. Should a node’s misbehavior penalty surpass a certain threshold (referred to as `DisallowListingThreshold`), the ALSP manager initiates the disallow listing process. When a remote node is disallow-listed, it is effectively isolated from the network by the `ConnectionGater` and `PeerManager` components, i.e., the existing -connections to that remote node are closed and no new connections are allowed to be established. +connections to that remote node are closed and new connections attempts are rejected. ##### Disallow Listing Process 1. The ALSP manager communicates with the `LibP2PNode` by calling its `OnDisallowListNotification` method to indicate that a particular remote node has been disallow-listed. From da1d5c4c560db8426a086c1066736cd31a16a18d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:49:52 -0700 Subject: [PATCH 145/815] Update network/alsp/manager/README.md Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/alsp/manager/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index f7b6b0362c8..c81dc848350 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -24,6 +24,7 @@ connections to that remote node are closed and new connections attempts are reje ##### Disallow Listing Process 1. The ALSP manager communicates with the `LibP2PNode` by calling its `OnDisallowListNotification` method to indicate that a particular remote node has been disallow-listed. 2. In response, the `LibP2PNode` takes two important actions: + a. It alerts the `PeerManager`, instructing it to sever the connection with the disallow-listed node. b. It notifies the `ConnectionGater` to block any incoming or outgoing connections to and from the disallow-listed node. This ensures that the disallow-listed node is effectively isolated from the network. From f5c7d2d8b55d47f0037e97a56499ddab7dee83ae Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:50:56 -0700 Subject: [PATCH 146/815] Update network/p2p/p2pnode/libp2pNode.go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/p2p/p2pnode/libp2pNode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 8a2ec5b107f..60a548c694b 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -518,7 +518,7 @@ func (n *Node) OnDisallowListNotification(peerId peer.ID, cause flownet.Disallow func (n *Node) OnAllowListNotification(peerId peer.ID, cause flownet.DisallowListedCause) { remainingCauses := n.disallowListedCache.AllowFor(peerId, cause) - n.logger.Warn(). + n.logger.Info(). Str("peer_id", peerId.String()). Str("causes", fmt.Sprintf("%v", cause)). Str("remaining_causes", fmt.Sprintf("%v", remainingCauses)). From c8ff213d9b2eae407241b9cfe9eb9fe6af1b27e4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:51:05 -0700 Subject: [PATCH 147/815] Update network/alsp/manager/README.md Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/alsp/manager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index c81dc848350..f09a774336b 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -27,7 +27,7 @@ connections to that remote node are closed and new connections attempts are reje a. It alerts the `PeerManager`, instructing it to sever the connection with the disallow-listed node. b. It notifies the `ConnectionGater` to block any incoming or outgoing connections to and from the disallow-listed node. -This ensures that the disallow-listed node is effectively isolated from the network. +This ensures that the disallow-listed node is effectively isolated from the local node's network. ##### Penalty Decay and Allow Listing Process The ALSP manager also includes a penalty decay mechanism, which gradually reduces the penalties of nodes over time upon regular hearbeat intervals (default is evey one second). From b483f0890822755e5479a630ca48db9a37431a1c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:51:21 -0700 Subject: [PATCH 148/815] Update network/alsp/manager/README.md Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/alsp/manager/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index f09a774336b..44e232884a7 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -36,6 +36,7 @@ the `ConnectionGater` to lift the block on the disallow-listed node and instruct 1. The ALSP manager calls the `OnAllowListNotification` method on the `LibP2PNode` to signify that a previously disallow-listed node is now allow-listed. 2. The `LibP2PNode` responds by: + a. Instructing the `ConnectionGater` to lift the block, thereby permitting connections with the now allow-listed node. b. Requesting the `PeerManager` to initiate an outbound connection with the allow-listed node. From 3d3f69f3ba95013658475fe1984246edc75ebe1e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:51:33 -0700 Subject: [PATCH 149/815] Update network/alsp/manager/README.md Co-authored-by: Misha --- network/alsp/manager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/README.md b/network/alsp/manager/README.md index 44e232884a7..36bf3deda9d 100644 --- a/network/alsp/manager/README.md +++ b/network/alsp/manager/README.md @@ -30,7 +30,7 @@ connections to that remote node are closed and new connections attempts are reje This ensures that the disallow-listed node is effectively isolated from the local node's network. ##### Penalty Decay and Allow Listing Process -The ALSP manager also includes a penalty decay mechanism, which gradually reduces the penalties of nodes over time upon regular hearbeat intervals (default is evey one second). +The ALSP manager also includes a penalty decay mechanism, which gradually reduces the penalties of nodes over time upon regular heartbeat intervals (default is every one second). Once a disallow-listed node's penalty decays back to zero, the node can be reintegrated into the network through the allow listing process. The allow-listing process involves allowing the `ConnectionGater` to lift the block on the disallow-listed node and instructing the `PeerManager` to initiate an outbound connection with the allow-listed node. From a9233f79213dd8a0d7dda9164a07fb775cd85476 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 09:52:05 -0700 Subject: [PATCH 150/815] Update network/alsp/manager/manager_test.go Co-authored-by: Misha --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index e9acafd3ef5..da49323c161 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -249,7 +249,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // creates a misbehavior report for the spammer report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) - // simulates the victim node reporting the spammer node misbehavior 120 times``` + // simulates the victim node reporting the spammer node misbehavior 120 times // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that // the spammer is definitely disallow-listed. From 430b369b25b5fe079822db827a69b68fb56bc666 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 10:59:19 -0700 Subject: [PATCH 151/815] adds disallow-listed status to cache --- network/alsp/internal/cache.go | 9 ++- network/alsp/internal/cache_test.go | 10 ++- network/alsp/manager/manager.go | 20 ++++- network/alsp/manager/manager_test.go | 105 ++++++++++++++++++++++++--- network/alsp/model/record.go | 14 +++- 5 files changed, 135 insertions(+), 23 deletions(-) diff --git a/network/alsp/internal/cache.go b/network/alsp/internal/cache.go index e414b1fb3f5..c29ae4bd988 100644 --- a/network/alsp/internal/cache.go +++ b/network/alsp/internal/cache.go @@ -166,10 +166,11 @@ func (s *SpamRecordCache) Get(originId flow.Identifier) (*model.ProtocolSpamReco // return a copy of the record (we do not want the caller to modify the record). return &model.ProtocolSpamRecord{ - OriginId: record.OriginId, - Decay: record.Decay, - CutoffCounter: record.CutoffCounter, - Penalty: record.Penalty, + OriginId: record.OriginId, + Decay: record.Decay, + CutoffCounter: record.CutoffCounter, + Penalty: record.Penalty, + DisallowListed: record.DisallowListed, }, true } diff --git a/network/alsp/internal/cache_test.go b/network/alsp/internal/cache_test.go index d41b3cec331..22efd456a8e 100644 --- a/network/alsp/internal/cache_test.go +++ b/network/alsp/internal/cache_test.go @@ -75,12 +75,13 @@ func TestSpamRecordCache_Adjust_Init(t *testing.T) { // adjusting a spam record for an origin ID that does not exist in the cache should initialize the record. initializedPenalty, err := cache.Adjust(originID1, adjustFuncIncrement) require.NoError(t, err, "expected no error") - require.Equal(t, float64(1), initializedPenalty, "expected initialized penalty to be 0") + require.Equal(t, float64(1), initializedPenalty, "expected initialized penalty to be 1") record1, ok := cache.Get(originID1) require.True(t, ok, "expected record to exist") require.NotNil(t, record1, "expected non-nil record") require.Equal(t, originID1, record1.OriginId, "expected record to have correct origin ID") + require.False(t, record1.DisallowListed, "expected record to not be disallow listed") require.Equal(t, cache.Size(), uint(1), "expected cache to have one record") // adjusting a spam record for an origin ID that already exists in the cache should not initialize the record, @@ -92,6 +93,7 @@ func TestSpamRecordCache_Adjust_Init(t *testing.T) { require.True(t, ok, "expected record to still exist") require.NotNil(t, record1Again, "expected non-nil record") require.Equal(t, originID1, record1Again.OriginId, "expected record to have correct origin ID") + require.False(t, record1Again.DisallowListed, "expected record not to be disallow listed") require.Equal(t, cache.Size(), uint(1), "expected cache to still have one record") // adjusting a spam record for a different origin ID should initialize the record. @@ -103,6 +105,7 @@ func TestSpamRecordCache_Adjust_Init(t *testing.T) { require.True(t, ok, "expected record to exist") require.NotNil(t, record2, "expected non-nil record") require.Equal(t, originID2, record2.OriginId, "expected record to have correct origin ID") + require.False(t, record2.DisallowListed, "expected record not to be disallow listed") require.Equal(t, cache.Size(), uint(2), "expected cache to have two records") } @@ -356,6 +359,8 @@ func TestSpamRecordCache_ConcurrentSameRecordAdjust(t *testing.T) { } adjustFn := func(record model.ProtocolSpamRecord) (model.ProtocolSpamRecord, error) { record.Penalty -= 1.0 + record.DisallowListed = true + record.Decay += 1.0 return record, nil // no-op } @@ -380,10 +385,13 @@ func TestSpamRecordCache_ConcurrentSameRecordAdjust(t *testing.T) { unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish") // ensure that the record is correctly initialized and adjusted in the cache + initDecay := model.SpamRecordFactory()(originID).Decay record, found := cache.Get(originID) require.True(t, found) require.NotNil(t, record) require.Equal(t, concurrentAttempts*-1.0, record.Penalty) + require.Equal(t, initDecay+concurrentAttempts*1.0, record.Decay) + require.True(t, record.DisallowListed) require.Equal(t, originID, record.OriginId) } diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index aa83955621b..941407e2637 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -317,13 +317,16 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // as long as record.Penalty is NOT below model.DisallowListingThreshold, // the node is considered allow-listed and can conduct inbound and outbound connections. // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. - if record.Penalty < model.DisallowListingThreshold { + if record.Penalty < model.DisallowListingThreshold && !record.DisallowListed { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ + record.DisallowListed = true m.logger.Warn(). Str("key", logging.KeySuspicious). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). + Float64("decay_speed", record.Decay). + Bool("disallow_listed", record.DisallowListed). Msg("node penalty is below threshold, disallow listing") m.disallowListingConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ FlowIds: flow.IdentifierList{id}, @@ -338,7 +341,14 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). - if record.Penalty == float64(0) { + if record.Penalty == float64(0) && record.DisallowListed { + record.DisallowListed = false + m.logger.Info(). + Hex("identifier", logging.ID(id)). + Uint64("cutoff_counter", record.CutoffCounter). + Float64("decay_speed", record.Decay). + Bool("disallow_listed", record.DisallowListed). + Msg("allow-listing a node that was disallow listed") // Penalty has fully decayed to zero and the node can be back in the allow list. m.disallowListingConsumer.OnAllowListNotification(&network.AllowListingUpdate{ FlowIds: flow.IdentifierList{id}, @@ -346,6 +356,12 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { }) } + m.logger.Trace(). + Hex("identifier", logging.ID(id)). + Uint64("cutoff_counter", record.CutoffCounter). + Float64("decay_speed", record.Decay). + Bool("disallow_listed", record.DisallowListed). + Msg("spam record decayed successfully") return record, nil }) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index e9acafd3ef5..1a4c619b129 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -2,7 +2,6 @@ package alspmgr_test import ( "context" - "fmt" "math" "math/rand" "sync" @@ -182,6 +181,8 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { require.Equal(t, totalPenalty, record.Penalty) // with just reporting a single misbehavior report, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) + // with just reporting a single misbehavior report, the node should not be disallowed. + require.False(t, record.DisallowListed) // the decay should be the default decay value. require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) } @@ -491,6 +492,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { } require.NotNil(t, record) require.Equal(t, penalty, record.Penalty) + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet require.Equal(t, uint64(0), record.CutoffCounter) // with just reporting a misbehavior, the cutoff counter should not be incremented. require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) // the decay should be the default decay value. @@ -609,6 +611,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall // all the misbehavior reports should be processed by now, so the penalty should be equal to the total penalty return false } + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. @@ -679,6 +682,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl // all the misbehavior reports should be processed by now, so the penalty should be equal to the total penalty return false } + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. @@ -735,7 +739,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential return false } require.NotNil(t, record) - + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. require.Equal(t, report.Penalty(), record.Penalty) // with just reporting a single misbehavior report, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) @@ -806,7 +810,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent return false } require.NotNil(t, record) - + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. require.Equal(t, report.Penalty(), record.Penalty) // with just reporting a single misbehavior report, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) @@ -884,11 +888,10 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti record, ok := cache.Get(originID) if !ok { - fmt.Println("not ok") return false } require.NotNil(t, record) - + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. require.Equal(t, totalPenalty, record.Penalty) // with just reporting a single misbehavior report, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) @@ -960,7 +963,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre return false } require.NotNil(t, record) - + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. require.Equal(t, totalPenalty, record.Penalty) // with just reporting a single misbehavior report, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) @@ -1034,6 +1037,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t if record.Penalty != report.Penalty()*float64(times) { return false } + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. @@ -1105,6 +1109,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { if record.Penalty != report.Penalty()*float64(times) { return false } + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. @@ -1215,6 +1220,7 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { require.Greater(t, record.Penalty, penaltyBeforeDecay) // with 3 heartbeats processed, the decayed penalty should be less than the value after 4 heartbeats. require.Less(t, record.Penalty, penaltyBeforeDecay+4*record.Decay) + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with just reporting a few misbehavior reports, the cutoff counter should not be incremented. require.Equal(t, uint64(0), record.CutoffCounter) // the decay should be the default decay value. @@ -1290,12 +1296,6 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { return true }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") - // eventually, we expect the ALSP manager to emit an allow list notification to the network layer when the penalty is decayed to zero. - consumer.On("OnAllowListNotification", &network.AllowListingUpdate{ - FlowIds: flow.IdentifierList{report.OriginId()}, - Cause: network.DisallowListedCauseAlsp, - }).Return(nil).Once() - // phase-2: default decay speed is 1000 and with 10 penalties in range of [-1, -10], the penalty should be decayed to zero in // a single heartbeat. time.Sleep(1 * time.Second) @@ -1305,12 +1305,92 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { require.True(t, ok) // the record should be in the cache require.NotNil(t, record) + require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. // with a single heartbeat and decay speed of 1000, the penalty should be decayed to zero. require.Equal(t, float64(0), record.Penalty) // the decay should be the default decay value. require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) } +// TestDecayMisbehaviorPenalty_DecayToZero_AllowListing tests that when the misbehavior penalty of an already disallow-listed +// peer is decayed to zero, the peer is allow-listed back in the network, and its spam record cache is updated accordingly. +func TestDecayMisbehaviorPenalty_DecayToZero_AllowListing(t *testing.T) { + cfg := managerCfgFixture() + consumer := mocknetwork.NewDisallowListNotificationConsumer(t) + + var cache alsp.SpamRecordCache + cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return cache + }), + } + m, err := alspmgr.NewMisbehaviorReportManager(cfg, consumer) + require.NoError(t, err) + + // start the ALSP manager + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + cancel() + unittest.RequireCloseBefore(t, m.Done(), 100*time.Millisecond, "ALSP manager did not stop") + }() + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + m.Start(signalerCtx) + unittest.RequireCloseBefore(t, m.Ready(), 100*time.Millisecond, "ALSP manager did not start") + + // simulates a disallow-listed peer in cache. + originId := unittest.IdentifierFixture() + penalty, err := cache.Adjust(originId, func(record model.ProtocolSpamRecord) (model.ProtocolSpamRecord, error) { + record.Penalty = -10 // set the penalty to -10 to simulate that the penalty has already been decayed for a while. + record.CutoffCounter = 1 + record.DisallowListed = true + record.OriginId = originId + record.Decay = model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay + return record, nil + }) + require.NoError(t, err) + require.Equal(t, float64(-10), penalty) + + // sanity check + record, ok := cache.Get(originId) + require.True(t, ok) // the record should be in the cache + require.NotNil(t, record) + require.Equal(t, float64(-10), record.Penalty) + require.True(t, record.DisallowListed) + require.Equal(t, uint64(1), record.CutoffCounter) + require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) + + // eventually, we expect the ALSP manager to emit an allow list notification to the network layer when the penalty is decayed to zero. + consumer.On("OnAllowListNotification", &network.AllowListingUpdate{ + FlowIds: flow.IdentifierList{originId}, + Cause: network.DisallowListedCauseAlsp, + }).Return(nil).Once() + + // wait for at most two heartbeats; default decay speed is 1000 and with a penalty of -10, the penalty should be decayed to zero in a single heartbeat. + require.Eventually(t, func() bool { + record, ok = cache.Get(originId) + if !ok { + return false + } + if record.DisallowListed { + return false // the peer should not be allow-listed yet. + } + if record.Penalty != float64(0) { + return false // the penalty should be decayed to zero. + } + if record.CutoffCounter != 1 { + return false // the cutoff counter should be incremented. + } + if record.Decay != model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay { + return false // the decay should be the default decay value. + } + + return true + + }, 2*time.Second, 10*time.Millisecond, "penalty was not decayed to zero") + +} + // TestDisallowListNotification tests the emission of the allow list notification to the network layer when the misbehavior // penalty of a node is dropped below the disallow-listing threshold. The test ensures that the disallow list notification is // emitted to the network layer when the misbehavior penalty is dropped below the disallow-listing threshold and that the @@ -1384,6 +1464,7 @@ func TestDisallowListNotification(t *testing.T) { if record.Penalty != report.Penalty()*float64(times)+record.Decay { return false } + require.True(t, record.DisallowListed) // the peer should be disallow-listed. // cuttoff counter should be incremented since the penalty is above the disallowlisting threshold. require.Equal(t, uint64(1), record.CutoffCounter) // the decay should be the default decay value. diff --git a/network/alsp/model/record.go b/network/alsp/model/record.go index cde105c1d11..088e7d81916 100644 --- a/network/alsp/model/record.go +++ b/network/alsp/model/record.go @@ -20,6 +20,11 @@ type ProtocolSpamRecord struct { // Note that the cutoff connections are recovered after a certain amount of time. CutoffCounter uint64 + // DisallowListed indicates whether the node is currently disallow-listed or not. When a node is in the disallow-list, + // the existing connections to the node are cut and no new connections are allowed to be established, neither incoming + // nor outgoing. + DisallowListed bool + // total Penalty value of the misbehaving node. Should be a negative value. Penalty float64 } @@ -44,10 +49,11 @@ type SpamRecordFactoryFunc func(flow.Identifier) ProtocolSpamRecord func SpamRecordFactory() SpamRecordFactoryFunc { return func(originId flow.Identifier) ProtocolSpamRecord { return ProtocolSpamRecord{ - OriginId: originId, - Decay: InitialDecaySpeed, - CutoffCounter: uint64(0), - Penalty: float64(0), + OriginId: originId, + Decay: InitialDecaySpeed, + DisallowListed: false, + CutoffCounter: uint64(0), + Penalty: float64(0), } } } From a3c613c1f2d18c82c18d20176bdaea66e0189b7a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 12:48:19 -0700 Subject: [PATCH 152/815] switches a log level --- network/p2p/connection/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/connection/connector.go b/network/p2p/connection/connector.go index 8b21e527f27..0065c395d46 100644 --- a/network/p2p/connection/connector.go +++ b/network/p2p/connection/connector.go @@ -110,7 +110,7 @@ func (l *Libp2pConnector) connectToPeers(ctx context.Context, peerIDs peer.IDSli for _, peerID := range peerIDs { if l.host.IsConnectedTo(peerID) { - l.log.Debug().Str("peer_id", peerID.String()).Msg("already connected to peer, skipping connection") + l.log.Trace().Str("peer_id", peerID.String()).Msg("already connected to peer, skipping connection") continue } peerCh <- peer.AddrInfo{ID: peerID} From 95535ab87034db15428a285d11815be611a7ae67 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 14 Jun 2023 13:11:57 -0700 Subject: [PATCH 153/815] fixes duplicate metrics panic --- cmd/access/node_builder/access_node_builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index fd5291fdb0e..821b10469c1 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1218,7 +1218,7 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri builder.LibP2PResourceManagerConfig, &p2p.DisallowListCacheConfig{ MaxSize: builder.BaseConfig.NetworkConfig.DisallowListCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PrivateNetwork), + Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), }). SetBasicResolver(builder.Resolver). SetSubscriptionFilter( From 63ff7270b688af82895286e38a5b70159ba5e1c6 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 15 Jun 2023 10:42:43 -0400 Subject: [PATCH 154/815] created default penalty params --- network/alsp/manager/manager.go | 12 ++++++++++++ network/alsp/model/params.go | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 941407e2637..1732fdec4a1 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -79,6 +79,9 @@ type MisbehaviorReportManager struct { // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. workerPool *worker.Pool[internal.ReportedMisbehaviorWork] + + // params is the ALSP module parameters used for penalties and decays. + penaltyParams model.PenaltyParams } var _ network.MisbehaviorReportManager = (*MisbehaviorReportManager)(nil) @@ -152,6 +155,14 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana } } +// WithDefaults sets the default values for the MisbehaviorReportManager. +//func WithDefaults() MisbehaviorReportManagerOption { +// return func(m *MisbehaviorReportManager) { +// m.cacheFactory = defaultSpamRecordCacheFactory() +// m.disablePenalty = false +// } +//} + // NewMisbehaviorReportManager creates a new instance of the MisbehaviorReportManager. // Args: // cfg: the configuration for the MisbehaviorReportManager. @@ -176,6 +187,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, cacheFactory: defaultSpamRecordCacheFactory(), + penaltyParams: model.DefaultParams(), } store := queue.NewHeroStore( diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 04a53a8f0c8..7376b432fc8 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -45,3 +45,13 @@ const ( // speed is 1, and it takes around a day to recover from each disallow-listing. InitialDecaySpeed = 1000 // (Don't change this value) ) + +type PenaltyParams struct { + DisallowListingThreshold int64 + DefaultPenaltyValue int64 + InitialDecaySpeed int64 +} + +func DefaultParams() PenaltyParams { + return PenaltyParams{DisallowListingThreshold: DisallowListingThreshold, DefaultPenaltyValue: DefaultPenaltyValue, InitialDecaySpeed: InitialDecaySpeed} +} From 36cda908b66f6ce9d285921e965b468b017bf15f Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Thu, 15 Jun 2023 11:45:21 -0700 Subject: [PATCH 155/815] [Execution] Check if block exists locally in RPC endpoints --- engine/execution/rpc/engine.go | 50 +++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/engine/execution/rpc/engine.go b/engine/execution/rpc/engine.go index 08de8c5919c..00b232e221f 100644 --- a/engine/execution/rpc/engine.go +++ b/engine/execution/rpc/engine.go @@ -27,6 +27,8 @@ import ( "github.com/onflow/flow-go/storage" ) +const DefaultMaxBlockRange = 250 + // Config defines the configurable options for the gRPC server. type Config struct { ListenAddr string @@ -98,6 +100,7 @@ func New( transactionResults: txResults, commits: commits, log: log, + maxBlockRange: DefaultMaxBlockRange, }, server: server, config: config, @@ -157,6 +160,7 @@ type handler struct { transactionResults storage.TransactionResults log zerolog.Logger commits storage.Commits + maxBlockRange int } var _ execution.ExecutionAPIServer = &handler{} @@ -176,6 +180,14 @@ func (h *handler) ExecuteScriptAtBlockID( return nil, err } + // return a more user friendly error if block does not exist + if _, err = h.headers.ByBlockID(blockID); err != nil { + if errors.Is(err, storage.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) + } + return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + } + value, err := h.engine.ExecuteScriptAtBlockID(ctx, req.GetScript(), req.GetArguments(), blockID) if err != nil { // return code 3 as this passes the litmus test in our context @@ -229,6 +241,22 @@ func (h *handler) GetEventsForBlockIDs(_ context.Context, return nil, err } + if len(blockIDs) > h.maxBlockRange { + return nil, status.Errorf(codes.InvalidArgument, "too many block IDs requested") + } + + // make sure all blocks exist first to fail fast and avoid unused lookups + // must verify block exists first, since ByBlockID* calls on sets like events or commits will + // return an empty slice if block does not exist + for _, blockID := range flowBlockIDs { + if _, err = h.headers.ByBlockID(blockID); err != nil { + if errors.Is(err, storage.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) + } + return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + } + } + results := make([]*execution.GetEventsForBlockIDsResponse_Result, len(blockIDs)) // collect all the events and create a EventsResponse_Result for each block @@ -394,6 +422,15 @@ func (h *handler) GetTransactionResultsByBlockID( return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) } + // must verify block exists first, since transactionResults.ByBlockID will return an empty slice + // if block does not exist + if _, err = h.headers.ByBlockID(blockID); err != nil { + if errors.Is(err, storage.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) + } + return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + } + // Get all tx results txResults, err := h.transactionResults.ByBlockID(blockID) if err != nil { @@ -496,6 +533,14 @@ func (h *handler) GetAccountAtBlockID( return nil, status.Errorf(codes.InvalidArgument, "invalid address: %v", err) } + // return a more user friendly error if block does not exist + if _, err = h.headers.ByBlockID(blockFlowID); err != nil { + if errors.Is(err, storage.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "block %s not found", blockFlowID) + } + return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + } + value, err := h.engine.GetAccount(ctx, flowAddress, blockFlowID) if errors.Is(err, storage.ErrNotFound) { return nil, status.Errorf(codes.NotFound, "account with address %s not found", flowAddress) @@ -540,7 +585,10 @@ func (h *handler) GetLatestBlockHeader( header, err = h.state.Final().Head() } if err != nil { - return nil, status.Errorf(codes.NotFound, "not found: %v", err) + // this header MUST exist in the db, otherwise the node likely has inconsistent state. + // Don't crash as a result of an external API request, but other components will likely panic. + h.log.Err(err).Msg("unable to get latest block header") + return nil, status.Errorf(codes.Internal, "unable to get latest header: %v", err) } return h.blockHeaderResponse(header) From 2dcd5dc6a605a3f06e0d8247c86e81f5a45bbe20 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:26:54 -0700 Subject: [PATCH 156/815] Update to use commits instead of headers --- engine/access/rpc/backend/backend_accounts.go | 5 +- engine/access/rpc/backend/backend_events.go | 2 +- .../rpc/backend/backend_transactions.go | 9 --- engine/execution/rpc/engine.go | 61 +++++++++---------- 4 files changed, 32 insertions(+), 45 deletions(-) diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index a3a41053c61..3684c92f5d0 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -109,11 +109,10 @@ func (b *backendAccounts) getAccountAtBlockID( func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { var errors *multierror.Error for _, execNode := range execNodes { - // TODO: use the GRPC Client interceptor start := time.Now() - resp, err := b.tryGetAccount(ctx, execNode, req) duration := time.Since(start) + if err == nil { // return if any execution node replied successfully b.log.Debug(). @@ -124,6 +123,7 @@ func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNode Msg("Successfully got account info") return resp, nil } + b.log.Error(). Str("execution_node", execNode.String()). Hex("block_id", req.GetBlockId()). @@ -131,6 +131,7 @@ func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNode Int64("rtt_ms", duration.Milliseconds()). Err(err). Msg("failed to execute GetAccount") + errors = multierror.Append(errors, err) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index f48ba395947..981d1456d54 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -187,7 +187,7 @@ func verifyAndConvertToAccessEvents( events, err := convert.MessagesToEventsFromVersion(result.GetEvents(), version) if err != nil { - return nil, fmt.Errorf("failed to unmarshall events in event %d with encoding version %s: %w", + return nil, fmt.Errorf("failed to unmarshal events in event %d with encoding version %s: %w", i, version.String(), err) } diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 5e955d78c5c..026e162357a 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -797,9 +797,6 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( Msg("Successfully got transaction results from any node") return resp, nil } - if status.Code(err) == codes.NotFound { - return nil, err - } errs = multierror.Append(errs, err) } @@ -856,9 +853,6 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( Msg("Successfully got transaction results from any node") return resp, nil } - if status.Code(err) == codes.NotFound { - return nil, err - } errs = multierror.Append(errs, err) } @@ -916,9 +910,6 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( Msg("Successfully got transaction results from any node") return resp, nil } - if status.Code(err) == codes.NotFound { - return nil, err - } errs = multierror.Append(errs, err) } diff --git a/engine/execution/rpc/engine.go b/engine/execution/rpc/engine.go index 00b232e221f..3cb49a6ca8c 100644 --- a/engine/execution/rpc/engine.go +++ b/engine/execution/rpc/engine.go @@ -27,7 +27,7 @@ import ( "github.com/onflow/flow-go/storage" ) -const DefaultMaxBlockRange = 250 +const DefaultMaxBlockRange = 300 // Config defines the configurable options for the gRPC server. type Config struct { @@ -166,7 +166,10 @@ type handler struct { var _ execution.ExecutionAPIServer = &handler{} // Ping responds to requests when the server is up. -func (h *handler) Ping(_ context.Context, _ *execution.PingRequest) (*execution.PingResponse, error) { +func (h *handler) Ping( + _ context.Context, + _ *execution.PingRequest, +) (*execution.PingResponse, error) { return &execution.PingResponse{}, nil } @@ -180,12 +183,12 @@ func (h *handler) ExecuteScriptAtBlockID( return nil, err } - // return a more user friendly error if block does not exist - if _, err = h.headers.ByBlockID(blockID); err != nil { + // return a more user friendly error if block has not been executed + if _, err = h.commits.ByBlockID(blockID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockID) } - return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) } value, err := h.engine.ExecuteScriptAtBlockID(ctx, req.GetScript(), req.GetArguments(), blockID) @@ -226,8 +229,10 @@ func (h *handler) GetRegisterAtBlockID( return res, nil } -func (h *handler) GetEventsForBlockIDs(_ context.Context, - req *execution.GetEventsForBlockIDsRequest) (*execution.GetEventsForBlockIDsResponse, error) { +func (h *handler) GetEventsForBlockIDs( + _ context.Context, + req *execution.GetEventsForBlockIDsRequest, +) (*execution.GetEventsForBlockIDsResponse, error) { // validate request blockIDs := req.GetBlockIds() @@ -245,18 +250,6 @@ func (h *handler) GetEventsForBlockIDs(_ context.Context, return nil, status.Errorf(codes.InvalidArgument, "too many block IDs requested") } - // make sure all blocks exist first to fail fast and avoid unused lookups - // must verify block exists first, since ByBlockID* calls on sets like events or commits will - // return an empty slice if block does not exist - for _, blockID := range flowBlockIDs { - if _, err = h.headers.ByBlockID(blockID); err != nil { - if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) - } - return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) - } - } - results := make([]*execution.GetEventsForBlockIDsResponse_Result, len(blockIDs)) // collect all the events and create a EventsResponse_Result for each block @@ -264,7 +257,7 @@ func (h *handler) GetEventsForBlockIDs(_ context.Context, // Check if block has been executed if _, err := h.commits.ByBlockID(bID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "state commitment for block ID %s does not exist", bID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", bID) } return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", bID) } @@ -422,13 +415,13 @@ func (h *handler) GetTransactionResultsByBlockID( return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) } - // must verify block exists first, since transactionResults.ByBlockID will return an empty slice - // if block does not exist - if _, err = h.headers.ByBlockID(blockID); err != nil { + // must verify block was locally executed first since transactionResults.ByBlockID will return + // an empty slice if block does not exist + if _, err = h.commits.ByBlockID(blockID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s not found", blockID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockID) } - return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) } // Get all tx results @@ -498,8 +491,10 @@ func (h *handler) GetTransactionResultsByBlockID( } // eventResult creates EventsResponse_Result from flow.Event for the given blockID -func (h *handler) eventResult(blockID flow.Identifier, - flowEvents []flow.Event) (*execution.GetEventsForBlockIDsResponse_Result, error) { +func (h *handler) eventResult( + blockID flow.Identifier, + flowEvents []flow.Event, +) (*execution.GetEventsForBlockIDsResponse_Result, error) { // convert events to event message events := convert.EventsToMessages(flowEvents) @@ -533,12 +528,12 @@ func (h *handler) GetAccountAtBlockID( return nil, status.Errorf(codes.InvalidArgument, "invalid address: %v", err) } - // return a more user friendly error if block does not exist - if _, err = h.headers.ByBlockID(blockFlowID); err != nil { + // return a more user friendly error if block has not been executed + if _, err = h.commits.ByBlockID(blockFlowID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s not found", blockFlowID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockFlowID) } - return nil, status.Errorf(codes.Internal, "failed to get header for block: %v", err) + return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockFlowID) } value, err := h.engine.GetAccount(ctx, flowAddress, blockFlowID) @@ -587,7 +582,7 @@ func (h *handler) GetLatestBlockHeader( if err != nil { // this header MUST exist in the db, otherwise the node likely has inconsistent state. // Don't crash as a result of an external API request, but other components will likely panic. - h.log.Err(err).Msg("unable to get latest block header") + h.log.Err(err).Msg("failed to get latest block header. potentially inconsistent protocol state.") return nil, status.Errorf(codes.Internal, "unable to get latest header: %v", err) } From 1d358f8eda01d977a4ec5d6868331b99ec758611 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 16 Jun 2023 18:24:49 -0400 Subject: [PATCH 157/815] typo --- network/internal/testutils/testUtil.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index ea25564c37a..f5ba6402495 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -291,7 +291,7 @@ func NetworkConfigFixture( return params } -// GenerateIDsAndMiddlewares returns nodeIDs, libp2pNodes, middlewares, and observables which can be subscirbed to in order to witness protect events from pubsub +// GenerateIDsAndMiddlewares returns nodeIDs, libp2pNodes, middlewares, and observables which can be subscribed to in order to witness events from pubsub func GenerateIDsAndMiddlewares(t *testing.T, n int, logger zerolog.Logger, From 99828d66e11a0ce82515e28f613dd660a7d08d9e Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 16 Jun 2023 18:27:07 -0400 Subject: [PATCH 158/815] typo --- network/alsp/manager/manager_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 3fd57430e21..1cb4568b4c2 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -194,7 +194,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { // TestHandleReportedMisbehavior_And_DisallowListing_Integration implements an end-to-end integration test for the // handling of reported misbehavior and disallow listing. // -// The test sets up 3 nodes, one victim, one honest, and one (alledged) spammer. +// The test sets up 3 nodes, one victim, one honest, and one (alleged) spammer. // Initially, the test ensures that all nodes are connected to each other. // Then, test imitates that victim node reports the spammer node for spamming. // The test generates enough spam reports to trigger the disallow-listing of the victim node. @@ -1388,7 +1388,6 @@ func TestDecayMisbehaviorPenalty_DecayToZero_AllowListing(t *testing.T) { return true }, 2*time.Second, 10*time.Millisecond, "penalty was not decayed to zero") - } // TestDisallowListNotification tests the emission of the allow list notification to the network layer when the misbehavior From a2ea98024514134cbf5aaa5c69fa49c6a88908c6 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 16 Jun 2023 18:28:09 -0400 Subject: [PATCH 159/815] un-exported disallowListingThreshold --- network/alsp/manager/manager.go | 15 ++++++++++++--- network/alsp/model/params.go | 16 ++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 1732fdec4a1..07355a4003f 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -155,6 +155,14 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana } } +// WithPenaltyParams sets the penalty parameters for the MisbehaviorReportManager. +// Useful for testing when tests need to override the default penalty parameters. +func WithPenaltyParams(params model.PenaltyParams) MisbehaviorReportManagerOption { + return func(m *MisbehaviorReportManager) { + m.penaltyParams = params + } +} + // WithDefaults sets the default values for the MisbehaviorReportManager. //func WithDefaults() MisbehaviorReportManagerOption { // return func(m *MisbehaviorReportManager) { @@ -326,10 +334,11 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). - // as long as record.Penalty is NOT below model.DisallowListingThreshold, + // as long as record.Penalty is NOT below model.disallowListingThreshold, // the node is considered allow-listed and can conduct inbound and outbound connections. - // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. - if record.Penalty < model.DisallowListingThreshold && !record.DisallowListed { + // Once it falls below model.disallowListingThreshold, it needs to be disallow listed. + //if record.Penalty < model.disallowListingThreshold && !record.DisallowListed { + if record.Penalty < m.penaltyParams.DisallowListingThreshold && !record.DisallowListed { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ record.DisallowListed = true diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 7376b432fc8..5e10728bab5 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -1,7 +1,7 @@ package model // To give a summary with the default value: -// 1. The penalty of each misbehavior is 0.01 * DisallowListingThreshold = -864 +// 1. The penalty of each misbehavior is 0.01 * disallowListingThreshold = -864 // 2. The penalty of each misbehavior is decayed by a decay value at each decay interval. The default decay value is 1000. // This means that by default if a node misbehaves 100 times in a second, it gets disallow-listed, and takes 86.4 seconds to recover. // We emphasize on the default penalty value can be amplified by the engine that reports the misbehavior. @@ -13,12 +13,12 @@ package model // around a day to recover. From this point on, the decay speed is 1, and it takes around a day to recover from each // disallow-listing. const ( - // DisallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. + // disallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. // If the overall penalty of this node drops below this threshold, the node is reported to be disallow-listed by // the networking layer, i.e., existing connections to the node are closed and the node is no longer allowed to connect till // its penalty is decayed back to zero. // maximum block-list period is 1 day - DisallowListingThreshold = -24 * 60 * 60 // (Don't change this value) + disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified @@ -27,7 +27,7 @@ const ( // decrease the number of misbehavior/sec that will result in disallow-listing the node. For example, if the engine // amplifies the penalty by 10, the number of misbehavior/sec that will result in disallow-listing the node will be // 10 times less than the default penalty value and the node will be disallow-listed after 10 times more misbehavior/sec. - DefaultPenaltyValue = 0.01 * DisallowListingThreshold // (Don't change this value) + DefaultPenaltyValue = 0.01 * disallowListingThreshold // (Don't change this value) // InitialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. // The decay speed is applied on an arithmetic progression. The penalty value of the node is the first term of the @@ -47,11 +47,11 @@ const ( ) type PenaltyParams struct { - DisallowListingThreshold int64 - DefaultPenaltyValue int64 - InitialDecaySpeed int64 + DisallowListingThreshold float64 + DefaultPenaltyValue float64 + InitialDecaySpeed int } func DefaultParams() PenaltyParams { - return PenaltyParams{DisallowListingThreshold: DisallowListingThreshold, DefaultPenaltyValue: DefaultPenaltyValue, InitialDecaySpeed: InitialDecaySpeed} + return PenaltyParams{DisallowListingThreshold: disallowListingThreshold, DefaultPenaltyValue: DefaultPenaltyValue, InitialDecaySpeed: InitialDecaySpeed} } From 9de85fee1a48797b9984641739b0b21a9b7bceeb Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 16 Jun 2023 18:41:07 -0400 Subject: [PATCH 160/815] increase timeout to fix flakiness --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 1cb4568b4c2..c4bcd307731 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -270,7 +270,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") // ensures that the spammer is disallow-listed by the victim - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) From 26ebb22211d49f515cf42253082443f7c9afebfd Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 17 Jun 2023 10:35:38 -0400 Subject: [PATCH 161/815] typos --- network/alsp/model/params.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 5e10728bab5..1a185910226 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -22,11 +22,11 @@ const ( // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified - // by the engine that reports the misbehavior. The penalty system is designed in a way that more than 100 misbehavior/sec + // by the engine that reports the misbehavior. The penalty system is designed in a way that more than 100 misbehaviors/sec // at the default penalty value will result in disallow-listing the node. By amplifying the penalty, the engine can // decrease the number of misbehavior/sec that will result in disallow-listing the node. For example, if the engine - // amplifies the penalty by 10, the number of misbehavior/sec that will result in disallow-listing the node will be - // 10 times less than the default penalty value and the node will be disallow-listed after 10 times more misbehavior/sec. + // amplifies the penalty by 10, the number of misbehaviors/sec that will result in disallow-listing the node will be + // 10 times less than the default penalty value and the node will be disallow-listed after 10 misbehaviors/sec. DefaultPenaltyValue = 0.01 * disallowListingThreshold // (Don't change this value) // InitialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. @@ -43,7 +43,7 @@ const ( // by 90% from 100 to 10, and it takes around 2.5 hours to recover. If the node is disallow-listed for the fourth time, // its decay speed is decreased by 90% from 10 to 1, and it takes around a day to recover. From this point on, the decay // speed is 1, and it takes around a day to recover from each disallow-listing. - InitialDecaySpeed = 1000 // (Don't change this value) + InitialDecaySpeed = 10000 // (Don't change this value) ) type PenaltyParams struct { From 1ff304434776cdb8ca4e4444cd98a58246baee36 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 17 Jun 2023 11:13:02 -0400 Subject: [PATCH 162/815] revert back InitialDecaySpeed --- network/alsp/model/params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 1a185910226..a23b6cb30a7 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -43,7 +43,7 @@ const ( // by 90% from 100 to 10, and it takes around 2.5 hours to recover. If the node is disallow-listed for the fourth time, // its decay speed is decreased by 90% from 10 to 1, and it takes around a day to recover. From this point on, the decay // speed is 1, and it takes around a day to recover from each disallow-listing. - InitialDecaySpeed = 10000 // (Don't change this value) + InitialDecaySpeed = 1000 // (Don't change this value) ) type PenaltyParams struct { From a33dd374872e8b28cd8c9bf554fd8d5eb00c3ace Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 17 Jun 2023 17:50:49 -0400 Subject: [PATCH 163/815] typo --- network/alsp/model/params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index a23b6cb30a7..5dff2efed19 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -24,7 +24,7 @@ const ( // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified // by the engine that reports the misbehavior. The penalty system is designed in a way that more than 100 misbehaviors/sec // at the default penalty value will result in disallow-listing the node. By amplifying the penalty, the engine can - // decrease the number of misbehavior/sec that will result in disallow-listing the node. For example, if the engine + // decrease the number of misbehaviors/sec that will result in disallow-listing the node. For example, if the engine // amplifies the penalty by 10, the number of misbehaviors/sec that will result in disallow-listing the node will be // 10 times less than the default penalty value and the node will be disallow-listed after 10 misbehaviors/sec. DefaultPenaltyValue = 0.01 * disallowListingThreshold // (Don't change this value) From 4e53cc808b35d20451bed957a694a2b09592c03d Mon Sep 17 00:00:00 2001 From: Misha Date: Sun, 18 Jun 2023 08:39:04 -0400 Subject: [PATCH 164/815] WIP TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration --- network/alsp/manager/manager_test.go | 140 +++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index c4bcd307731..996028b4aba 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -281,6 +281,146 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) } +// TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration implements an end-to-end integration test for the +// handling of repeated reported misbehavior and disallow listing. +func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration(t *testing.T) { + cfg := managerCfgFixture() + + // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute + // of the network, we need to configure the ALSP manager via the network configuration, and let the network create + // the ALSP manager. + var victimSpamRecordCacheCache alsp.SpamRecordCache + cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return victimSpamRecordCacheCache + }), + } + + ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( + t, + 3, + unittest.Logger(), + unittest.NetworkCodec(), + unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector())) + sms := testutils.GenerateSubscriptionManagers(t, mws) + networkCfg := testutils.NetworkConfigFixture(t, unittest.Logger(), *ids[0], ids, mws[0], sms[0], p2p.WithAlspConfig(cfg)) + victimNetwork, err := p2p.NewNetwork(networkCfg) + require.NoError(t, err) + + // index of the victim node in the nodes slice. + victimIndex := 0 + // index of the spammer node in the nodes slice (the node that will be reported for misbehavior and disallow-listed by victim). + spammerIndex := 1 + // other node (not victim and not spammer) that we have to ensure is not affected by the disallow-listing of the spammer. + honestIndex := 2 + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}, 100*time.Millisecond) + defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) + defer cancel() + + p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) + // initially victim and spammer should be able to connect to each other. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + + e := mocknetwork.NewEngine(t) + con, err := victimNetwork.Register(channels.TestNetworkChannel, e) + require.NoError(t, err) + + // creates a misbehavior report for the spammer + report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) + + // simulates the victim node reporting the spammer node misbehavior 120 times + // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is definitely disallow-listed. + reportCount := 120 + wg := sync.WaitGroup{} + for i := 0; i < reportCount; i++ { + wg.Add(1) + // reports the misbehavior + r := report // capture range variable + go func() { + defer wg.Done() + + con.ReportMisbehavior(r) + }() + } + + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // ensures that the spammer is disallow-listed by the victim + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + + // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + // it is allow-listed again. + p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) + + // the dissalow listing should last about 86 seconds so we wait for 90 seconds to be sure + t.Logf("about to sleep for 110 seconds") + time.Sleep(110 * time.Second) + t.Logf("just finished sleeping for 110 seconds") + + // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + t.Logf("about to report misbehavior again") + + // simulates the victim node reporting the spammer node misbehavior 120 times + // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is definitely disallow-listed. + reportCount = 120 + wg = sync.WaitGroup{} + for i := 0; i < reportCount; i++ { + wg.Add(1) + // reports the misbehavior + r := report // capture range variable + go func() { + defer wg.Done() + + con.ReportMisbehavior(r) + }() + } + + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + + // loop 10 times + totalSleepTime := 0 + for i := 0; i < 10; i++ { + // log the iteration + t.Logf("iteration %d", i) + + // the spammer should still be disallow-listed + p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) + + // the victim should still be connected to the honest node + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + // the honest node should still be connected to the spammer node + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + // sleep for 10 seconds + sleepSec := 10 + t.Logf("sleeping for %d seconds", sleepSec) + time.Sleep(time.Duration(sleepSec) * time.Second) + totalSleepTime += sleepSec + + // log total sleep time + t.Logf("Finished sleeping, total sleep time %d seconds", totalSleepTime) + } + + // this should fail because the spammer is not disallow-listed + //p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) +} + // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. // It checks that when a misbehavior report is received by the ALSP manager, the metrics are recorded. // It fails the test if the metrics are not recorded or if they are recorded incorrectly. From dedbab3499b18e4538b1c50faeafa47b91098ce2 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 19 Jun 2023 13:05:35 -0400 Subject: [PATCH 165/815] debug test failure --- network/alsp/manager/manager_test.go | 21 +++++++++++++-------- network/alsp/model/params.go | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 996028b4aba..9874ebf6044 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -355,21 +355,23 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) - // the dissalow listing should last about 86 seconds so we wait for 90 seconds to be sure - t.Logf("about to sleep for 110 seconds") - time.Sleep(110 * time.Second) - t.Logf("just finished sleeping for 110 seconds") + // disallow listing should last about 86 seconds so we wait a little longer to be sure + t.Logf("about to sleep for 10 seconds") + time.Sleep(10 * time.Second) + t.Logf("just finished sleeping for 10 seconds") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Second) - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + + // all the nodes should be able to connect to each other again. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) t.Logf("about to report misbehavior again") @@ -394,7 +396,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // loop 10 times totalSleepTime := 0 - for i := 0; i < 10; i++ { + for i := 0; i < 11; i++ { // log the iteration t.Logf("iteration %d", i) @@ -417,6 +419,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Logf("Finished sleeping, total sleep time %d seconds", totalSleepTime) } + // after serving the disallow-listing period for the second time, the spammer should be able to connect to the victim node again. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // this should fail because the spammer is not disallow-listed //p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) } diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 5dff2efed19..1f10d342aab 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -18,7 +18,8 @@ const ( // the networking layer, i.e., existing connections to the node are closed and the node is no longer allowed to connect till // its penalty is decayed back to zero. // maximum block-list period is 1 day - disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) + //disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) + disallowListingThreshold = -24 * 60 * 6 // (Don't change this value) // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified From e38f9a66be7de163343496e61bc2b576241705d2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 19 Jun 2023 10:45:47 -0700 Subject: [PATCH 166/815] updated manager --- network/alsp/manager/manager.go | 27 ++++++++++++++++++++++++++- network/alsp/manager/manager_test.go | 14 +++++++++++--- network/p2p/connection/peerManager.go | 2 +- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 07355a4003f..1e8cce1e720 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -43,6 +43,15 @@ var ( type SpamRecordCacheFactory func(zerolog.Logger, uint32, module.HeroCacheMetrics) alsp.SpamRecordCache +// SpamRecordDecayFunc is the function that calculates the decay of the spam record. +type SpamRecordDecayFunc func(model.ProtocolSpamRecord) float64 + +func defaultSpamRecordDecayFunc() SpamRecordDecayFunc { + return func(record model.ProtocolSpamRecord) float64 { + return math.Min(record.Penalty+record.Decay, 0) + } +} + // defaultSpamRecordCacheFactory is the default spam record cache factory. It creates a new spam record cache with the given parameter. func defaultSpamRecordCacheFactory() SpamRecordCacheFactory { return func(logger zerolog.Logger, size uint32, cacheMetrics module.HeroCacheMetrics) alsp.SpamRecordCache { @@ -82,6 +91,9 @@ type MisbehaviorReportManager struct { // params is the ALSP module parameters used for penalties and decays. penaltyParams model.PenaltyParams + + // decayFunc is the function that calculates the decay of the spam record. + decayFunc SpamRecordDecayFunc } var _ network.MisbehaviorReportManager = (*MisbehaviorReportManager)(nil) @@ -163,6 +175,12 @@ func WithPenaltyParams(params model.PenaltyParams) MisbehaviorReportManagerOptio } } +func WithDecayFunc(f SpamRecordDecayFunc) MisbehaviorReportManagerOption { + return func(m *MisbehaviorReportManager) { + m.decayFunc = f + } +} + // WithDefaults sets the default values for the MisbehaviorReportManager. //func WithDefaults() MisbehaviorReportManagerOption { // return func(m *MisbehaviorReportManager) { @@ -196,6 +214,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n disallowListingConsumer: consumer, cacheFactory: defaultSpamRecordCacheFactory(), penaltyParams: model.DefaultParams(), + decayFunc: defaultSpamRecordDecayFunc(), } store := queue.NewHeroStore( @@ -358,7 +377,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed // is a positive number. So the penalty is getting closer to zero. // We use math.Min() to make sure the penalty is never positive. - record.Penalty = math.Min(record.Penalty+record.Decay, 0) + record.Penalty = m.decayFunc(record) // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). @@ -439,6 +458,12 @@ func (m *MisbehaviorReportManager) processMisbehaviorReport(report internal.Repo return record, fmt.Errorf("penalty value is positive, expected negative %f", report.Penalty) } record.Penalty += report.Penalty // penalty value is negative. We add it to the current penalty. + lg = lg.With(). + Float64("penalty_before_update", record.Penalty). + Uint64("cutoff_counter", record.CutoffCounter). + Float64("decay_speed", record.Decay). + Bool("disallow_listed", record.DisallowListed). + Logger() return record, nil }) if err != nil { diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 996028b4aba..d656e44f3cf 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -285,6 +285,15 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // handling of repeated reported misbehavior and disallow listing. func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration(t *testing.T) { cfg := managerCfgFixture() + resetZero := false + decayFuc := func(record model.ProtocolSpamRecord) float64 { + if resetZero { + return 0 + } else { + // decay as usual + return math.Min(record.Penalty+record.Decay, 0) + } + } // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute // of the network, we need to configure the ALSP manager via the network configuration, and let the network create @@ -295,6 +304,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCacheCache }), + alspmgr.WithDecayFunc(decayFuc), } ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( @@ -363,9 +373,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) // the dissalow listing should last about 86 seconds so we wait for 90 seconds to be sure - t.Logf("about to sleep for 110 seconds") - time.Sleep(110 * time.Second) - t.Logf("just finished sleeping for 110 seconds") + resetZero = true // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Second) diff --git a/network/p2p/connection/peerManager.go b/network/p2p/connection/peerManager.go index 05cc7c47129..81bd125bf26 100644 --- a/network/p2p/connection/peerManager.go +++ b/network/p2p/connection/peerManager.go @@ -41,7 +41,7 @@ type PeerManager struct { // and it uses the connector to actually connect or disconnect from peers. func NewPeerManager(logger zerolog.Logger, updateInterval time.Duration, connector p2p.Connector) *PeerManager { pm := &PeerManager{ - logger: logger, + logger: logger.With().Str("component", "peer-manager").Logger(), connector: connector, peerRequestQ: make(chan struct{}, 1), peerUpdateInterval: updateInterval, From fddb2a84e2ac6ebc1d6265264ae749271941a8a2 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 20:30:12 +0300 Subject: [PATCH 167/815] Added integration test for CB. --- engine/access/rpc/backend/backend_accounts.go | 2 +- engine/access/rpc/backend/backend_events.go | 2 +- engine/access/rpc/backend/backend_scripts.go | 2 +- .../rpc/backend/backend_transactions.go | 8 +- .../access/access_circuit_breaker_test.go | 190 ++++++++++++++++++ 5 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 integration/tests/access/access_circuit_breaker_test.go diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index 205fa0f3ab8..119c59a8b15 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -152,7 +152,7 @@ func (b *backendAccounts) tryGetAccount(ctx context.Context, execNode *flow.Iden resp, err := execRPCClient.GetAccountAtBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(execNode.Address) } return nil, err diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index fc163459518..e1fcfb087d2 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -252,7 +252,7 @@ func (b *backendEvents) tryGetEvents(ctx context.Context, resp, err := execRPCClient.GetEventsForBlockIDs(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(execNode.Address) } return nil, err diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 80b611d243c..2777944299b 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -206,7 +206,7 @@ func (b *backendScripts) tryExecuteScript(ctx context.Context, executorAddress s execResp, err := execRPCClient.ExecuteScriptAtBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(executorAddress) } return nil, status.Errorf(status.Code(err), "failed to execute the script on the execution node %s: %v", executorAddress, err) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index a9ba3002d40..048cb2d223f 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -155,7 +155,7 @@ func (b *backendTransactions) sendTransactionToCollector(ctx context.Context, err = b.grpcTxSend(ctx, collectionRPC, tx) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateAccessAPIClient(collectionNodeAddr) } return fmt.Errorf("failed to send transaction to collection node at %s: %w", collectionNodeAddr, err) @@ -811,7 +811,7 @@ func (b *backendTransactions) tryGetTransactionResult( resp, err := execRPCClient.GetTransactionResult(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(execNode.Address) } return nil, err @@ -874,7 +874,7 @@ func (b *backendTransactions) tryGetTransactionResultsByBlockID( resp, err := execRPCClient.GetTransactionResultsByBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(execNode.Address) } return nil, err @@ -938,7 +938,7 @@ func (b *backendTransactions) tryGetTransactionResultByIndex( resp, err := execRPCClient.GetTransactionResultByIndex(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable { + if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { b.connFactory.InvalidateExecutionAPIClient(execNode.Address) } return nil, err diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go new file mode 100644 index 00000000000..0c025b11565 --- /dev/null +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -0,0 +1,190 @@ +package access + +import ( + "context" + "fmt" + sdk "github.com/onflow/flow-go-sdk" + sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go-sdk/templates" + "github.com/onflow/flow-go/integration/testnet" + "github.com/onflow/flow-go/integration/tests/lib" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/utils/unittest" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "testing" + "time" +) + +func TestAccessCircuitBreaker(t *testing.T) { + suite.Run(t, new(AccessCircuitBreakerSuite)) +} + +type AccessCircuitBreakerSuite struct { + suite.Suite + + log zerolog.Logger + + // root context for the current test + ctx context.Context + cancel context.CancelFunc + + net *testnet.FlowNetwork +} + +var requestTimeout = 3*time.Second +var cbRestoreTimeout = 6*time.Second + +func (s *AccessCircuitBreakerSuite) TearDownTest() { + s.log.Info().Msg("================> Start TearDownTest") + s.net.Remove() + s.cancel() + s.log.Info().Msg("================> Finish TearDownTest") +} + + +func (s *AccessCircuitBreakerSuite) SetupTest() { + s.log = unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel) + s.log.Info().Msg("================> SetupTest") + defer func() { + s.log.Info().Msg("================> Finish SetupTest") + }() + + // need one access node with enabled circuit breaker + nodeConfigs := []testnet.NodeConfig{ + testnet.NewNodeConfig( + flow.RoleAccess, + testnet.WithLogLevel(zerolog.InfoLevel), + testnet.WithAdditionalFlag("--circuit-breaker-enabled=true"), + testnet.WithAdditionalFlag(fmt.Sprintf("--circuit-breaker-restore-timeout=%s", cbRestoreTimeout.String())), + testnet.WithAdditionalFlag("--circuit-breaker-max-failures=1"), + testnet.WithAdditionalFlag(fmt.Sprintf("--collection-client-timeout=%s", requestTimeout.String())), + ), + } + // need one execution node + exeConfig := testnet.NewNodeConfig(flow.RoleExecution, testnet.WithLogLevel(zerolog.FatalLevel)) + nodeConfigs = append(nodeConfigs, exeConfig) + + // need one dummy verification node (unused ghost) + verConfig := testnet.NewNodeConfig(flow.RoleVerification, testnet.WithLogLevel(zerolog.FatalLevel), testnet.AsGhost()) + nodeConfigs = append(nodeConfigs, verConfig) + + // need one controllable collection node + collConfig := testnet.NewNodeConfig(flow.RoleCollection, testnet.WithLogLevel(zerolog.FatalLevel), testnet.WithAdditionalFlag("--hotstuff-proposal-duration=100ms")) + nodeConfigs = append(nodeConfigs, collConfig) + + // need three consensus nodes (unused ghost) + for n := 0; n < 3; n++ { + conID := unittest.IdentifierFixture() + nodeConfig := testnet.NewNodeConfig(flow.RoleConsensus, + testnet.WithLogLevel(zerolog.FatalLevel), + testnet.WithID(conID), + testnet.AsGhost()) + nodeConfigs = append(nodeConfigs, nodeConfig) + } + + conf := testnet.NewNetworkConfig("access_api_test", nodeConfigs) + s.net = testnet.PrepareFlowNetwork(s.T(), conf, flow.Localnet) + + // start the network + s.T().Logf("starting flow network with docker containers") + s.ctx, s.cancel = context.WithCancel(context.Background()) + + s.net.Start(s.ctx) +} + +func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { + ctx, cancel := context.WithCancel(s.ctx) + defer cancel() + + // 1. Get collection node + collectionContainer := s.net.ContainerByName("collection_1") + + + // 2. Get Access Node container and client + accessContainer := s.net.ContainerByName(testnet.PrimaryAN) + + // Check if access node was created with circuit breaker flags + require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-enabled")) + require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-restore-timeout")) + require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-max-failures")) + + accessClient, err := accessContainer.TestnetClient() + assert.NoError(s.T(), err, "failed to get collection node client") + + latestBlockID, err := accessClient.GetLatestBlockID(ctx) + require.NoError(s.T(), err) + + // create new account to deploy Counter to + accountPrivateKey := lib.RandomPrivateKey() + + accountKey := sdk.NewAccountKey(). + FromPrivateKey(accountPrivateKey). + SetHashAlgo(sdkcrypto.SHA3_256). + SetWeight(sdk.AccountKeyWeightThreshold) + + serviceAddress := sdk.Address(accessClient.Chain.ServiceAddress()) + + // Generate the account creation transaction + createAccountTx, err := templates.CreateAccount( + []*sdk.AccountKey{accountKey}, + []templates.Contract{ + { + Name: lib.CounterContract.Name, + Source: lib.CounterContract.ToCadence(), + }, + }, serviceAddress) + require.NoError(s.T(), err) + + createAccountTx. + SetReferenceBlockID(sdk.Identifier(latestBlockID)). + SetProposalKey(serviceAddress, 0, accessClient.GetSeqNumber()). + SetPayer(serviceAddress). + SetGasLimit(9999) + + // sign transaction + + childCtx, cancel := context.WithTimeout(ctx, time.Second*10) + signedTx, err := accessClient.SignTransaction(createAccountTx) + require.NoError(s.T(), err) + cancel() + + // 3. Disconnect collection node from network to activate Circuit Breaker + err = collectionContainer.Disconnect() + require.NoError(s.T(), err, "failed to pause connection node") + + //4. Send couple transactions to proof circuit breaker opens correctly + sendTransaction := func(ctx context.Context, tx *sdk.Transaction) (time.Duration, error) { + childCtx, cancel = context.WithTimeout(ctx, time.Second*10) + start := time.Now() + err := accessClient.SendTransaction(childCtx, tx) + duration := time.Since(start) + defer cancel() + + return duration, err + } + + // try to send transaction first time. Should wait at least timeout time and return Unknown error + duration, err := sendTransaction(ctx, signedTx) + assert.Equal(s.T(), codes.Unknown, status.Code(err)) + assert.GreaterOrEqual(s.T(), requestTimeout, duration) + + // try to send transaction second time. Should wait less then a second cause CB configured to break after firs fail + duration, err = sendTransaction(ctx, signedTx) + assert.Equal(s.T(), codes.Unknown, status.Code(err)) + assert.Greater(s.T(), time.Second, duration) + + // connect again + err = collectionContainer.Connect() + require.NoError(s.T(), err, "failed to start collection node") + // wait to restore circuit breaker + time.Sleep(cbRestoreTimeout) + + // try to send transaction third time. Transaction should be send successful + _, err = sendTransaction(ctx, signedTx) + require.NoError(s.T(), err, "transaction should be sent") +} From 84d33960a872445dd8c26389fc166cbdaf74c404 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 21:02:56 +0300 Subject: [PATCH 168/815] Change circuit breaker config to pointer. --- cmd/access/node_builder/access_node_builder.go | 2 +- engine/access/rpc/backend/backend_transactions.go | 1 + engine/access/rpc/backend/connection_factory.go | 5 ++++- engine/access/rpc/backend/connection_factory_test.go | 4 ++-- engine/access/rpc/engine.go | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index cdf66e66bf0..1d08851e780 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -161,7 +161,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { FixedExecutionNodeIDs: nil, ArchiveAddressList: nil, MaxMsgSize: grpcutils.DefaultMaxMsgSize, - CircuitBreakerConfig: backend.CircuitBreakerConfig{ + CircuitBreakerConfig: &backend.CircuitBreakerConfig{ Enabled: false, RestoreTimeout: time.Duration(60) * time.Second, MaxFailures: 5, diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 048cb2d223f..f8c9f5385ea 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -128,6 +128,7 @@ func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sa return nil, fmt.Errorf("could not get local cluster by txID: %x", tx.ID()) } + // samples ony used when circuit breaker is disabled if !b.circuitBreakerEnabled { // select a random subset of collection nodes from the cluster to be tried in order targetNodes = targetNodes.Sample(sampleSize) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index a5f63ab9eb1..acb635bd37c 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -62,7 +62,7 @@ type ConnectionFactoryImpl struct { AccessMetrics module.AccessMetrics Log zerolog.Logger mutex sync.Mutex - CircuitBreakerConfig CircuitBreakerConfig + CircuitBreakerConfig *CircuitBreakerConfig } type CircuitBreakerConfig struct { @@ -263,8 +263,10 @@ func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration if cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ + // here restore timeout defined to automatically return circuit breaker to HalfClose state Timeout: cf.CircuitBreakerConfig.RestoreTimeout, ReadyToTrip: func(counts gobreaker.Counts) bool { + // here number of maximum failures will be checked, before circuit breaker go to Open state return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, }) @@ -278,6 +280,7 @@ func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { + // The invoker should be called from circuit breaker execute, to catch each fails and react according to settings _, err := circuitBreaker.Execute(func() (interface{}, error) { err := invoker(ctx, method, req, reply, cc, opts...) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 99bbad2e05f..b7b7aedda40 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -428,7 +428,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { // set the execution grpc client requestTimeout connectionFactory.ExecutionNodeGRPCTimeout = requestTimeout // set the configuration for circuit breaker - connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ + connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, RestoreTimeout: circuitBreakerRestoreTimeout, @@ -498,7 +498,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { // set the collection grpc client requestTimeout connectionFactory.CollectionNodeGRPCTimeout = requestTimeout // set the configuration for circuit breaker - connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ + connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, RestoreTimeout: circuitBreakerRestoreTimeout, diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index c4b36c263b6..0634f908ba9 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -48,7 +48,7 @@ type Config struct { PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node - CircuitBreakerConfig backend.CircuitBreakerConfig // the configuration for circuit breaker + CircuitBreakerConfig *backend.CircuitBreakerConfig // the configuration for circuit breaker } // Engine exposes the server with a simplified version of the Access API. From 152b7699d19ee7c894f292cc026391ec8577dae7 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 21:10:08 +0300 Subject: [PATCH 169/815] Added CB config checks. --- engine/access/rpc/backend/connection_factory.go | 2 +- engine/access/rpc/engine.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index acb635bd37c..ffaa1c72172 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -261,7 +261,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration) grpc.DialOption { var clientInterceptors []grpc.UnaryClientInterceptor - if cf.CircuitBreakerConfig.Enabled { + if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ // here restore timeout defined to automatically return circuit breaker to HalfClose state Timeout: cf.CircuitBreakerConfig.RestoreTimeout, diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 0634f908ba9..7f9a62809c8 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -175,6 +175,11 @@ func NewBuilder(log zerolog.Logger, CircuitBreakerConfig: config.CircuitBreakerConfig, } + circuitBreakerEnabled := false + if config.CircuitBreakerConfig != nil { + circuitBreakerEnabled = config.CircuitBreakerConfig.Enabled + } + backend := backend.New(state, collectionRPC, historicalAccessNodes, @@ -194,7 +199,7 @@ func NewBuilder(log zerolog.Logger, log, backend.DefaultSnapshotHistoryLimit, config.ArchiveAddressList, - config.CircuitBreakerConfig.Enabled, + circuitBreakerEnabled, ) finalizedCache, finalizedCacheWorker, err := events.NewFinalizedHeaderCache(state) From 09669612ffd337ca40a3b4ec9a56ae793f1ddbbc Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 21:11:42 +0300 Subject: [PATCH 170/815] remove unnecessary changes --- cmd/util/cmd/execution-state-extract/export_report.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 35f744445ee..e69de29bb2d 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +0,0 @@ -{ - "EpochCounter": 0, - "PreviousStateCommitment": "6d2eb6ae0aa575274a45362f9bd175ec6f030f79552c5fed27ada030d3799a53", - "CurrentStateCommitment": "6d2eb6ae0aa575274a45362f9bd175ec6f030f79552c5fed27ada030d3799a53", - "ReportSucceeded": true -} \ No newline at end of file From 691438e33de3732fcc9c53416cf2bde9aac39212 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 21:14:59 +0300 Subject: [PATCH 171/815] revert unnecessary commited file --- cmd/util/cmd/execution-state-extract/export_report.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json deleted file mode 100644 index e69de29bb2d..00000000000 From 1915b4a76745f91e9a67c5c52d1c1375b058257a Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 20 Jun 2023 21:48:52 +0300 Subject: [PATCH 172/815] Added comment to unit tests --- engine/access/rpc/backend/connection_factory_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index b7b7aedda40..8a60430b0a2 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -408,7 +408,7 @@ func TestConnectionPoolStale(t *testing.T) { assert.Equal(t, resp, expected) } -// TestCircuitBreakerExecutionNode +// TestCircuitBreakerExecutionNode tests circuit breaker states changed for execution nodes func TestCircuitBreakerExecutionNode(t *testing.T) { requestTimeout := 1 * time.Second circuitBreakerRestoreTimeout := 3 * time.Second @@ -477,7 +477,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { assert.Equal(t, nil, err) } -// TestCircuitBreakerCollectionNode +// TestCircuitBreakerCollectionNode tests circuit breaker states changed for collection nodes func TestCircuitBreakerCollectionNode(t *testing.T) { requestTimeout := 1 * time.Second circuitBreakerRestoreTimeout := 3 * time.Second @@ -536,7 +536,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) - cn.handler.On("Ping", testifymock.Anything, req).Unset() + //cn.handler.On("Ping", testifymock.Anything, req).Unset() cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) //Wait until Circuit breaker go to Half-open state From 661b92e1f61b74b37e2e10c833a7dbbb7bf708ec Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 20 Jun 2023 19:00:16 -0400 Subject: [PATCH 173/815] replaced sleep with function option to decay to zero --- network/alsp/manager/manager.go | 11 +++++ network/alsp/manager/manager_test.go | 68 ++++++++++++++-------------- network/alsp/model/params.go | 3 +- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 1e8cce1e720..6cc040bf543 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -341,6 +341,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { allIds := m.cache.Identities() for _, id := range allIds { + m.logger.Info().Hex("identifier", logging.ID(id)).Msg("onHeartbeat - looping through spam records") penalty, err := m.cache.Adjust(id, func(record model.ProtocolSpamRecord) (model.ProtocolSpamRecord, error) { if record.Penalty > 0 { // sanity check; this should never happen. @@ -377,7 +378,17 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed // is a positive number. So the penalty is getting closer to zero. // We use math.Min() to make sure the penalty is never positive. + m.logger.Info(). + Hex("identifier", logging.ID(id)). + Bool("disallow_listed", record.DisallowListed). + Float64("penalty", record.Penalty). + Msg("onHeartbeat - before adjusting penalty via decayFunc") record.Penalty = m.decayFunc(record) + m.logger.Info(). + Hex("identifier", logging.ID(id)). + Bool("disallow_listed", record.DisallowListed). + Float64("penalty", record.Penalty). + Msg("onHeartbeat - after adjusting penalty via decayFunc") // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 7092efcac71..5dbe5f2fc98 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -278,7 +278,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. - p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) } // TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration implements an end-to-end integration test for the @@ -287,10 +287,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio cfg := managerCfgFixture() resetZero := false decayFuc := func(record model.ProtocolSpamRecord) float64 { + t.Logf("decayFuc called with record: %+v", record) if resetZero { + // decay to zero in a single heart beat + t.Logf("resetZero is true, so decay to zero") return 0 } else { // decay as usual + t.Logf("resetZero is false, so decay as usual") return math.Min(record.Penalty+record.Decay, 0) } } @@ -362,25 +366,33 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") // ensures that the spammer is disallow-listed by the victim + //victimSpamRecordCacheCache.Get(ids[spammerIndex].NodeID) + record, ok := victimSpamRecordCacheCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + + // ensures that the spammer is disallow-listed by the victim + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + // it is allow-listed again. p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - // it is allow-listed again. - p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) - - // the dissalow listing should last about 86 seconds so we wait for 90 seconds to be sure + // decay the disallow-listing penalty of the spammer node to zero. + t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") resetZero = true + t.Logf("decayed the disallow-listing penalty of the spammer node to zero") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + // go back to regular decay to prepare for the next set of misbehavior reports. + resetZero = false t.Logf("about to report misbehavior again") // simulates the victim node reporting the spammer node misbehavior 120 times @@ -402,36 +414,24 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // loop 10 times - totalSleepTime := 0 - for i := 0; i < 11; i++ { - // log the iteration - t.Logf("iteration %d", i) - - // the spammer should still be disallow-listed - p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) - - // the victim should still be connected to the honest node - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - - // the honest node should still be connected to the spammer node - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // sleep for 10 seconds - sleepSec := 10 - t.Logf("sleeping for %d seconds", sleepSec) - time.Sleep(time.Duration(sleepSec) * time.Second) - totalSleepTime += sleepSec + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + // it is allow-listed again. + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) - // log total sleep time - t.Logf("Finished sleeping, total sleep time %d seconds", totalSleepTime) - } + // decay the disallow-listing penalty of the spammer node to zero. + t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") + resetZero = true + t.Logf("decayed the disallow-listing penalty of the spammer node to zero (2nd time)") - // after serving the disallow-listing period for the second time, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) - // this should fail because the spammer is not disallow-listed - //p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}) + // all the nodes should be able to connect to each other again. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) } // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. @@ -1458,7 +1458,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { require.True(t, ok) // the record should be in the cache require.NotNil(t, record) - require.False(t, record.DisallowListed) // the peer should not be disallow listed yet. + require.False(t, record.DisallowListed) // the peer should not be disallow listed. // with a single heartbeat and decay speed of 1000, the penalty should be decayed to zero. require.Equal(t, float64(0), record.Penalty) // the decay should be the default decay value. diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 1f10d342aab..5dff2efed19 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -18,8 +18,7 @@ const ( // the networking layer, i.e., existing connections to the node are closed and the node is no longer allowed to connect till // its penalty is decayed back to zero. // maximum block-list period is 1 day - //disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) - disallowListingThreshold = -24 * 60 * 6 // (Don't change this value) + disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified From 270cc40d14476e6082d6d268b3b64ad5eb38844a Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 07:33:50 -0400 Subject: [PATCH 174/815] clean up --- network/alsp/manager/manager.go | 8 -------- network/alsp/manager/manager_test.go | 10 +++++----- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 6cc040bf543..5fb92d9efc8 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -181,14 +181,6 @@ func WithDecayFunc(f SpamRecordDecayFunc) MisbehaviorReportManagerOption { } } -// WithDefaults sets the default values for the MisbehaviorReportManager. -//func WithDefaults() MisbehaviorReportManagerOption { -// return func(m *MisbehaviorReportManager) { -// m.cacheFactory = defaultSpamRecordCacheFactory() -// m.disablePenalty = false -// } -//} - // NewMisbehaviorReportManager creates a new instance of the MisbehaviorReportManager. // Args: // cfg: the configuration for the MisbehaviorReportManager. diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 1cc8ebb87c6..5c0ec5e7a74 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -285,7 +285,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration implements an end-to-end integration test for the // handling of repeated reported misbehavior and disallow listing. func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration(t *testing.T) { - cfg := managerCfgFixture() + cfg := managerCfgFixture(t) resetZero := false decayFuc := func(record model.ProtocolSpamRecord) float64 { t.Logf("decayFuc called with record: %+v", record) @@ -415,14 +415,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") resetZero = true From 79e00f3ce8bc82ace8ab5d8e8b12fcf3ab920e88 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 07:42:37 -0400 Subject: [PATCH 175/815] clean up - removed func DefaultParams() --- network/alsp/manager/manager.go | 24 +++++++++++------------- network/alsp/model/params.go | 12 ++++-------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 5fb92d9efc8..0195d3b8c48 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -89,9 +89,6 @@ type MisbehaviorReportManager struct { // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. workerPool *worker.Pool[internal.ReportedMisbehaviorWork] - // params is the ALSP module parameters used for penalties and decays. - penaltyParams model.PenaltyParams - // decayFunc is the function that calculates the decay of the spam record. decayFunc SpamRecordDecayFunc } @@ -167,14 +164,16 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana } } -// WithPenaltyParams sets the penalty parameters for the MisbehaviorReportManager. -// Useful for testing when tests need to override the default penalty parameters. -func WithPenaltyParams(params model.PenaltyParams) MisbehaviorReportManagerOption { - return func(m *MisbehaviorReportManager) { - m.penaltyParams = params - } -} - +// WithDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. +// Args: +// +// f: the decay function. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. func WithDecayFunc(f SpamRecordDecayFunc) MisbehaviorReportManagerOption { return func(m *MisbehaviorReportManager) { m.decayFunc = f @@ -205,7 +204,6 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, cacheFactory: defaultSpamRecordCacheFactory(), - penaltyParams: model.DefaultParams(), decayFunc: defaultSpamRecordDecayFunc(), } @@ -350,7 +348,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // the node is considered allow-listed and can conduct inbound and outbound connections. // Once it falls below model.disallowListingThreshold, it needs to be disallow listed. //if record.Penalty < model.disallowListingThreshold && !record.DisallowListed { - if record.Penalty < m.penaltyParams.DisallowListingThreshold && !record.DisallowListed { + if record.Penalty < model.DisallowListingThreshold && !record.DisallowListed { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ record.DisallowListed = true diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 5dff2efed19..0df82b1952a 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -1,7 +1,7 @@ package model // To give a summary with the default value: -// 1. The penalty of each misbehavior is 0.01 * disallowListingThreshold = -864 +// 1. The penalty of each misbehavior is 0.01 * DisallowListingThreshold = -864 // 2. The penalty of each misbehavior is decayed by a decay value at each decay interval. The default decay value is 1000. // This means that by default if a node misbehaves 100 times in a second, it gets disallow-listed, and takes 86.4 seconds to recover. // We emphasize on the default penalty value can be amplified by the engine that reports the misbehavior. @@ -13,12 +13,12 @@ package model // around a day to recover. From this point on, the decay speed is 1, and it takes around a day to recover from each // disallow-listing. const ( - // disallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. + // DisallowListingThreshold is the threshold for concluding a node behavior is malicious and disallow-listing the node. // If the overall penalty of this node drops below this threshold, the node is reported to be disallow-listed by // the networking layer, i.e., existing connections to the node are closed and the node is no longer allowed to connect till // its penalty is decayed back to zero. // maximum block-list period is 1 day - disallowListingThreshold = -24 * 60 * 60 // (Don't change this value) + DisallowListingThreshold = -24 * 60 * 60 // (Don't change this value) // DefaultPenaltyValue is the default penalty value for misbehaving nodes. // By default, each reported infringement will be penalized by this value. However, the penalty can be amplified @@ -27,7 +27,7 @@ const ( // decrease the number of misbehaviors/sec that will result in disallow-listing the node. For example, if the engine // amplifies the penalty by 10, the number of misbehaviors/sec that will result in disallow-listing the node will be // 10 times less than the default penalty value and the node will be disallow-listed after 10 misbehaviors/sec. - DefaultPenaltyValue = 0.01 * disallowListingThreshold // (Don't change this value) + DefaultPenaltyValue = 0.01 * DisallowListingThreshold // (Don't change this value) // InitialDecaySpeed is the initial decay speed of the penalty of a misbehaving node. // The decay speed is applied on an arithmetic progression. The penalty value of the node is the first term of the @@ -51,7 +51,3 @@ type PenaltyParams struct { DefaultPenaltyValue float64 InitialDecaySpeed int } - -func DefaultParams() PenaltyParams { - return PenaltyParams{DisallowListingThreshold: disallowListingThreshold, DefaultPenaltyValue: DefaultPenaltyValue, InitialDecaySpeed: InitialDecaySpeed} -} From 236269a1e30321825459123a571274cfb535a307 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 21 Jun 2023 14:43:55 +0300 Subject: [PATCH 176/815] linted --- .../access/access_circuit_breaker_test.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index 0c025b11565..07b9f98ddbf 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -3,6 +3,16 @@ package access import ( "context" "fmt" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go-sdk/templates" @@ -10,14 +20,6 @@ import ( "github.com/onflow/flow-go/integration/tests/lib" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "testing" - "time" ) func TestAccessCircuitBreaker(t *testing.T) { @@ -36,8 +38,8 @@ type AccessCircuitBreakerSuite struct { net *testnet.FlowNetwork } -var requestTimeout = 3*time.Second -var cbRestoreTimeout = 6*time.Second +var requestTimeout = 3 * time.Second +var cbRestoreTimeout = 6 * time.Second func (s *AccessCircuitBreakerSuite) TearDownTest() { s.log.Info().Msg("================> Start TearDownTest") @@ -46,7 +48,6 @@ func (s *AccessCircuitBreakerSuite) TearDownTest() { s.log.Info().Msg("================> Finish TearDownTest") } - func (s *AccessCircuitBreakerSuite) SetupTest() { s.log = unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel) s.log.Info().Msg("================> SetupTest") @@ -104,7 +105,6 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // 1. Get collection node collectionContainer := s.net.ContainerByName("collection_1") - // 2. Get Access Node container and client accessContainer := s.net.ContainerByName(testnet.PrimaryAN) From ac0d9e87ca24a6b1685f6d2cddd1baed94ca93c1 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 21 Jun 2023 18:30:37 +0300 Subject: [PATCH 177/815] simplify logic for interceptors --- apiproxy/access_api_proxy.go | 4 +-- .../node_builder/access_node_builder.go | 2 +- .../export_report.json | 6 ++++ engine/access/apiproxy/access_api_proxy.go | 4 +-- .../access/rpc/backend/connection_factory.go | 31 ++++++++++++------- 5 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/apiproxy/access_api_proxy.go b/apiproxy/access_api_proxy.go index dfe610f5857..0bd90bd0999 100644 --- a/apiproxy/access_api_proxy.go +++ b/apiproxy/access_api_proxy.go @@ -86,7 +86,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithInsecure(), //nolint:staticcheck - grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) + backend.WithClientTimeoutOption(timeout)) if err != nil { return err } @@ -100,7 +100,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcutils.DefaultMaxMsgSize)), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) + backend.WithClientTimeoutOption(timeout)) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 766462f1ded..6e653805f44 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -923,7 +923,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.rpcConf.CollectionAddr, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout))) + backend.WithClientTimeoutOption(builder.rpcConf.CollectionClientTimeout)) if err != nil { return err } diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json new file mode 100644 index 00000000000..6393b54664b --- /dev/null +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -0,0 +1,6 @@ +{ + "EpochCounter": 0, + "PreviousStateCommitment": "f4a516703d828532a8537dc81f98fffecf6228733364d17efa3332e177592b54", + "CurrentStateCommitment": "f4a516703d828532a8537dc81f98fffecf6228733364d17efa3332e177592b54", + "ReportSucceeded": true +} \ No newline at end of file diff --git a/engine/access/apiproxy/access_api_proxy.go b/engine/access/apiproxy/access_api_proxy.go index ce95c1cca28..ed47ef167eb 100644 --- a/engine/access/apiproxy/access_api_proxy.go +++ b/engine/access/apiproxy/access_api_proxy.go @@ -65,7 +65,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) + backend.WithClientTimeoutOption(timeout)) if err != nil { return err } @@ -79,7 +79,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - grpc.WithUnaryInterceptor(backend.WithClientUnaryInterceptor(timeout))) + backend.WithClientTimeoutOption(timeout)) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index ffaa1c72172..00ecf14d2e2 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -92,6 +92,13 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D Timeout: timeout, } + var connInterceptors []grpc.UnaryClientInterceptor + cbInterceptor := cf.withCircuitBreakerInterceptor() + if cbInterceptor != nil { + connInterceptors = append(connInterceptors, cbInterceptor) + } + connInterceptors = append(connInterceptors, WithClientTimeoutInterceptor(timeout)) + // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached // The connections should be safe to be persisted and reused // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams @@ -101,7 +108,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(cf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithKeepaliveParams(keepaliveParams), - cf.withChainUnaryInterceptor(timeout), + grpc.WithChainUnaryInterceptor(connInterceptors...), ) if err != nil { return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) @@ -258,9 +265,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration) grpc.DialOption { - var clientInterceptors []grpc.UnaryClientInterceptor - +func (cf *ConnectionFactoryImpl) withCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ // here restore timeout defined to automatically return circuit breaker to HalfClose state @@ -271,7 +276,7 @@ func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration }, }) - interceptor := func( + circuitBreakerInterceptor := func( ctx context.Context, method string, req interface{}, @@ -280,6 +285,7 @@ func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { + fmt.Println("!!! circuitBreakerInterceptor") // The invoker should be called from circuit breaker execute, to catch each fails and react according to settings _, err := circuitBreaker.Execute(func() (interface{}, error) { err := invoker(ctx, method, req, reply, cc, opts...) @@ -288,15 +294,14 @@ func (cf *ConnectionFactoryImpl) withChainUnaryInterceptor(timeout time.Duration }) return err } - clientInterceptors = append(clientInterceptors, interceptor) - } - clientInterceptors = append(clientInterceptors, WithClientUnaryInterceptor(timeout)) + return circuitBreakerInterceptor + } - return grpc.WithChainUnaryInterceptor(clientInterceptors...) + return nil } -func WithClientUnaryInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { +func WithClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -310,7 +315,7 @@ func WithClientUnaryInterceptor(timeout time.Duration) grpc.UnaryClientIntercept ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) defer cancel() - + fmt.Println("!!! clientTimeoutInterceptor") // call the remote GRPC using the short context err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) @@ -319,3 +324,7 @@ func WithClientUnaryInterceptor(timeout time.Duration) grpc.UnaryClientIntercept return clientTimeoutInterceptor } + +func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { + return grpc.WithUnaryInterceptor(WithClientTimeoutInterceptor(timeout)) +} From 6017683edd1e2bd1eda8bc495ddd7da4ee1f0861 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 21 Jun 2023 20:33:36 +0300 Subject: [PATCH 178/815] added invalidate interceptor --- .../export_report.json | 4 +- engine/access/rpc/backend/backend_accounts.go | 3 - engine/access/rpc/backend/backend_events.go | 3 - engine/access/rpc/backend/backend_scripts.go | 3 - .../rpc/backend/backend_transactions.go | 12 ---- .../access/rpc/backend/connection_factory.go | 62 ++++++++++++++++--- .../rpc/backend/connection_factory_test.go | 2 +- 7 files changed, 58 insertions(+), 31 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 6393b54664b..6f3e833a6a5 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "f4a516703d828532a8537dc81f98fffecf6228733364d17efa3332e177592b54", - "CurrentStateCommitment": "f4a516703d828532a8537dc81f98fffecf6228733364d17efa3332e177592b54", + "PreviousStateCommitment": "f305e934fb48402557dbf73b9b6abf6217a582ffa441ce6f6e84a22d7e82b887", + "CurrentStateCommitment": "f305e934fb48402557dbf73b9b6abf6217a582ffa441ce6f6e84a22d7e82b887", "ReportSucceeded": true } \ No newline at end of file diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index 119c59a8b15..f0e3bf1aed3 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -152,9 +152,6 @@ func (b *backendAccounts) tryGetAccount(ctx context.Context, execNode *flow.Iden resp, err := execRPCClient.GetAccountAtBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(execNode.Address) - } return nil, err } return resp, nil diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index e1fcfb087d2..bc1c0f50f62 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -252,9 +252,6 @@ func (b *backendEvents) tryGetEvents(ctx context.Context, resp, err := execRPCClient.GetEventsForBlockIDs(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(execNode.Address) - } return nil, err } return resp, nil diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 2777944299b..cbb73fb25bc 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -206,9 +206,6 @@ func (b *backendScripts) tryExecuteScript(ctx context.Context, executorAddress s execResp, err := execRPCClient.ExecuteScriptAtBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(executorAddress) - } return nil, status.Errorf(status.Code(err), "failed to execute the script on the execution node %s: %v", executorAddress, err) } return execResp.GetValue(), nil diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 2f2111a9ca3..025be9c04e7 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -156,9 +156,6 @@ func (b *backendTransactions) sendTransactionToCollector(ctx context.Context, err = b.grpcTxSend(ctx, collectionRPC, tx) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateAccessAPIClient(collectionNodeAddr) - } return fmt.Errorf("failed to send transaction to collection node at %s: %w", collectionNodeAddr, err) } return nil @@ -828,9 +825,6 @@ func (b *backendTransactions) tryGetTransactionResult( resp, err := execRPCClient.GetTransactionResult(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(execNode.Address) - } return nil, err } @@ -891,9 +885,6 @@ func (b *backendTransactions) tryGetTransactionResultsByBlockID( resp, err := execRPCClient.GetTransactionResultsByBlockID(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(execNode.Address) - } return nil, err } @@ -955,9 +946,6 @@ func (b *backendTransactions) tryGetTransactionResultByIndex( resp, err := execRPCClient.GetTransactionResultByIndex(ctx, req) if err != nil { - if status.Code(err) == codes.Unavailable && !b.circuitBreakerEnabled { - b.connFactory.InvalidateExecutionAPIClient(execNode.Address) - } return nil, err } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 00ecf14d2e2..bca83eb70b2 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -8,6 +8,9 @@ import ( "sync" "time" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" @@ -24,6 +27,13 @@ import ( // DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node const DefaultClientTimeout = 3 * time.Second +type clientType int + +const ( + AccessClient clientType = iota + ExecutionClient +) + // ConnectionFactory is used to create an access api client type ConnectionFactory interface { GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) @@ -79,7 +89,7 @@ type CachedClient struct { } // createConnection creates new gRPC connections to remote node -func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration) (*grpc.ClientConn, error) { +func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration, clientType clientType) (*grpc.ClientConn, error) { if timeout == 0 { timeout = DefaultClientTimeout @@ -93,10 +103,17 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D } var connInterceptors []grpc.UnaryClientInterceptor + cbInterceptor := cf.withCircuitBreakerInterceptor() if cbInterceptor != nil { connInterceptors = append(connInterceptors, cbInterceptor) } + + ciInterceptor := cf.withClientInvalidationInterceptor(address, clientType) + if ciInterceptor != nil { + connInterceptors = append(connInterceptors, ciInterceptor) + } + connInterceptors = append(connInterceptors, WithClientTimeoutInterceptor(timeout)) // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached @@ -116,7 +133,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D return conn, nil } -func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { +func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout time.Duration, clientType clientType) (*grpc.ClientConn, error) { var conn *grpc.ClientConn var store *CachedClient cacheHit := false @@ -143,7 +160,7 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout if conn == nil || conn.GetState() == connectivity.Shutdown { var err error - conn, err = cf.createConnection(grpcAddress, timeout) + conn, err = cf.createConnection(grpcAddress, timeout, clientType) if err != nil { return nil, err } @@ -170,14 +187,14 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.Acce var conn *grpc.ClientConn if cf.ConnectionsCache != nil { - conn, err = cf.retrieveConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) + conn, err = cf.retrieveConnection(grpcAddress, cf.CollectionNodeGRPCTimeout, AccessClient) if err != nil { return nil, nil, err } return access.NewAccessAPIClient(conn), &noopCloser{}, err } - conn, err = cf.createConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) + conn, err = cf.createConnection(grpcAddress, cf.CollectionNodeGRPCTimeout, AccessClient) if err != nil { return nil, nil, err } @@ -203,14 +220,14 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio var conn *grpc.ClientConn if cf.ConnectionsCache != nil { - conn, err = cf.retrieveConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) + conn, err = cf.retrieveConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout, ExecutionClient) if err != nil { return nil, nil, err } return execution.NewExecutionAPIClient(conn), &noopCloser{}, nil } - conn, err = cf.createConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) + conn, err = cf.createConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout, ExecutionClient) if err != nil { return nil, nil, err } @@ -265,6 +282,37 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } +func (cf *ConnectionFactoryImpl) withClientInvalidationInterceptor(address string, clientType clientType) grpc.UnaryClientInterceptor { + if cf.CircuitBreakerConfig == nil || !cf.CircuitBreakerConfig.Enabled { + clientInvalidationInterceptor := func( + ctx context.Context, + method string, + req interface{}, + reply interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, + ) error { + fmt.Println("!!! clientInvalidationInterceptor") + err := invoker(ctx, method, req, reply, cc, opts...) + if status.Code(err) == codes.Unavailable { + switch clientType { + case AccessClient: + cf.InvalidateAccessAPIClient(address) + case ExecutionClient: + cf.InvalidateExecutionAPIClient(address) + } + } + + return err + } + + return clientInvalidationInterceptor + } + + return nil +} + func (cf *ConnectionFactoryImpl) withCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 8a60430b0a2..c81201fc953 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -536,7 +536,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) - //cn.handler.On("Ping", testifymock.Anything, req).Unset() + cn.handler.On("Ping", testifymock.Anything, req).Unset() cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) //Wait until Circuit breaker go to Half-open state From 5144f91cb4775e8e9570d31d7d69d48c9d2eb397 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 17:01:40 -0400 Subject: [PATCH 179/815] moved out test helper functions to separate file --- network/alsp/manager/manager_helper_test.go | 94 +++++++++++++++++++++ network/alsp/manager/manager_test.go | 74 ---------------- 2 files changed, 94 insertions(+), 74 deletions(-) create mode 100644 network/alsp/manager/manager_helper_test.go diff --git a/network/alsp/manager/manager_helper_test.go b/network/alsp/manager/manager_helper_test.go new file mode 100644 index 00000000000..4e7a30ec450 --- /dev/null +++ b/network/alsp/manager/manager_helper_test.go @@ -0,0 +1,94 @@ +package alspmgr_test + +import ( + "math" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/config" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/alsp" + alspmgr "github.com/onflow/flow-go/network/alsp/manager" + "github.com/onflow/flow-go/network/alsp/model" + "github.com/onflow/flow-go/network/mocknetwork" + "github.com/onflow/flow-go/utils/unittest" +) + +// This file contains helper functions for testing the ALSP manager. + +// createRandomMisbehaviorReportsForOriginId creates a slice of random misbehavior reports for a single origin id. +// Args: +// - t: the testing.T instance +// - originID: the origin id of the misbehavior reports +// - numReports: the number of misbehavior reports to create +// Returns: +// - []network.MisbehaviorReport: the slice of misbehavior reports +// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. +func createRandomMisbehaviorReportsForOriginId(t *testing.T, originID flow.Identifier, numReports int) []network.MisbehaviorReport { + reports := make([]network.MisbehaviorReport, numReports) + + for i := 0; i < numReports; i++ { + reports[i] = misbehaviorReportFixture(t, originID) + } + + return reports +} + +// createRandomMisbehaviorReports creates a slice of random misbehavior reports. +// Args: +// - t: the testing.T instance +// - numReports: the number of misbehavior reports to create +// Returns: +// - []network.MisbehaviorReport: the slice of misbehavior reports +// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. +func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.MisbehaviorReport { + reports := make([]network.MisbehaviorReport, numReports) + + for i := 0; i < numReports; i++ { + reports[i] = misbehaviorReportFixture(t, unittest.IdentifierFixture()) + } + + return reports +} + +// managerCfgFixture creates a new MisbehaviorReportManagerConfig with default values for testing. +func managerCfgFixture(t *testing.T) *alspmgr.MisbehaviorReportManagerConfig { + c, err := config.DefaultConfig() + require.NoError(t, err) + return &alspmgr.MisbehaviorReportManagerConfig{ + Logger: unittest.Logger(), + SpamRecordCacheSize: c.NetworkConfig.AlspConfig.SpamRecordCacheSize, + SpamReportQueueSize: c.NetworkConfig.AlspConfig.SpamReportQueueSize, + HeartBeatInterval: c.NetworkConfig.AlspConfig.HearBeatInterval, + AlspMetrics: metrics.NewNoopCollector(), + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + } +} + +// misbehaviorReportFixture creates a mock misbehavior report for a single origin id. +// Args: +// - t: the testing.T instance +// - originID: the origin id of the misbehavior report +// Returns: +// - network.MisbehaviorReport: the misbehavior report +// Note: the penalty of the misbehavior report is randomly chosen between -1 and -10. +func misbehaviorReportFixture(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, math.Min(-1, float64(-1-rand.Intn(10)))) +} + +func misbehaviorReportFixtureWithDefaultPenalty(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, model.DefaultPenaltyValue) +} + +func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, penalty float64) network.MisbehaviorReport { + report := mocknetwork.NewMisbehaviorReport(t) + report.On("OriginId").Return(originID) + report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) + report.On("Penalty").Return(penalty) + + return report +} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 5c0ec5e7a74..03482359ae7 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -13,7 +13,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" @@ -1626,76 +1625,3 @@ func TestDisallowListNotification(t *testing.T) { return true }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") } - -// misbehaviorReportFixture creates a mock misbehavior report for a single origin id. -// Args: -// - t: the testing.T instance -// - originID: the origin id of the misbehavior report -// Returns: -// - network.MisbehaviorReport: the misbehavior report -// Note: the penalty of the misbehavior report is randomly chosen between -1 and -10. -func misbehaviorReportFixture(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { - return misbehaviorReportFixtureWithPenalty(t, originID, math.Min(-1, float64(-1-rand.Intn(10)))) -} - -func misbehaviorReportFixtureWithDefaultPenalty(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { - return misbehaviorReportFixtureWithPenalty(t, originID, model.DefaultPenaltyValue) -} - -func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, penalty float64) network.MisbehaviorReport { - report := mocknetwork.NewMisbehaviorReport(t) - report.On("OriginId").Return(originID) - report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) - report.On("Penalty").Return(penalty) - - return report -} - -// createRandomMisbehaviorReportsForOriginId creates a slice of random misbehavior reports for a single origin id. -// Args: -// - t: the testing.T instance -// - originID: the origin id of the misbehavior reports -// - numReports: the number of misbehavior reports to create -// Returns: -// - []network.MisbehaviorReport: the slice of misbehavior reports -// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. -func createRandomMisbehaviorReportsForOriginId(t *testing.T, originID flow.Identifier, numReports int) []network.MisbehaviorReport { - reports := make([]network.MisbehaviorReport, numReports) - - for i := 0; i < numReports; i++ { - reports[i] = misbehaviorReportFixture(t, originID) - } - - return reports -} - -// createRandomMisbehaviorReports creates a slice of random misbehavior reports. -// Args: -// - t: the testing.T instance -// - numReports: the number of misbehavior reports to create -// Returns: -// - []network.MisbehaviorReport: the slice of misbehavior reports -// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. -func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.MisbehaviorReport { - reports := make([]network.MisbehaviorReport, numReports) - - for i := 0; i < numReports; i++ { - reports[i] = misbehaviorReportFixture(t, unittest.IdentifierFixture()) - } - - return reports -} - -// managerCfgFixture creates a new MisbehaviorReportManagerConfig with default values for testing. -func managerCfgFixture(t *testing.T) *alspmgr.MisbehaviorReportManagerConfig { - c, err := config.DefaultConfig() - require.NoError(t, err) - return &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: c.NetworkConfig.AlspConfig.SpamRecordCacheSize, - SpamReportQueueSize: c.NetworkConfig.AlspConfig.SpamReportQueueSize, - HeartBeatInterval: c.NetworkConfig.AlspConfig.HearBeatInterval, - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } -} From 8caa38e63a953de4ed8cb5a109ef6e4281753b83 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 17:27:35 -0400 Subject: [PATCH 180/815] moved WithDecayFunc() to test helper --- network/alsp/manager/manager.go | 24 ++++----------------- network/alsp/manager/manager_helper_test.go | 18 +++++++++++++++- network/alsp/manager/manager_test.go | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 0195d3b8c48..4601e54ab33 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -89,8 +89,8 @@ type MisbehaviorReportManager struct { // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. workerPool *worker.Pool[internal.ReportedMisbehaviorWork] - // decayFunc is the function that calculates the decay of the spam record. - decayFunc SpamRecordDecayFunc + // DecayFunc is the function that calculates the decay of the spam record. + DecayFunc SpamRecordDecayFunc } var _ network.MisbehaviorReportManager = (*MisbehaviorReportManager)(nil) @@ -164,22 +164,6 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana } } -// WithDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. -// Args: -// -// f: the decay function. -// -// Returns: -// -// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. -// -// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. -func WithDecayFunc(f SpamRecordDecayFunc) MisbehaviorReportManagerOption { - return func(m *MisbehaviorReportManager) { - m.decayFunc = f - } -} - // NewMisbehaviorReportManager creates a new instance of the MisbehaviorReportManager. // Args: // cfg: the configuration for the MisbehaviorReportManager. @@ -204,7 +188,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, cacheFactory: defaultSpamRecordCacheFactory(), - decayFunc: defaultSpamRecordDecayFunc(), + DecayFunc: defaultSpamRecordDecayFunc(), } store := queue.NewHeroStore( @@ -373,7 +357,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). Msg("onHeartbeat - before adjusting penalty via decayFunc") - record.Penalty = m.decayFunc(record) + record.Penalty = m.DecayFunc(record) m.logger.Info(). Hex("identifier", logging.ID(id)). Bool("disallow_listed", record.DisallowListed). diff --git a/network/alsp/manager/manager_helper_test.go b/network/alsp/manager/manager_helper_test.go index 4e7a30ec450..c94f3d4905e 100644 --- a/network/alsp/manager/manager_helper_test.go +++ b/network/alsp/manager/manager_helper_test.go @@ -12,7 +12,7 @@ import ( "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/alsp" - alspmgr "github.com/onflow/flow-go/network/alsp/manager" + "github.com/onflow/flow-go/network/alsp/manager" "github.com/onflow/flow-go/network/alsp/model" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/utils/unittest" @@ -92,3 +92,19 @@ func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, return report } + +// WithDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. +// Args: +// +// f: the decay function. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. +func WithDecayFunc(f alspmgr.SpamRecordDecayFunc) alspmgr.MisbehaviorReportManagerOption { + return func(m *alspmgr.MisbehaviorReportManager) { + m.DecayFunc = f + } +} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 03482359ae7..4ab9c9506aa 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -308,7 +308,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCacheCache }), - alspmgr.WithDecayFunc(decayFuc), + WithDecayFunc(decayFuc), } ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( From 7403dcf385d2a2de23425c35cd34ea24f895d742 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 17:47:43 -0400 Subject: [PATCH 181/815] rename to fastDecay, fastDecayFunc --- network/alsp/manager/manager_test.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 4ab9c9506aa..442393353cf 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -285,16 +285,16 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // handling of repeated reported misbehavior and disallow listing. func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration(t *testing.T) { cfg := managerCfgFixture(t) - resetZero := false - decayFuc := func(record model.ProtocolSpamRecord) float64 { + fastDecay := false + fastDecayFunc := func(record model.ProtocolSpamRecord) float64 { t.Logf("decayFuc called with record: %+v", record) - if resetZero { + if fastDecay { // decay to zero in a single heart beat - t.Logf("resetZero is true, so decay to zero") + t.Logf("fastDecay is true, so decay to zero") return 0 } else { // decay as usual - t.Logf("resetZero is false, so decay as usual") + t.Logf("fastDecay is false, so decay as usual") return math.Min(record.Penalty+record.Decay, 0) } } @@ -302,13 +302,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // this test is assessing the integration of the ALSP manager with the network. As the ALSP manager is an attribute // of the network, we need to configure the ALSP manager via the network configuration, and let the network create // the ALSP manager. - var victimSpamRecordCacheCache alsp.SpamRecordCache + var victimSpamRecordCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { - victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) - return victimSpamRecordCacheCache + victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) + return victimSpamRecordCache }), - WithDecayFunc(decayFuc), + WithDecayFunc(fastDecayFunc), } ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( @@ -366,8 +366,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") // ensures that the spammer is disallow-listed by the victim - //victimSpamRecordCacheCache.Get(ids[spammerIndex].NodeID) - record, ok := victimSpamRecordCacheCache.Get(ids[spammerIndex].NodeID) + record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) require.True(t, ok) require.NotNil(t, record) @@ -382,7 +381,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") - resetZero = true + fastDecay = true t.Logf("decayed the disallow-listing penalty of the spammer node to zero") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. @@ -392,7 +391,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) // go back to regular decay to prepare for the next set of misbehavior reports. - resetZero = false + fastDecay = false t.Logf("about to report misbehavior again") // simulates the victim node reporting the spammer node misbehavior 120 times @@ -424,7 +423,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") - resetZero = true + fastDecay = true t.Logf("decayed the disallow-listing penalty of the spammer node to zero (2nd time)") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. From 559c4e9cfb5ddbccf85353604b8eec916e40d1ef Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 00:48:03 +0300 Subject: [PATCH 182/815] Added execution and collection nodes iterators. --- .../export_report.json | 4 +- engine/access/rpc/backend/backend.go | 79 +++++++++--------- engine/access/rpc/backend/backend_accounts.go | 20 ++--- engine/access/rpc/backend/backend_events.go | 23 +++--- engine/access/rpc/backend/backend_scripts.go | 27 +++--- engine/access/rpc/backend/backend_test.go | 28 +++++-- .../rpc/backend/backend_transactions.go | 82 ++++++++----------- engine/access/rpc/backend/node_iterator.go | 79 ++++++++++++++++++ 8 files changed, 210 insertions(+), 132 deletions(-) create mode 100644 engine/access/rpc/backend/node_iterator.go diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 6f3e833a6a5..fe9eccd9727 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "f305e934fb48402557dbf73b9b6abf6217a582ffa441ce6f6e84a22d7e82b887", - "CurrentStateCommitment": "f305e934fb48402557dbf73b9b6abf6217a582ffa441ce6f6e84a22d7e82b887", + "PreviousStateCommitment": "5bdf6c643d9f0dbc78adf5923895df3bbf52867c406444d7fd101b9942f7d1f4", + "CurrentStateCommitment": "5bdf6c643d9f0dbc78adf5923895df3bbf52867c406444d7fd101b9942f7d1f4", "ReportSucceeded": true } \ No newline at end of file diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 6c63ca1d35f..993f88fb046 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -22,9 +22,6 @@ import ( "github.com/onflow/flow-go/storage" ) -// maxExecutionNodesCnt is the max number of execution nodes that will be contacted to complete an execution api request -const maxExecutionNodesCnt = 3 - // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block const minExecutionNodesCnt = 2 @@ -109,44 +106,48 @@ func New( log.Fatal().Err(err).Msg("failed to initialize script logging cache") } + collIteratorFactory := CollectionNodeIteratorFactory{circuitBreakerEnabled: circuitBreakerEnabled} + execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: circuitBreakerEnabled} + b := &Backend{ state: state, // create the sub-backends backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, - loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, - circuitBreakerEnabled: circuitBreakerEnabled, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + state: state, + log: log, + metrics: accessMetrics, + loggedScripts: loggedScripts, + archiveAddressList: archiveAddressList, + execIteratorFactory: execIteratorFactory, }, backendTransactions: backendTransactions{ - staticCollectionRPC: collectionRPC, - state: state, - chainID: chainID, - collections: collections, - blocks: blocks, - transactions: transactions, - executionReceipts: executionReceipts, - transactionValidator: configureTransactionValidator(state, chainID), - transactionMetrics: accessMetrics, - retry: retry, - connFactory: connFactory, - previousAccessNodes: historicalAccessNodes, - log: log, - circuitBreakerEnabled: circuitBreakerEnabled, + staticCollectionRPC: collectionRPC, + state: state, + chainID: chainID, + collections: collections, + blocks: blocks, + transactions: transactions, + executionReceipts: executionReceipts, + transactionValidator: configureTransactionValidator(state, chainID), + transactionMetrics: accessMetrics, + retry: retry, + connFactory: connFactory, + previousAccessNodes: historicalAccessNodes, + log: log, + collIteratorFactory: collIteratorFactory, + execIteratorFactory: execIteratorFactory, }, backendEvents: backendEvents{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - maxHeightRange: maxHeightRange, - circuitBreakerEnabled: circuitBreakerEnabled, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + maxHeightRange: maxHeightRange, + execIteratorFactory: execIteratorFactory, }, backendBlockHeaders: backendBlockHeaders{ headers: headers, @@ -157,12 +158,12 @@ func New( state: state, }, backendAccounts: backendAccounts{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - circuitBreakerEnabled: circuitBreakerEnabled, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + execIteratorFactory: execIteratorFactory, }, backendExecutionResults: backendExecutionResults{ executionResults: executionResults, diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index f0e3bf1aed3..b38f552631e 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -18,12 +18,12 @@ import ( ) type backendAccounts struct { - state protocol.State - headers storage.Headers - executionReceipts storage.ExecutionReceipts - connFactory ConnectionFactory - log zerolog.Logger - circuitBreakerEnabled bool + state protocol.State + headers storage.Headers + executionReceipts storage.ExecutionReceipts + connFactory ConnectionFactory + log zerolog.Logger + execIteratorFactory ExecutionNodeIteratorFactory } func (b *backendAccounts) GetAccount(ctx context.Context, address flow.Address) (*flow.Account, error) { @@ -108,13 +108,11 @@ func (b *backendAccounts) getAccountAtBlockID( // other ENs are logged and swallowed. If all ENs fail to return a valid response, then an // error aggregating all failures is returned. func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { - if !b.circuitBreakerEnabled { - execNodes = execNodes.Sample(maxExecutionNodesCnt) - } - var errors *multierror.Error - for _, execNode := range execNodes { + execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + + for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { // TODO: use the GRPC Client interceptor start := time.Now() diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index bc1c0f50f62..74a64cc10bb 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -21,13 +21,13 @@ import ( ) type backendEvents struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - maxHeightRange uint - circuitBreakerEnabled bool + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + maxHeightRange uint + execIteratorFactory ExecutionNodeIteratorFactory } // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and @@ -210,13 +210,12 @@ func verifyAndConvertToAccessEvents( func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { - if !b.circuitBreakerEnabled { - execNodes = execNodes.Sample(maxExecutionNodesCnt) - } - var errors *multierror.Error + + execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + // try to get events from one of the execution nodes - for _, execNode := range execNodes { + for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { start := time.Now() resp, err := b.tryGetEvents(ctx, execNode, req) duration := time.Since(start) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index cbb73fb25bc..8a1cd7b9cb2 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -24,15 +24,15 @@ import ( const uniqueScriptLoggingTimeWindow = 10 * time.Minute type backendScripts struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - metrics module.BackendScriptsMetrics - loggedScripts *lru.Cache - archiveAddressList []string - circuitBreakerEnabled bool + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + metrics module.BackendScriptsMetrics + loggedScripts *lru.Cache + archiveAddressList []string + execIteratorFactory ExecutionNodeIteratorFactory } func (b *backendScripts) ExecuteScriptAtLatestBlock( @@ -100,12 +100,11 @@ func (b *backendScripts) findScriptExecutors( return nil, err } - if !b.circuitBreakerEnabled { - executors = executors.Sample(maxExecutionNodesCnt) - } - executorAddrs := make([]string, 0, len(executors)) - for _, executor := range executors { + execNodeIter := b.execIteratorFactory.CreateNodeIterator(executors) + + // try to get events from one of the execution nodes + for executor := execNodeIter.Next(); executor != nil; executor = execNodeIter.Next() { executorAddrs = append(executorAddrs, executor.Address) } return executorAddrs, nil diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 8f572daf172..01f1c9bddc5 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -2160,13 +2160,22 @@ func (suite *Suite) TestExecutionNodesForBlockID() { if fixedENs != nil { fixedENIdentifiers = fixedENs.NodeIDs() } - actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) - require.NoError(suite.T(), err) - actualList = actualList.Sample(maxExecutionNodesCnt) if expectedENs == nil { expectedENs = flow.IdentityList{} } + + allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) + require.NoError(suite.T(), err) + + execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: false} + execIterator := execIteratorFactory.CreateNodeIterator(allExecNodes) + + actualList := flow.IdentityList{} + for actual := execIterator.Next(); actual != nil; actual = execIterator.Next() { + actualList = append(actualList, actual) + } + if len(expectedENs) > maxExecutionNodesCnt { for _, actual := range actualList { require.Contains(suite.T(), expectedENs, actual) @@ -2182,9 +2191,18 @@ func (suite *Suite) TestExecutionNodesForBlockID() { attempt2Receipts = flow.ExecutionReceiptList{} attempt3Receipts = flow.ExecutionReceiptList{} suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) - actualList, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) - actualList = actualList.Sample(maxExecutionNodesCnt) + + allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) + + execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: false} + execIterator := execIteratorFactory.CreateNodeIterator(allExecNodes) + + actualList := flow.IdentityList{} + for actual := execIterator.Next(); actual != nil; actual = execIterator.Next() { + actualList = append(actualList, actual) + } + require.Equal(suite.T(), len(actualList), maxExecutionNodesCnt) }) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 025be9c04e7..f9adf6fa4e4 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -24,23 +24,22 @@ import ( "github.com/onflow/flow-go/storage" ) -const collectionNodesToTry uint = 3 - type backendTransactions struct { - staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node - transactions storage.Transactions - executionReceipts storage.ExecutionReceipts - collections storage.Collections - blocks storage.Blocks - state protocol.State - chainID flow.ChainID - transactionMetrics module.TransactionMetrics - transactionValidator *access.TransactionValidator - retry *Retry - connFactory ConnectionFactory - previousAccessNodes []accessproto.AccessAPIClient - log zerolog.Logger - circuitBreakerEnabled bool + staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node + transactions storage.Transactions + executionReceipts storage.ExecutionReceipts + collections storage.Collections + blocks storage.Blocks + state protocol.State + chainID flow.ChainID + transactionMetrics module.TransactionMetrics + transactionValidator *access.TransactionValidator + retry *Retry + connFactory ConnectionFactory + previousAccessNodes []accessproto.AccessAPIClient + log zerolog.Logger + collIteratorFactory CollectionNodeIteratorFactory + execIteratorFactory ExecutionNodeIteratorFactory } // SendTransaction forwards the transaction to the collection node @@ -85,8 +84,8 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T return b.grpcTxSend(ctx, b.staticCollectionRPC, tx) } - // otherwise choose a random set of collections nodes to try - collAddrs, err := b.chooseCollectionNodes(tx, collectionNodesToTry) + // otherwise choose all collection nodes to try + collNodes, err := b.chooseCollectionNodes(tx) if err != nil { return fmt.Errorf("failed to determine collection node for tx %x: %w", tx, err) } @@ -100,9 +99,11 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T } defer logAnyError() + collNodeIter := b.collIteratorFactory.CreateNodeIterator(collNodes) + // try sending the transaction to one of the chosen collection nodes - for _, addr := range collAddrs { - err = b.sendTransactionToCollector(ctx, tx, addr) + for colNode := collNodeIter.Next(); colNode != nil; colNode = collNodeIter.Next() { + err = b.sendTransactionToCollector(ctx, tx, colNode.Address) if err == nil { return nil } @@ -114,7 +115,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T // chooseCollectionNodes finds a random subset of size sampleSize of collection node addresses from the // collection node cluster responsible for the given tx -func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sampleSize uint) ([]string, error) { +func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody) (flow.IdentityList, error) { // retrieve the set of collector clusters clusters, err := b.state.Final().Epochs().Current().Clustering() @@ -128,19 +129,7 @@ func (b *backendTransactions) chooseCollectionNodes(tx *flow.TransactionBody, sa return nil, fmt.Errorf("could not get local cluster by txID: %x", tx.ID()) } - // samples ony used when circuit breaker is disabled - if !b.circuitBreakerEnabled { - // select a random subset of collection nodes from the cluster to be tried in order - targetNodes = targetNodes.Sample(sampleSize) - } - - // collect the addresses of all the chosen collection nodes - var targetAddrs = make([]string, len(targetNodes)) - for i, id := range targetNodes { - targetAddrs[i] = id.Address - } - - return targetAddrs, nil + return targetNodes, nil } // sendTransactionToCollection sends the transaction to the given collection node via grpc @@ -780,10 +769,6 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionResultRequest, ) (*execproto.GetTransactionResultResponse, error) { - if !b.circuitBreakerEnabled { - execNodes = execNodes.Sample(maxExecutionNodesCnt) - } - var errs *multierror.Error logAnyError := func() { errToReturn := errs.ErrorOrNil() @@ -792,8 +777,11 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( } } defer logAnyError() + + execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + // try to execute the script on one of the execution nodes - for _, execNode := range execNodes { + for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { resp, err := b.tryGetTransactionResult(ctx, execNode, req) if err == nil { b.log.Debug(). @@ -836,10 +824,6 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionsByBlockIDRequest, ) (*execproto.GetTransactionResultsResponse, error) { - if !b.circuitBreakerEnabled { - execNodes = execNodes.Sample(maxExecutionNodesCnt) - } - var errs *multierror.Error defer func() { @@ -854,7 +838,9 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( return nil, errors.New("zero execution nodes") } - for _, execNode := range execNodes { + execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + + for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { resp, err := b.tryGetTransactionResultsByBlockID(ctx, execNode, req) if err == nil { b.log.Debug(). @@ -896,10 +882,6 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionByIndexRequest, ) (*execproto.GetTransactionResultResponse, error) { - if !b.circuitBreakerEnabled { - execNodes = execNodes.Sample(maxExecutionNodesCnt) - } - var errs *multierror.Error logAnyError := func() { errToReturn := errs.ErrorOrNil() @@ -913,8 +895,10 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( return nil, errors.New("zero execution nodes provided") } + execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + // try to execute the script on one of the execution nodes - for _, execNode := range execNodes { + for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { resp, err := b.tryGetTransactionResultByIndex(ctx, execNode, req) if err == nil { b.log.Debug(). diff --git a/engine/access/rpc/backend/node_iterator.go b/engine/access/rpc/backend/node_iterator.go new file mode 100644 index 00000000000..a929f0e7899 --- /dev/null +++ b/engine/access/rpc/backend/node_iterator.go @@ -0,0 +1,79 @@ +package backend + +import "github.com/onflow/flow-go/model/flow" + +// maxExecutionNodesCnt is the max number of execution nodes that will be contacted to complete an execution api request +const maxExecutionNodesCnt = 3 + +const collectionNodesToTry = 3 + +type NodeIterator interface { + Next() *flow.Identity +} + +type NodeIteratorFactory interface { + CreateNodeIterator(nodes flow.IdentityList) NodeIterator +} + +var _ NodeIteratorFactory = (*ExecutionNodeIteratorFactory)(nil) +var _ NodeIterator = (*ExecutionNodeIterator)(nil) +var _ NodeIteratorFactory = (*CollectionNodeIteratorFactory)(nil) +var _ NodeIterator = (*CollectionNodeIterator)(nil) + +type ExecutionNodeIteratorFactory struct { + circuitBreakerEnabled bool +} + +func (e *ExecutionNodeIteratorFactory) CreateNodeIterator(nodes flow.IdentityList) NodeIterator { + if !e.circuitBreakerEnabled { + nodes = nodes.Sample(maxExecutionNodesCnt) + } + + return &ExecutionNodeIterator{ + nodes: nodes, + index: 0, + } +} + +type ExecutionNodeIterator struct { + nodes flow.IdentityList + index int +} + +func (e *ExecutionNodeIterator) Next() *flow.Identity { + if e.index < len(e.nodes) { + next := e.nodes[e.index] + e.index++ + return next + } + return nil +} + +type CollectionNodeIteratorFactory struct { + circuitBreakerEnabled bool +} + +func (c *CollectionNodeIteratorFactory) CreateNodeIterator(nodes flow.IdentityList) NodeIterator { + if !c.circuitBreakerEnabled { + nodes = nodes.Sample(collectionNodesToTry) + } + + return &CollectionNodeIterator{ + nodes: nodes, + index: 0, + } +} + +type CollectionNodeIterator struct { + nodes flow.IdentityList + index int +} + +func (c *CollectionNodeIterator) Next() *flow.Identity { + if c.index < len(c.nodes) { + next := c.nodes[c.index] + c.index++ + return next + } + return nil +} From 444ccefbfdd7c7b5a65b53f934a3ba711933e61d Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 21 Jun 2023 17:50:00 -0400 Subject: [PATCH 183/815] lint fix --- network/alsp/manager/manager_helper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_helper_test.go b/network/alsp/manager/manager_helper_test.go index c94f3d4905e..1f6630ee44f 100644 --- a/network/alsp/manager/manager_helper_test.go +++ b/network/alsp/manager/manager_helper_test.go @@ -12,7 +12,7 @@ import ( "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/alsp" - "github.com/onflow/flow-go/network/alsp/manager" + alspmgr "github.com/onflow/flow-go/network/alsp/manager" "github.com/onflow/flow-go/network/alsp/model" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/utils/unittest" From 41edd8f7abb282183b075dcbb36815ea96bfcf96 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 11:09:27 +0300 Subject: [PATCH 184/815] Removed debug output --- engine/access/rpc/backend/connection_factory.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index bca83eb70b2..7e447fe94e4 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -293,7 +293,6 @@ func (cf *ConnectionFactoryImpl) withClientInvalidationInterceptor(address strin invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - fmt.Println("!!! clientInvalidationInterceptor") err := invoker(ctx, method, req, reply, cc, opts...) if status.Code(err) == codes.Unavailable { switch clientType { @@ -333,7 +332,6 @@ func (cf *ConnectionFactoryImpl) withCircuitBreakerInterceptor() grpc.UnaryClien invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - fmt.Println("!!! circuitBreakerInterceptor") // The invoker should be called from circuit breaker execute, to catch each fails and react according to settings _, err := circuitBreaker.Execute(func() (interface{}, error) { err := invoker(ctx, method, req, reply, cc, opts...) @@ -363,7 +361,6 @@ func WithClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterce ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) defer cancel() - fmt.Println("!!! clientTimeoutInterceptor") // call the remote GRPC using the short context err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) From e10d556e492099b9cef2ccad5f44b64700b6af80 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 22 Jun 2023 07:43:29 -0400 Subject: [PATCH 185/815] check spam record fields on each violation --- network/alsp/manager/manager.go | 27 ++------- network/alsp/manager/manager_helper_test.go | 16 +++++ network/alsp/manager/manager_test.go | 67 ++++++++++++++------- 3 files changed, 65 insertions(+), 45 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 4601e54ab33..8ffc3858434 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -69,12 +69,12 @@ type MisbehaviorReportManager struct { component.Component logger zerolog.Logger metrics module.AlspMetrics - // cacheFactory is the factory for creating the spam record cache. MisbehaviorReportManager is coming with a + // CacheFactory is the factory for creating the spam record cache. MisbehaviorReportManager is coming with a // default factory that creates a new spam record cache with the given parameter. However, this factory can be // overridden with a custom factory. - cacheFactory SpamRecordCacheFactory + CacheFactory SpamRecordCacheFactory // cache is the spam record cache that stores the spam records for the authorized nodes. It is initialized by - // invoking the cacheFactory. + // invoking the CacheFactory. cache alsp.SpamRecordCache // disablePenalty indicates whether applying the penalty to the misbehaving node is disabled. // When disabled, the ALSP module logs the misbehavior reports and updates the metrics, but does not apply the penalty. @@ -147,23 +147,6 @@ func (c MisbehaviorReportManagerConfig) validate() error { type MisbehaviorReportManagerOption func(*MisbehaviorReportManager) -// WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. -// Args: -// -// f: the spam record cache factory. -// -// Returns: -// -// a MisbehaviorReportManagerOption that sets the spam record cache for the MisbehaviorReportManager. -// -// Note: this option is useful primarily for testing purposes. The default factory should be sufficient for the production, and -// do not change it unless you are confident that you know what you are doing. -func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportManagerOption { - return func(m *MisbehaviorReportManager) { - m.cacheFactory = f - } -} - // NewMisbehaviorReportManager creates a new instance of the MisbehaviorReportManager. // Args: // cfg: the configuration for the MisbehaviorReportManager. @@ -187,7 +170,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n metrics: cfg.AlspMetrics, disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, - cacheFactory: defaultSpamRecordCacheFactory(), + CacheFactory: defaultSpamRecordCacheFactory(), DecayFunc: defaultSpamRecordDecayFunc(), } @@ -205,7 +188,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n opt(m) } - m.cache = m.cacheFactory( + m.cache = m.CacheFactory( lg, cfg.SpamRecordCacheSize, metrics.ApplicationLayerSpamRecordCacheMetricFactory(cfg.HeroCacheMetricsFactory, cfg.NetworkType)) diff --git a/network/alsp/manager/manager_helper_test.go b/network/alsp/manager/manager_helper_test.go index 1f6630ee44f..f089c4afffd 100644 --- a/network/alsp/manager/manager_helper_test.go +++ b/network/alsp/manager/manager_helper_test.go @@ -108,3 +108,19 @@ func WithDecayFunc(f alspmgr.SpamRecordDecayFunc) alspmgr.MisbehaviorReportManag m.DecayFunc = f } } + +// WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. +// Args: +// +// f: the spam record cache factory. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the spam record cache for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default factory should be sufficient for production. +func WithSpamRecordsCacheFactory(f alspmgr.SpamRecordCacheFactory) alspmgr.MisbehaviorReportManagerOption { + return func(m *alspmgr.MisbehaviorReportManager) { + m.CacheFactory = f + } +} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 442393353cf..583d84b4b38 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -110,7 +110,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { // the ALSP manager. var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -209,7 +209,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // the ALSP manager. var victimSpamRecordCacheCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { victimSpamRecordCacheCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCacheCache }), @@ -304,7 +304,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // the ALSP manager. var victimSpamRecordCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCache }), @@ -365,11 +365,6 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // ensures that the spammer is disallow-listed by the victim - record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) - // ensures that the spammer is disallow-listed by the victim // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. @@ -379,6 +374,17 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + + // check the penalty of the spammer node. It should be equal to the disallow-listing penalty. + + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + require.Equal(t, float64(1000), record.Decay) + require.Equal(t, true, record.DisallowListed) + require.Equal(t, uint64(1), record.CutoffCounter) + // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") fastDecay = true @@ -390,6 +396,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + require.Equal(t, float64(1000), record.Decay) + assert.Equal(t, false, record.DisallowListed) + require.Equal(t, uint64(1), record.CutoffCounter) + // go back to regular decay to prepare for the next set of misbehavior reports. fastDecay = false t.Logf("about to report misbehavior again") @@ -421,6 +432,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + require.Equal(t, float64(1000), record.Decay) + require.Equal(t, true, record.DisallowListed) + assert.Equal(t, uint64(2), record.CutoffCounter) + // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") fastDecay = true @@ -431,6 +447,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + require.Equal(t, float64(1000), record.Decay) + assert.Equal(t, false, record.DisallowListed) + assert.Equal(t, 2, record.CutoffCounter) } // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. @@ -534,7 +555,7 @@ func TestNewMisbehaviorReportManager(t *testing.T) { consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -605,7 +626,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -665,7 +686,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T // we use a mock cache but we do not expect any calls to the cache, since the penalty is disabled. var cache *mockalsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = mockalsp.NewSpamRecordCache(t) return cache }), @@ -720,7 +741,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -782,7 +803,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -853,7 +874,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -913,7 +934,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -983,7 +1004,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1064,7 +1085,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1139,7 +1160,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1207,7 +1228,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1297,7 +1318,7 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1387,7 +1408,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1472,7 +1493,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero_AllowListing(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1552,7 +1573,7 @@ func TestDisallowListNotification(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), From bd1116f415e4524da350586e38fda6f9e9af9bae Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 22 Jun 2023 08:32:34 -0400 Subject: [PATCH 186/815] fix tests - get spam record before each check --- network/alsp/manager/manager_test.go | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 583d84b4b38..57ed424ec0b 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -378,8 +378,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.True(t, ok) require.NotNil(t, record) - // check the penalty of the spammer node. It should be equal to the disallow-listing penalty. - + // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) require.Equal(t, float64(1000), record.Decay) require.Equal(t, true, record.DisallowListed) @@ -396,9 +395,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + + require.Equal(t, float64(0), record.Penalty) require.Equal(t, float64(1000), record.Decay) - assert.Equal(t, false, record.DisallowListed) + require.Equal(t, false, record.DisallowListed) require.Equal(t, uint64(1), record.CutoffCounter) // go back to regular decay to prepare for the next set of misbehavior reports. @@ -426,16 +429,20 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) require.Equal(t, float64(1000), record.Decay) require.Equal(t, true, record.DisallowListed) - assert.Equal(t, uint64(2), record.CutoffCounter) + require.Equal(t, uint64(2), record.CutoffCounter) // decay the disallow-listing penalty of the spammer node to zero. t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") @@ -448,10 +455,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + + require.Equal(t, float64(0), record.Penalty) require.Equal(t, float64(1000), record.Decay) - assert.Equal(t, false, record.DisallowListed) - assert.Equal(t, 2, record.CutoffCounter) + require.Equal(t, false, record.DisallowListed) + require.Equal(t, uint64(2), record.CutoffCounter) } // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. From bbf5bebdeec9afb6b2f2836587a3f40b4302787f Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 15:33:24 +0300 Subject: [PATCH 187/815] Renamed node selector and interceptor creator functions --- .../node_builder/access_node_builder.go | 2 +- .../export_report.json | 6 --- engine/access/rpc/backend/backend.go | 12 +++-- engine/access/rpc/backend/backend_accounts.go | 6 +-- engine/access/rpc/backend/backend_events.go | 6 +-- engine/access/rpc/backend/backend_scripts.go | 6 +-- engine/access/rpc/backend/backend_test.go | 12 ++--- .../rpc/backend/backend_transactions.go | 19 ++++---- .../access/rpc/backend/connection_factory.go | 14 +++--- engine/access/rpc/backend/node_iterator.go | 45 ++++++++----------- 10 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 6e653805f44..151bcbbf74f 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -163,7 +163,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { MaxMsgSize: grpcutils.DefaultMaxMsgSize, CircuitBreakerConfig: &backend.CircuitBreakerConfig{ Enabled: false, - RestoreTimeout: time.Duration(60) * time.Second, + RestoreTimeout: 60 * time.Second, MaxFailures: 5, }, }, diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json deleted file mode 100644 index fe9eccd9727..00000000000 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "EpochCounter": 0, - "PreviousStateCommitment": "5bdf6c643d9f0dbc78adf5923895df3bbf52867c406444d7fd101b9942f7d1f4", - "CurrentStateCommitment": "5bdf6c643d9f0dbc78adf5923895df3bbf52867c406444d7fd101b9942f7d1f4", - "ReportSucceeded": true -} \ No newline at end of file diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 993f88fb046..42a0fcd26a1 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -106,8 +106,7 @@ func New( log.Fatal().Err(err).Msg("failed to initialize script logging cache") } - collIteratorFactory := CollectionNodeIteratorFactory{circuitBreakerEnabled: circuitBreakerEnabled} - execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: circuitBreakerEnabled} + nodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled} b := &Backend{ state: state, @@ -121,7 +120,7 @@ func New( metrics: accessMetrics, loggedScripts: loggedScripts, archiveAddressList: archiveAddressList, - execIteratorFactory: execIteratorFactory, + nodeSelectorFactory: nodeSelectorFactory, }, backendTransactions: backendTransactions{ staticCollectionRPC: collectionRPC, @@ -137,8 +136,7 @@ func New( connFactory: connFactory, previousAccessNodes: historicalAccessNodes, log: log, - collIteratorFactory: collIteratorFactory, - execIteratorFactory: execIteratorFactory, + nodeSelectorFactory: nodeSelectorFactory, }, backendEvents: backendEvents{ state: state, @@ -147,7 +145,7 @@ func New( connFactory: connFactory, log: log, maxHeightRange: maxHeightRange, - execIteratorFactory: execIteratorFactory, + nodeSelectorFactory: nodeSelectorFactory, }, backendBlockHeaders: backendBlockHeaders{ headers: headers, @@ -163,7 +161,7 @@ func New( executionReceipts: executionReceipts, connFactory: connFactory, log: log, - execIteratorFactory: execIteratorFactory, + nodeSelectorFactory: nodeSelectorFactory, }, backendExecutionResults: backendExecutionResults{ executionResults: executionResults, diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index b38f552631e..f3fd12c82f9 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -23,7 +23,7 @@ type backendAccounts struct { executionReceipts storage.ExecutionReceipts connFactory ConnectionFactory log zerolog.Logger - execIteratorFactory ExecutionNodeIteratorFactory + nodeSelectorFactory NodeSelectorFactory } func (b *backendAccounts) GetAccount(ctx context.Context, address flow.Address) (*flow.Account, error) { @@ -110,9 +110,9 @@ func (b *backendAccounts) getAccountAtBlockID( func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { var errors *multierror.Error - execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { // TODO: use the GRPC Client interceptor start := time.Now() diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index 74a64cc10bb..b82abf0b8c1 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -27,7 +27,7 @@ type backendEvents struct { connFactory ConnectionFactory log zerolog.Logger maxHeightRange uint - execIteratorFactory ExecutionNodeIteratorFactory + nodeSelectorFactory NodeSelectorFactory } // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and @@ -212,10 +212,10 @@ func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { var errors *multierror.Error - execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) // try to get events from one of the execution nodes - for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { start := time.Now() resp, err := b.tryGetEvents(ctx, execNode, req) duration := time.Since(start) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 8a1cd7b9cb2..3d251139531 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -32,7 +32,7 @@ type backendScripts struct { metrics module.BackendScriptsMetrics loggedScripts *lru.Cache archiveAddressList []string - execIteratorFactory ExecutionNodeIteratorFactory + nodeSelectorFactory NodeSelectorFactory } func (b *backendScripts) ExecuteScriptAtLatestBlock( @@ -101,10 +101,10 @@ func (b *backendScripts) findScriptExecutors( } executorAddrs := make([]string, 0, len(executors)) - execNodeIter := b.execIteratorFactory.CreateNodeIterator(executors) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(executors) // try to get events from one of the execution nodes - for executor := execNodeIter.Next(); executor != nil; executor = execNodeIter.Next() { + for executor := execNodeSelector.Next(); executor != nil; executor = execNodeSelector.Next() { executorAddrs = append(executorAddrs, executor.Address) } return executorAddrs, nil diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 01f1c9bddc5..eed923727d0 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -2168,11 +2168,11 @@ func (suite *Suite) TestExecutionNodesForBlockID() { allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) - execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: false} - execIterator := execIteratorFactory.CreateNodeIterator(allExecNodes) + execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} + execSelector := execNodeSelectorFactory.SelectExecutionNodes(allExecNodes) actualList := flow.IdentityList{} - for actual := execIterator.Next(); actual != nil; actual = execIterator.Next() { + for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { actualList = append(actualList, actual) } @@ -2195,11 +2195,11 @@ func (suite *Suite) TestExecutionNodesForBlockID() { allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) require.NoError(suite.T(), err) - execIteratorFactory := ExecutionNodeIteratorFactory{circuitBreakerEnabled: false} - execIterator := execIteratorFactory.CreateNodeIterator(allExecNodes) + execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} + execSelector := execNodeSelectorFactory.SelectExecutionNodes(allExecNodes) actualList := flow.IdentityList{} - for actual := execIterator.Next(); actual != nil; actual = execIterator.Next() { + for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { actualList = append(actualList, actual) } diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index f9adf6fa4e4..05cd0645e42 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -38,8 +38,7 @@ type backendTransactions struct { connFactory ConnectionFactory previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger - collIteratorFactory CollectionNodeIteratorFactory - execIteratorFactory ExecutionNodeIteratorFactory + nodeSelectorFactory NodeSelectorFactory } // SendTransaction forwards the transaction to the collection node @@ -99,10 +98,10 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T } defer logAnyError() - collNodeIter := b.collIteratorFactory.CreateNodeIterator(collNodes) + collNodeSelector := b.nodeSelectorFactory.SelectCollectionNodes(collNodes) // try sending the transaction to one of the chosen collection nodes - for colNode := collNodeIter.Next(); colNode != nil; colNode = collNodeIter.Next() { + for colNode := collNodeSelector.Next(); colNode != nil; colNode = collNodeSelector.Next() { err = b.sendTransactionToCollector(ctx, tx, colNode.Address) if err == nil { return nil @@ -778,10 +777,10 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( } defer logAnyError() - execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) // try to execute the script on one of the execution nodes - for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { resp, err := b.tryGetTransactionResult(ctx, execNode, req) if err == nil { b.log.Debug(). @@ -838,9 +837,9 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( return nil, errors.New("zero execution nodes") } - execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { resp, err := b.tryGetTransactionResultsByBlockID(ctx, execNode, req) if err == nil { b.log.Debug(). @@ -895,10 +894,10 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( return nil, errors.New("zero execution nodes provided") } - execNodeIter := b.execIteratorFactory.CreateNodeIterator(execNodes) + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) // try to execute the script on one of the execution nodes - for execNode := execNodeIter.Next(); execNode != nil; execNode = execNodeIter.Next() { + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { resp, err := b.tryGetTransactionResultByIndex(ctx, execNode, req) if err == nil { b.log.Debug(). diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 7e447fe94e4..2df7cb305c0 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -104,17 +104,17 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor - cbInterceptor := cf.withCircuitBreakerInterceptor() + cbInterceptor := cf.createCircuitBreakerInterceptor() if cbInterceptor != nil { connInterceptors = append(connInterceptors, cbInterceptor) } - ciInterceptor := cf.withClientInvalidationInterceptor(address, clientType) + ciInterceptor := cf.createClientInvalidationInterceptor(address, clientType) if ciInterceptor != nil { connInterceptors = append(connInterceptors, ciInterceptor) } - connInterceptors = append(connInterceptors, WithClientTimeoutInterceptor(timeout)) + connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached // The connections should be safe to be persisted and reused @@ -282,7 +282,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func (cf *ConnectionFactoryImpl) withClientInvalidationInterceptor(address string, clientType clientType) grpc.UnaryClientInterceptor { +func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor(address string, clientType clientType) grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig == nil || !cf.CircuitBreakerConfig.Enabled { clientInvalidationInterceptor := func( ctx context.Context, @@ -312,7 +312,7 @@ func (cf *ConnectionFactoryImpl) withClientInvalidationInterceptor(address strin return nil } -func (cf *ConnectionFactoryImpl) withCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { +func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ // here restore timeout defined to automatically return circuit breaker to HalfClose state @@ -347,7 +347,7 @@ func (cf *ConnectionFactoryImpl) withCircuitBreakerInterceptor() grpc.UnaryClien return nil } -func WithClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { +func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -371,5 +371,5 @@ func WithClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterce } func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { - return grpc.WithUnaryInterceptor(WithClientTimeoutInterceptor(timeout)) + return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) } diff --git a/engine/access/rpc/backend/node_iterator.go b/engine/access/rpc/backend/node_iterator.go index a929f0e7899..df6e0cf395b 100644 --- a/engine/access/rpc/backend/node_iterator.go +++ b/engine/access/rpc/backend/node_iterator.go @@ -7,25 +7,16 @@ const maxExecutionNodesCnt = 3 const collectionNodesToTry = 3 -type NodeIterator interface { +type NodeSelector interface { Next() *flow.Identity } -type NodeIteratorFactory interface { - CreateNodeIterator(nodes flow.IdentityList) NodeIterator -} - -var _ NodeIteratorFactory = (*ExecutionNodeIteratorFactory)(nil) -var _ NodeIterator = (*ExecutionNodeIterator)(nil) -var _ NodeIteratorFactory = (*CollectionNodeIteratorFactory)(nil) -var _ NodeIterator = (*CollectionNodeIterator)(nil) - -type ExecutionNodeIteratorFactory struct { +type NodeSelectorFactory struct { circuitBreakerEnabled bool } -func (e *ExecutionNodeIteratorFactory) CreateNodeIterator(nodes flow.IdentityList) NodeIterator { - if !e.circuitBreakerEnabled { +func (n *NodeSelectorFactory) SelectExecutionNodes(nodes flow.IdentityList) NodeSelector { + if !n.circuitBreakerEnabled { nodes = nodes.Sample(maxExecutionNodesCnt) } @@ -35,6 +26,19 @@ func (e *ExecutionNodeIteratorFactory) CreateNodeIterator(nodes flow.IdentityLis } } +func (n *NodeSelectorFactory) SelectCollectionNodes(nodes flow.IdentityList) NodeSelector { + if !n.circuitBreakerEnabled { + nodes = nodes.Sample(collectionNodesToTry) + } + + return &CollectionNodeIterator{ + nodes: nodes, + index: 0, + } +} + +var _ NodeSelector = (*ExecutionNodeIterator)(nil) + type ExecutionNodeIterator struct { nodes flow.IdentityList index int @@ -49,20 +53,7 @@ func (e *ExecutionNodeIterator) Next() *flow.Identity { return nil } -type CollectionNodeIteratorFactory struct { - circuitBreakerEnabled bool -} - -func (c *CollectionNodeIteratorFactory) CreateNodeIterator(nodes flow.IdentityList) NodeIterator { - if !c.circuitBreakerEnabled { - nodes = nodes.Sample(collectionNodesToTry) - } - - return &CollectionNodeIterator{ - nodes: nodes, - index: 0, - } -} +var _ NodeSelector = (*CollectionNodeIterator)(nil) type CollectionNodeIterator struct { nodes flow.IdentityList From 6054ef7bb9fca39ef4e3baa2da7ecf56411f8514 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 16:40:11 +0300 Subject: [PATCH 188/815] Added comments --- .../node_builder/access_node_builder.go | 8 +- .../access/rpc/backend/connection_factory.go | 9 ++ engine/access/rpc/backend/node_iterator.go | 70 --------------- engine/access/rpc/backend/node_selector.go | 86 +++++++++++++++++++ 4 files changed, 99 insertions(+), 74 deletions(-) delete mode 100644 engine/access/rpc/backend/node_iterator.go create mode 100644 engine/access/rpc/backend/node_selector.go diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 151bcbbf74f..d0b7696c7a1 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -687,9 +687,9 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringToIntVar(&builder.apiBurstlimits, "api-burst-limits", defaultConfig.apiBurstlimits, "burst limits for Access API methods e.g. Ping=100,GetTransaction=100 etc.") flags.BoolVar(&builder.supportsObserver, "supports-observer", defaultConfig.supportsObserver, "true if this staked access node supports observer or follower connections") flags.StringVar(&builder.PublicNetworkConfig.BindAddress, "public-network-address", defaultConfig.PublicNetworkConfig.BindAddress, "staked access node's public network bind address") - flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "whether to enable the circuit breaker for collection and execution node connections") - flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "initial timeout for circuit breaker to try connect again. Default value is 60s") - flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxFailures, "circuit-breaker-max-failures", defaultConfig.rpcConf.CircuitBreakerConfig.MaxFailures, "number of consecutive failures to break connection. Default value is 5") + flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "specifies whether the circuit breaker is enabled for collection and execution API clients.") + flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "duration after which the circuit breaker will restore the connection to the client after closing it due to failures. Default value is 60s") + flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxFailures, "circuit-breaker-max-failures", defaultConfig.rpcConf.CircuitBreakerConfig.MaxFailures, "maximum number of failed calls to the client that will cause the circuit breaker to close the connection. Default value is 5") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") @@ -756,7 +756,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { } if builder.rpcConf.CircuitBreakerConfig.Enabled { if builder.rpcConf.CircuitBreakerConfig.MaxFailures == 0 { - return errors.New("circuit-breaker-max-request-to-break must be greater than 0") + return errors.New("circuit-breaker-max-failures must be greater than 0") } } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 2df7cb305c0..cab254e879e 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -75,6 +75,15 @@ type ConnectionFactoryImpl struct { CircuitBreakerConfig *CircuitBreakerConfig } +// CircuitBreakerConfig is a configuration struct for the circuit breaker. +// +// Enabled specifies whether the circuit breaker is enabled for collection and execution API clients. +// +// RestoreTimeout specifies the duration after which the circuit breaker will restore the connection to the client +// after closing it due to failures. +// +// MaxFailures specifies the maximum number of failed calls to the client that will cause the circuit breaker +// to close the connection. type CircuitBreakerConfig struct { Enabled bool RestoreTimeout time.Duration diff --git a/engine/access/rpc/backend/node_iterator.go b/engine/access/rpc/backend/node_iterator.go deleted file mode 100644 index df6e0cf395b..00000000000 --- a/engine/access/rpc/backend/node_iterator.go +++ /dev/null @@ -1,70 +0,0 @@ -package backend - -import "github.com/onflow/flow-go/model/flow" - -// maxExecutionNodesCnt is the max number of execution nodes that will be contacted to complete an execution api request -const maxExecutionNodesCnt = 3 - -const collectionNodesToTry = 3 - -type NodeSelector interface { - Next() *flow.Identity -} - -type NodeSelectorFactory struct { - circuitBreakerEnabled bool -} - -func (n *NodeSelectorFactory) SelectExecutionNodes(nodes flow.IdentityList) NodeSelector { - if !n.circuitBreakerEnabled { - nodes = nodes.Sample(maxExecutionNodesCnt) - } - - return &ExecutionNodeIterator{ - nodes: nodes, - index: 0, - } -} - -func (n *NodeSelectorFactory) SelectCollectionNodes(nodes flow.IdentityList) NodeSelector { - if !n.circuitBreakerEnabled { - nodes = nodes.Sample(collectionNodesToTry) - } - - return &CollectionNodeIterator{ - nodes: nodes, - index: 0, - } -} - -var _ NodeSelector = (*ExecutionNodeIterator)(nil) - -type ExecutionNodeIterator struct { - nodes flow.IdentityList - index int -} - -func (e *ExecutionNodeIterator) Next() *flow.Identity { - if e.index < len(e.nodes) { - next := e.nodes[e.index] - e.index++ - return next - } - return nil -} - -var _ NodeSelector = (*CollectionNodeIterator)(nil) - -type CollectionNodeIterator struct { - nodes flow.IdentityList - index int -} - -func (c *CollectionNodeIterator) Next() *flow.Identity { - if c.index < len(c.nodes) { - next := c.nodes[c.index] - c.index++ - return next - } - return nil -} diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go new file mode 100644 index 00000000000..48831337ba3 --- /dev/null +++ b/engine/access/rpc/backend/node_selector.go @@ -0,0 +1,86 @@ +package backend + +import "github.com/onflow/flow-go/model/flow" + +// maxExecutionNodesCnt is the maximum number of execution nodes that will be contacted to complete an execution API request. +const maxExecutionNodesCnt = 3 + +// maxCollectionNodesCnt is the maximum number of collection nodes that will be contacted to complete a collection API request. +const maxCollectionNodesCnt = 3 + +// NodeSelector is an interface that represents the ability to select node identities that the access node is trying to reach. +// It encapsulates the internal logic of node selection and provides a way to change implementations for different types +// of nodes. Implementations of this interface should define the Next method, which returns the next node identity to be +// selected. +type NodeSelector interface { + Next() *flow.Identity +} + +// NodeSelectorFactory is a factory for creating node selectors based on factory configuration and node type. +type NodeSelectorFactory struct { + circuitBreakerEnabled bool +} + +// SelectExecutionNodes selects the configured number of execution node identities from the provided list of execution nodes +// and returns an execution node selector to iterate through them. +func (n *NodeSelectorFactory) SelectExecutionNodes(executionNodes flow.IdentityList) NodeSelector { + // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. + if !n.circuitBreakerEnabled { + executionNodes = executionNodes.Sample(maxExecutionNodesCnt) + } + + return &ExecutionNodeSelector{ + nodes: executionNodes, + index: 0, + } +} + +// SelectCollectionNodes selects the configured number of collection node identities from the provided list of collection nodes +// and returns a collection node selector to iterate through them. +func (n *NodeSelectorFactory) SelectCollectionNodes(collectionNodes flow.IdentityList) NodeSelector { + // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. + if !n.circuitBreakerEnabled { + collectionNodes = collectionNodes.Sample(maxCollectionNodesCnt) + } + + return &CollectionNodeSelector{ + nodes: collectionNodes, + index: 0, + } +} + +var _ NodeSelector = (*ExecutionNodeSelector)(nil) + +// ExecutionNodeSelector is a specific implementation of an execution node selector. +type ExecutionNodeSelector struct { + nodes flow.IdentityList + index int +} + +// Next returns the next execution node in the selector. +func (e *ExecutionNodeSelector) Next() *flow.Identity { + if e.index < len(e.nodes) { + next := e.nodes[e.index] + e.index++ + return next + } + return nil +} + +var _ NodeSelector = (*CollectionNodeSelector)(nil) + +// CollectionNodeSelector is a specific implementation of a collection node selector. +type CollectionNodeSelector struct { + nodes flow.IdentityList + index int +} + +// Next returns the next collection node in the selector. +func (c *CollectionNodeSelector) Next() *flow.Identity { + if c.index < len(c.nodes) { + next := c.nodes[c.index] + c.index++ + return next + } + return nil +} From b045dd0c2291c15fe34b9391695aa460eb308ccd Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 17:54:59 +0300 Subject: [PATCH 189/815] Added more comments --- engine/access/rpc/backend/backend.go | 1 + engine/access/rpc/backend/backend_scripts.go | 1 - .../access/rpc/backend/connection_factory.go | 49 ++++++++++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 42a0fcd26a1..2da4be060c7 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -106,6 +106,7 @@ func New( log.Fatal().Err(err).Msg("failed to initialize script logging cache") } + // create configured node selection factory to be used in sub-backend logic nodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled} b := &Backend{ diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 3d251139531..46d247a12aa 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -103,7 +103,6 @@ func (b *backendScripts) findScriptExecutors( executorAddrs := make([]string, 0, len(executors)) execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(executors) - // try to get events from one of the execution nodes for executor := execNodeSelector.Next(); executor != nil; executor = execNodeSelector.Next() { executorAddrs = append(executorAddrs, executor.Address) } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index cab254e879e..9d66a4caeba 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -291,7 +291,13 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor(address string, clientType clientType) grpc.UnaryClientInterceptor { +// createClientInvalidationInterceptor creates a client interceptor for client invalidation. It should only be created +// if the circuit breaker is disabled. If the response from the server indicates an unavailable status, it invalidates +// the corresponding client. +func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor( + address string, + clientType clientType, +) grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig == nil || !cf.CircuitBreakerConfig.Enabled { clientInvalidationInterceptor := func( ctx context.Context, @@ -321,13 +327,33 @@ func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor(address str return nil } +// The simplified representation and description of circuit breaker pattern, that used to handle node connectivity: +// +// Circuit Open --> Circuit Half-Open --> Circuit Closed +// ^ | +// | | +// +--------------------------------------+ +// +// The "Circuit Open" state represents the circuit being open, indicating that the node is not available. +// This state is entered when the number of consecutive failures exceeds the maximum allowed failures. +// +// The "Circuit Half-Open" state represents the circuit transitioning from the open state to the half-open +// state after a configured restore timeout. In this state, the circuit allows a limited number of requests +// to test if the node has recovered. +// +// The "Circuit Closed" state represents the circuit being closed, indicating that the node is available. +// This state is initial or entered when the test requests in the half-open state succeed. + +// createCircuitBreakerInterceptor creates a client interceptor for circuit breaker functionality. It should only be +// created if the circuit breaker is enabled. All invocations will go through the circuit breaker to be tracked for +// success or failure of the call. func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ - // here restore timeout defined to automatically return circuit breaker to HalfClose state + // The restore timeout is defined to automatically return the circuit breaker to the HalfClose state. Timeout: cf.CircuitBreakerConfig.RestoreTimeout, ReadyToTrip: func(counts gobreaker.Counts) bool { - // here number of maximum failures will be checked, before circuit breaker go to Open state + // The number of maximum failures is checked before the circuit breaker goes to the Open state. return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, }) @@ -341,7 +367,13 @@ func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryCli invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - // The invoker should be called from circuit breaker execute, to catch each fails and react according to settings + // The circuit breaker integration occurs here, where all invoked calls to the node pass through the + // CircuitBreaker.Execute method. This method counts successful and failed invocations, and switches to the + // "StateOpen" when the maximum failure threshold is reached. When the circuit breaker is in the "StateOpen" + // it immediately rejects connections and returns without waiting for the call timeout. After the + // "RestoreTimeout" period elapses, the circuit breaker transitions to the "StateHalfOpen" and attempts the + // invocation again. If the invocation fails, it returns to the "StateOpen"; otherwise, it transitions to + // the "StateClosed" and handles invocations as usual. _, err := circuitBreaker.Execute(func() (interface{}, error) { err := invoker(ctx, method, req, reply, cc, opts...) @@ -356,6 +388,7 @@ func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryCli return nil } +// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { clientTimeoutInterceptor := func( ctx context.Context, @@ -366,11 +399,11 @@ func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInter invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - // create a context that expires after timeout + // Create a context that expires after the specified timeout. ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - // call the remote GRPC using the short context + + // Call the remote GRPC using the short context. err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) return err @@ -379,6 +412,8 @@ func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInter return clientTimeoutInterceptor } +// WithClientTimeoutOption is a helper function to create a GRPC dial option +// with the specified client timeout interceptor. func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) } From 0e8fd98f4a6dd49d053245ce0f710b5dbb3f8994 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 22 Jun 2023 21:13:31 +0300 Subject: [PATCH 190/815] Added comments for tests --- .../rpc/backend/connection_factory_test.go | 73 +++++++++++++------ .../access/access_circuit_breaker_test.go | 31 +++++--- 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index c81201fc953..553948f0a93 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -408,140 +408,165 @@ func TestConnectionPoolStale(t *testing.T) { assert.Equal(t, resp, expected) } -// TestCircuitBreakerExecutionNode tests circuit breaker states changed for execution nodes +// TestCircuitBreakerExecutionNode tests the circuit breaker state changes for execution nodes. func TestCircuitBreakerExecutionNode(t *testing.T) { requestTimeout := 1 * time.Second circuitBreakerRestoreTimeout := 3 * time.Second - // create an execution node + + // Create an execution node for testing. en := new(executionNode) en.start(t) defer en.stop(t) - // setup the handler mock to not respond within the requestTimeout + // Set up the handler mock to not respond within the requestTimeout. req := &execution.PingRequest{} resp := &execution.PingResponse{} en.handler.On("Ping", testifymock.Anything, req).After(2*requestTimeout).Return(resp, nil) - // create the factory + + // Create the connection factory. connectionFactory := new(ConnectionFactoryImpl) - // set the execution grpc port + + // Set the execution gRPC port. connectionFactory.ExecutionGRPCPort = en.port - // set the execution grpc client requestTimeout + + // Set the execution gRPC client requestTimeout. connectionFactory.ExecutionNodeGRPCTimeout = requestTimeout - // set the configuration for circuit breaker + + // Set the configuration for the circuit breaker. connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } - // set the connection pool cache size + + // Set the connection pool cache size. cacheSize := 1 cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) connectionFactory.ConnectionsCache = cache connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting + + // Set metrics reporting. connectionFactory.AccessMetrics = metrics.NewNoopCollector() - // create the execution API client + // Create the execution API client. client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) assert.NoError(t, err) ctx := context.Background() + + // Helper function to make the Ping call to the execution node and measure the duration. callAndMeasurePingDuration := func() (time.Duration, error) { start := time.Now() - // make the call to the execution node + // Make the call to the execution node. _, err = client.Ping(ctx, req) en.handler.AssertCalled(t, "Ping", testifymock.Anything, req) return time.Since(start), err } + // Call and measure the duration for the first invocation. duration, err := callAndMeasurePingDuration() assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) assert.LessOrEqual(t, requestTimeout, duration) + // Call and measure the duration for the second invocation (circuit breaker state is now "Open"). duration, err = callAndMeasurePingDuration() assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) + // Reset the mock Ping for the next invocation to return response without delay en.handler.On("Ping", testifymock.Anything, req).Unset() en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) - //Wait until Circuit breaker go to Half-open state + // Wait until the circuit breaker transitions to the "HalfOpen" state. time.Sleep(circuitBreakerRestoreTimeout + time.Second) + // Call and measure the duration for the third invocation (circuit breaker state is now "HalfOpen"). duration, err = callAndMeasurePingDuration() assert.Greater(t, requestTimeout, duration) assert.Equal(t, nil, err) } -// TestCircuitBreakerCollectionNode tests circuit breaker states changed for collection nodes +// TestCircuitBreakerCollectionNode tests the circuit breaker state changes for collection nodes. func TestCircuitBreakerCollectionNode(t *testing.T) { requestTimeout := 1 * time.Second circuitBreakerRestoreTimeout := 3 * time.Second - // create a collection node + + // Create a collection node for testing. cn := new(collectionNode) cn.start(t) defer cn.stop(t) - // set up the handler mock to not respond within the requestTimeout + // Set up the handler mock to not respond within the requestTimeout. req := &access.PingRequest{} resp := &access.PingResponse{} cn.handler.On("Ping", testifymock.Anything, req).After(2*requestTimeout).Return(resp, nil) - // create the factory + // Create the connection factory. connectionFactory := new(ConnectionFactoryImpl) - // set the collection grpc port + + // Set the collection gRPC port. connectionFactory.CollectionGRPCPort = cn.port - // set the collection grpc client requestTimeout + + // Set the collection gRPC client requestTimeout. connectionFactory.CollectionNodeGRPCTimeout = requestTimeout - // set the configuration for circuit breaker + + // Set the configuration for the circuit breaker. connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } - // set the connection pool cache size + + // Set the connection pool cache size. cacheSize := 1 cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) connectionFactory.ConnectionsCache = cache connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting + + // Set metrics reporting. connectionFactory.AccessMetrics = metrics.NewNoopCollector() - // create the collection API client + // Create the collection API client. client, _, err := connectionFactory.GetAccessAPIClient(cn.listener.Addr().String()) assert.NoError(t, err) ctx := context.Background() + + // Helper function to make the Ping call to the collection node and measure the duration. callAndMeasurePingDuration := func() (time.Duration, error) { start := time.Now() - // make the call to the collection node + // Make the call to the collection node. _, err = client.Ping(ctx, req) cn.handler.AssertCalled(t, "Ping", testifymock.Anything, req) return time.Since(start), err } + // Call and measure the duration for the first invocation. duration, err := callAndMeasurePingDuration() assert.Equal(t, codes.DeadlineExceeded, status.Code(err)) assert.LessOrEqual(t, requestTimeout, duration) + // Call and measure the duration for the second invocation (circuit breaker state is now "Open"). duration, err = callAndMeasurePingDuration() assert.Equal(t, gobreaker.ErrOpenState, err) assert.Greater(t, requestTimeout, duration) + // Reset the mock Ping for the next invocation to return response without delay cn.handler.On("Ping", testifymock.Anything, req).Unset() cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) - //Wait until Circuit breaker go to Half-open state + // Wait until the circuit breaker transitions to the "HalfOpen" state. time.Sleep(circuitBreakerRestoreTimeout + time.Second) + // Call and measure the duration for the third invocation (circuit breaker state is now "HalfOpen"). duration, err = callAndMeasurePingDuration() assert.Greater(t, requestTimeout, duration) assert.Equal(t, nil, err) diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index 07b9f98ddbf..d99c292465e 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -98,14 +98,20 @@ func (s *AccessCircuitBreakerSuite) SetupTest() { s.net.Start(s.ctx) } +// TestCircuitBreaker tests the behavior of the circuit breaker. It verifies the circuit breaker's ability to open, +// prevent further requests, and restore after a timeout. It is done in a few steps: +// 1. Get the collection node and disconnect it from the network. +// 2. Try to send a transaction multiple times to observe the decrease in waiting time for a failed response. +// 3. Connect the collection node to the network and wait for the circuit breaker restore time. +// 4. Successfully send a transaction. func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { ctx, cancel := context.WithCancel(s.ctx) defer cancel() - // 1. Get collection node + // 1. Get the collection node collectionContainer := s.net.ContainerByName("collection_1") - // 2. Get Access Node container and client + // 2. Get the Access Node container and client accessContainer := s.net.ContainerByName(testnet.PrimaryAN) // Check if access node was created with circuit breaker flags @@ -119,7 +125,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { latestBlockID, err := accessClient.GetLatestBlockID(ctx) require.NoError(s.T(), err) - // create new account to deploy Counter to + // Create a new account to deploy Counter to accountPrivateKey := lib.RandomPrivateKey() accountKey := sdk.NewAccountKey(). @@ -146,18 +152,17 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { SetPayer(serviceAddress). SetGasLimit(9999) - // sign transaction - + // Sign the transaction childCtx, cancel := context.WithTimeout(ctx, time.Second*10) signedTx, err := accessClient.SignTransaction(createAccountTx) require.NoError(s.T(), err) cancel() - // 3. Disconnect collection node from network to activate Circuit Breaker + // 3. Disconnect the collection node from the network to activate the Circuit Breaker err = collectionContainer.Disconnect() require.NoError(s.T(), err, "failed to pause connection node") - //4. Send couple transactions to proof circuit breaker opens correctly + // 4. Send a couple of transactions to test if the circuit breaker opens correctly sendTransaction := func(ctx context.Context, tx *sdk.Transaction) (time.Duration, error) { childCtx, cancel = context.WithTimeout(ctx, time.Second*10) start := time.Now() @@ -168,23 +173,25 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { return duration, err } - // try to send transaction first time. Should wait at least timeout time and return Unknown error + // Try to send the transaction for the first time. It should wait at least the timeout time and return Unknown error duration, err := sendTransaction(ctx, signedTx) assert.Equal(s.T(), codes.Unknown, status.Code(err)) assert.GreaterOrEqual(s.T(), requestTimeout, duration) - // try to send transaction second time. Should wait less then a second cause CB configured to break after firs fail + // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker + // is configured to break after the first failure duration, err = sendTransaction(ctx, signedTx) assert.Equal(s.T(), codes.Unknown, status.Code(err)) assert.Greater(s.T(), time.Second, duration) - // connect again + // Reconnect the collection node err = collectionContainer.Connect() require.NoError(s.T(), err, "failed to start collection node") - // wait to restore circuit breaker + + // Wait for the circuit breaker to restore time.Sleep(cbRestoreTimeout) - // try to send transaction third time. Transaction should be send successful + // Try to send the transaction for the third time. The transaction should be sent successfully _, err = sendTransaction(ctx, signedTx) require.NoError(s.T(), err, "transaction should be sent") } From 5cfc254bbbe49e84296d2efa5409af90e4b5e22b Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 23 Jun 2023 08:51:22 -0400 Subject: [PATCH 191/815] using loop of expected decays - test failing --- network/alsp/manager/manager_test.go | 282 ++++++++++++++++++--------- 1 file changed, 188 insertions(+), 94 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 57ed424ec0b..4af452ddb41 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -346,123 +346,217 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // creates a misbehavior report for the spammer report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) - // simulates the victim node reporting the spammer node misbehavior 120 times - // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after - // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - // the spammer is definitely disallow-listed. - reportCount := 120 - wg := sync.WaitGroup{} - for i := 0; i < reportCount; i++ { - wg.Add(1) - // reports the misbehavior - r := report // capture range variable - go func() { - defer wg.Done() + // loop through the list of expected penalties and ensure that the spammer is disallow-listed after each report for + // the correct amount of time. - con.ReportMisbehavior(r) - }() - } + // data driven test of expected penalties - key is string describing the test case, value is a list of expected int penalties + // for each report. - unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + expectedDecaysMap := map[string][]int{ + "10x decrease in decay": {1000, 100, 10, 1}, // list of expected decay values after each disallow listing - // ensures that the spammer is disallow-listed by the victim - // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - // it is allow-listed again. - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + //"1x decrease in decay": {1000, 100, 100, 100}, // list of expected decay values after each disallow listing + } - // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + for testCase, expectedDecays := range expectedDecaysMap { - record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + // reset cutoff counter before each test case + expectedCutoffCounter := 0 - // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - require.Equal(t, float64(1000), record.Decay) - require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(1), record.CutoffCounter) + // creat subtest for each expected decay list + t.Run(testCase, func(t *testing.T) { + expectedCutoffCounter++ // cutoff counter is expected to be incremented after each disallow listing - // decay the disallow-listing penalty of the spammer node to zero. - t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") - fastDecay = true - t.Logf("decayed the disallow-listing penalty of the spammer node to zero") + // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected + for _, expectedDecay := range expectedDecays { - // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) + // reset the decay function to the default + fastDecay = false - // all the nodes should be able to connect to each other again. - p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + // simulates the victim node reporting the spammer node misbehavior 10 times + // as each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is definitely disallow-listed. + reportCount := 120 + wg := sync.WaitGroup{} + for i := 0; i < reportCount; i++ { + wg.Add(1) + // reports the misbehavior + r := report // capture range variable + go func() { + defer wg.Done() - record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + con.ReportMisbehavior(r) + }() + } - require.Equal(t, float64(0), record.Penalty) - require.Equal(t, float64(1000), record.Decay) - require.Equal(t, false, record.DisallowListed) - require.Equal(t, uint64(1), record.CutoffCounter) + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // go back to regular decay to prepare for the next set of misbehavior reports. - fastDecay = false - t.Logf("about to report misbehavior again") + // ensures that the spammer is disallow-listed by the victim + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + // it is allow-listed again. + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) - // simulates the victim node reporting the spammer node misbehavior 120 times - // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after - // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - // the spammer is definitely disallow-listed. - reportCount = 120 - wg = sync.WaitGroup{} - for i := 0; i < reportCount; i++ { - wg.Add(1) - // reports the misbehavior - r := report // capture range variable - go func() { - defer wg.Done() + // ensures that the spammer is not disallow-listed by the honest node + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[spammerIndex]}, 1*time.Millisecond, 100*time.Millisecond) - con.ReportMisbehavior(r) - }() - } + // ensures that the spammer is disallow-listed for the expected amount of time - unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - - // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - // it is allow-listed again. - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) - // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + require.Equal(t, float64(expectedDecay), record.Decay) + require.Equal(t, true, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + // decay the disallow-listing penalty of the spammer node to zero. + t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") + fastDecay = true + t.Logf("decayed the disallow-listing penalty of the spammer node to zero") - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - require.Equal(t, float64(1000), record.Decay) - require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(2), record.CutoffCounter) + // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) - // decay the disallow-listing penalty of the spammer node to zero. - t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") - fastDecay = true - t.Logf("decayed the disallow-listing penalty of the spammer node to zero (2nd time)") + // all the nodes should be able to connect to each other again. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) - // all the nodes should be able to connect to each other again. - p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + require.Equal(t, float64(0), record.Penalty) + //require.Equal(t, float64(1000), record.Decay) + require.Equal(t, false, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + // go back to regular decay to prepare for the next set of misbehavior reports. + fastDecay = false + t.Logf("about to report misbehavior again") + } + t.Logf("finished test case: %s", testCase) + }) + } - require.Equal(t, float64(0), record.Penalty) - require.Equal(t, float64(1000), record.Decay) - require.Equal(t, false, record.DisallowListed) - require.Equal(t, uint64(2), record.CutoffCounter) + // simulates the victim node reporting the spammer node misbehavior 120 times + // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is definitely disallow-listed. + //reportCount := 120 + //wg := sync.WaitGroup{} + //for i := 0; i < reportCount; i++ { + // wg.Add(1) + // // reports the misbehavior + // r := report // capture range variable + // go func() { + // defer wg.Done() + // + // con.ReportMisbehavior(r) + // }() + //} + // + //unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + // + //// ensures that the spammer is disallow-listed by the victim + //// while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + //// it is allow-listed again. + //p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + // + //// despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // + //record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + //require.True(t, ok) + //require.NotNil(t, record) + // + //// check the penalty of the spammer node. It should be greater than the disallow-listing threshold. + //require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + //require.Equal(t, float64(1000), record.Decay) + //require.Equal(t, true, record.DisallowListed) + //require.Equal(t, uint64(1), record.CutoffCounter) + // + //// decay the disallow-listing penalty of the spammer node to zero. + //t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") + //fastDecay = true + //t.Logf("decayed the disallow-listing penalty of the spammer node to zero") + // + //// after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) + // + //// all the nodes should be able to connect to each other again. + //p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + // + //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + //require.True(t, ok) + //require.NotNil(t, record) + // + //require.Equal(t, float64(0), record.Penalty) + //require.Equal(t, float64(1000), record.Decay) + //require.Equal(t, false, record.DisallowListed) + //require.Equal(t, uint64(1), record.CutoffCounter) + // + //// go back to regular decay to prepare for the next set of misbehavior reports. + //fastDecay = false + //t.Logf("about to report misbehavior again") + // + //// simulates the victim node reporting the spammer node misbehavior 120 times + //// to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after + //// 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + //// the spammer is definitely disallow-listed. + //reportCount = 120 + //wg = sync.WaitGroup{} + //for i := 0; i < reportCount; i++ { + // wg.Add(1) + // // reports the misbehavior + // r := report // capture range variable + // go func() { + // defer wg.Done() + // + // con.ReportMisbehavior(r) + // }() + //} + // + //unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + // + //// while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + //// it is allow-listed again. + //p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + // + //// despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) + // + //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + //require.True(t, ok) + //require.NotNil(t, record) + // + //require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + //require.Equal(t, float64(1000), record.Decay) + //require.Equal(t, true, record.DisallowListed) + //require.Equal(t, uint64(2), record.CutoffCounter) + // + //// decay the disallow-listing penalty of the spammer node to zero. + //t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") + //fastDecay = true + //t.Logf("decayed the disallow-listing penalty of the spammer node to zero (2nd time)") + // + //// after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) + // + //// all the nodes should be able to connect to each other again. + //p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + // + //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + //require.True(t, ok) + //require.NotNil(t, record) + // + //require.Equal(t, float64(0), record.Penalty) + //require.Equal(t, float64(1000), record.Decay) + //require.Equal(t, false, record.DisallowListed) + //require.Equal(t, uint64(2), record.CutoffCounter) } // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. From 6a770b380913ae62d366920978adfb63a40d5357 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 23 Jun 2023 21:38:54 -0400 Subject: [PATCH 192/815] decay list implementation --- network/alsp/internal/cache.go | 1 + network/alsp/manager/manager.go | 4 ++++ network/alsp/manager/manager_test.go | 23 ++++++++++++++++------- network/alsp/model/record.go | 21 +++++++++++++++++++-- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/network/alsp/internal/cache.go b/network/alsp/internal/cache.go index c29ae4bd988..28b673d637a 100644 --- a/network/alsp/internal/cache.go +++ b/network/alsp/internal/cache.go @@ -168,6 +168,7 @@ func (s *SpamRecordCache) Get(originId flow.Identifier) (*model.ProtocolSpamReco return &model.ProtocolSpamRecord{ OriginId: record.OriginId, Decay: record.Decay, + DecayList: record.DecayList, CutoffCounter: record.CutoffCounter, Penalty: record.Penalty, DisallowListed: record.DisallowListed, diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 8ffc3858434..64a248b1a33 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -351,6 +351,10 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // (it requires enabling the batch mode end-to-end including the cache in middleware). if record.Penalty == float64(0) && record.DisallowListed { record.DisallowListed = false + + // after fully decaying the penalty, update decay for next disallow listing + record.UpdateDecay() + m.logger.Info(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 4af452ddb41..51e5d0d6a13 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -361,14 +361,15 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio for testCase, expectedDecays := range expectedDecaysMap { // reset cutoff counter before each test case + t.Log("resetting cutoff counter") expectedCutoffCounter := 0 // creat subtest for each expected decay list t.Run(testCase, func(t *testing.T) { - expectedCutoffCounter++ // cutoff counter is expected to be incremented after each disallow listing - + t.Logf("starting test case: %s", testCase) // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected - for _, expectedDecay := range expectedDecays { + for i, _ := range expectedDecays { + t.Logf("starting iteration %d", i) // reset the decay function to the default fastDecay = false @@ -392,10 +393,12 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + expectedCutoffCounter++ // cutoff counter is expected to be incremented after each disallow listing + // ensures that the spammer is disallow-listed by the victim // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless // it is allow-listed again. - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) // ensures that the spammer is not disallow-listed by the honest node p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[spammerIndex]}, 1*time.Millisecond, 100*time.Millisecond) @@ -408,7 +411,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - require.Equal(t, float64(expectedDecay), record.Decay) + + expectedDecayAfterCutoff := expectedDecays[i] + if i < len(expectedDecays)-1 { + expectedDecayAfterCutoff = expectedDecays[i+1] + } + + require.Equal(t, float64(expectedDecays[i]), record.Decay) require.Equal(t, true, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) @@ -418,7 +427,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Logf("decayed the disallow-listing penalty of the spammer node to zero") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 4*time.Second) // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -428,7 +437,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.NotNil(t, record) require.Equal(t, float64(0), record.Penalty) - //require.Equal(t, float64(1000), record.Decay) + require.Equal(t, float64(expectedDecayAfterCutoff), record.Decay) require.Equal(t, false, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) diff --git a/network/alsp/model/record.go b/network/alsp/model/record.go index 088e7d81916..7d5a459fea5 100644 --- a/network/alsp/model/record.go +++ b/network/alsp/model/record.go @@ -13,8 +13,14 @@ type ProtocolSpamRecord struct { OriginId flow.Identifier // Decay speed of Penalty for this misbehaving node. Each node may have a different Decay speed based on its behavior. + // Subsequent disallow listings of the node will decrease the Decay speed of the node so it will take longer to be allow-listed. Decay float64 + // DecayList is a list of decay values that are used to decay the Penalty value of the misbehaving node on subsequent disallow listings. + // The decay values are used from left to right (left most value is used for the first disallow listing) and once the right most decay + // value is reached, any subsequent disallow listings will continue to use the right most decay value. + DecayList []float64 + // CutoffCounter is a counter that is used to determine how many times the connections to the node has been cut due to // its Penalty value dropping below the disallow-listing threshold. // Note that the cutoff connections are recovered after a certain amount of time. @@ -29,6 +35,15 @@ type ProtocolSpamRecord struct { Penalty float64 } +// UpdateDecay updates the decay value of the record. This allows the decay to be different on subsequent disallow listings. +// The decay value is updated based on the DecayList. If the DecayList is empty, the decay value is not updated. +func (r *ProtocolSpamRecord) UpdateDecay() { + if len(r.DecayList) > 0 { + r.Decay = r.DecayList[0] + r.DecayList = r.DecayList[1:] + } +} + // RecordAdjustFunc is a function that is used to adjust the fields of a ProtocolSpamRecord. // The function is called with the current record and should return the adjusted record. // Returned error indicates that the adjustment is not applied, and the record should not be updated. @@ -49,8 +64,10 @@ type SpamRecordFactoryFunc func(flow.Identifier) ProtocolSpamRecord func SpamRecordFactory() SpamRecordFactoryFunc { return func(originId flow.Identifier) ProtocolSpamRecord { return ProtocolSpamRecord{ - OriginId: originId, - Decay: InitialDecaySpeed, + OriginId: originId, + Decay: InitialDecaySpeed, + // slow down decay 10x after each disallow-listing (e.g. 1000, 100, 10, 1) + DecayList: []float64{InitialDecaySpeed * .1, InitialDecaySpeed * .01, InitialDecaySpeed * .001}, DisallowListed: false, CutoffCounter: uint64(0), Penalty: float64(0), From 0daeb03b274ea143a61cdb36fa9ddd42cec7dc77 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 24 Jun 2023 10:20:27 -0400 Subject: [PATCH 193/815] more debugging, decays in test --- network/alsp/manager/manager.go | 3 +++ network/alsp/manager/manager_test.go | 34 ++++++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 64a248b1a33..fc2b0ba77d0 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -322,6 +322,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { m.logger.Warn(). Str("key", logging.KeySuspicious). Hex("identifier", logging.ID(id)). + Float64("penalty", record.Penalty). Uint64("cutoff_counter", record.CutoffCounter). Float64("decay_speed", record.Decay). Bool("disallow_listed", record.DisallowListed). @@ -337,12 +338,14 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // We use math.Min() to make sure the penalty is never positive. m.logger.Info(). Hex("identifier", logging.ID(id)). + Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). Msg("onHeartbeat - before adjusting penalty via decayFunc") record.Penalty = m.DecayFunc(record) m.logger.Info(). Hex("identifier", logging.ID(id)). + Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). Msg("onHeartbeat - after adjusting penalty via decayFunc") diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 51e5d0d6a13..13518093e94 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -287,14 +287,18 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio cfg := managerCfgFixture(t) fastDecay := false fastDecayFunc := func(record model.ProtocolSpamRecord) float64 { - t.Logf("decayFuc called with record: %+v", record) + now := time.Now() + + t.Logf("%s decayFuc called with record: %+v", now.Format(time.RFC3339), record) if fastDecay { + now = time.Now() // decay to zero in a single heart beat - t.Logf("fastDecay is true, so decay to zero") + t.Logf("%s fastDecay is true, so decay to zero", now.Format(time.RFC3339)) return 0 } else { + now = time.Now() // decay as usual - t.Logf("fastDecay is false, so decay as usual") + t.Logf("%s fastDecay is false, so decay as usual", now.Format(time.RFC3339)) return math.Min(record.Penalty+record.Decay, 0) } } @@ -353,7 +357,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // for each report. expectedDecaysMap := map[string][]int{ - "10x decrease in decay": {1000, 100, 10, 1}, // list of expected decay values after each disallow listing + "10x decrease in decay": {1000, 100, 10, 1, 1, 1}, // list of expected decay values after each disallow listing //"1x decrease in decay": {1000, 100, 100, 100}, // list of expected decay values after each disallow listing } @@ -368,7 +372,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Run(testCase, func(t *testing.T) { t.Logf("starting test case: %s", testCase) // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected - for i, _ := range expectedDecays { + for i := range expectedDecays { t.Logf("starting iteration %d", i) // reset the decay function to the default @@ -412,22 +416,23 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - expectedDecayAfterCutoff := expectedDecays[i] - if i < len(expectedDecays)-1 { - expectedDecayAfterCutoff = expectedDecays[i+1] - } - require.Equal(t, float64(expectedDecays[i]), record.Decay) require.Equal(t, true, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + now := time.Now() + // decay the disallow-listing penalty of the spammer node to zero. - t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") + t.Logf("%s about to decay the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) fastDecay = true - t.Logf("decayed the disallow-listing penalty of the spammer node to zero") + // add time stamp to t.Logf() message to ensure that the message is printed in the correct order + now = time.Now() + t.Logf("%s decayed the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 4*time.Second) + now = time.Now() + t.Logf("%s spammer node is able to connect to the victim node again", now.Format(time.RFC3339)) // all the nodes should be able to connect to each other again. p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -436,6 +441,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.True(t, ok) require.NotNil(t, record) + expectedDecayAfterCutoff := expectedDecays[i] + if i < len(expectedDecays)-1 { + expectedDecayAfterCutoff = expectedDecays[i+1] + } + require.Equal(t, float64(0), record.Penalty) require.Equal(t, float64(expectedDecayAfterCutoff), record.Decay) require.Equal(t, false, record.DisallowListed) From 60909308f8fb7462e071d3db2d11278ad3ee1fcb Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Sat, 24 Jun 2023 21:55:48 +0300 Subject: [PATCH 194/815] Apply suggestions from code review Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- engine/access/rpc/backend/connection_factory.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 9d66a4caeba..701f1458b4f 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -113,14 +113,10 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor - cbInterceptor := cf.createCircuitBreakerInterceptor() - if cbInterceptor != nil { - connInterceptors = append(connInterceptors, cbInterceptor) - } - - ciInterceptor := cf.createClientInvalidationInterceptor(address, clientType) - if ciInterceptor != nil { - connInterceptors = append(connInterceptors, ciInterceptor) + if cf.CircuitBreakerConfig.Enabled { + connInterceptors = append(connInterceptors, cf.createCircuitBreakerInterceptor()) + } else { + connInterceptors = append(connInterceptors, cf.createClientInvalidationInterceptor(address, clientType)) } connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) From f84fbcd1a3a338ec91cd1d185d13eae60b2bcff1 Mon Sep 17 00:00:00 2001 From: Misha Date: Sun, 25 Jun 2023 10:33:55 -0400 Subject: [PATCH 195/815] check penalty was decayed by expected decay after each disallow listing --- network/alsp/manager/manager_test.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 13518093e94..af34ede77a8 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -373,7 +373,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Logf("starting test case: %s", testCase) // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected for i := range expectedDecays { - t.Logf("starting iteration %d", i) + t.Logf("starting iteration %d with expected decay %d", i, expectedDecays[i]) // reset the decay function to the default fastDecay = false @@ -420,6 +420,26 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.Equal(t, true, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + penalty1 := record.Penalty + + // wait for one heartbeat to be processed. + time.Sleep(1 * time.Second) + + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) + + // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + + require.Equal(t, float64(expectedDecays[i]), record.Decay) + require.Equal(t, true, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + penalty2 := record.Penalty + + // check that the penalty has decayed by the expected amount in one heartbeat + require.Equal(t, float64(expectedDecays[i]), penalty2-penalty1) + now := time.Now() // decay the disallow-listing penalty of the spammer node to zero. From 54670af59e6a2daf2ff8eb567c7ee010e3ec82c3 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 29 Jun 2023 21:04:18 +0300 Subject: [PATCH 196/815] Implemented atomic request counter --- .../node_builder/access_node_builder.go | 2 +- engine/access/apiproxy/access_api_proxy.go | 4 +- .../access/rpc/backend/connection_factory.go | 80 +++++++++++++++---- .../rpc/backend/connection_factory_test.go | 72 ++++++++++++++++- 4 files changed, 137 insertions(+), 21 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index c1aba0b8a81..c63a6001c49 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -910,7 +910,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.rpcConf.CollectionAddr, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(builder.rpcConf.CollectionClientTimeout)) + backend.WithClientTimeoutOption(builder.rpcConf.CollectionClientTimeout)) if err != nil { return err } diff --git a/engine/access/apiproxy/access_api_proxy.go b/engine/access/apiproxy/access_api_proxy.go index d72ec5bb5e2..ed47ef167eb 100644 --- a/engine/access/apiproxy/access_api_proxy.go +++ b/engine/access/apiproxy/access_api_proxy.go @@ -65,7 +65,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientTimeoutOption(timeout)) if err != nil { return err } @@ -79,7 +79,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientUnaryInterceptor(timeout)) + backend.WithClientTimeoutOption(timeout)) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 63ead3d3e32..a1debd0535c 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -6,6 +6,7 @@ import ( "io" "net" "sync" + "sync/atomic" "time" lru "github.com/hashicorp/golang-lru" @@ -64,10 +65,11 @@ type ConnectionFactoryImpl struct { } type CachedClient struct { - ClientConn *grpc.ClientConn - Address string - mutex sync.Mutex - timeout time.Duration + ClientConn *grpc.ClientConn + Address string + mutex sync.Mutex + timeout time.Duration + requestCounter int32 } // createConnection creates new gRPC connections to remote node @@ -84,6 +86,17 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D Timeout: timeout, } + var connInterceptors []grpc.UnaryClientInterceptor + + if cf.ConnectionsCache != nil { + if res, ok := cf.ConnectionsCache.Get(address); ok { + cachedClient := res.(*CachedClient) + connInterceptors = append(connInterceptors, cf.createRequestWatcherInterceptor(cachedClient)) + } + } + + connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) + // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached // The connections should be safe to be persisted and reused // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams @@ -93,7 +106,8 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(cf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithKeepaliveParams(keepaliveParams), - WithClientUnaryInterceptor(timeout)) + grpc.WithChainUnaryInterceptor(connInterceptors...), + ) if err != nil { return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) } @@ -111,9 +125,10 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout conn = store.ClientConn } else { store = &CachedClient{ - ClientConn: nil, - Address: grpcAddress, - timeout: timeout, + ClientConn: nil, + Address: grpcAddress, + timeout: timeout, + requestCounter: 0, } cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") cf.ConnectionsCache.Add(grpcAddress, store) @@ -230,9 +245,13 @@ func (s *CachedClient) Close() { if conn == nil { return } - // allow time for any existing requests to finish before closing the connection - time.Sleep(s.timeout + 1*time.Second) - conn.Close() + + for { + if atomic.LoadInt32(&s.requestCounter) == 0 { + conn.Close() + return + } + } } // getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the @@ -249,8 +268,31 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { +// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. +func (cf *ConnectionFactoryImpl) createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { + requestWatcherInterceptor := func( + ctx context.Context, + method string, + req interface{}, + reply interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, + ) error { + atomic.AddInt32(&cachedClient.requestCounter, 1) + + err := invoker(ctx, method, req, reply, cc, opts...) + + atomic.AddInt32(&cachedClient.requestCounter, -1) + + return err + } + + return requestWatcherInterceptor +} +// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. +func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { clientTimeoutInterceptor := func( ctx context.Context, method string, @@ -260,17 +302,21 @@ func WithClientUnaryInterceptor(timeout time.Duration) grpc.DialOption { invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - - // create a context that expires after timeout + // Create a context that expires after the specified timeout. ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - // call the remote GRPC using the short context + // Call the remote GRPC using the short context. err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) return err } - return grpc.WithUnaryInterceptor(clientTimeoutInterceptor) + return clientTimeoutInterceptor +} + +// WithClientTimeoutOption is a helper function to create a GRPC dial option +// with the specified client timeout interceptor. +func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { + return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index fa4801a5897..dec8e9746f5 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -3,6 +3,7 @@ package backend import ( "context" "fmt" + "google.golang.org/grpc/connectivity" "net" "strconv" "strings" @@ -106,7 +107,7 @@ func TestProxyAccessAPIConnectionReuse(t *testing.T) { // set the collection grpc port connectionFactory.CollectionGRPCPort = cn.port // set the connection pool cache size - cacheSize := 5 + cacheSize := 1 cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) @@ -407,6 +408,75 @@ func TestConnectionPoolStale(t *testing.T) { assert.Equal(t, resp, expected) } +// TestExecutionNodeClientClosedGracefully +func TestExecutionNodeClientClosedGracefully(t *testing.T) { + + timeout := 2 * time.Second + + // create an execution node + en := new(executionNode) + en.start(t) + defer en.stop(t) + + // setup the handler mock to not respond within the timeout + req := &execution.PingRequest{} + resp := &execution.PingResponse{} + en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) + + // create the factory + connectionFactory := new(ConnectionFactoryImpl) + // set the execution grpc port + connectionFactory.ExecutionGRPCPort = en.port + // set the execution grpc client timeout + connectionFactory.ExecutionNodeGRPCTimeout = timeout + // set the connection pool cache size + cacheSize := 1 + cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { + evictedValue.(*CachedClient).Close() + }) + connectionFactory.ConnectionsCache = cache + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() + + clientAddress := en.listener.Addr().String() + // create the execution API client + client, _, err := connectionFactory.GetExecutionAPIClient(clientAddress) + assert.NoError(t, err) + + result, _ := cache.Get(clientAddress) + clientConn := result.(*CachedClient).ClientConn + clientConn.GetState() + + ctx := context.Background() + + go func() { + for { + state := clientConn.GetState() + fmt.Printf("\t!!! State: %s\n", state.String()) + if state == connectivity.Shutdown { + fmt.Printf("\t\t!!! Was shutdown: %s\n", state.String()) + return + } + } + }() + + //// Schedule the function to run after the timeout + //time.AfterFunc(3*time.Millisecond, func() { + // fmt.Println("InvalidateExecutionAPIClient") + // connectionFactory.InvalidateExecutionAPIClient(clientAddress) + //}) + fmt.Println("Pings` started") + for i := 1; i <= 100; i++ { + //fmt.Printf("Ping %d %s\n", i, clientConn.GetState().String()) + fmt.Printf("Ping %d\n", i) + _, err = client.Ping(ctx, req) + //fmt.Printf("State %s\n", clientConn.GetState().String()) + } + fmt.Println("Pings` finished") + connectionFactory.InvalidateExecutionAPIClient(clientAddress) +} + // node mocks a flow node that runs a GRPC server type node struct { server *grpc.Server From fa9acbc5a6456873e9e7e475601dbd6c22ef7562 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 30 Jun 2023 22:17:31 +0300 Subject: [PATCH 197/815] Implemented graceful close and test. --- .../access/rpc/backend/connection_factory.go | 65 +++++--- .../rpc/backend/connection_factory_test.go | 151 +++++++++++------- 2 files changed, 136 insertions(+), 80 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index a1debd0535c..8a914893502 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -6,9 +6,12 @@ import ( "io" "net" "sync" - "sync/atomic" "time" + "go.uber.org/atomic" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" @@ -69,7 +72,26 @@ type CachedClient struct { Address string mutex sync.Mutex timeout time.Duration - requestCounter int32 + requestCounter *atomic.Int32 + closeRequested *atomic.Bool + done chan struct{} +} + +func (s *CachedClient) UpdateConnection(conn *grpc.ClientConn) { + s.ClientConn = conn + s.closeRequested.Store(false) + s.requestCounter.Store(0) + s.done = make(chan struct{}, 1) +} + +func NewCachedClient(timeout time.Duration, address string) *CachedClient { + return &CachedClient{ + Address: address, + timeout: timeout, + requestCounter: atomic.NewInt32(0), + closeRequested: atomic.NewBool(false), + done: make(chan struct{}, 1), + } } // createConnection creates new gRPC connections to remote node @@ -91,7 +113,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D if cf.ConnectionsCache != nil { if res, ok := cf.ConnectionsCache.Get(address); ok { cachedClient := res.(*CachedClient) - connInterceptors = append(connInterceptors, cf.createRequestWatcherInterceptor(cachedClient)) + connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) } } @@ -124,18 +146,15 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout store = res.(*CachedClient) conn = store.ClientConn } else { - store = &CachedClient{ - ClientConn: nil, - Address: grpcAddress, - timeout: timeout, - requestCounter: 0, - } + store = NewCachedClient(timeout, grpcAddress) + cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") cf.ConnectionsCache.Add(grpcAddress, store) if cf.AccessMetrics != nil { cf.AccessMetrics.ConnectionAddedToPool() } } + cf.mutex.Unlock() store.mutex.Lock() defer store.mutex.Unlock() @@ -146,7 +165,7 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout if err != nil { return nil, err } - store.ClientConn = conn + store.UpdateConnection(conn) if cf.AccessMetrics != nil { if cacheHit { cf.AccessMetrics.ConnectionFromPoolUpdated() @@ -246,12 +265,12 @@ func (s *CachedClient) Close() { return } - for { - if atomic.LoadInt32(&s.requestCounter) == 0 { - conn.Close() - return - } + s.closeRequested.Store(true) + + if s.requestCounter.Load() > 0 { + <-s.done } + conn.Close() } // getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the @@ -269,7 +288,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { } // createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. -func (cf *ConnectionFactoryImpl) createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { +func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { requestWatcherInterceptor := func( ctx context.Context, method string, @@ -279,11 +298,21 @@ func (cf *ConnectionFactoryImpl) createRequestWatcherInterceptor(cachedClient *C invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - atomic.AddInt32(&cachedClient.requestCounter, 1) + if cachedClient.closeRequested.Load() { + return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) + } + + cachedClient.requestCounter.Add(1) err := invoker(ctx, method, req, reply, cc, opts...) - atomic.AddInt32(&cachedClient.requestCounter, -1) + count := cachedClient.requestCounter.Add(-1) + if cachedClient.closeRequested.Load() && count == 0 { + select { + case cachedClient.done <- struct{}{}: + default: + } + } return err } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index dec8e9746f5..005d3bc2f55 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -3,7 +3,6 @@ package backend import ( "context" "fmt" - "google.golang.org/grpc/connectivity" "net" "strconv" "strings" @@ -11,6 +10,9 @@ import ( "testing" "time" + "go.uber.org/atomic" + "pgregory.net/rapid" + lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" @@ -408,73 +410,98 @@ func TestConnectionPoolStale(t *testing.T) { assert.Equal(t, resp, expected) } -// TestExecutionNodeClientClosedGracefully +// TestExecutionNodeClientClosedGracefully tests the scenario where the execution node client is closed gracefully. +// +// Test Steps: +// - Generate a random number of requests and start goroutines to handle each request. +// - Invalidate the execution API client. +// - Wait for all goroutines to finish. +// - Verify that the number of completed requests matches the number of sent responses. func TestExecutionNodeClientClosedGracefully(t *testing.T) { + // Add createExecNode function to recreate it each time for rapid test + createExecNode := func() (*executionNode, func()) { + en := new(executionNode) + en.start(t) + return en, func() { + en.stop(t) + } + } - timeout := 2 * time.Second - - // create an execution node - en := new(executionNode) - en.start(t) - defer en.stop(t) - - // setup the handler mock to not respond within the timeout - req := &execution.PingRequest{} - resp := &execution.PingResponse{} - en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) - - // create the factory - connectionFactory := new(ConnectionFactoryImpl) - // set the execution grpc port - connectionFactory.ExecutionGRPCPort = en.port - // set the execution grpc client timeout - connectionFactory.ExecutionNodeGRPCTimeout = timeout - // set the connection pool cache size - cacheSize := 1 - cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { - evictedValue.(*CachedClient).Close() - }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting - connectionFactory.AccessMetrics = metrics.NewNoopCollector() - - clientAddress := en.listener.Addr().String() - // create the execution API client - client, _, err := connectionFactory.GetExecutionAPIClient(clientAddress) - assert.NoError(t, err) + // Add rapid test, to check graceful close on different number of requests + rapid.Check(t, func(t *rapid.T) { + en, closer := createExecNode() + defer closer() + + // setup the handler mock to not respond within the timeout + req := &execution.PingRequest{} + resp := &execution.PingResponse{} + respSent := atomic.NewUint64(0) + en.handler.On("Ping", testifymock.Anything, req).Run(func(_ testifymock.Arguments) { + respSent.Inc() + }).Return(resp, nil) + + // create the factory + connectionFactory := new(ConnectionFactoryImpl) + // set the execution grpc port + connectionFactory.ExecutionGRPCPort = en.port + // set the execution grpc client timeout + connectionFactory.ExecutionNodeGRPCTimeout = time.Second + // set the connection pool cache size + cacheSize := 1 + cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { + evictedValue.(*CachedClient).Close() + }) + connectionFactory.ConnectionsCache = cache + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() + + clientAddress := en.listener.Addr().String() + // create the execution API client + client, _, err := connectionFactory.GetExecutionAPIClient(clientAddress) + assert.NoError(t, err) - result, _ := cache.Get(clientAddress) - clientConn := result.(*CachedClient).ClientConn - clientConn.GetState() + result, _ := cache.Get(clientAddress) + clientConn := result.(*CachedClient).ClientConn + clientConn.GetState() + + ctx := context.Background() + + // Generate random number of requests + nofRequests := rapid.IntRange(10, 100).Draw(t, "nofRequests").(int) + reqCompleted := atomic.NewUint64(0) + + var waitGroup sync.WaitGroup + + for i := 0; i < nofRequests; i++ { + waitGroup.Add(1) + + // call Ping request from different goroutines + go func() { + defer waitGroup.Done() + _, err := client.Ping(ctx, req) + + if err == nil { + reqCompleted.Inc() + } else { + if st, ok := status.FromError(err); ok { + if st.Code() == codes.Unavailable { + return + } + } + fmt.Println("!!! Failed: ", err) + t.Fail() + } + }() + } - ctx := context.Background() + // Close connection + connectionFactory.InvalidateExecutionAPIClient(clientAddress) - go func() { - for { - state := clientConn.GetState() - fmt.Printf("\t!!! State: %s\n", state.String()) - if state == connectivity.Shutdown { - fmt.Printf("\t\t!!! Was shutdown: %s\n", state.String()) - return - } - } - }() + waitGroup.Wait() - //// Schedule the function to run after the timeout - //time.AfterFunc(3*time.Millisecond, func() { - // fmt.Println("InvalidateExecutionAPIClient") - // connectionFactory.InvalidateExecutionAPIClient(clientAddress) - //}) - fmt.Println("Pings` started") - for i := 1; i <= 100; i++ { - //fmt.Printf("Ping %d %s\n", i, clientConn.GetState().String()) - fmt.Printf("Ping %d\n", i) - _, err = client.Ping(ctx, req) - //fmt.Printf("State %s\n", clientConn.GetState().String()) - } - fmt.Println("Pings` finished") - connectionFactory.InvalidateExecutionAPIClient(clientAddress) + assert.Equal(t, reqCompleted.Load(), respSent.Load()) + }) } // node mocks a flow node that runs a GRPC server From f906baa6640ddb3fee675072119f2726a8f02264 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 30 Jun 2023 22:58:38 +0300 Subject: [PATCH 198/815] Added comments --- engine/access/rpc/backend/connection_factory.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 8a914893502..84c331f1599 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -256,20 +256,26 @@ func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) } } +// Close closes the CachedClient connection func (s *CachedClient) Close() { s.mutex.Lock() conn := s.ClientConn s.ClientConn = nil s.mutex.Unlock() + if conn == nil { return } + // Mark the connection for closure s.closeRequested.Store(true) + // If there are ongoing requests, wait for them to complete if s.requestCounter.Load() > 0 { <-s.done } + + // Close the connection conn.Close() } @@ -287,7 +293,7 @@ func getGRPCAddress(address string, grpcPort uint) (string, error) { return grpcAddress, nil } -// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. +// createRequestWatcherInterceptor creates a request watcher interceptor to wait for unfinished request before close func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { requestWatcherInterceptor := func( ctx context.Context, @@ -298,16 +304,21 @@ func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClien invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { + // Prevent new request from being sent if the connection is marked for closure if cachedClient.closeRequested.Load() { return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) } + // Increment the request counter to track ongoing requests cachedClient.requestCounter.Add(1) + // Invoke the actual RPC method err := invoker(ctx, method, req, reply, cc, opts...) + // Decrement the request counter and check if the connection is marked for closure and no more ongoing requests count := cachedClient.requestCounter.Add(-1) if cachedClient.closeRequested.Load() && count == 0 { + // Signal that all ongoing requests have completed select { case cachedClient.done <- struct{}{}: default: From 5d8f69d07c2a7308ae6bdcc943dd29b9e1f07bd6 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 4 Jul 2023 13:04:56 +0300 Subject: [PATCH 199/815] Added retries --- cmd/access/node_builder/access_node_builder.go | 6 +++++- engine/access/rpc/backend/connection_factory.go | 4 ++++ engine/access/rpc/backend/connection_factory_test.go | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index d0b7696c7a1..84554dda002 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -165,6 +165,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { Enabled: false, RestoreTimeout: 60 * time.Second, MaxFailures: 5, + MaxRetries: 1, }, }, stateStreamConf: state_stream.Config{ @@ -690,7 +691,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "specifies whether the circuit breaker is enabled for collection and execution API clients.") flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "duration after which the circuit breaker will restore the connection to the client after closing it due to failures. Default value is 60s") flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxFailures, "circuit-breaker-max-failures", defaultConfig.rpcConf.CircuitBreakerConfig.MaxFailures, "maximum number of failed calls to the client that will cause the circuit breaker to close the connection. Default value is 5") - + flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRetries, "circuit-breaker-max-retries", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRetries, "maximum number of retries call to check if connection restored after timeout. Default value is 1") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") flags.StringVar(&builder.executionDataDir, "execution-data-dir", defaultConfig.executionDataDir, "directory to use for Execution Data database") @@ -758,6 +759,9 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { if builder.rpcConf.CircuitBreakerConfig.MaxFailures == 0 { return errors.New("circuit-breaker-max-failures must be greater than 0") } + if builder.rpcConf.CircuitBreakerConfig.MaxRetries == 0 { + return errors.New("circuit-breaker-max-retries must be greater than 0") + } } return nil diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 701f1458b4f..d9bca57aab8 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -84,10 +84,13 @@ type ConnectionFactoryImpl struct { // // MaxFailures specifies the maximum number of failed calls to the client that will cause the circuit breaker // to close the connection. +// +// MaxRetries specifies the maximum number of retries call to check if connection restored after timeout. type CircuitBreakerConfig struct { Enabled bool RestoreTimeout time.Duration MaxFailures uint32 + MaxRetries uint32 } type CachedClient struct { @@ -352,6 +355,7 @@ func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryCli // The number of maximum failures is checked before the circuit breaker goes to the Open state. return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, + MaxRequests: cf.CircuitBreakerConfig.MaxRetries, }) circuitBreakerInterceptor := func( diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 553948f0a93..ff746b93a98 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -436,6 +436,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, + MaxRetries: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } @@ -518,6 +519,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, + MaxRetries: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } From 0705c9a80a27cac30c6604f58a1cc0fb97b47253 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 20:08:38 -0400 Subject: [PATCH 200/815] using updated testutils.LibP2PNodeForMiddlewareFixture(t, 3) --- network/alsp/manager/manager_test.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 0b90e762fed..271069bd7ba 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -16,7 +16,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/alsp" @@ -299,14 +298,10 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio WithDecayFunc(fastDecayFunc), } - ids, nodes, mws, _, _ := testutils.GenerateIDsAndMiddlewares( - t, - 3, - unittest.Logger(), - unittest.NetworkCodec(), - unittest.NetworkSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector())) - sms := testutils.GenerateSubscriptionManagers(t, mws) - networkCfg := testutils.NetworkConfigFixture(t, unittest.Logger(), *ids[0], ids, mws[0], sms[0], p2p.WithAlspConfig(cfg)) + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) + victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) From f6573433edb56d92765f46184b128a14556ba448 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 20:36:41 -0400 Subject: [PATCH 201/815] disable p2p backoff so test passes --- network/alsp/manager/manager_test.go | 3 ++- network/internal/testutils/testUtil.go | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 271069bd7ba..eef83e576d3 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -298,7 +298,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio WithDecayFunc(fastDecayFunc), } - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3) + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, + p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 2457c7c6af7..2fd89fdb698 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -130,7 +130,6 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // []p2p.LibP2PNode - list of libp2p nodes created. // []observable.Observable - list of observables created for each node. func LibP2PNodeForMiddlewareFixture(t *testing.T, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode, []observable.Observable) { - libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) From 1795d3b0e8ad3ced1e1acda7527db0e83d6248cd Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 20:41:57 -0400 Subject: [PATCH 202/815] clean up --- network/alsp/manager/manager_test.go | 118 --------------------------- 1 file changed, 118 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index eef83e576d3..41b662c311f 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -458,124 +458,6 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Logf("finished test case: %s", testCase) }) } - - // simulates the victim node reporting the spammer node misbehavior 120 times - // to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after - // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - // the spammer is definitely disallow-listed. - //reportCount := 120 - //wg := sync.WaitGroup{} - //for i := 0; i < reportCount; i++ { - // wg.Add(1) - // // reports the misbehavior - // r := report // capture range variable - // go func() { - // defer wg.Done() - // - // con.ReportMisbehavior(r) - // }() - //} - // - //unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // - //// ensures that the spammer is disallow-listed by the victim - //// while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - //// it is allow-listed again. - //p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) - // - //// despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // - //record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - //require.True(t, ok) - //require.NotNil(t, record) - // - //// check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - //require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - //require.Equal(t, float64(1000), record.Decay) - //require.Equal(t, true, record.DisallowListed) - //require.Equal(t, uint64(1), record.CutoffCounter) - // - //// decay the disallow-listing penalty of the spammer node to zero. - //t.Logf("about to decay the disallow-listing penalty of the spammer node to zero") - //fastDecay = true - //t.Logf("decayed the disallow-listing penalty of the spammer node to zero") - // - //// after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) - // - //// all the nodes should be able to connect to each other again. - //p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - // - //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - //require.True(t, ok) - //require.NotNil(t, record) - // - //require.Equal(t, float64(0), record.Penalty) - //require.Equal(t, float64(1000), record.Decay) - //require.Equal(t, false, record.DisallowListed) - //require.Equal(t, uint64(1), record.CutoffCounter) - // - //// go back to regular decay to prepare for the next set of misbehavior reports. - //fastDecay = false - //t.Logf("about to report misbehavior again") - // - //// simulates the victim node reporting the spammer node misbehavior 120 times - //// to the network. As each report has the default penalty, ideally the spammer should be disallow-listed after - //// 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - //// the spammer is definitely disallow-listed. - //reportCount = 120 - //wg = sync.WaitGroup{} - //for i := 0; i < reportCount; i++ { - // wg.Add(1) - // // reports the misbehavior - // r := report // capture range variable - // go func() { - // defer wg.Done() - // - // con.ReportMisbehavior(r) - // }() - //} - // - //unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - // - //// while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - //// it is allow-listed again. - //p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) - // - //// despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[victimIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // - //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - //require.True(t, ok) - //require.NotNil(t, record) - // - //require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - //require.Equal(t, float64(1000), record.Decay) - //require.Equal(t, true, record.DisallowListed) - //require.Equal(t, uint64(2), record.CutoffCounter) - // - //// decay the disallow-listing penalty of the spammer node to zero. - //t.Logf("about to decay the disallow-listing penalty of the spammer node to zero (2nd time)") - //fastDecay = true - //t.Logf("decayed the disallow-listing penalty of the spammer node to zero (2nd time)") - // - //// after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - //p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 1*time.Second) - // - //// all the nodes should be able to connect to each other again. - //p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - // - //record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - //require.True(t, ok) - //require.NotNil(t, record) - // - //require.Equal(t, float64(0), record.Penalty) - //require.Equal(t, float64(1000), record.Decay) - //require.Equal(t, false, record.DisallowListed) - //require.Equal(t, uint64(2), record.CutoffCounter) } // TestMisbehaviorReportMetrics tests the recording of misbehavior report metrics. From e784ddef9185182ad6b18ed3d0f22390a73e7ed3 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 20:51:26 -0400 Subject: [PATCH 203/815] reduce wait time for connecting --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 41b662c311f..12df0b431ef 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -430,7 +430,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Logf("%s decayed the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 4*time.Second) + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 3*time.Second) now = time.Now() t.Logf("%s spammer node is able to connect to the victim node again", now.Format(time.RFC3339)) From 812f3314bf8c32f01a2ddd412aee182211aa9b6c Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 21:24:35 -0400 Subject: [PATCH 204/815] remove data driven table --- network/alsp/manager/manager_test.go | 192 ++++++++++++--------------- 1 file changed, 88 insertions(+), 104 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 12df0b431ef..a8d83c60950 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -330,133 +330,117 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // creates a misbehavior report for the spammer report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) - // loop through the list of expected penalties and ensure that the spammer is disallow-listed after each report for - // the correct amount of time. - - // data driven test of expected penalties - key is string describing the test case, value is a list of expected int penalties - // for each report. - - expectedDecaysMap := map[string][]int{ - "10x decrease in decay": {1000, 100, 10, 1, 1, 1}, // list of expected decay values after each disallow listing - - //"1x decrease in decay": {1000, 100, 100, 100}, // list of expected decay values after each disallow listing - } - - for testCase, expectedDecays := range expectedDecaysMap { - - // reset cutoff counter before each test case - t.Log("resetting cutoff counter") - expectedCutoffCounter := 0 - - // creat subtest for each expected decay list - t.Run(testCase, func(t *testing.T) { - t.Logf("starting test case: %s", testCase) - // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected - for i := range expectedDecays { - t.Logf("starting iteration %d with expected decay %d", i, expectedDecays[i]) + expectedDecays := []int{1000, 100, 10, 1, 1, 1} // list of expected decay values after each disallow listing + + t.Log("resetting cutoff counter") + expectedCutoffCounter := 0 + + // creat subtest for each expected decay list + //t.Run(testCase, func(t *testing.T) { + // t.Logf("starting test case: %s", testCase) + // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected + for i := range expectedDecays { + t.Logf("starting iteration %d with expected decay %d", i, expectedDecays[i]) + + // reset the decay function to the default + fastDecay = false + + // simulates the victim node reporting the spammer node misbehavior 10 times + // as each report has the default penalty, ideally the spammer should be disallow-listed after + // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that + // the spammer is definitely disallow-listed. + reportCount := 120 + wg := sync.WaitGroup{} + for i := 0; i < reportCount; i++ { + wg.Add(1) + // reports the misbehavior + r := report // capture range variable + go func() { + defer wg.Done() - // reset the decay function to the default - fastDecay = false + con.ReportMisbehavior(r) + }() + } - // simulates the victim node reporting the spammer node misbehavior 10 times - // as each report has the default penalty, ideally the spammer should be disallow-listed after - // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that - // the spammer is definitely disallow-listed. - reportCount := 120 - wg := sync.WaitGroup{} - for i := 0; i < reportCount; i++ { - wg.Add(1) - // reports the misbehavior - r := report // capture range variable - go func() { - defer wg.Done() + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") - con.ReportMisbehavior(r) - }() - } + expectedCutoffCounter++ // cutoff counter is expected to be incremented after each disallow listing - unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") + // ensures that the spammer is disallow-listed by the victim + // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless + // it is allow-listed again. + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) - expectedCutoffCounter++ // cutoff counter is expected to be incremented after each disallow listing + // ensures that the spammer is not disallow-listed by the honest node + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[spammerIndex]}, 1*time.Millisecond, 100*time.Millisecond) - // ensures that the spammer is disallow-listed by the victim - // while the spammer node is disallow-listed, it cannot connect to the victim node. Also, the victim node cannot directly dial and connect to the spammer node, unless - // it is allow-listed again. - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 3*time.Second) + // ensures that the spammer is disallow-listed for the expected amount of time - // ensures that the spammer is not disallow-listed by the honest node - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[spammerIndex]}, 1*time.Millisecond, 100*time.Millisecond) + record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) - // ensures that the spammer is disallow-listed for the expected amount of time + // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + require.Equal(t, float64(expectedDecays[i]), record.Decay) + require.Equal(t, true, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + penalty1 := record.Penalty - require.Equal(t, float64(expectedDecays[i]), record.Decay) - require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + // wait for one heartbeat to be processed. + time.Sleep(1 * time.Second) - penalty1 := record.Penalty + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) - // wait for one heartbeat to be processed. - time.Sleep(1 * time.Second) + // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. + require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + require.Equal(t, float64(expectedDecays[i]), record.Decay) + require.Equal(t, true, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + penalty2 := record.Penalty - // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + // check that the penalty has decayed by the expected amount in one heartbeat + require.Equal(t, float64(expectedDecays[i]), penalty2-penalty1) - require.Equal(t, float64(expectedDecays[i]), record.Decay) - require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - penalty2 := record.Penalty + now := time.Now() - // check that the penalty has decayed by the expected amount in one heartbeat - require.Equal(t, float64(expectedDecays[i]), penalty2-penalty1) + // decay the disallow-listing penalty of the spammer node to zero. + t.Logf("%s about to decay the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) + fastDecay = true + // add time stamp to t.Logf() message to ensure that the message is printed in the correct order + now = time.Now() + t.Logf("%s decayed the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) - now := time.Now() + // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. + p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 3*time.Second) + now = time.Now() + t.Log("spammer node is able to connect to the victim node again") - // decay the disallow-listing penalty of the spammer node to zero. - t.Logf("%s about to decay the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) - fastDecay = true - // add time stamp to t.Logf() message to ensure that the message is printed in the correct order - now = time.Now() - t.Logf("%s decayed the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) + // all the nodes should be able to connect to each other again. + p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) - // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. - p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 3*time.Second) - now = time.Now() - t.Logf("%s spammer node is able to connect to the victim node again", now.Format(time.RFC3339)) + record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) + require.True(t, ok) + require.NotNil(t, record) - // all the nodes should be able to connect to each other again. - p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) + expectedDecayAfterCutoff := expectedDecays[i] + if i < len(expectedDecays)-1 { + expectedDecayAfterCutoff = expectedDecays[i+1] + } - record, ok = victimSpamRecordCache.Get(ids[spammerIndex].NodeID) - require.True(t, ok) - require.NotNil(t, record) + require.Equal(t, float64(0), record.Penalty) + require.Equal(t, float64(expectedDecayAfterCutoff), record.Decay) + require.Equal(t, false, record.DisallowListed) + require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - expectedDecayAfterCutoff := expectedDecays[i] - if i < len(expectedDecays)-1 { - expectedDecayAfterCutoff = expectedDecays[i+1] - } - - require.Equal(t, float64(0), record.Penalty) - require.Equal(t, float64(expectedDecayAfterCutoff), record.Decay) - require.Equal(t, false, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) - - // go back to regular decay to prepare for the next set of misbehavior reports. - fastDecay = false - t.Logf("about to report misbehavior again") - } - t.Logf("finished test case: %s", testCase) - }) + // go back to regular decay to prepare for the next set of misbehavior reports. + fastDecay = false + t.Logf("about to report misbehavior again") } } From 26e950f74a10a36000dc982c43dd790a0891d38e Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 6 Jul 2023 21:32:36 -0400 Subject: [PATCH 205/815] removed timestamp from logs --- network/alsp/manager/manager_test.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index a8d83c60950..95470478d6e 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -270,18 +270,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio cfg := managerCfgFixture(t) fastDecay := false fastDecayFunc := func(record model.ProtocolSpamRecord) float64 { - now := time.Now() - - t.Logf("%s decayFuc called with record: %+v", now.Format(time.RFC3339), record) + t.Logf("decayFuc called with record: %+v", record) if fastDecay { - now = time.Now() // decay to zero in a single heart beat - t.Logf("%s fastDecay is true, so decay to zero", now.Format(time.RFC3339)) + t.Log("fastDecay is true, so decay to zero") return 0 } else { - now = time.Now() // decay as usual - t.Logf("%s fastDecay is false, so decay as usual", now.Format(time.RFC3339)) + t.Log("fastDecay is false, so decay as usual") return math.Min(record.Penalty+record.Decay, 0) } } @@ -335,9 +331,6 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio t.Log("resetting cutoff counter") expectedCutoffCounter := 0 - // creat subtest for each expected decay list - //t.Run(testCase, func(t *testing.T) { - // t.Logf("starting test case: %s", testCase) // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected for i := range expectedDecays { t.Logf("starting iteration %d with expected decay %d", i, expectedDecays[i]) @@ -407,18 +400,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // check that the penalty has decayed by the expected amount in one heartbeat require.Equal(t, float64(expectedDecays[i]), penalty2-penalty1) - now := time.Now() - // decay the disallow-listing penalty of the spammer node to zero. - t.Logf("%s about to decay the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) + t.Log("about to decay the disallow-listing penalty of the spammer node to zero") fastDecay = true - // add time stamp to t.Logf() message to ensure that the message is printed in the correct order - now = time.Now() - t.Logf("%s decayed the disallow-listing penalty of the spammer node to zero", now.Format(time.RFC3339)) + t.Log("decayed the disallow-listing penalty of the spammer node to zero") // after serving the disallow-listing period, the spammer should be able to connect to the victim node again. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[victimIndex]}, 1*time.Millisecond, 3*time.Second) - now = time.Now() t.Log("spammer node is able to connect to the victim node again") // all the nodes should be able to connect to each other again. @@ -440,7 +428,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // go back to regular decay to prepare for the next set of misbehavior reports. fastDecay = false - t.Logf("about to report misbehavior again") + t.Log("about to report misbehavior again") } } From 318fdb1fe9e17633fc916743e7da26acbe0fd947 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Thu, 6 Jul 2023 19:42:51 +0200 Subject: [PATCH 206/815] Fix Version Beacon event conversion --- model/convert/service_event.go | 22 +++++++--------------- utils/unittest/service_events_fixtures.go | 4 ++-- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/model/convert/service_event.go b/model/convert/service_event.go index b3e9902e1e4..7a406ed3e71 100644 --- a/model/convert/service_event.go +++ b/model/convert/service_event.go @@ -1183,22 +1183,14 @@ func convertSemverVersion(structVal cadence.Struct) ( return "", err } - version := fmt.Sprintf( - "%d.%d.%d%s", - major, - minor, - patch, - preRelease, - ) - _, err = semver.NewVersion(version) - if err != nil { - return "", fmt.Errorf( - "invalid semver %s: %w", - version, - err, - ) + version := semver.Version{ + Major: int64(major), + Minor: int64(minor), + Patch: int64(patch), + PreRelease: semver.PreRelease(preRelease), } - return version, nil + + return version.String(), nil } diff --git a/utils/unittest/service_events_fixtures.go b/utils/unittest/service_events_fixtures.go index 8ae5a5b6a62..57d236589c7 100644 --- a/utils/unittest/service_events_fixtures.go +++ b/utils/unittest/service_events_fixtures.go @@ -165,7 +165,7 @@ func VersionBeaconFixtureByChainID(chain flow.ChainID) (flow.Event, *flow.Versio VersionBoundaries: []flow.VersionBoundary{ { BlockHeight: 44, - Version: "2.13.7", + Version: "2.13.7-test", }, }, Sequence: 5, @@ -671,7 +671,7 @@ func createVersionBeaconEvent() cadence.Event { cadence.UInt8(7), // preRelease - cadence.NewOptional(cadence.String("")), + cadence.NewOptional(cadence.String("test")), }).WithType(semverType) versionBoundary := cadence.NewStruct([]cadence.Value{ From f253727fd058ad99e220adb4aed5225b8ad9b8c2 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Fri, 7 Jul 2023 13:44:33 +0200 Subject: [PATCH 207/815] additional tests for version beacon conversion --- model/convert/service_event.go | 5 + model/convert/service_event_test.go | 175 ++++++++++++++++++++++ utils/unittest/service_events_fixtures.go | 16 +- 3 files changed, 188 insertions(+), 8 deletions(-) diff --git a/model/convert/service_event.go b/model/convert/service_event.go index 7a406ed3e71..81c1d9cb9a4 100644 --- a/model/convert/service_event.go +++ b/model/convert/service_event.go @@ -965,6 +965,11 @@ func convertServiceEventVersionBeacon(event flow.Event) (*flow.ServiceEvent, err return nil, err } + // a converted version beacon event should also be valid + if err := versionBeacon.Validate(); err != nil { + return nil, fmt.Errorf("invalid VersionBeacon event: %w", err) + } + // create the service event serviceEvent := &flow.ServiceEvent{ Type: flow.ServiceEventVersionBeacon, diff --git a/model/convert/service_event_test.go b/model/convert/service_event_test.go index 6652f3e3b8e..88ba8c4d3ca 100644 --- a/model/convert/service_event_test.go +++ b/model/convert/service_event_test.go @@ -8,7 +8,9 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" @@ -164,3 +166,176 @@ func TestDecodeCadenceValue(t *testing.T) { ) } } + +func TestVersionBeaconEventConversion(t *testing.T) { + versionBoundaryType := unittest.NewNodeVersionBeaconVersionBoundaryStructType() + semverType := unittest.NewNodeVersionBeaconSemverStructType() + eventType := unittest.NewNodeVersionBeaconVersionBeaconEventType() + + type vbTestCase struct { + name string + event cadence.Event + converted *flow.VersionBeacon + expectAndHandleError func(t *testing.T, err error) + } + + runVersionBeaconTestCase := func(t *testing.T, test vbTestCase) { + chainID := flow.Emulator + t.Run(test.name, func(t *testing.T) { + events, err := systemcontracts.ServiceEventsForChain(chainID) + if err != nil { + panic(err) + } + + event := unittest.EventFixture(events.VersionBeacon.EventType(), 1, 1, unittest.IdentifierFixture(), 0) + event.Payload, err = ccf.Encode(test.event) + require.NoError(t, err) + + // convert Cadence types to Go types + serviceEvent, err := convert.ServiceEvent(chainID, event) + + if test.expectAndHandleError != nil { + require.Error(t, err) + test.expectAndHandleError(t, err) + return + } + + require.NoError(t, err) + require.NotNil(t, event) + + // cast event type to version beacon + actual, ok := serviceEvent.Event.(*flow.VersionBeacon) + require.True(t, ok) + + require.Equal(t, test.converted, actual) + }) + } + + runVersionBeaconTestCase(t, + vbTestCase{ + name: "with pre-release", + event: cadence.NewEvent( + []cadence.Value{ + // versionBoundaries + cadence.NewArray( + []cadence.Value{ + cadence.NewStruct( + []cadence.Value{ + // blockHeight + cadence.UInt64(44), + // version + cadence.NewStruct( + []cadence.Value{ + // major + cadence.UInt8(2), + // minor + cadence.UInt8(13), + // patch + cadence.UInt8(7), + // preRelease + cadence.NewOptional(cadence.String("test")), + }, + ).WithType(semverType), + }, + ).WithType(versionBoundaryType), + }, + ).WithType(cadence.NewVariableSizedArrayType(versionBoundaryType)), + // sequence + cadence.UInt64(5), + }, + ).WithType(eventType), + converted: &flow.VersionBeacon{ + VersionBoundaries: []flow.VersionBoundary{ + { + BlockHeight: 44, + Version: "2.13.7-test", + }, + }, + Sequence: 5, + }, + }, + ) + + runVersionBeaconTestCase(t, + vbTestCase{ + name: "without pre-release", + event: cadence.NewEvent( + []cadence.Value{ + // versionBoundaries + cadence.NewArray( + []cadence.Value{ + cadence.NewStruct( + []cadence.Value{ + // blockHeight + cadence.UInt64(44), + // version + cadence.NewStruct( + []cadence.Value{ + // major + cadence.UInt8(2), + // minor + cadence.UInt8(13), + // patch + cadence.UInt8(7), + // preRelease + cadence.NewOptional(nil), + }, + ).WithType(semverType), + }, + ).WithType(versionBoundaryType), + }, + ).WithType(cadence.NewVariableSizedArrayType(versionBoundaryType)), + // sequence + cadence.UInt64(5), + }, + ).WithType(eventType), + converted: &flow.VersionBeacon{ + VersionBoundaries: []flow.VersionBoundary{ + { + BlockHeight: 44, + Version: "2.13.7", + }, + }, + Sequence: 5, + }, + }, + ) + runVersionBeaconTestCase(t, + vbTestCase{ + name: "invalid pre-release", + event: cadence.NewEvent( + []cadence.Value{ + // versionBoundaries + cadence.NewArray( + []cadence.Value{ + cadence.NewStruct( + []cadence.Value{ + // blockHeight + cadence.UInt64(44), + // version + cadence.NewStruct( + []cadence.Value{ + // major + cadence.UInt8(2), + // minor + cadence.UInt8(13), + // patch + cadence.UInt8(7), + // preRelease + cadence.NewOptional(cadence.String("/slashes.not.allowed")), + }, + ).WithType(semverType), + }, + ).WithType(versionBoundaryType), + }, + ).WithType(cadence.NewVariableSizedArrayType(versionBoundaryType)), + // sequence + cadence.UInt64(5), + }, + ).WithType(eventType), + expectAndHandleError: func(t *testing.T, err error) { + require.ErrorContains(t, err, "failed to validate pre-release") + }, + }, + ) +} diff --git a/utils/unittest/service_events_fixtures.go b/utils/unittest/service_events_fixtures.go index 57d236589c7..74e5600c154 100644 --- a/utils/unittest/service_events_fixtures.go +++ b/utils/unittest/service_events_fixtures.go @@ -656,9 +656,9 @@ func createEpochCommittedEvent() cadence.Event { } func createVersionBeaconEvent() cadence.Event { - versionBoundaryType := newNodeVersionBeaconVersionBoundaryStructType() + versionBoundaryType := NewNodeVersionBeaconVersionBoundaryStructType() - semverType := newNodeVersionBeaconSemverStructType() + semverType := NewNodeVersionBeaconSemverStructType() semver := cadence.NewStruct([]cadence.Value{ // major @@ -690,7 +690,7 @@ func createVersionBeaconEvent() cadence.Event { // sequence cadence.UInt64(5), - }).WithType(newNodeVersionBeaconVersionBeaconEventType()) + }).WithType(NewNodeVersionBeaconVersionBeaconEventType()) } func newFlowClusterQCVoteStructType() cadence.Type { @@ -943,7 +943,7 @@ func newFlowClusterQCClusterQCStructType() *cadence.StructType { } } -func newNodeVersionBeaconVersionBeaconEventType() *cadence.EventType { +func NewNodeVersionBeaconVersionBeaconEventType() *cadence.EventType { // A.01cf0e2f2f715450.NodeVersionBeacon.VersionBeacon @@ -956,7 +956,7 @@ func newNodeVersionBeaconVersionBeaconEventType() *cadence.EventType { Fields: []cadence.Field{ { Identifier: "versionBoundaries", - Type: cadence.NewVariableSizedArrayType(newNodeVersionBeaconVersionBoundaryStructType()), + Type: cadence.NewVariableSizedArrayType(NewNodeVersionBeaconVersionBoundaryStructType()), }, { Identifier: "sequence", @@ -966,7 +966,7 @@ func newNodeVersionBeaconVersionBeaconEventType() *cadence.EventType { } } -func newNodeVersionBeaconVersionBoundaryStructType() *cadence.StructType { +func NewNodeVersionBeaconVersionBoundaryStructType() *cadence.StructType { // A.01cf0e2f2f715450.NodeVersionBeacon.VersionBoundary @@ -983,13 +983,13 @@ func newNodeVersionBeaconVersionBoundaryStructType() *cadence.StructType { }, { Identifier: "version", - Type: newNodeVersionBeaconSemverStructType(), + Type: NewNodeVersionBeaconSemverStructType(), }, }, } } -func newNodeVersionBeaconSemverStructType() *cadence.StructType { +func NewNodeVersionBeaconSemverStructType() *cadence.StructType { // A.01cf0e2f2f715450.NodeVersionBeacon.Semver From f63d05903b4671bdd814ae5487bfcb36efe95db0 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 7 Jul 2023 15:03:34 +0300 Subject: [PATCH 208/815] Added node communicator. --- engine/access/rpc/backend/backend.go | 34 ++-- engine/access/rpc/backend/backend_accounts.go | 71 ++++---- engine/access/rpc/backend/backend_events.go | 74 ++++---- engine/access/rpc/backend/backend_test.go | 1 + .../rpc/backend/backend_transactions.go | 164 +++++++++--------- .../access/rpc/backend/connection_factory.go | 2 +- .../access/rpc/backend/node_communicator.go | 90 ++++++++++ 7 files changed, 265 insertions(+), 171 deletions(-) create mode 100644 engine/access/rpc/backend/node_communicator.go diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 8cf6d246791..2c3fdcc7e0d 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -117,8 +117,8 @@ func New( archivePorts[idx] = port } - // create configured node selection factory to be used in sub-backend logic - nodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled} + // create node communicator, that will be used in sub-backend logic for interacting with API calls + nodeCommunicator := NewNodeCommunicator(circuitBreakerEnabled) b := &Backend{ state: state, @@ -133,7 +133,7 @@ func New( loggedScripts: loggedScripts, archiveAddressList: archiveAddressList, archivePorts: archivePorts, - nodeSelectorFactory: nodeSelectorFactory, + nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, }, backendTransactions: backendTransactions{ staticCollectionRPC: collectionRPC, @@ -149,16 +149,16 @@ func New( connFactory: connFactory, previousAccessNodes: historicalAccessNodes, log: log, - nodeSelectorFactory: nodeSelectorFactory, + nodeCommunicator: nodeCommunicator, }, backendEvents: backendEvents{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - maxHeightRange: maxHeightRange, - nodeSelectorFactory: nodeSelectorFactory, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + maxHeightRange: maxHeightRange, + nodeCommunicator: nodeCommunicator, }, backendBlockHeaders: backendBlockHeaders{ headers: headers, @@ -169,12 +169,12 @@ func New( state: state, }, backendAccounts: backendAccounts{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - nodeSelectorFactory: nodeSelectorFactory, + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + nodeCommunicator: nodeCommunicator, }, backendExecutionResults: backendExecutionResults{ executionResults: executionResults, diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index f3fd12c82f9..c1eaa1e98e2 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/hashicorp/go-multierror" execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc/codes" @@ -18,12 +17,12 @@ import ( ) type backendAccounts struct { - state protocol.State - headers storage.Headers - executionReceipts storage.ExecutionReceipts - connFactory ConnectionFactory - log zerolog.Logger - nodeSelectorFactory NodeSelectorFactory + state protocol.State + headers storage.Headers + executionReceipts storage.ExecutionReceipts + connFactory ConnectionFactory + log zerolog.Logger + nodeCommunicator *NodeCommunicator } func (b *backendAccounts) GetAccount(ctx context.Context, address flow.Address) (*flow.Account, error) { @@ -108,37 +107,39 @@ func (b *backendAccounts) getAccountAtBlockID( // other ENs are logged and swallowed. If all ENs fail to return a valid response, then an // error aggregating all failures is returned. func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { - var errors *multierror.Error - - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - // TODO: use the GRPC Client interceptor - start := time.Now() - - resp, err := b.tryGetAccount(ctx, execNode, req) - duration := time.Since(start) - if err == nil { - // return if any execution node replied successfully - b.log.Debug(). - Str("execution_node", execNode.String()). + var resp *execproto.GetAccountAtBlockIDResponse + errToReturn := b.nodeCommunicator.CallAvailableExecutionNode( + execNodes, + func(node *flow.Identity) error { + var err error + // TODO: use the GRPC Client interceptor + start := time.Now() + + resp, err = b.tryGetAccount(ctx, node, req) + duration := time.Since(start) + if err == nil { + // return if any execution node replied successfully + b.log.Debug(). + Str("execution_node", node.String()). + Hex("block_id", req.GetBlockId()). + Hex("address", req.GetAddress()). + Int64("rtt_ms", duration.Milliseconds()). + Msg("Successfully got account info") + return nil + } + b.log.Error(). + Str("execution_node", node.String()). Hex("block_id", req.GetBlockId()). Hex("address", req.GetAddress()). Int64("rtt_ms", duration.Milliseconds()). - Msg("Successfully got account info") - return resp, nil - } - b.log.Error(). - Str("execution_node", execNode.String()). - Hex("block_id", req.GetBlockId()). - Hex("address", req.GetAddress()). - Int64("rtt_ms", duration.Milliseconds()). - Err(err). - Msg("failed to execute GetAccount") - errors = multierror.Append(errors, err) - } - - return nil, errors.ErrorOrNil() + Err(err). + Msg("failed to execute GetAccount") + return err + }, + nil, + ) + + return resp, errToReturn } func (b *backendAccounts) tryGetAccount(ctx context.Context, execNode *flow.Identity, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index b82abf0b8c1..38bd723a077 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -7,7 +7,6 @@ import ( "fmt" "time" - "github.com/hashicorp/go-multierror" execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc/codes" @@ -21,13 +20,13 @@ import ( ) type backendEvents struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - maxHeightRange uint - nodeSelectorFactory NodeSelectorFactory + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + maxHeightRange uint + nodeCommunicator *NodeCommunicator } // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and @@ -210,34 +209,37 @@ func verifyAndConvertToAccessEvents( func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { - var errors *multierror.Error - - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - - // try to get events from one of the execution nodes - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - start := time.Now() - resp, err := b.tryGetEvents(ctx, execNode, req) - duration := time.Since(start) - - logger := b.log.With(). - Str("execution_node", execNode.String()). - Str("event", req.GetType()). - Int("blocks", len(req.BlockIds)). - Int64("rtt_ms", duration.Milliseconds()). - Logger() - - if err == nil { - // return if any execution node replied successfully - logger.Debug().Msg("Successfully got events") - return resp, execNode, nil - } - - logger.Err(err).Msg("failed to execute GetEvents") - - errors = multierror.Append(errors, err) - } - return nil, nil, errors.ErrorOrNil() + var resp *execproto.GetEventsForBlockIDsResponse + var execNode *flow.Identity + errToReturn := b.nodeCommunicator.CallAvailableExecutionNode( + execNodes, + func(node *flow.Identity) error { + var err error + start := time.Now() + resp, err = b.tryGetEvents(ctx, node, req) + duration := time.Since(start) + + logger := b.log.With(). + Str("execution_node", node.String()). + Str("event", req.GetType()). + Int("blocks", len(req.BlockIds)). + Int64("rtt_ms", duration.Milliseconds()). + Logger() + + if err == nil { + // return if any execution node replied successfully + logger.Debug().Msg("Successfully got events") + execNode = node + return nil + } + + logger.Err(err).Msg("failed to execute GetEvents") + return err + }, + nil, + ) + + return resp, execNode, errToReturn } func (b *backendEvents) tryGetEvents(ctx context.Context, diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 493d5be069e..be1024ec4bd 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -2372,6 +2372,7 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { suite.log, DefaultSnapshotHistoryLimit, []string{fullArchiveAddress}, + false, ) // mock parameters diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index de5cc4f878c..b2abff13eb1 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -6,7 +6,6 @@ import ( "fmt" "time" - "github.com/hashicorp/go-multierror" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/entities" execproto "github.com/onflow/flow/protobuf/go/flow/execution" @@ -38,7 +37,7 @@ type backendTransactions struct { connFactory ConnectionFactory previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger - nodeSelectorFactory NodeSelectorFactory + nodeCommunicator *NodeCommunicator } // SendTransaction forwards the transaction to the collection node @@ -89,27 +88,24 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T return fmt.Errorf("failed to determine collection node for tx %x: %w", tx, err) } - var sendErrors *multierror.Error + var sendError error logAnyError := func() { - err = sendErrors.ErrorOrNil() - if err != nil { + if sendError != nil { b.log.Info().Err(err).Msg("failed to send transactions to collector nodes") } } defer logAnyError() - collNodeSelector := b.nodeSelectorFactory.SelectCollectionNodes(collNodes) - // try sending the transaction to one of the chosen collection nodes - for colNode := collNodeSelector.Next(); colNode != nil; colNode = collNodeSelector.Next() { - err = b.sendTransactionToCollector(ctx, tx, colNode.Address) - if err == nil { - return nil + sendError = b.nodeCommunicator.CallAvailableConnectionNode(collNodes, func(node *flow.Identity) error { + err = b.sendTransactionToCollector(ctx, tx, node.Address) + if err != nil { + return err } - sendErrors = multierror.Append(sendErrors, err) - } + return nil + }) - return sendErrors.ErrorOrNil() + return sendError } // chooseCollectionNodes finds a random subset of size sampleSize of collection node addresses from the @@ -768,35 +764,36 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionResultRequest, ) (*execproto.GetTransactionResultResponse, error) { - var errs *multierror.Error - logAnyError := func() { - errToReturn := errs.ErrorOrNil() + var errToReturn error + + defer func() { if errToReturn != nil { b.log.Info().Err(errToReturn).Msg("failed to get transaction result from execution nodes") } - } - defer logAnyError() - - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) + }() - // try to execute the script on one of the execution nodes - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - resp, err := b.tryGetTransactionResult(ctx, execNode, req) - if err == nil { - b.log.Debug(). - Str("execution_node", execNode.String()). - Hex("block_id", req.GetBlockId()). - Hex("transaction_id", req.GetTransactionId()). - Msg("Successfully got transaction results from any node") - return resp, nil - } - if status.Code(err) == codes.NotFound { - return nil, err - } - errs = multierror.Append(errs, err) - } + var resp *execproto.GetTransactionResultResponse + errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + execNodes, + func(node *flow.Identity) error { + var err error + resp, err = b.tryGetTransactionResult(ctx, node, req) + if err == nil { + b.log.Debug(). + Str("execution_node", node.String()). + Hex("block_id", req.GetBlockId()). + Hex("transaction_id", req.GetTransactionId()). + Msg("Successfully got transaction results from any node") + return nil + } + return err + }, + func(err error) bool { + return status.Code(err) == codes.NotFound + }, + ) - return nil, errs.ErrorOrNil() + return resp, errToReturn } func (b *backendTransactions) tryGetTransactionResult( @@ -823,12 +820,12 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionsByBlockIDRequest, ) (*execproto.GetTransactionResultsResponse, error) { - var errs *multierror.Error + var errToReturn error defer func() { // log the errors - if err := errs.ErrorOrNil(); err != nil { - b.log.Err(errs).Msg("failed to get transaction results from execution nodes") + if errToReturn != nil { + b.log.Err(errToReturn).Msg("failed to get transaction results from execution nodes") } }() @@ -837,24 +834,27 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( return nil, errors.New("zero execution nodes") } - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - resp, err := b.tryGetTransactionResultsByBlockID(ctx, execNode, req) - if err == nil { - b.log.Debug(). - Str("execution_node", execNode.String()). - Hex("block_id", req.GetBlockId()). - Msg("Successfully got transaction results from any node") - return resp, nil - } - if status.Code(err) == codes.NotFound { - return nil, err - } - errs = multierror.Append(errs, err) - } + var resp *execproto.GetTransactionResultsResponse + errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + execNodes, + func(node *flow.Identity) error { + var err error + resp, err = b.tryGetTransactionResultsByBlockID(ctx, node, req) + if err == nil { + b.log.Debug(). + Str("execution_node", node.String()). + Hex("block_id", req.GetBlockId()). + Msg("Successfully got transaction results from any node") + return nil + } + return err + }, + func(err error) bool { + return status.Code(err) == codes.NotFound + }, + ) - return nil, errs.ErrorOrNil() + return resp, errToReturn } func (b *backendTransactions) tryGetTransactionResultsByBlockID( @@ -881,39 +881,39 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( execNodes flow.IdentityList, req *execproto.GetTransactionByIndexRequest, ) (*execproto.GetTransactionResultResponse, error) { - var errs *multierror.Error - logAnyError := func() { - errToReturn := errs.ErrorOrNil() + var errToReturn error + defer func() { if errToReturn != nil { b.log.Info().Err(errToReturn).Msg("failed to get transaction result from execution nodes") } - } - defer logAnyError() + }() if len(execNodes) == 0 { return nil, errors.New("zero execution nodes provided") } - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(execNodes) - - // try to execute the script on one of the execution nodes - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - resp, err := b.tryGetTransactionResultByIndex(ctx, execNode, req) - if err == nil { - b.log.Debug(). - Str("execution_node", execNode.String()). - Hex("block_id", req.GetBlockId()). - Uint32("index", req.GetIndex()). - Msg("Successfully got transaction results from any node") - return resp, nil - } - if status.Code(err) == codes.NotFound { - return nil, err - } - errs = multierror.Append(errs, err) - } + var resp *execproto.GetTransactionResultResponse + errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + execNodes, + func(node *flow.Identity) error { + var err error + resp, err = b.tryGetTransactionResultByIndex(ctx, node, req) + if err == nil { + b.log.Debug(). + Str("execution_node", node.String()). + Hex("block_id", req.GetBlockId()). + Uint32("index", req.GetIndex()). + Msg("Successfully got transaction results from any node") + return nil + } + return err + }, + func(err error) bool { + return status.Code(err) == codes.NotFound + }, + ) - return nil, errs.ErrorOrNil() + return resp, errToReturn } func (b *backendTransactions) tryGetTransactionResultByIndex( diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 93b74dc7989..3f459f4d674 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -116,7 +116,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor - if cf.CircuitBreakerConfig.Enabled { + if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { connInterceptors = append(connInterceptors, cf.createCircuitBreakerInterceptor()) } else { connInterceptors = append(connInterceptors, cf.createClientInvalidationInterceptor(address, clientType)) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go new file mode 100644 index 00000000000..70932764155 --- /dev/null +++ b/engine/access/rpc/backend/node_communicator.go @@ -0,0 +1,90 @@ +package backend + +import ( + "github.com/hashicorp/go-multierror" + "github.com/sony/gobreaker" + + "github.com/onflow/flow-go/model/flow" +) + +// maxFailedRequestCount represents the maximum number of failed requests before returning errors. +const maxFailedRequestCount = 3 + +// NodeCommunicator is responsible for calling available nodes in the backend. +type NodeCommunicator struct { + circuitBreakerEnabled bool + nodeSelectorFactory NodeSelectorFactory +} + +// NewNodeCommunicator creates a new instance of NodeCommunicator. +func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { + return &NodeCommunicator{ + circuitBreakerEnabled: circuitBreakerEnabled, + nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, + } +} + +// CallAvailableExecutionNode calls the provided function on the available execution nodes. +// It iterates through the execution nodes and executes the function. +// If an error occurs, it applies the custom error handler (if provided) and keeps track of the errors. +// If the error occurs in circuit breaker, it continues to the next execution node. +// If the maximum failed request count is reached, it returns the accumulated errors. +func (b *NodeCommunicator) CallAvailableExecutionNode( + nodes flow.IdentityList, + call func(node *flow.Identity) error, + customErrorHandler func(err error) bool, +) error { + var errs *multierror.Error + execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(nodes) + + for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { + err := call(execNode) + if err == nil { + return nil + } + + if customErrorHandler != nil && customErrorHandler(err) { + return err + } + + if err == gobreaker.ErrOpenState { + continue + } + + errs = multierror.Append(errs, err) + if len(errs.Errors) >= maxFailedRequestCount { + return errs.ErrorOrNil() + } + } + + return errs.ErrorOrNil() +} + +// CallAvailableConnectionNode calls the provided function on the available connection nodes. +// It iterates through the connection nodes and executes the function. +// If an error occurs, it keeps track of the errors. +// If the error occurs in circuit breaker, it continues to the next execution node. +// If the maximum failed request count is reached, it returns the accumulated errors. +func (b *NodeCommunicator) CallAvailableConnectionNode(nodes flow.IdentityList, call func(node *flow.Identity) error) error { + var errs *multierror.Error + + collNodeSelector := b.nodeSelectorFactory.SelectCollectionNodes(nodes) + + for colNode := collNodeSelector.Next(); colNode != nil; colNode = collNodeSelector.Next() { + err := call(colNode) + if err == nil { + return nil + } + + if err == gobreaker.ErrOpenState { + continue + } + + errs = multierror.Append(errs, err) + if len(errs.Errors) >= maxFailedRequestCount { + return errs.ErrorOrNil() + } + } + + return errs.ErrorOrNil() +} From c55bb5acb66c5accf10fbc74ef37f1eded93b734 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 7 Jul 2023 15:16:45 +0300 Subject: [PATCH 209/815] Rename MaxRetries to MaxRequest --- cmd/access/node_builder/access_node_builder.go | 8 ++++---- engine/access/rpc/backend/connection_factory.go | 6 +++--- engine/access/rpc/backend/connection_factory_test.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 672969e66b4..1400bf46873 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -164,7 +164,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { Enabled: false, RestoreTimeout: 60 * time.Second, MaxFailures: 5, - MaxRetries: 1, + MaxRequests: 1, }, }, stateStreamConf: state_stream.Config{ @@ -690,7 +690,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.BoolVar(&builder.rpcConf.CircuitBreakerConfig.Enabled, "circuit-breaker-enabled", defaultConfig.rpcConf.CircuitBreakerConfig.Enabled, "specifies whether the circuit breaker is enabled for collection and execution API clients.") flags.DurationVar(&builder.rpcConf.CircuitBreakerConfig.RestoreTimeout, "circuit-breaker-restore-timeout", defaultConfig.rpcConf.CircuitBreakerConfig.RestoreTimeout, "duration after which the circuit breaker will restore the connection to the client after closing it due to failures. Default value is 60s") flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxFailures, "circuit-breaker-max-failures", defaultConfig.rpcConf.CircuitBreakerConfig.MaxFailures, "maximum number of failed calls to the client that will cause the circuit breaker to close the connection. Default value is 5") - flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRetries, "circuit-breaker-max-retries", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRetries, "maximum number of retries call to check if connection restored after timeout. Default value is 1") + flags.Uint32Var(&builder.rpcConf.CircuitBreakerConfig.MaxRequests, "circuit-breaker-max-requests", defaultConfig.rpcConf.CircuitBreakerConfig.MaxRequests, "maximum number of requests to check if connection restored after timeout. Default value is 1") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", defaultConfig.executionDataSyncEnabled, "whether to enable the execution data sync protocol") flags.StringVar(&builder.executionDataDir, "execution-data-dir", defaultConfig.executionDataDir, "directory to use for Execution Data database") @@ -758,8 +758,8 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { if builder.rpcConf.CircuitBreakerConfig.MaxFailures == 0 { return errors.New("circuit-breaker-max-failures must be greater than 0") } - if builder.rpcConf.CircuitBreakerConfig.MaxRetries == 0 { - return errors.New("circuit-breaker-max-retries must be greater than 0") + if builder.rpcConf.CircuitBreakerConfig.MaxRequests == 0 { + return errors.New("circuit-breaker-max-requests must be greater than 0") } } diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 3f459f4d674..98e84ddffe2 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -85,12 +85,12 @@ type ConnectionFactoryImpl struct { // MaxFailures specifies the maximum number of failed calls to the client that will cause the circuit breaker // to close the connection. // -// MaxRetries specifies the maximum number of retries call to check if connection restored after timeout. +// MaxRequests specifies the maximum number of requests to check if connection restored after timeout. type CircuitBreakerConfig struct { Enabled bool RestoreTimeout time.Duration MaxFailures uint32 - MaxRetries uint32 + MaxRequests uint32 } type CachedClient struct { @@ -359,7 +359,7 @@ func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryCli // The number of maximum failures is checked before the circuit breaker goes to the Open state. return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, - MaxRequests: cf.CircuitBreakerConfig.MaxRetries, + MaxRequests: cf.CircuitBreakerConfig.MaxRequests, }) circuitBreakerInterceptor := func( diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index ff746b93a98..73a0be84f84 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -436,7 +436,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, - MaxRetries: 1, + MaxRequests: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } @@ -519,7 +519,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, - MaxRetries: 1, + MaxRequests: 1, RestoreTimeout: circuitBreakerRestoreTimeout, } From d80f54465bf1feb991534a7bcc578e34f36395fe Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 7 Jul 2023 16:14:54 +0300 Subject: [PATCH 210/815] Update engine/access/rpc/backend/connection_factory_test.go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- engine/access/rpc/backend/connection_factory_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 005d3bc2f55..4156ed480cd 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -484,13 +484,7 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { if err == nil { reqCompleted.Inc() } else { - if st, ok := status.FromError(err); ok { - if st.Code() == codes.Unavailable { - return - } - } - fmt.Println("!!! Failed: ", err) - t.Fail() + require.Equalf(t, codes.Unavailable, status.Code(err), "unexpected error: %v", err) } }() } From 0ce5a84e18d0e358827e6c4ae6cee1db9564b605 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 7 Jul 2023 21:00:15 +0300 Subject: [PATCH 211/815] fixed mistakes and typos --- engine/access/rpc/backend/backend_transactions.go | 2 +- engine/access/rpc/backend/node_communicator.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index b2abff13eb1..45e5b6eb1ef 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -97,7 +97,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T defer logAnyError() // try sending the transaction to one of the chosen collection nodes - sendError = b.nodeCommunicator.CallAvailableConnectionNode(collNodes, func(node *flow.Identity) error { + sendError = b.nodeCommunicator.CallAvailableCollectionNode(collNodes, func(node *flow.Identity) error { err = b.sendTransactionToCollector(ctx, tx, node.Address) if err != nil { return err diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 70932764155..00963457d86 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -60,12 +60,12 @@ func (b *NodeCommunicator) CallAvailableExecutionNode( return errs.ErrorOrNil() } -// CallAvailableConnectionNode calls the provided function on the available connection nodes. -// It iterates through the connection nodes and executes the function. +// CallAvailableCollectionNode calls the provided function on the available collection nodes. +// It iterates through the collection nodes and executes the function. // If an error occurs, it keeps track of the errors. -// If the error occurs in circuit breaker, it continues to the next execution node. +// If the error occurs in circuit breaker, it continues to the next collection node. // If the maximum failed request count is reached, it returns the accumulated errors. -func (b *NodeCommunicator) CallAvailableConnectionNode(nodes flow.IdentityList, call func(node *flow.Identity) error) error { +func (b *NodeCommunicator) CallAvailableCollectionNode(nodes flow.IdentityList, call func(node *flow.Identity) error) error { var errs *multierror.Error collNodeSelector := b.nodeSelectorFactory.SelectCollectionNodes(nodes) From 7b7658e3a5e317cb00ada966742ac2e465c2bfe9 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 11 Jul 2023 15:08:21 +0300 Subject: [PATCH 212/815] Fixed remarks --- .../access/rpc/backend/connection_factory.go | 142 ++++++------------ .../rpc/backend/connection_factory_test.go | 3 +- 2 files changed, 52 insertions(+), 93 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 74dc36af36a..114a73307a1 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -17,7 +17,6 @@ import ( "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/keepalive" @@ -70,32 +69,13 @@ type ConnectionFactoryImpl struct { type CachedClient struct { ClientConn *grpc.ClientConn Address string - mutex sync.Mutex timeout time.Duration - requestCounter *atomic.Int32 closeRequested *atomic.Bool - done chan struct{} -} - -func (s *CachedClient) UpdateConnection(conn *grpc.ClientConn) { - s.ClientConn = conn - s.closeRequested.Store(false) - s.requestCounter.Store(0) - s.done = make(chan struct{}, 1) -} - -func NewCachedClient(timeout time.Duration, address string) *CachedClient { - return &CachedClient{ - Address: address, - timeout: timeout, - requestCounter: atomic.NewInt32(0), - closeRequested: atomic.NewBool(false), - done: make(chan struct{}, 1), - } + wg sync.WaitGroup } // createConnection creates new gRPC connections to remote node -func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration) (*grpc.ClientConn, error) { +func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration, cachedClient *CachedClient) (*grpc.ClientConn, error) { if timeout == 0 { timeout = DefaultClientTimeout @@ -110,11 +90,8 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor - if cf.ConnectionsCache != nil { - if res, ok := cf.ConnectionsCache.Get(address); ok { - cachedClient := res.(*CachedClient) - connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) - } + if cachedClient != nil { + connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) } connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) @@ -137,45 +114,35 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D } func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { - var conn *grpc.ClientConn - var store *CachedClient - cacheHit := false cf.mutex.Lock() - if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { - cacheHit = true - store = res.(*CachedClient) - conn = store.ClientConn - } else { - store = NewCachedClient(timeout, grpcAddress) - - cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") - cf.ConnectionsCache.Add(grpcAddress, store) - if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionAddedToPool() - } + + store := &CachedClient{ + ClientConn: nil, + Address: grpcAddress, + timeout: timeout, + closeRequested: atomic.NewBool(false), + wg: sync.WaitGroup{}, } - cf.mutex.Unlock() - store.mutex.Lock() - defer store.mutex.Unlock() + cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") + cf.ConnectionsCache.Add(grpcAddress, store) + if cf.AccessMetrics != nil { + cf.AccessMetrics.ConnectionAddedToPool() + } - if conn == nil || conn.GetState() == connectivity.Shutdown { - var err error - conn, err = cf.createConnection(grpcAddress, timeout) - if err != nil { - return nil, err - } - store.UpdateConnection(conn) - if cf.AccessMetrics != nil { - if cacheHit { - cf.AccessMetrics.ConnectionFromPoolUpdated() - } - cf.AccessMetrics.NewConnectionEstablished() - cf.AccessMetrics.TotalConnectionsInPool(uint(cf.ConnectionsCache.Len()), cf.CacheSize) - } - } else if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionFromPoolReused() + conn, err := cf.createConnection(grpcAddress, timeout, store) + if err != nil { + return nil, err } + store.ClientConn = conn + + if cf.AccessMetrics != nil { + cf.AccessMetrics.NewConnectionEstablished() + cf.AccessMetrics.TotalConnectionsInPool(uint(cf.ConnectionsCache.Len()), cf.CacheSize) + } + + cf.mutex.Unlock() + return conn, nil } @@ -199,7 +166,7 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port return access.NewAccessAPIClient(conn), &noopCloser{}, err } - conn, err = cf.createConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) + conn, err = cf.createConnection(grpcAddress, cf.CollectionNodeGRPCTimeout, nil) if err != nil { return nil, nil, err } @@ -232,7 +199,7 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio return execution.NewExecutionAPIClient(conn), &noopCloser{}, nil } - conn, err = cf.createConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) + conn, err = cf.createConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout, nil) if err != nil { return nil, nil, err } @@ -249,38 +216,36 @@ func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { } } +// invalidateAPIClient invalidates the access API client associated with the given address and port. +// It removes the cached client from the ConnectionsCache and closes the connection. func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) { grpcAddress, _ := getGRPCAddress(address, port) if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { store := res.(*CachedClient) - store.Close() - if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionFromPoolInvalidated() + // Check if it is possible to remove cached client from cache, or it is already removed or being processed + if cf.ConnectionsCache.Remove(grpcAddress) { + // Close the connection only if it is successfully removed from the cache + store.Close() + if cf.AccessMetrics != nil { + cf.AccessMetrics.ConnectionFromPoolInvalidated() + } } } } -// Close closes the CachedClient connection +// Close closes the CachedClient connection. It marks the connection for closure and waits asynchronously for ongoing +// requests to complete before closing the connection. func (s *CachedClient) Close() { - s.mutex.Lock() - conn := s.ClientConn - s.ClientConn = nil - s.mutex.Unlock() - - if conn == nil { - return - } - // Mark the connection for closure s.closeRequested.Store(true) - // If there are ongoing requests, wait for them to complete - if s.requestCounter.Load() > 0 { - <-s.done - } + // If there are ongoing requests, wait for them to complete asynchronously + go func() { + s.wg.Wait() - // Close the connection - conn.Close() + // Close the connection + s.ClientConn.Close() + }() } // getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the @@ -314,20 +279,13 @@ func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClien } // Increment the request counter to track ongoing requests - cachedClient.requestCounter.Add(1) + cachedClient.wg.Add(1) // Invoke the actual RPC method err := invoker(ctx, method, req, reply, cc, opts...) - // Decrement the request counter and check if the connection is marked for closure and no more ongoing requests - count := cachedClient.requestCounter.Add(-1) - if cachedClient.closeRequested.Load() && count == 0 { - // Signal that all ongoing requests have completed - select { - case cachedClient.done <- struct{}{}: - default: - } - } + // Decrement the request counter + cachedClient.wg.Done() return err } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 4156ed480cd..6ddfbe12a2a 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -18,6 +18,7 @@ import ( "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/stretchr/testify/assert" testifymock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -432,7 +433,7 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { en, closer := createExecNode() defer closer() - // setup the handler mock to not respond within the timeout + // setup the handler mock req := &execution.PingRequest{} resp := &execution.PingResponse{} respSent := atomic.NewUint64(0) From 398b49c0b7dcbb4839dd64e570297ee4e2b86ab6 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 11:19:44 -0400 Subject: [PATCH 213/815] add component and worker pool to rpc sent tracker --- config/default-config.yml | 4 + module/metrics/herocache.go | 9 +++ module/metrics/labels.go | 1 + network/netconf/flags.go | 16 ++-- network/p2p/p2pconf/gossipsub.go | 4 + network/p2p/tracer/gossipSubMeshTracer.go | 41 +++++++--- .../p2p/tracer/internal/rpc_sent_tracker.go | 79 ++++++++++++++++--- 7 files changed, 127 insertions(+), 27 deletions(-) diff --git a/config/default-config.yml b/config/default-config.yml index 9834694b0e2..896668301df 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -66,6 +66,10 @@ network-config: # The default RPC sent tracker cache size. The RPC sent tracker is used to track RPC control messages sent from the local node. # Note: this cache size must be large enough to keep a history of sent messages in a reasonable time window of past history. gossipsub-rpc-sent-tracker-cache-size: 1_000_000 + # Cache size of the rpc sent tracker queue used for async tracking. + gossipsub-rpc-sent-tracker-queue-cache-size: 1000 + # Number of workers for rpc sent tracker worker pool. + gossipsub-rpc-sent-tracker-workers: 5 # Peer scoring is the default value for enabling peer scoring gossipsub-peer-scoring-enabled: true # Gossipsub rpc inspectors configs diff --git a/module/metrics/herocache.go b/module/metrics/herocache.go index f3a88341c87..f82cd84bb57 100644 --- a/module/metrics/herocache.go +++ b/module/metrics/herocache.go @@ -155,6 +155,15 @@ func GossipSubRPCSentTrackerMetricFactory(f HeroCacheMetricsFactory, networkType return f(namespaceNetwork, r) } +func GossipSubRPCSentTrackerQueueMetricFactory(f HeroCacheMetricsFactory, networkType network.NetworkingType) module.HeroCacheMetrics { + // we don't use the public prefix for the metrics here for sake of backward compatibility of metric name. + r := ResourceNetworkingRPCSentTrackerQueue + if networkType == network.PublicNetwork { + r = PrependPublicPrefix(r) + } + return f(namespaceNetwork, r) +} + func RpcInspectorNotificationQueueMetricFactory(f HeroCacheMetricsFactory, networkType network.NetworkingType) module.HeroCacheMetrics { r := ResourceNetworkingRpcInspectorNotificationQueue if networkType == network.PublicNetwork { diff --git a/module/metrics/labels.go b/module/metrics/labels.go index 9febc9ab391..8000576b4d9 100644 --- a/module/metrics/labels.go +++ b/module/metrics/labels.go @@ -93,6 +93,7 @@ const ( ResourceNetworkingRpcClusterPrefixReceivedCache = "rpc_cluster_prefixed_received_cache" ResourceNetworkingDisallowListCache = "disallow_list_cache" ResourceNetworkingRPCSentTrackerCache = "gossipsub_rpc_sent_tracker_cache" + ResourceNetworkingRPCSentTrackerQueue = "gossipsub_rpc_sent_tracker_queue" ResourceFollowerPendingBlocksCache = "follower_pending_block_cache" // follower engine ResourceFollowerLoopCertifiedBlocksChannel = "follower_loop_certified_blocks_channel" // follower loop, certified blocks buffered channel diff --git a/network/netconf/flags.go b/network/netconf/flags.go index bdf821aa60b..045755fde7b 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -37,10 +37,12 @@ const ( gracePeriod = "libp2p-grace-period" silencePeriod = "libp2p-silence-period" // gossipsub - peerScoring = "gossipsub-peer-scoring-enabled" - localMeshLogInterval = "gossipsub-local-mesh-logging-interval" - rpcSentTrackerCacheSize = "gossipsub-rpc-sent-tracker-cache-size" - scoreTracerInterval = "gossipsub-score-tracer-interval" + peerScoring = "gossipsub-peer-scoring-enabled" + localMeshLogInterval = "gossipsub-local-mesh-logging-interval" + rpcSentTrackerCacheSize = "gossipsub-rpc-sent-tracker-cache-size" + rpcSentTrackerQueueCacheSize = "gossipsub-rpc-sent-tracker-queue-cache-size" + rpcSentTrackerNumOfWorkers = "gossipsub-rpc-sent-tracker-workers" + scoreTracerInterval = "gossipsub-score-tracer-interval" // gossipsub validation inspector gossipSubRPCInspectorNotificationCacheSize = "gossipsub-rpc-inspector-notification-cache-size" validationInspectorNumberOfWorkers = "gossipsub-rpc-validation-inspector-workers" @@ -67,8 +69,8 @@ func AllFlagNames() []string { return []string{ networkingConnectionPruning, preferredUnicastsProtocols, receivedMessageCacheSize, peerUpdateInterval, unicastMessageTimeout, unicastCreateStreamRetryDelay, dnsCacheTTL, disallowListNotificationCacheSize, dryRun, lockoutDuration, messageRateLimit, bandwidthRateLimit, bandwidthBurstLimit, memoryLimitRatio, - fileDescriptorsRatio, peerBaseLimitConnsInbound, highWatermark, lowWatermark, gracePeriod, silencePeriod, peerScoring, localMeshLogInterval, rpcSentTrackerCacheSize, scoreTracerInterval, - gossipSubRPCInspectorNotificationCacheSize, validationInspectorNumberOfWorkers, validationInspectorInspectMessageQueueCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheSize, + fileDescriptorsRatio, peerBaseLimitConnsInbound, highWatermark, lowWatermark, gracePeriod, silencePeriod, peerScoring, localMeshLogInterval, rpcSentTrackerCacheSize, rpcSentTrackerQueueCacheSize, rpcSentTrackerNumOfWorkers, + scoreTracerInterval, gossipSubRPCInspectorNotificationCacheSize, validationInspectorNumberOfWorkers, validationInspectorInspectMessageQueueCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheDecay, validationInspectorClusterPrefixHardThreshold, ihaveSyncSampleSizePercentage, ihaveAsyncSampleSizePercentage, ihaveMaxSampleSize, metricsInspectorNumberOfWorkers, metricsInspectorCacheSize, alspDisabled, alspSpamRecordCacheSize, alspSpamRecordQueueSize, alspHearBeatInterval, } @@ -109,6 +111,8 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { flags.Duration(localMeshLogInterval, config.GossipSubConfig.LocalMeshLogInterval, "logging interval for local mesh in gossipsub") flags.Duration(scoreTracerInterval, config.GossipSubConfig.ScoreTracerInterval, "logging interval for peer score tracer in gossipsub, set to 0 to disable") flags.Uint32(rpcSentTrackerCacheSize, config.GossipSubConfig.RPCSentTrackerCacheSize, "cache size of the rpc sent tracker used by the gossipsub mesh tracer.") + flags.Uint32(rpcSentTrackerQueueCacheSize, config.GossipSubConfig.RPCSentTrackerQueueCacheSize, "cache size of the rpc sent tracker worker queue.") + flags.Int(rpcSentTrackerNumOfWorkers, config.GossipSubConfig.RpcSentTrackerNumOfWorkers, "number of workers for the rpc sent tracker worker pool.") // gossipsub RPC control message validation limits used for validation configuration and rate limiting flags.Int(validationInspectorNumberOfWorkers, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.NumberOfWorkers, "number of gossupsub RPC control message validation inspector component workers") flags.Uint32(validationInspectorInspectMessageQueueCacheSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.CacheSize, "cache size for gossipsub RPC validation inspector events worker pool queue.") diff --git a/network/p2p/p2pconf/gossipsub.go b/network/p2p/p2pconf/gossipsub.go index d297f5cba8b..b76ff9f4c9b 100644 --- a/network/p2p/p2pconf/gossipsub.go +++ b/network/p2p/p2pconf/gossipsub.go @@ -23,6 +23,10 @@ type GossipSubConfig struct { ScoreTracerInterval time.Duration `mapstructure:"gossipsub-score-tracer-interval"` // RPCSentTrackerCacheSize cache size of the rpc sent tracker used by the gossipsub mesh tracer. RPCSentTrackerCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-cache-size"` + // RPCSentTrackerQueueCacheSize cache size of the rpc sent tracker queue used for async tracking. + RPCSentTrackerQueueCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` + // RpcSentTrackerNumOfWorkers number of workers for rpc sent tracker worker pool. + RpcSentTrackerNumOfWorkers int `mapstructure:"gossipsub-rpc-sent-tracker-workers"` // PeerScoring is whether to enable GossipSub peer scoring. PeerScoring bool `mapstructure:"gossipsub-peer-scoring-enabled"` } diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index 1cc25fd2565..27b9ebc8f0f 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -50,12 +50,15 @@ type GossipSubMeshTracer struct { var _ p2p.PubSubTracer = (*GossipSubMeshTracer)(nil) type GossipSubMeshTracerConfig struct { - Logger zerolog.Logger - Metrics module.GossipSubLocalMeshMetrics - IDProvider module.IdentityProvider - LoggerInterval time.Duration - RpcSentTrackerCacheCollector module.HeroCacheMetrics - RpcSentTrackerCacheSize uint32 + Logger zerolog.Logger + Metrics module.GossipSubLocalMeshMetrics + IDProvider module.IdentityProvider + LoggerInterval time.Duration + RpcSentTrackerCacheCollector module.HeroCacheMetrics + RpcSentTrackerCacheSize uint32 + RpcSentTrackerWorkerQueueCacheCollector module.HeroCacheMetrics + RpcSentTrackerWorkerQueueCacheSize uint32 + RpcSentTrackerNumOfWorkers int } // NewGossipSubMeshTracer creates a new *GossipSubMeshTracer. @@ -64,13 +67,21 @@ type GossipSubMeshTracerConfig struct { // Returns: // - *GossipSubMeshTracer: new mesh tracer. func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTracer { - rpcSentTracker := internal.NewRPCSentTracker(config.Logger, config.RpcSentTrackerCacheSize, config.RpcSentTrackerCacheCollector) + lg := config.Logger.With().Str("component", "gossipsub_topology_tracer").Logger() + rpcSentTracker := internal.NewRPCSentTracker(&internal.RPCSentTrackerConfig{ + Logger: lg, + RPCSentCacheSize: config.RpcSentTrackerCacheSize, + RPCSentCacheCollector: config.RpcSentTrackerCacheCollector, + WorkerQueueCacheCollector: config.RpcSentTrackerWorkerQueueCacheCollector, + WorkerQueueCacheSize: config.RpcSentTrackerWorkerQueueCacheSize, + NumOfWorkers: config.RpcSentTrackerNumOfWorkers, + }) g := &GossipSubMeshTracer{ RawTracer: NewGossipSubNoopTracer(), topicMeshMap: make(map[string]map[peer.ID]struct{}), idProvider: config.IDProvider, metrics: config.Metrics, - logger: config.Logger.With().Str("component", "gossipsub_topology_tracer").Logger(), + logger: lg, loggerInterval: config.LoggerInterval, rpcSentTracker: rpcSentTracker, } @@ -80,6 +91,15 @@ func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTra ready() g.logLoop(ctx) }). + AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + lg.Debug().Msg("starting rpc sent tracker") + g.rpcSentTracker.Start(ctx) + lg.Debug().Msg("rpc sent tracker started") + + <-g.rpcSentTracker.Done() + lg.Debug().Msg("rpc sent tracker stopped") + }). Build() return g @@ -155,10 +175,7 @@ func (t *GossipSubMeshTracer) Prune(p peer.ID, topic string) { // SendRPC is called when a RPC is sent. Currently, the GossipSubMeshTracer tracks iHave RPC messages that have been sent. // This function can be updated to track other control messages in the future as required. func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { - switch { - case len(rpc.GetControl().GetIhave()) > 0: - t.rpcSentTracker.OnIHaveRPCSent(rpc.GetControl().GetIhave()) - } + t.rpcSentTracker.RPCSent(rpc) } // logLoop logs the mesh peers of the local node for each topic at a regular interval. diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 6d44ac984a3..bb707883cae 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -1,32 +1,93 @@ package internal import ( + pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/rs/zerolog" + "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/mempool/queue" p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) +// trackRpcSentWork is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed +// by the *RPCSentTracker. +type trackRpcSentWork struct { + rpc *pubsub.RPC +} + // RPCSentTracker tracks RPC messages that are sent. type RPCSentTracker struct { - cache *rpcSentCache + component.Component + cache *rpcSentCache + workerPool *worker.Pool[trackRpcSentWork] +} + +// RPCSentTrackerConfig configuration for the RPCSentTracker. +type RPCSentTrackerConfig struct { + Logger zerolog.Logger + //RPCSentCacheSize size of the *rpcSentCache cache. + RPCSentCacheSize uint32 + // RPCSentCacheCollector metrics collector for the *rpcSentCache cache. + RPCSentCacheCollector module.HeroCacheMetrics + // WorkerQueueCacheCollector metrics factory for the worker pool. + WorkerQueueCacheCollector module.HeroCacheMetrics + // WorkerQueueCacheSize the worker pool herostore cache size. + WorkerQueueCacheSize uint32 + // NumOfWorkers number of workers in the worker pool. + NumOfWorkers int } // NewRPCSentTracker returns a new *NewRPCSentTracker. -func NewRPCSentTracker(logger zerolog.Logger, sizeLimit uint32, collector module.HeroCacheMetrics) *RPCSentTracker { - config := &rpcCtrlMsgSentCacheConfig{ - sizeLimit: sizeLimit, - logger: logger, - collector: collector, +func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { + cacheConfig := &rpcCtrlMsgSentCacheConfig{ + sizeLimit: config.RPCSentCacheSize, + logger: config.Logger, + collector: config.RPCSentCacheCollector, + } + + store := queue.NewHeroStore( + config.WorkerQueueCacheSize, + config.Logger, + config.WorkerQueueCacheCollector) + + tracker := &RPCSentTracker{cache: newRPCSentCache(cacheConfig)} + tracker.workerPool = worker.NewWorkerPoolBuilder[trackRpcSentWork]( + config.Logger, + store, + tracker.rpcSent).Build() + + builder := component.NewComponentManagerBuilder() + for i := 0; i < config.NumOfWorkers; i++ { + builder.AddWorker(tracker.workerPool.WorkerLogic()) } - return &RPCSentTracker{cache: newRPCSentCache(config)} + tracker.Component = builder.Build() + + return tracker } -// OnIHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. +// RPCSent submits the control message to the worker queue for async tracking. // Args: // - *pubsub.RPC: the rpc sent. -func (t *RPCSentTracker) OnIHaveRPCSent(iHaves []*pb.ControlIHave) { +func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) { + t.workerPool.Submit(trackRpcSentWork{rpc}) +} + +// rpcSent tracks control messages sent in *pubsub.RPC. +func (t *RPCSentTracker) rpcSent(work trackRpcSentWork) error { + switch { + case len(work.rpc.GetControl().GetIhave()) > 0: + t.iHaveRPCSent(work.rpc.GetControl().GetIhave()) + } + return nil +} + +// iHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. +// Args: +// - []*pb.ControlIHave: list of iHave control messages. +func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) { controlMsgType := p2pmsg.CtrlMsgIHave for _, iHave := range iHaves { topicID := iHave.GetTopicID() From e373b014b0e83fef093530c699d38492486c0e89 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 11:23:43 -0400 Subject: [PATCH 214/815] update all usages of NewGossipSubMeshTracer --- cmd/access/node_builder/access_node_builder.go | 15 +++++++++------ cmd/observer/node_builder/observer_builder.go | 15 +++++++++------ follower/follower_builder.go | 15 +++++++++------ network/internal/p2pfixtures/fixtures.go | 15 +++++++++------ network/p2p/p2pbuilder/libp2pNodeBuilder.go | 15 +++++++++------ network/p2p/tracer/gossipSubMeshTracer_test.go | 15 +++++++++------ 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 5edd2629ee2..5e0d790af8e 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1192,12 +1192,15 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: networkMetrics, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + Logger: builder.Logger, + Metrics: networkMetrics, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 78ddc464fb7..4f3c5ea7738 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -703,12 +703,15 @@ func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: builder.Metrics.Network, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + Logger: builder.Logger, + Metrics: builder.Metrics.Network, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index e2eb43cb49c..06626555855 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -605,12 +605,15 @@ func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: builder.Metrics.Network, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + Logger: builder.Logger, + Metrics: builder.Metrics.Network, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 29ee0509fbb..5a8e60932c8 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -104,12 +104,15 @@ func CreateNode(t *testing.T, networkKey crypto.PrivateKey, sporkID flow.Identif require.NoError(t, err) meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: metrics.NewNoopCollector(), - IDProvider: idProvider, - LoggerInterval: defaultFlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), - RpcSentTrackerCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + Logger: logger, + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: defaultFlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), + RpcSentTrackerCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultFlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.NewNoopCollector(), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 8e550b4fa94..2034b9971f6 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -496,12 +496,15 @@ func DefaultNodeBuilder( } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: metricsCfg.Metrics, - IDProvider: idProvider, - LoggerInterval: gossipCfg.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(metricsCfg.HeroCacheFactory, flownet.PrivateNetwork), - RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, + Logger: logger, + Metrics: metricsCfg.Metrics, + IDProvider: idProvider, + LoggerInterval: gossipCfg.LocalMeshLogInterval, + RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(metricsCfg.HeroCacheFactory, flownet.PrivateNetwork), + RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: gossipCfg.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: gossipCfg.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(metricsCfg.HeroCacheFactory, flownet.PrivateNetwork), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index a2da0584f94..6fb4e96316f 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -66,12 +66,15 @@ func TestGossipSubMeshTracer(t *testing.T) { // meshTracer logs at 1 second intervals for sake of testing. collector := mockmodule.NewGossipSubLocalMeshMetrics(t) meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: collector, - IDProvider: idProvider, - LoggerInterval: time.Second, - RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), - RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + Logger: logger, + Metrics: collector, + IDProvider: idProvider, + LoggerInterval: time.Second, + RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), + RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + RpcSentTrackerWorkerQueueCacheCollector: metrics.NewNoopCollector(), } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) tracerNode, tracerId := p2ptest.NodeFixture( From 2a83452ad7136d3fbece08f7160d30fd6420b61b Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 11:39:01 -0400 Subject: [PATCH 215/815] update rpc sent tracker test --- .../tracer/internal/rpc_sent_tracker_test.go | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 7b9c4ec9acb..3293dcafd5c 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -1,7 +1,9 @@ package internal import ( + "context" "testing" + "time" pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" @@ -9,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/config" + "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/utils/unittest" @@ -22,14 +25,23 @@ func TestNewRPCSentTracker(t *testing.T) { // TestRPCSentTracker_IHave ensures *RPCSentTracker tracks sent iHave control messages as expected. func TestRPCSentTracker_IHave(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + tracker := mockTracker(t) require.NotNil(t, tracker) + tracker.Start(signalerCtx) + defer func() { + cancel() + unittest.RequireComponentsDoneBefore(t, time.Second, tracker) + }() + t.Run("WasIHaveRPCSent should return false for iHave message Id that has not been tracked", func(t *testing.T) { require.False(t, tracker.WasIHaveRPCSent("topic_id", "message_id")) }) - t.Run("WasIHaveRPCSent should return true for iHave message after it is tracked with OnIHaveRPCSent", func(t *testing.T) { + t.Run("WasIHaveRPCSent should return true for iHave message after it is tracked with iHaveRPCSent", func(t *testing.T) { numOfMsgIds := 100 testCases := []struct { topic string @@ -49,7 +61,12 @@ func TestRPCSentTracker_IHave(t *testing.T) { } } rpc := rpcFixture(withIhaves(iHaves)) - tracker.OnIHaveRPCSent(rpc.GetControl().GetIhave()) + tracker.RPCSent(rpc) + + // eventually we should have tracked numOfMsgIds per single topic + require.Eventually(t, func() bool { + return tracker.cache.size() == uint(len(testCases)*numOfMsgIds) + }, time.Second, 100*time.Millisecond) for _, testCase := range testCases { for _, messageID := range testCase.messageIDS { @@ -60,11 +77,16 @@ func TestRPCSentTracker_IHave(t *testing.T) { } func mockTracker(t *testing.T) *RPCSentTracker { - logger := zerolog.Nop() cfg, err := config.DefaultConfig() require.NoError(t, err) - collector := metrics.NewNoopCollector() - tracker := NewRPCSentTracker(logger, cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, collector) + tracker := NewRPCSentTracker(&RPCSentTrackerConfig{ + Logger: zerolog.Nop(), + RPCSentCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RPCSentCacheCollector: metrics.NewNoopCollector(), + WorkerQueueCacheCollector: metrics.NewNoopCollector(), + WorkerQueueCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + NumOfWorkers: 1, + }) return tracker } From 14ef77dc8784e243a03e4e60c85ac36b0aceb712 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 11:46:26 -0400 Subject: [PATCH 216/815] Update rpc_sent_tracker.go --- network/p2p/tracer/internal/rpc_sent_tracker.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index bb707883cae..8e7195d1db0 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -12,9 +12,9 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) -// trackRpcSentWork is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed +// trackRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed // by the *RPCSentTracker. -type trackRpcSentWork struct { +type trackRPC struct { rpc *pubsub.RPC } @@ -22,7 +22,7 @@ type trackRpcSentWork struct { type RPCSentTracker struct { component.Component cache *rpcSentCache - workerPool *worker.Pool[trackRpcSentWork] + workerPool *worker.Pool[trackRPC] } // RPCSentTrackerConfig configuration for the RPCSentTracker. @@ -54,7 +54,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.WorkerQueueCacheCollector) tracker := &RPCSentTracker{cache: newRPCSentCache(cacheConfig)} - tracker.workerPool = worker.NewWorkerPoolBuilder[trackRpcSentWork]( + tracker.workerPool = worker.NewWorkerPoolBuilder[trackRPC]( config.Logger, store, tracker.rpcSent).Build() @@ -72,11 +72,11 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { // Args: // - *pubsub.RPC: the rpc sent. func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) { - t.workerPool.Submit(trackRpcSentWork{rpc}) + t.workerPool.Submit(trackRPC{rpc}) } // rpcSent tracks control messages sent in *pubsub.RPC. -func (t *RPCSentTracker) rpcSent(work trackRpcSentWork) error { +func (t *RPCSentTracker) rpcSent(work trackRPC) error { switch { case len(work.rpc.GetControl().GetIhave()) > 0: t.iHaveRPCSent(work.rpc.GetControl().GetIhave()) From a916db0048c4ea9212e025e43182f35f8e5fecaa Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Wed, 12 Jul 2023 19:06:50 +0200 Subject: [PATCH 217/815] Remove dual authorizers for system transaction --- ...ChunkTransactionTemplateDualAuthorizer.cdc | 18 -------- fvm/blueprints/system.go | 41 ------------------- 2 files changed, 59 deletions(-) delete mode 100644 fvm/blueprints/scripts/systemChunkTransactionTemplateDualAuthorizer.cdc diff --git a/fvm/blueprints/scripts/systemChunkTransactionTemplateDualAuthorizer.cdc b/fvm/blueprints/scripts/systemChunkTransactionTemplateDualAuthorizer.cdc deleted file mode 100644 index 7c5d60d2a97..00000000000 --- a/fvm/blueprints/scripts/systemChunkTransactionTemplateDualAuthorizer.cdc +++ /dev/null @@ -1,18 +0,0 @@ -import FlowEpoch from 0xEPOCHADDRESS -import NodeVersionBeacon from 0xNODEVERSIONBEACONADDRESS - -transaction { - prepare(serviceAccount: AuthAccount, epochAccount: AuthAccount) { - let epochHeartbeat = - serviceAccount.borrow<&FlowEpoch.Heartbeat>(from: FlowEpoch.heartbeatStoragePath) ?? - epochAccount.borrow<&FlowEpoch.Heartbeat>(from: FlowEpoch.heartbeatStoragePath) ?? - panic("Could not borrow heartbeat from storage path") - epochHeartbeat.advanceBlock() - - let versionBeaconHeartbeat = - serviceAccount.borrow<&NodeVersionBeacon.Heartbeat>(from: NodeVersionBeacon.HeartbeatStoragePath) ?? - epochAccount.borrow<&NodeVersionBeacon.Heartbeat>(from: NodeVersionBeacon.HeartbeatStoragePath) ?? - panic("Couldn't borrow NodeVersionBeacon.Heartbeat Resource") - versionBeaconHeartbeat.heartbeat() - } -} diff --git a/fvm/blueprints/system.go b/fvm/blueprints/system.go index f4c6893b34b..eb1a0a33ba2 100644 --- a/fvm/blueprints/system.go +++ b/fvm/blueprints/system.go @@ -28,19 +28,6 @@ func SystemChunkTransaction(chain flow.Chain) (*flow.TransactionBody, error) { return nil, fmt.Errorf("could not get system contracts for chain: %w", err) } - // this is only true for testnet, sandboxnet and mainnet. - if contracts.Epoch.Address != chain.ServiceAddress() { - // Temporary workaround because the heartbeat resources need to be moved - // to the service account: - // - the system chunk will attempt to load both Epoch and VersionBeacon - // resources from either the service account or the staking account - // - the service account committee can then safely move the resources - // at any time - // - once the resources are moved, this workaround should be removed - // after version v0.31.0 - return systemChunkTransactionDualAuthorizers(chain, contracts) - } - tx := flow.NewTransactionBody(). SetScript( []byte(templates.ReplaceAddresses( @@ -51,35 +38,7 @@ func SystemChunkTransaction(chain flow.Chain) (*flow.TransactionBody, error) { }, )), ). - AddAuthorizer(contracts.Epoch.Address). - SetGasLimit(SystemChunkTransactionGasLimit) - - return tx, nil -} - -// systemChunkTransactionTemplateDualAuthorizer is the same as systemChunkTransactionTemplate -// but it looks for the heartbeat resources on two different accounts. -// -//go:embed scripts/systemChunkTransactionTemplateDualAuthorizer.cdc -var systemChunkTransactionTemplateDualAuthorizer string - -func systemChunkTransactionDualAuthorizers( - chain flow.Chain, - contracts *systemcontracts.SystemContracts, -) (*flow.TransactionBody, error) { - - tx := flow.NewTransactionBody(). - SetScript( - []byte(templates.ReplaceAddresses( - systemChunkTransactionTemplateDualAuthorizer, - templates.Environment{ - EpochAddress: contracts.Epoch.Address.Hex(), - NodeVersionBeaconAddress: contracts.NodeVersionBeacon.Address.Hex(), - }, - )), - ). AddAuthorizer(chain.ServiceAddress()). - AddAuthorizer(contracts.Epoch.Address). SetGasLimit(SystemChunkTransactionGasLimit) return tx, nil From e38c31e39f83c2a6b65c794010744d2da87a41a7 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 17:11:13 -0400 Subject: [PATCH 218/815] track last highest iHaves size --- module/component/component.go | 15 ++++- .../mock/component_manager_builder.go | 16 ++++++ network/p2p/tracer/gossipSubMeshTracer.go | 5 +- .../p2p/tracer/internal/rpc_sent_tracker.go | 57 +++++++++++++++---- .../tracer/internal/rpc_sent_tracker_test.go | 56 ++++++++++++++++++ 5 files changed, 135 insertions(+), 14 deletions(-) diff --git a/module/component/component.go b/module/component/component.go index 34f8f61cf14..a75534d386a 100644 --- a/module/component/component.go +++ b/module/component/component.go @@ -143,7 +143,8 @@ func NoopWorker(ctx irrecoverable.SignalerContext, ready ReadyFunc) { type ComponentManagerBuilder interface { // AddWorker adds a worker routine for the ComponentManager AddWorker(ComponentWorker) ComponentManagerBuilder - + // AddWorkers adds n number of worker routines for the ComponentManager. + AddWorkers(int, ComponentWorker) ComponentManagerBuilder // Build builds and returns a new ComponentManager instance Build() *ComponentManager } @@ -157,15 +158,23 @@ func NewComponentManagerBuilder() ComponentManagerBuilder { return &componentManagerBuilderImpl{} } -// AddWorker adds a ComponentWorker closure to the ComponentManagerBuilder // All worker functions will be run in parallel when the ComponentManager is started. // Note: AddWorker is not concurrency-safe, and should only be called on an individual builder -// within a single goroutine. +// within a single goroutine.// AddWorker adds a ComponentWorker closure to the ComponentManagerBuilder + func (c *componentManagerBuilderImpl) AddWorker(worker ComponentWorker) ComponentManagerBuilder { c.workers = append(c.workers, worker) return c } +// AddWorkers adds n number of workers for the ComponentManager. +func (c *componentManagerBuilderImpl) AddWorkers(n int, worker ComponentWorker) ComponentManagerBuilder { + for i := 0; i < n; i++ { + c.workers = append(c.workers, worker) + } + return c +} + // Build returns a new ComponentManager instance with the configured workers // Build may be called multiple times to create multiple individual ComponentManagers. This will // result in the worker routines being called multiple times. If this is unsafe, do not call it diff --git a/module/component/mock/component_manager_builder.go b/module/component/mock/component_manager_builder.go index c414ddc6663..570ecda9269 100644 --- a/module/component/mock/component_manager_builder.go +++ b/module/component/mock/component_manager_builder.go @@ -28,6 +28,22 @@ func (_m *ComponentManagerBuilder) AddWorker(_a0 component.ComponentWorker) comp return r0 } +// AddWorkers provides a mock function with given fields: _a0, _a1 +func (_m *ComponentManagerBuilder) AddWorkers(_a0 int, _a1 component.ComponentWorker) component.ComponentManagerBuilder { + ret := _m.Called(_a0, _a1) + + var r0 component.ComponentManagerBuilder + if rf, ok := ret.Get(0).(func(int, component.ComponentWorker) component.ComponentManagerBuilder); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(component.ComponentManagerBuilder) + } + } + + return r0 +} + // Build provides a mock function with given fields: func (_m *ComponentManagerBuilder) Build() *component.ComponentManager { ret := _m.Called() diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index 27b9ebc8f0f..cd0d548e3c0 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -175,7 +175,10 @@ func (t *GossipSubMeshTracer) Prune(p peer.ID, topic string) { // SendRPC is called when a RPC is sent. Currently, the GossipSubMeshTracer tracks iHave RPC messages that have been sent. // This function can be updated to track other control messages in the future as required. func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { - t.rpcSentTracker.RPCSent(rpc) + err := t.rpcSentTracker.RPCSent(rpc) + if err != nil { + t.logger.Err(err).Msg("failed to track sent pubsbub rpc") + } } // logLoop logs the mesh peers of the local node for each topic at a regular interval. diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 8e7195d1db0..09f29436c49 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -1,9 +1,13 @@ package internal import ( + "crypto/rand" + "fmt" + pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/rs/zerolog" + "go.uber.org/atomic" "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" @@ -15,7 +19,9 @@ import ( // trackRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed // by the *RPCSentTracker. type trackRPC struct { - rpc *pubsub.RPC + // Nonce prevents deduplication in the hero store + Nonce []byte + rpc *pubsub.RPC } // RPCSentTracker tracks RPC messages that are sent. @@ -23,6 +29,8 @@ type RPCSentTracker struct { component.Component cache *rpcSentCache workerPool *worker.Pool[trackRPC] + // lastHighestIHaveRPCSize tracks the size of the last largest iHave rpc control message sent. + lastHighestIHaveRPCSize *atomic.Int64 } // RPCSentTrackerConfig configuration for the RPCSentTracker. @@ -53,17 +61,18 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.Logger, config.WorkerQueueCacheCollector) - tracker := &RPCSentTracker{cache: newRPCSentCache(cacheConfig)} + tracker := &RPCSentTracker{ + cache: newRPCSentCache(cacheConfig), + lastHighestIHaveRPCSize: atomic.NewInt64(0), + } tracker.workerPool = worker.NewWorkerPoolBuilder[trackRPC]( config.Logger, store, tracker.rpcSent).Build() - builder := component.NewComponentManagerBuilder() - for i := 0; i < config.NumOfWorkers; i++ { - builder.AddWorker(tracker.workerPool.WorkerLogic()) - } - tracker.Component = builder.Build() + tracker.Component = component.NewComponentManagerBuilder(). + AddWorkers(config.NumOfWorkers, tracker.workerPool.WorkerLogic()). + Build() return tracker } @@ -71,19 +80,32 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { // RPCSent submits the control message to the worker queue for async tracking. // Args: // - *pubsub.RPC: the rpc sent. -func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) { - t.workerPool.Submit(trackRPC{rpc}) +func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) error { + n, err := nonce() + if err != nil { + return fmt.Errorf("failed to get track rpc work nonce: %w", err) + } + t.workerPool.Submit(trackRPC{Nonce: n, rpc: rpc}) + return nil } // rpcSent tracks control messages sent in *pubsub.RPC. func (t *RPCSentTracker) rpcSent(work trackRPC) error { switch { case len(work.rpc.GetControl().GetIhave()) > 0: - t.iHaveRPCSent(work.rpc.GetControl().GetIhave()) + iHave := work.rpc.GetControl().GetIhave() + t.iHaveRPCSent(iHave) + t.updateLastHighestIHaveRPCSize(int64(len(iHave))) } return nil } +func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { + if t.lastHighestIHaveRPCSize.Load() < size { + t.lastHighestIHaveRPCSize.Store(size) + } +} + // iHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. // Args: // - []*pb.ControlIHave: list of iHave control messages. @@ -106,3 +128,18 @@ func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) { func (t *RPCSentTracker) WasIHaveRPCSent(topicID, messageID string) bool { return t.cache.has(topicID, messageID, p2pmsg.CtrlMsgIHave) } + +// LastHighestIHaveRPCSize returns the last highest size of iHaves sent in an rpc. +func (t *RPCSentTracker) LastHighestIHaveRPCSize() int64 { + return t.lastHighestIHaveRPCSize.Load() +} + +// nonce returns random string that is used to store unique items in herocache. +func nonce() ([]byte, error) { + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + return nil, err + } + return b, nil +} diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 3293dcafd5c..47e897442f8 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -76,6 +76,62 @@ func TestRPCSentTracker_IHave(t *testing.T) { }) } +// TestRPCSentTracker_IHave ensures *RPCSentTracker tracks the last largest iHave size as expected. +func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + tracker := mockTracker(t) + require.NotNil(t, tracker) + + tracker.Start(signalerCtx) + defer func() { + cancel() + unittest.RequireComponentsDoneBefore(t, time.Second, tracker) + }() + + expectedLastHighestSize := 1000 + // adding a single message ID to the iHave enables us to track the expected cache size by the amount of iHaves. + numOfMessageIds := 1 + testCases := []struct { + rpcFixture *pubsub.RPC + numOfIhaves int + }{ + {rpcFixture(withIhaves(mockIHaveFixture(10, numOfMessageIds))), 10}, + {rpcFixture(withIhaves(mockIHaveFixture(100, numOfMessageIds))), 100}, + {rpcFixture(withIhaves(mockIHaveFixture(expectedLastHighestSize, numOfMessageIds))), expectedLastHighestSize}, + {rpcFixture(withIhaves(mockIHaveFixture(999, numOfMessageIds))), 999}, + {rpcFixture(withIhaves(mockIHaveFixture(23, numOfMessageIds))), 23}, + } + + expectedCacheSize := 0 + for _, testCase := range testCases { + require.NoError(t, tracker.RPCSent(testCase.rpcFixture)) + expectedCacheSize += testCase.numOfIhaves + } + + // eventually we should have tracked numOfMsgIds per single topic + require.Eventually(t, func() bool { + return tracker.cache.size() == uint(expectedCacheSize) + }, time.Second, 100*time.Millisecond) + + require.Equal(t, int64(expectedLastHighestSize), tracker.LastHighestIHaveRPCSize()) +} + +// mockIHaveFixture generate list of iHaves of size n. Each iHave will be created with m number of random message ids. +func mockIHaveFixture(n, m int) []*pb.ControlIHave { + iHaves := make([]*pb.ControlIHave, n) + for i := 0; i < n; i++ { + // topic does not have to be a valid flow topic, for teting purposes we can use a random string + topic := unittest.IdentifierFixture().String() + iHaves[i] = &pb.ControlIHave{ + TopicID: &topic, + MessageIDs: unittest.IdentifierListFixture(m).Strings(), + } + } + return iHaves +} + func mockTracker(t *testing.T) *RPCSentTracker { cfg, err := config.DefaultConfig() require.NoError(t, err) From 20b917e20457aa33e48c943418a82842a2d8b186 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 17:48:39 -0400 Subject: [PATCH 219/815] reset LastHighestIhavesSent every 1 minute --- network/p2p/tracer/gossipSubMeshTracer.go | 16 +++++---- .../p2p/tracer/internal/rpc_sent_tracker.go | 36 +++++++++++++++++-- .../tracer/internal/rpc_sent_tracker_test.go | 32 +++++++++++------ 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index cd0d548e3c0..21bc9e5de6c 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -24,6 +24,9 @@ const ( // MeshLogIntervalWarnMsg is the message logged by the tracer every logInterval if there are unknown peers in the mesh. MeshLogIntervalWarnMsg = "unknown peers in topic mesh peers of local node since last heartbeat" + + // defaultLastHighestIHaveRPCSizeResetInterval this default interval should always be equal to the gossipsub heart beat interval. + defaultLastHighestIHaveRPCSizeResetInterval = time.Minute ) // The GossipSubMeshTracer component in the GossipSub pubsub.RawTracer that is designed to track the local @@ -69,12 +72,13 @@ type GossipSubMeshTracerConfig struct { func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTracer { lg := config.Logger.With().Str("component", "gossipsub_topology_tracer").Logger() rpcSentTracker := internal.NewRPCSentTracker(&internal.RPCSentTrackerConfig{ - Logger: lg, - RPCSentCacheSize: config.RpcSentTrackerCacheSize, - RPCSentCacheCollector: config.RpcSentTrackerCacheCollector, - WorkerQueueCacheCollector: config.RpcSentTrackerWorkerQueueCacheCollector, - WorkerQueueCacheSize: config.RpcSentTrackerWorkerQueueCacheSize, - NumOfWorkers: config.RpcSentTrackerNumOfWorkers, + Logger: lg, + RPCSentCacheSize: config.RpcSentTrackerCacheSize, + RPCSentCacheCollector: config.RpcSentTrackerCacheCollector, + WorkerQueueCacheCollector: config.RpcSentTrackerWorkerQueueCacheCollector, + WorkerQueueCacheSize: config.RpcSentTrackerWorkerQueueCacheSize, + NumOfWorkers: config.RpcSentTrackerNumOfWorkers, + LastHighestIhavesSentResetInterval: defaultLastHighestIHaveRPCSizeResetInterval, }) g := &GossipSubMeshTracer{ RawTracer: NewGossipSubNoopTracer(), diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 09f29436c49..63ae18edb81 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -3,6 +3,7 @@ package internal import ( "crypto/rand" "fmt" + "time" pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" @@ -12,6 +13,7 @@ import ( "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/mempool/queue" p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) @@ -30,7 +32,8 @@ type RPCSentTracker struct { cache *rpcSentCache workerPool *worker.Pool[trackRPC] // lastHighestIHaveRPCSize tracks the size of the last largest iHave rpc control message sent. - lastHighestIHaveRPCSize *atomic.Int64 + lastHighestIHaveRPCSize *atomic.Int64 + lastHighestIHaveRPCSizeResetInterval time.Duration } // RPCSentTrackerConfig configuration for the RPCSentTracker. @@ -46,6 +49,8 @@ type RPCSentTrackerConfig struct { WorkerQueueCacheSize uint32 // NumOfWorkers number of workers in the worker pool. NumOfWorkers int + // LastHighestIhavesSentResetInterval the refresh interval to reset the lastHighestIHaveRPCSize. + LastHighestIhavesSentResetInterval time.Duration } // NewRPCSentTracker returns a new *NewRPCSentTracker. @@ -62,8 +67,9 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.WorkerQueueCacheCollector) tracker := &RPCSentTracker{ - cache: newRPCSentCache(cacheConfig), - lastHighestIHaveRPCSize: atomic.NewInt64(0), + cache: newRPCSentCache(cacheConfig), + lastHighestIHaveRPCSize: atomic.NewInt64(0), + lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, } tracker.workerPool = worker.NewWorkerPoolBuilder[trackRPC]( config.Logger, @@ -71,6 +77,10 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { tracker.rpcSent).Build() tracker.Component = component.NewComponentManagerBuilder(). + AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + tracker.lastHighestIHaveRPCSizeResetLoop(ctx) + }). AddWorkers(config.NumOfWorkers, tracker.workerPool.WorkerLogic()). Build() @@ -134,6 +144,26 @@ func (t *RPCSentTracker) LastHighestIHaveRPCSize() int64 { return t.lastHighestIHaveRPCSize.Load() } +// lastHighestIHaveRPCSizeResetLoop resets the lastHighestIHaveRPCSize to 0 on each interval tick. +func (t *RPCSentTracker) lastHighestIHaveRPCSizeResetLoop(ctx irrecoverable.SignalerContext) { + ticker := time.NewTicker(t.lastHighestIHaveRPCSizeResetInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + default: + } + + select { + case <-ctx.Done(): + return + case <-ticker.C: + t.lastHighestIHaveRPCSize.Store(0) + } + } +} + // nonce returns random string that is used to store unique items in herocache. func nonce() ([]byte, error) { b := make([]byte, 16) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 47e897442f8..7d2e9d57a6a 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -19,7 +19,7 @@ import ( // TestNewRPCSentTracker ensures *RPCSenTracker is created as expected. func TestNewRPCSentTracker(t *testing.T) { - tracker := mockTracker(t) + tracker := mockTracker(t, time.Minute) require.NotNil(t, tracker) } @@ -28,7 +28,7 @@ func TestRPCSentTracker_IHave(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - tracker := mockTracker(t) + tracker := mockTracker(t, time.Minute) require.NotNil(t, tracker) tracker.Start(signalerCtx) @@ -81,7 +81,7 @@ func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - tracker := mockTracker(t) + tracker := mockTracker(t, 3*time.Second) require.NotNil(t, tracker) tracker.Start(signalerCtx) @@ -116,6 +116,17 @@ func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { }, time.Second, 100*time.Millisecond) require.Equal(t, int64(expectedLastHighestSize), tracker.LastHighestIHaveRPCSize()) + + // after setting sending large RPC lastHighestIHaveRPCSize should reset to 0 after lastHighestIHaveRPCSize reset loop tick + largeIhave := 50000 + require.NoError(t, tracker.RPCSent(rpcFixture(withIhaves(mockIHaveFixture(largeIhave, numOfMessageIds))))) + require.Eventually(t, func() bool { + return tracker.LastHighestIHaveRPCSize() == int64(largeIhave) + }, 1*time.Second, 100*time.Millisecond) + + require.Eventually(t, func() bool { + return tracker.LastHighestIHaveRPCSize() == 0 + }, 4*time.Second, 100*time.Millisecond) } // mockIHaveFixture generate list of iHaves of size n. Each iHave will be created with m number of random message ids. @@ -132,16 +143,17 @@ func mockIHaveFixture(n, m int) []*pb.ControlIHave { return iHaves } -func mockTracker(t *testing.T) *RPCSentTracker { +func mockTracker(t *testing.T, lastHighestIhavesSentResetInterval time.Duration) *RPCSentTracker { cfg, err := config.DefaultConfig() require.NoError(t, err) tracker := NewRPCSentTracker(&RPCSentTrackerConfig{ - Logger: zerolog.Nop(), - RPCSentCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RPCSentCacheCollector: metrics.NewNoopCollector(), - WorkerQueueCacheCollector: metrics.NewNoopCollector(), - WorkerQueueCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - NumOfWorkers: 1, + Logger: zerolog.Nop(), + RPCSentCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RPCSentCacheCollector: metrics.NewNoopCollector(), + WorkerQueueCacheCollector: metrics.NewNoopCollector(), + WorkerQueueCacheSize: cfg.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + NumOfWorkers: 1, + LastHighestIhavesSentResetInterval: lastHighestIhavesSentResetInterval, }) return tracker } From f19483fde61fd2eba129b4749b99815acbfb9336 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 12 Jul 2023 17:52:49 -0400 Subject: [PATCH 220/815] Update rpc_sent_tracker_test.go --- network/p2p/tracer/internal/rpc_sent_tracker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 7d2e9d57a6a..5324be9d459 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -61,7 +61,7 @@ func TestRPCSentTracker_IHave(t *testing.T) { } } rpc := rpcFixture(withIhaves(iHaves)) - tracker.RPCSent(rpc) + require.NoError(t, tracker.RPCSent(rpc)) // eventually we should have tracked numOfMsgIds per single topic require.Eventually(t, func() bool { From 51b646b709f7f44823f37f4b3338dd0ba4237d1f Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 16 Jul 2023 17:01:52 +0100 Subject: [PATCH 221/815] Refactor badger cache and all the client code to use generics instead of interface{} --- storage/badger/approvals.go | 23 ++++----- storage/badger/cache.go | 67 ++++++++++++------------- storage/badger/cache_test.go | 6 +-- storage/badger/chunkDataPacks.go | 24 ++++----- storage/badger/cluster_payloads.go | 20 +++----- storage/badger/commits.go | 20 +++----- storage/badger/dkg_state.go | 20 +++----- storage/badger/epoch_commits.go | 20 +++----- storage/badger/epoch_setups.go | 20 +++----- storage/badger/epoch_statuses.go | 20 +++----- storage/badger/events.go | 29 +++++------ storage/badger/guarantees.go | 18 +++---- storage/badger/headers.go | 41 +++++++--------- storage/badger/index.go | 18 +++---- storage/badger/my_receipts.go | 18 +++---- storage/badger/qcs.go | 20 ++++---- storage/badger/receipts.go | 17 +++---- storage/badger/results.go | 19 +++---- storage/badger/seals.go | 20 +++----- storage/badger/transaction_results.go | 71 +++++++++++---------------- storage/badger/transactions.go | 22 ++++----- 21 files changed, 226 insertions(+), 307 deletions(-) diff --git a/storage/badger/approvals.go b/storage/badger/approvals.go index 5c33ef123f3..c50a4455301 100644 --- a/storage/badger/approvals.go +++ b/storage/badger/approvals.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -17,20 +16,18 @@ import ( // ResultApprovals implements persistent storage for result approvals. type ResultApprovals struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.ResultApproval] } func NewResultApprovals(collector module.CacheMetrics, db *badger.DB) *ResultApprovals { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - approval := val.(*flow.ResultApproval) - return transaction.WithTx(operation.SkipDuplicates(operation.InsertResultApproval(approval))) + store := func(key flow.Identifier, val *flow.ResultApproval) func(*transaction.Tx) error { + return transaction.WithTx(operation.SkipDuplicates(operation.InsertResultApproval(val))) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - approvalID := key.(flow.Identifier) + retrieve := func(approvalID flow.Identifier) func(tx *badger.Txn) (*flow.ResultApproval, error) { var approval flow.ResultApproval - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (*flow.ResultApproval, error) { err := operation.RetrieveResultApproval(approvalID, &approval)(tx) return &approval, err } @@ -38,10 +35,10 @@ func NewResultApprovals(collector module.CacheMetrics, db *badger.DB) *ResultApp res := &ResultApprovals{ db: db, - cache: newCache(collector, metrics.ResourceResultApprovals, - withLimit(flow.DefaultTransactionExpiry+100), - withStore(store), - withRetrieve(retrieve)), + cache: newCache[flow.Identifier, *flow.ResultApproval](collector, metrics.ResourceResultApprovals, + withLimit[flow.Identifier, *flow.ResultApproval](flow.DefaultTransactionExpiry+100), + withStore[flow.Identifier, *flow.ResultApproval](store), + withRetrieve[flow.Identifier, *flow.ResultApproval](retrieve)), } return res @@ -57,7 +54,7 @@ func (r *ResultApprovals) byID(approvalID flow.Identifier) func(*badger.Txn) (*f if err != nil { return nil, err } - return val.(*flow.ResultApproval), nil + return val, nil } } diff --git a/storage/badger/cache.go b/storage/badger/cache.go index 5af5d23f8b1..0ac36a63b29 100644 --- a/storage/badger/cache.go +++ b/storage/badger/cache.go @@ -5,92 +5,91 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - lru "github.com/hashicorp/golang-lru" - + lru "github.com/hashicorp/golang-lru/v2" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/transaction" ) -func withLimit(limit uint) func(*Cache) { - return func(c *Cache) { +func withLimit[K comparable, V any](limit uint) func(*Cache[K, V]) { + return func(c *Cache[K, V]) { c.limit = limit } } -type storeFunc func(key interface{}, val interface{}) func(*transaction.Tx) error +type storeFunc[K comparable, V any] func(key K, val V) func(*transaction.Tx) error const DefaultCacheSize = uint(1000) -func withStore(store storeFunc) func(*Cache) { - return func(c *Cache) { +func withStore[K comparable, V any](store storeFunc[K, V]) func(*Cache[K, V]) { + return func(c *Cache[K, V]) { c.store = store } } -func noStore(key interface{}, val interface{}) func(*transaction.Tx) error { +func noStore[K comparable, V any](_ K, _ V) func(*transaction.Tx) error { return func(tx *transaction.Tx) error { return fmt.Errorf("no store function for cache put available") } } -func noopStore(key interface{}, val interface{}) func(*transaction.Tx) error { +func noopStore[K comparable, V any](_ K, _ V) func(*transaction.Tx) error { return func(tx *transaction.Tx) error { return nil } } -type retrieveFunc func(key interface{}) func(*badger.Txn) (interface{}, error) +type retrieveFunc[K comparable, V any] func(key K) func(*badger.Txn) (V, error) -func withRetrieve(retrieve retrieveFunc) func(*Cache) { - return func(c *Cache) { +func withRetrieve[K comparable, V any](retrieve retrieveFunc[K, V]) func(*Cache[K, V]) { + return func(c *Cache[K, V]) { c.retrieve = retrieve } } -func noRetrieve(key interface{}) func(*badger.Txn) (interface{}, error) { - return func(tx *badger.Txn) (interface{}, error) { - return nil, fmt.Errorf("no retrieve function for cache get available") +func noRetrieve[K comparable, V any](_ K) func(*badger.Txn) (V, error) { + return func(tx *badger.Txn) (V, error) { + var nullV V + return nullV, fmt.Errorf("no retrieve function for cache get available") } } -type Cache struct { +type Cache[K comparable, V any] struct { metrics module.CacheMetrics limit uint - store storeFunc - retrieve retrieveFunc + store storeFunc[K, V] + retrieve retrieveFunc[K, V] resource string - cache *lru.Cache + cache *lru.Cache[K, V] } -func newCache(collector module.CacheMetrics, resourceName string, options ...func(*Cache)) *Cache { - c := Cache{ +func newCache[K comparable, V any](collector module.CacheMetrics, resourceName string, options ...func(*Cache[K, V])) *Cache[K, V] { + c := Cache[K, V]{ metrics: collector, limit: 1000, - store: noStore, - retrieve: noRetrieve, + store: noStore[K, V], + retrieve: noRetrieve[K, V], resource: resourceName, } for _, option := range options { option(&c) } - c.cache, _ = lru.New(int(c.limit)) + c.cache, _ = lru.New[K, V](int(c.limit)) c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) return &c } // IsCached returns true if the key exists in the cache. // It DOES NOT check whether the key exists in the underlying data store. -func (c *Cache) IsCached(key any) bool { - exists := c.cache.Contains(key) - return exists +func (c *Cache[K, V]) IsCached(key K) bool { + return c.cache.Contains(key) } // Get will try to retrieve the resource from cache first, and then from the // injected. During normal operations, the following error returns are expected: // - `storage.ErrNotFound` if key is unknown. -func (c *Cache) Get(key interface{}) func(*badger.Txn) (interface{}, error) { - return func(tx *badger.Txn) (interface{}, error) { +func (c *Cache[K, V]) Get(key K) func(*badger.Txn) (V, error) { + return func(tx *badger.Txn) (V, error) { // check if we have it in the cache resource, cached := c.cache.Get(key) @@ -102,10 +101,12 @@ func (c *Cache) Get(key interface{}) func(*badger.Txn) (interface{}, error) { // get it from the database resource, err := c.retrieve(key)(tx) if err != nil { + if errors.Is(err, storage.ErrNotFound) { c.metrics.CacheNotFound(c.resource) } - return nil, fmt.Errorf("could not retrieve resource: %w", err) + var nullV V + return nullV, fmt.Errorf("could not retrieve resource: %w", err) } c.metrics.CacheMiss(c.resource) @@ -120,12 +121,12 @@ func (c *Cache) Get(key interface{}) func(*badger.Txn) (interface{}, error) { } } -func (c *Cache) Remove(key interface{}) { +func (c *Cache[K, V]) Remove(key K) { c.cache.Remove(key) } // Insert will add a resource directly to the cache with the given ID -func (c *Cache) Insert(key interface{}, resource interface{}) { +func (c *Cache[K, V]) Insert(key K, resource V) { // cache the resource and eject least recently used one if we reached limit evicted := c.cache.Add(key, resource) if !evicted { @@ -134,7 +135,7 @@ func (c *Cache) Insert(key interface{}, resource interface{}) { } // PutTx will return tx which adds a resource to the cache with the given ID. -func (c *Cache) PutTx(key interface{}, resource interface{}) func(*transaction.Tx) error { +func (c *Cache[K, V]) PutTx(key K, resource V) func(*transaction.Tx) error { storeOps := c.store(key, resource) // assemble DB operations to store resource (no execution) return func(tx *transaction.Tx) error { diff --git a/storage/badger/cache_test.go b/storage/badger/cache_test.go index fdc0e73dc51..048ba359b69 100644 --- a/storage/badger/cache_test.go +++ b/storage/badger/cache_test.go @@ -3,15 +3,15 @@ package badger import ( "testing" - "github.com/stretchr/testify/assert" - + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/utils/unittest" + "github.com/stretchr/testify/assert" ) // TestCache_Exists tests existence checking items in the cache. func TestCache_Exists(t *testing.T) { - cache := newCache(metrics.NewNoopCollector(), "test") + cache := newCache[flow.Identifier, any](metrics.NewNoopCollector(), "test") t.Run("non-existent", func(t *testing.T) { key := unittest.IdentifierFixture() diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index c54a95c1c80..1f44627ccce 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -17,28 +16,25 @@ import ( type ChunkDataPacks struct { db *badger.DB collections storage.Collections - byChunkIDCache *Cache + byChunkIDCache *Cache[flow.Identifier, *badgermodel.StoredChunkDataPack] } func NewChunkDataPacks(collector module.CacheMetrics, db *badger.DB, collections storage.Collections, byChunkIDCacheSize uint) *ChunkDataPacks { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - chdp := val.(*badgermodel.StoredChunkDataPack) - return transaction.WithTx(operation.SkipDuplicates(operation.InsertChunkDataPack(chdp))) + store := func(key flow.Identifier, val *badgermodel.StoredChunkDataPack) func(*transaction.Tx) error { + return transaction.WithTx(operation.SkipDuplicates(operation.InsertChunkDataPack(val))) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - chunkID := key.(flow.Identifier) - - var c badgermodel.StoredChunkDataPack - return func(tx *badger.Txn) (interface{}, error) { - err := operation.RetrieveChunkDataPack(chunkID, &c)(tx) + retrieve := func(key flow.Identifier) func(tx *badger.Txn) (*badgermodel.StoredChunkDataPack, error) { + return func(tx *badger.Txn) (*badgermodel.StoredChunkDataPack, error) { + var c badgermodel.StoredChunkDataPack + err := operation.RetrieveChunkDataPack(key, &c)(tx) return &c, err } } - cache := newCache(collector, metrics.ResourceChunkDataPack, - withLimit(byChunkIDCacheSize), + cache := newCache[flow.Identifier, *badgermodel.StoredChunkDataPack](collector, metrics.ResourceChunkDataPack, + withLimit[flow.Identifier, *badgermodel.StoredChunkDataPack](byChunkIDCacheSize), withStore(store), withRetrieve(retrieve), ) @@ -135,7 +131,7 @@ func (ch *ChunkDataPacks) retrieveCHDP(chunkID flow.Identifier) func(*badger.Txn if err != nil { return nil, err } - return val.(*badgermodel.StoredChunkDataPack), nil + return val, nil } } diff --git a/storage/badger/cluster_payloads.go b/storage/badger/cluster_payloads.go index 84e260b9a75..2073d5a8662 100644 --- a/storage/badger/cluster_payloads.go +++ b/storage/badger/cluster_payloads.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -16,30 +15,27 @@ import ( // cluster consensus. type ClusterPayloads struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *cluster.Payload] } func NewClusterPayloads(cacheMetrics module.CacheMetrics, db *badger.DB) *ClusterPayloads { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - blockID := key.(flow.Identifier) - payload := val.(*cluster.Payload) + store := func(blockID flow.Identifier, payload *cluster.Payload) func(*transaction.Tx) error { return transaction.WithTx(procedure.InsertClusterPayload(blockID, payload)) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) + retrieve := func(key flow.Identifier) func(tx *badger.Txn) (*cluster.Payload, error) { var payload cluster.Payload - return func(tx *badger.Txn) (interface{}, error) { - err := procedure.RetrieveClusterPayload(blockID, &payload)(tx) + return func(tx *badger.Txn) (*cluster.Payload, error) { + err := procedure.RetrieveClusterPayload(key, &payload)(tx) return &payload, err } } cp := &ClusterPayloads{ db: db, - cache: newCache(cacheMetrics, metrics.ResourceClusterPayload, - withLimit(flow.DefaultTransactionExpiry*4), + cache: newCache[flow.Identifier, *cluster.Payload](cacheMetrics, metrics.ResourceClusterPayload, + withLimit[flow.Identifier, *cluster.Payload](flow.DefaultTransactionExpiry*4), withStore(store), withRetrieve(retrieve)), } @@ -56,7 +52,7 @@ func (cp *ClusterPayloads) retrieveTx(blockID flow.Identifier) func(*badger.Txn) if err != nil { return nil, err } - return val.(*cluster.Payload), nil + return val, nil } } diff --git a/storage/badger/commits.go b/storage/badger/commits.go index af60946d043..6417d10e679 100644 --- a/storage/badger/commits.go +++ b/storage/badger/commits.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -13,21 +12,18 @@ import ( type Commits struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, flow.StateCommitment] } func NewCommits(collector module.CacheMetrics, db *badger.DB) *Commits { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - blockID := key.(flow.Identifier) - commit := val.(flow.StateCommitment) + store := func(blockID flow.Identifier, commit flow.StateCommitment) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.IndexStateCommitment(blockID, commit))) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) - var commit flow.StateCommitment - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (flow.StateCommitment, error) { + return func(tx *badger.Txn) (flow.StateCommitment, error) { + var commit flow.StateCommitment err := operation.LookupStateCommitment(blockID, &commit)(tx) return commit, err } @@ -35,8 +31,8 @@ func NewCommits(collector module.CacheMetrics, db *badger.DB) *Commits { c := &Commits{ db: db, - cache: newCache(collector, metrics.ResourceCommit, - withLimit(1000), + cache: newCache[flow.Identifier, flow.StateCommitment](collector, metrics.ResourceCommit, + withLimit[flow.Identifier, flow.StateCommitment](1000), withStore(store), withRetrieve(retrieve), ), @@ -55,7 +51,7 @@ func (c *Commits) retrieveTx(blockID flow.Identifier) func(tx *badger.Txn) (flow if err != nil { return flow.DummyStateCommitment, err } - return val.(flow.StateCommitment), nil + return val, nil } } diff --git a/storage/badger/dkg_state.go b/storage/badger/dkg_state.go index 63beb4c23a2..9052999c484 100644 --- a/storage/badger/dkg_state.go +++ b/storage/badger/dkg_state.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" @@ -18,7 +17,7 @@ import ( // computed keys. Must be instantiated using secrets database. type DKGState struct { db *badger.DB - keyCache *Cache + keyCache *Cache[uint64, *encodable.RandomBeaconPrivKey] } // NewDKGState returns the DKGState implementation backed by Badger DB. @@ -28,23 +27,20 @@ func NewDKGState(collector module.CacheMetrics, db *badger.DB) (*DKGState, error return nil, fmt.Errorf("cannot instantiate dkg state storage in non-secret db: %w", err) } - storeKey := func(key interface{}, val interface{}) func(*transaction.Tx) error { - epochCounter := key.(uint64) - info := val.(*encodable.RandomBeaconPrivKey) + storeKey := func(epochCounter uint64, info *encodable.RandomBeaconPrivKey) func(*transaction.Tx) error { return transaction.WithTx(operation.InsertMyBeaconPrivateKey(epochCounter, info)) } - retrieveKey := func(key interface{}) func(*badger.Txn) (interface{}, error) { - epochCounter := key.(uint64) - var info encodable.RandomBeaconPrivKey - return func(tx *badger.Txn) (interface{}, error) { + retrieveKey := func(epochCounter uint64) func(*badger.Txn) (*encodable.RandomBeaconPrivKey, error) { + return func(tx *badger.Txn) (*encodable.RandomBeaconPrivKey, error) { + var info encodable.RandomBeaconPrivKey err := operation.RetrieveMyBeaconPrivateKey(epochCounter, &info)(tx) return &info, err } } - cache := newCache(collector, metrics.ResourceBeaconKey, - withLimit(10), + cache := newCache[uint64, *encodable.RandomBeaconPrivKey](collector, metrics.ResourceBeaconKey, + withLimit[uint64, *encodable.RandomBeaconPrivKey](10), withStore(storeKey), withRetrieve(retrieveKey), ) @@ -67,7 +63,7 @@ func (ds *DKGState) retrieveKeyTx(epochCounter uint64) func(tx *badger.Txn) (*en if err != nil { return nil, err } - return val.(*encodable.RandomBeaconPrivKey), nil + return val, nil } } diff --git a/storage/badger/epoch_commits.go b/storage/badger/epoch_commits.go index 8f9022e7f09..6a302cc6009 100644 --- a/storage/badger/epoch_commits.go +++ b/storage/badger/epoch_commits.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -12,21 +11,18 @@ import ( type EpochCommits struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.EpochCommit] } func NewEpochCommits(collector module.CacheMetrics, db *badger.DB) *EpochCommits { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - id := key.(flow.Identifier) - commit := val.(*flow.EpochCommit) + store := func(id flow.Identifier, commit *flow.EpochCommit) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.InsertEpochCommit(id, commit))) } - retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { - id := key.(flow.Identifier) - var commit flow.EpochCommit - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(id flow.Identifier) func(*badger.Txn) (*flow.EpochCommit, error) { + return func(tx *badger.Txn) (*flow.EpochCommit, error) { + var commit flow.EpochCommit err := operation.RetrieveEpochCommit(id, &commit)(tx) return &commit, err } @@ -34,8 +30,8 @@ func NewEpochCommits(collector module.CacheMetrics, db *badger.DB) *EpochCommits ec := &EpochCommits{ db: db, - cache: newCache(collector, metrics.ResourceEpochCommit, - withLimit(4*flow.DefaultTransactionExpiry), + cache: newCache[flow.Identifier, *flow.EpochCommit](collector, metrics.ResourceEpochCommit, + withLimit[flow.Identifier, *flow.EpochCommit](4*flow.DefaultTransactionExpiry), withStore(store), withRetrieve(retrieve)), } @@ -53,7 +49,7 @@ func (ec *EpochCommits) retrieveTx(commitID flow.Identifier) func(tx *badger.Txn if err != nil { return nil, err } - return val.(*flow.EpochCommit), nil + return val, nil } } diff --git a/storage/badger/epoch_setups.go b/storage/badger/epoch_setups.go index 4b31db4b867..38f0139313f 100644 --- a/storage/badger/epoch_setups.go +++ b/storage/badger/epoch_setups.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -12,22 +11,19 @@ import ( type EpochSetups struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.EpochSetup] } // NewEpochSetups instantiates a new EpochSetups storage. func NewEpochSetups(collector module.CacheMetrics, db *badger.DB) *EpochSetups { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - id := key.(flow.Identifier) - setup := val.(*flow.EpochSetup) + store := func(id flow.Identifier, setup *flow.EpochSetup) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.InsertEpochSetup(id, setup))) } - retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { - id := key.(flow.Identifier) - var setup flow.EpochSetup - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(id flow.Identifier) func(*badger.Txn) (*flow.EpochSetup, error) { + return func(tx *badger.Txn) (*flow.EpochSetup, error) { + var setup flow.EpochSetup err := operation.RetrieveEpochSetup(id, &setup)(tx) return &setup, err } @@ -35,8 +31,8 @@ func NewEpochSetups(collector module.CacheMetrics, db *badger.DB) *EpochSetups { es := &EpochSetups{ db: db, - cache: newCache(collector, metrics.ResourceEpochSetup, - withLimit(4*flow.DefaultTransactionExpiry), + cache: newCache[flow.Identifier, *flow.EpochSetup](collector, metrics.ResourceEpochSetup, + withLimit[flow.Identifier, *flow.EpochSetup](4*flow.DefaultTransactionExpiry), withStore(store), withRetrieve(retrieve)), } @@ -54,7 +50,7 @@ func (es *EpochSetups) retrieveTx(setupID flow.Identifier) func(tx *badger.Txn) if err != nil { return nil, err } - return val.(*flow.EpochSetup), nil + return val, nil } } diff --git a/storage/badger/epoch_statuses.go b/storage/badger/epoch_statuses.go index e5be69ab080..7fb6e93629c 100644 --- a/storage/badger/epoch_statuses.go +++ b/storage/badger/epoch_statuses.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -12,22 +11,19 @@ import ( type EpochStatuses struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.EpochStatus] } // NewEpochStatuses ... func NewEpochStatuses(collector module.CacheMetrics, db *badger.DB) *EpochStatuses { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - blockID := key.(flow.Identifier) - status := val.(*flow.EpochStatus) + store := func(blockID flow.Identifier, status *flow.EpochStatus) func(*transaction.Tx) error { return transaction.WithTx(operation.InsertEpochStatus(blockID, status)) } - retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) - var status flow.EpochStatus - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(blockID flow.Identifier) func(*badger.Txn) (*flow.EpochStatus, error) { + return func(tx *badger.Txn) (*flow.EpochStatus, error) { + var status flow.EpochStatus err := operation.RetrieveEpochStatus(blockID, &status)(tx) return &status, err } @@ -35,8 +31,8 @@ func NewEpochStatuses(collector module.CacheMetrics, db *badger.DB) *EpochStatus es := &EpochStatuses{ db: db, - cache: newCache(collector, metrics.ResourceEpochStatus, - withLimit(4*flow.DefaultTransactionExpiry), + cache: newCache[flow.Identifier, *flow.EpochStatus](collector, metrics.ResourceEpochStatus, + withLimit[flow.Identifier, *flow.EpochStatus](4*flow.DefaultTransactionExpiry), withStore(store), withRetrieve(retrieve)), } @@ -54,7 +50,7 @@ func (es *EpochStatuses) retrieveTx(blockID flow.Identifier) func(tx *badger.Txn if err != nil { return nil, err } - return val.(*flow.EpochStatus), nil + return val, nil } } diff --git a/storage/badger/events.go b/storage/badger/events.go index 59112f01e36..eb9478af07c 100644 --- a/storage/badger/events.go +++ b/storage/badger/events.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -14,14 +13,13 @@ import ( type Events struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, []flow.Event] } func NewEvents(collector module.CacheMetrics, db *badger.DB) *Events { - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) ([]flow.Event, error) { var events []flow.Event - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) ([]flow.Event, error) { err := operation.LookupEventsByBlockID(blockID, &events)(tx) return events, handleError(err, flow.Event{}) } @@ -29,8 +27,8 @@ func NewEvents(collector module.CacheMetrics, db *badger.DB) *Events { return &Events{ db: db, - cache: newCache(collector, metrics.ResourceEvents, - withStore(noopStore), + cache: newCache[flow.Identifier, []flow.Event](collector, metrics.ResourceEvents, + withStore(noopStore[flow.Identifier, []flow.Event]), withRetrieve(retrieve)), } } @@ -77,7 +75,7 @@ func (e *Events) ByBlockID(blockID flow.Identifier) ([]flow.Event, error) { if err != nil { return nil, err } - return val.([]flow.Event), nil + return val, nil } // ByBlockIDTransactionID returns the events for the given block ID and transaction ID @@ -142,14 +140,13 @@ func (e *Events) BatchRemoveByBlockID(blockID flow.Identifier, batch storage.Bat type ServiceEvents struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, []flow.Event] } func NewServiceEvents(collector module.CacheMetrics, db *badger.DB) *ServiceEvents { - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) ([]flow.Event, error) { var events []flow.Event - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) ([]flow.Event, error) { err := operation.LookupServiceEventsByBlockID(blockID, &events)(tx) return events, handleError(err, flow.Event{}) } @@ -157,9 +154,9 @@ func NewServiceEvents(collector module.CacheMetrics, db *badger.DB) *ServiceEven return &ServiceEvents{ db: db, - cache: newCache(collector, metrics.ResourceEvents, - withStore(noopStore), - withRetrieve(retrieve)), + cache: newCache[flow.Identifier, []flow.Event](collector, metrics.ResourceEvents, + withStore(noopStore[flow.Identifier, []flow.Event]), + withRetrieve[flow.Identifier, []flow.Event](retrieve)), } } @@ -190,7 +187,7 @@ func (e *ServiceEvents) ByBlockID(blockID flow.Identifier) ([]flow.Event, error) if err != nil { return nil, err } - return val.([]flow.Event), nil + return val, nil } // RemoveByBlockID removes service events by block ID diff --git a/storage/badger/guarantees.go b/storage/badger/guarantees.go index 23bc929db9c..9087c5463f3 100644 --- a/storage/badger/guarantees.go +++ b/storage/badger/guarantees.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -13,21 +12,18 @@ import ( // Guarantees implements persistent storage for collection guarantees. type Guarantees struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.CollectionGuarantee] } func NewGuarantees(collector module.CacheMetrics, db *badger.DB, cacheSize uint) *Guarantees { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - collID := key.(flow.Identifier) - guarantee := val.(*flow.CollectionGuarantee) + store := func(collID flow.Identifier, guarantee *flow.CollectionGuarantee) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.InsertGuarantee(collID, guarantee))) } - retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { - collID := key.(flow.Identifier) + retrieve := func(collID flow.Identifier) func(*badger.Txn) (*flow.CollectionGuarantee, error) { var guarantee flow.CollectionGuarantee - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (*flow.CollectionGuarantee, error) { err := operation.RetrieveGuarantee(collID, &guarantee)(tx) return &guarantee, err } @@ -35,8 +31,8 @@ func NewGuarantees(collector module.CacheMetrics, db *badger.DB, cacheSize uint) g := &Guarantees{ db: db, - cache: newCache(collector, metrics.ResourceGuarantee, - withLimit(cacheSize), + cache: newCache[flow.Identifier, *flow.CollectionGuarantee](collector, metrics.ResourceGuarantee, + withLimit[flow.Identifier, *flow.CollectionGuarantee](cacheSize), withStore(store), withRetrieve(retrieve)), } @@ -54,7 +50,7 @@ func (g *Guarantees) retrieveTx(collID flow.Identifier) func(*badger.Txn) (*flow if err != nil { return nil, err } - return val.(*flow.CollectionGuarantee), nil + return val, nil } } diff --git a/storage/badger/headers.go b/storage/badger/headers.go index ca7a37b524c..17a1f7feaa8 100644 --- a/storage/badger/headers.go +++ b/storage/badger/headers.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -18,39 +17,33 @@ import ( // Headers implements a simple read-only header storage around a badger DB. type Headers struct { db *badger.DB - cache *Cache - heightCache *Cache + cache *Cache[flow.Identifier, *flow.Header] + heightCache *Cache[uint64, flow.Identifier] } func NewHeaders(collector module.CacheMetrics, db *badger.DB) *Headers { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - blockID := key.(flow.Identifier) - header := val.(*flow.Header) + store := func(blockID flow.Identifier, header *flow.Header) func(*transaction.Tx) error { return transaction.WithTx(operation.InsertHeader(blockID, header)) } // CAUTION: should only be used to index FINALIZED blocks by their // respective height - storeHeight := func(key interface{}, val interface{}) func(*transaction.Tx) error { - height := key.(uint64) - id := val.(flow.Identifier) + storeHeight := func(height uint64, id flow.Identifier) func(*transaction.Tx) error { return transaction.WithTx(operation.IndexBlockHeight(height, id)) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.Header, error) { var header flow.Header - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (*flow.Header, error) { err := operation.RetrieveHeader(blockID, &header)(tx) return &header, err } } - retrieveHeight := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - height := key.(uint64) - var id flow.Identifier - return func(tx *badger.Txn) (interface{}, error) { + retrieveHeight := func(height uint64) func(tx *badger.Txn) (flow.Identifier, error) { + return func(tx *badger.Txn) (flow.Identifier, error) { + var id flow.Identifier err := operation.LookupBlockHeight(height, &id)(tx) return id, err } @@ -58,15 +51,15 @@ func NewHeaders(collector module.CacheMetrics, db *badger.DB) *Headers { h := &Headers{ db: db, - cache: newCache(collector, metrics.ResourceHeader, - withLimit(4*flow.DefaultTransactionExpiry), + cache: newCache[flow.Identifier, *flow.Header](collector, metrics.ResourceHeader, + withLimit[flow.Identifier, *flow.Header](4*flow.DefaultTransactionExpiry), withStore(store), withRetrieve(retrieve)), - heightCache: newCache(collector, metrics.ResourceFinalizedHeight, - withLimit(4*flow.DefaultTransactionExpiry), - withStore(storeHeight), - withRetrieve(retrieveHeight)), + heightCache: newCache[uint64, flow.Identifier](collector, metrics.ResourceFinalizedHeight, + withLimit[uint64, flow.Identifier](4*flow.DefaultTransactionExpiry), + withStore[uint64, flow.Identifier](storeHeight), + withRetrieve[uint64, flow.Identifier](retrieveHeight)), } return h @@ -82,7 +75,7 @@ func (h *Headers) retrieveTx(blockID flow.Identifier) func(*badger.Txn) (*flow.H if err != nil { return nil, err } - return val.(*flow.Header), nil + return val, nil } } @@ -93,7 +86,7 @@ func (h *Headers) retrieveIdByHeightTx(height uint64) func(*badger.Txn) (flow.Id if err != nil { return flow.ZeroID, fmt.Errorf("failed to retrieve block ID for height %d: %w", height, err) } - return blockID.(flow.Identifier), nil + return blockID, nil } } diff --git a/storage/badger/index.go b/storage/badger/index.go index 4a5b4ba32b6..7c8dcffbd7b 100644 --- a/storage/badger/index.go +++ b/storage/badger/index.go @@ -4,7 +4,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -16,21 +15,18 @@ import ( // Index implements a simple read-only payload storage around a badger DB. type Index struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.Index] } func NewIndex(collector module.CacheMetrics, db *badger.DB) *Index { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - blockID := key.(flow.Identifier) - index := val.(*flow.Index) + store := func(blockID flow.Identifier, index *flow.Index) func(*transaction.Tx) error { return transaction.WithTx(procedure.InsertIndex(blockID, index)) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.Index, error) { var index flow.Index - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (*flow.Index, error) { err := procedure.RetrieveIndex(blockID, &index)(tx) return &index, err } @@ -38,8 +34,8 @@ func NewIndex(collector module.CacheMetrics, db *badger.DB) *Index { p := &Index{ db: db, - cache: newCache(collector, metrics.ResourceIndex, - withLimit(flow.DefaultTransactionExpiry+100), + cache: newCache[flow.Identifier, *flow.Index](collector, metrics.ResourceIndex, + withLimit[flow.Identifier, *flow.Index](flow.DefaultTransactionExpiry+100), withStore(store), withRetrieve(retrieve)), } @@ -57,7 +53,7 @@ func (i *Index) retrieveTx(blockID flow.Identifier) func(*badger.Txn) (*flow.Ind if err != nil { return nil, err } - return val.(*flow.Index), nil + return val, nil } } diff --git a/storage/badger/my_receipts.go b/storage/badger/my_receipts.go index 37054a35145..fa1f3130837 100644 --- a/storage/badger/my_receipts.go +++ b/storage/badger/my_receipts.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -21,14 +20,13 @@ import ( type MyExecutionReceipts struct { genericReceipts *ExecutionReceipts db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.ExecutionReceipt] } // NewMyExecutionReceipts creates instance of MyExecutionReceipts which is a wrapper wrapper around badger.ExecutionReceipts // It's useful for execution nodes to keep track of produced execution receipts. func NewMyExecutionReceipts(collector module.CacheMetrics, db *badger.DB, receipts *ExecutionReceipts) *MyExecutionReceipts { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - receipt := val.(*flow.ExecutionReceipt) + store := func(key flow.Identifier, receipt *flow.ExecutionReceipt) func(*transaction.Tx) error { // assemble DB operations to store receipt (no execution) storeReceiptOps := receipts.storeTx(receipt) // assemble DB operations to index receipt as one of my own (no execution) @@ -68,10 +66,8 @@ func NewMyExecutionReceipts(collector module.CacheMetrics, db *badger.DB, receip } } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) - - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { + return func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { var receiptID flow.Identifier err := operation.LookupOwnExecutionReceipt(blockID, &receiptID)(tx) if err != nil { @@ -88,8 +84,8 @@ func NewMyExecutionReceipts(collector module.CacheMetrics, db *badger.DB, receip return &MyExecutionReceipts{ genericReceipts: receipts, db: db, - cache: newCache(collector, metrics.ResourceMyReceipt, - withLimit(flow.DefaultTransactionExpiry+100), + cache: newCache[flow.Identifier, *flow.ExecutionReceipt](collector, metrics.ResourceMyReceipt, + withLimit[flow.Identifier, *flow.ExecutionReceipt](flow.DefaultTransactionExpiry+100), withStore(store), withRetrieve(retrieve)), } @@ -108,7 +104,7 @@ func (m *MyExecutionReceipts) myReceipt(blockID flow.Identifier) func(*badger.Tx if err != nil { return nil, err } - return val.(*flow.ExecutionReceipt), nil + return val, nil } } diff --git a/storage/badger/qcs.go b/storage/badger/qcs.go index 432a0f8dfd2..51e01afd82f 100644 --- a/storage/badger/qcs.go +++ b/storage/badger/qcs.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -14,7 +13,7 @@ import ( // QuorumCertificates implements persistent storage for quorum certificates. type QuorumCertificates struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.QuorumCertificate] } var _ storage.QuorumCertificates = (*QuorumCertificates)(nil) @@ -22,15 +21,14 @@ var _ storage.QuorumCertificates = (*QuorumCertificates)(nil) // NewQuorumCertificates Creates QuorumCertificates instance which is a database of quorum certificates // which supports storing, caching and retrieving by block ID. func NewQuorumCertificates(collector module.CacheMetrics, db *badger.DB, cacheSize uint) *QuorumCertificates { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - qc := val.(*flow.QuorumCertificate) + store := func(_ flow.Identifier, qc *flow.QuorumCertificate) func(*transaction.Tx) error { + //qc := val.(*flow.QuorumCertificate) return transaction.WithTx(operation.InsertQuorumCertificate(qc)) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - blockID := key.(flow.Identifier) - var qc flow.QuorumCertificate - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.QuorumCertificate, error) { + return func(tx *badger.Txn) (*flow.QuorumCertificate, error) { + var qc flow.QuorumCertificate err := operation.RetrieveQuorumCertificate(blockID, &qc)(tx) return &qc, err } @@ -38,8 +36,8 @@ func NewQuorumCertificates(collector module.CacheMetrics, db *badger.DB, cacheSi return &QuorumCertificates{ db: db, - cache: newCache(collector, metrics.ResourceQC, - withLimit(cacheSize), + cache: newCache[flow.Identifier, *flow.QuorumCertificate](collector, metrics.ResourceQC, + withLimit[flow.Identifier, *flow.QuorumCertificate](cacheSize), withStore(store), withRetrieve(retrieve)), } @@ -61,6 +59,6 @@ func (q *QuorumCertificates) retrieveTx(blockID flow.Identifier) func(*badger.Tx if err != nil { return nil, err } - return val.(*flow.QuorumCertificate), nil + return val, nil } } diff --git a/storage/badger/receipts.go b/storage/badger/receipts.go index fb4996b82d3..a6b0cf14d43 100644 --- a/storage/badger/receipts.go +++ b/storage/badger/receipts.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -18,14 +17,13 @@ import ( type ExecutionReceipts struct { db *badger.DB results *ExecutionResults - cache *Cache + cache *Cache[flow.Identifier, *flow.ExecutionReceipt] } // NewExecutionReceipts Creates ExecutionReceipts instance which is a database of receipts which // supports storing and indexing receipts by receipt ID and block ID. func NewExecutionReceipts(collector module.CacheMetrics, db *badger.DB, results *ExecutionResults, cacheSize uint) *ExecutionReceipts { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - receipt := val.(*flow.ExecutionReceipt) + store := func(receiptTD flow.Identifier, receipt *flow.ExecutionReceipt) func(*transaction.Tx) error { receiptID := receipt.ID() // assemble DB operations to store result (no execution) @@ -54,9 +52,8 @@ func NewExecutionReceipts(collector module.CacheMetrics, db *badger.DB, results } } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - receiptID := key.(flow.Identifier) - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(receiptID flow.Identifier) func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { + return func(tx *badger.Txn) (*flow.ExecutionReceipt, error) { var meta flow.ExecutionReceiptMeta err := operation.RetrieveExecutionReceiptMeta(receiptID, &meta)(tx) if err != nil { @@ -73,8 +70,8 @@ func NewExecutionReceipts(collector module.CacheMetrics, db *badger.DB, results return &ExecutionReceipts{ db: db, results: results, - cache: newCache(collector, metrics.ResourceReceipt, - withLimit(cacheSize), + cache: newCache[flow.Identifier, *flow.ExecutionReceipt](collector, metrics.ResourceReceipt, + withLimit[flow.Identifier, *flow.ExecutionReceipt](cacheSize), withStore(store), withRetrieve(retrieve)), } @@ -92,7 +89,7 @@ func (r *ExecutionReceipts) byID(receiptID flow.Identifier) func(*badger.Txn) (* if err != nil { return nil, err } - return val.(*flow.ExecutionReceipt), nil + return val, nil } } diff --git a/storage/badger/results.go b/storage/badger/results.go index c6160f5dcb5..f6be4f6318b 100644 --- a/storage/badger/results.go +++ b/storage/badger/results.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -17,22 +16,20 @@ import ( // ExecutionResults implements persistent storage for execution results. type ExecutionResults struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.ExecutionResult] } var _ storage.ExecutionResults = (*ExecutionResults)(nil) func NewExecutionResults(collector module.CacheMetrics, db *badger.DB) *ExecutionResults { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - result := val.(*flow.ExecutionResult) + store := func(_ flow.Identifier, result *flow.ExecutionResult) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.InsertExecutionResult(result))) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - resultID := key.(flow.Identifier) - var result flow.ExecutionResult - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(resultID flow.Identifier) func(tx *badger.Txn) (*flow.ExecutionResult, error) { + return func(tx *badger.Txn) (*flow.ExecutionResult, error) { + var result flow.ExecutionResult err := operation.RetrieveExecutionResult(resultID, &result)(tx) return &result, err } @@ -40,8 +37,8 @@ func NewExecutionResults(collector module.CacheMetrics, db *badger.DB) *Executio res := &ExecutionResults{ db: db, - cache: newCache(collector, metrics.ResourceResult, - withLimit(flow.DefaultTransactionExpiry+100), + cache: newCache[flow.Identifier, *flow.ExecutionResult](collector, metrics.ResourceResult, + withLimit[flow.Identifier, *flow.ExecutionResult](flow.DefaultTransactionExpiry+100), withStore(store), withRetrieve(retrieve)), } @@ -59,7 +56,7 @@ func (r *ExecutionResults) byID(resultID flow.Identifier) func(*badger.Txn) (*fl if err != nil { return nil, err } - return val.(*flow.ExecutionResult), nil + return val, nil } } diff --git a/storage/badger/seals.go b/storage/badger/seals.go index aa68511ed7e..c5772ea371a 100644 --- a/storage/badger/seals.go +++ b/storage/badger/seals.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -16,21 +15,18 @@ import ( type Seals struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.Seal] } func NewSeals(collector module.CacheMetrics, db *badger.DB) *Seals { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - sealID := key.(flow.Identifier) - seal := val.(*flow.Seal) + store := func(sealID flow.Identifier, seal *flow.Seal) func(*transaction.Tx) error { return transaction.WithTx(operation.SkipDuplicates(operation.InsertSeal(sealID, seal))) } - retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { - sealID := key.(flow.Identifier) - var seal flow.Seal - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(sealID flow.Identifier) func(*badger.Txn) (*flow.Seal, error) { + return func(tx *badger.Txn) (*flow.Seal, error) { + var seal flow.Seal err := operation.RetrieveSeal(sealID, &seal)(tx) return &seal, err } @@ -38,8 +34,8 @@ func NewSeals(collector module.CacheMetrics, db *badger.DB) *Seals { s := &Seals{ db: db, - cache: newCache(collector, metrics.ResourceSeal, - withLimit(flow.DefaultTransactionExpiry+100), + cache: newCache[flow.Identifier, *flow.Seal](collector, metrics.ResourceSeal, + withLimit[flow.Identifier, *flow.Seal](flow.DefaultTransactionExpiry+100), withStore(store), withRetrieve(retrieve)), } @@ -57,7 +53,7 @@ func (s *Seals) retrieveTx(sealID flow.Identifier) func(*badger.Txn) (*flow.Seal if err != nil { return nil, err } - return val.(*flow.Seal), err + return val, err } } diff --git a/storage/badger/transaction_results.go b/storage/badger/transaction_results.go index 77cc103e8b5..6057b210127 100644 --- a/storage/badger/transaction_results.go +++ b/storage/badger/transaction_results.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -16,9 +15,9 @@ import ( type TransactionResults struct { db *badger.DB - cache *Cache - indexCache *Cache - blockCache *Cache + cache *Cache[string, flow.TransactionResult] + indexCache *Cache[string, flow.TransactionResult] + blockCache *Cache[string, []flow.TransactionResult] } func KeyFromBlockIDTransactionID(blockID flow.Identifier, txID flow.Identifier) string { @@ -83,43 +82,43 @@ func KeyToBlockID(key string) (flow.Identifier, error) { } func NewTransactionResults(collector module.CacheMetrics, db *badger.DB, transactionResultsCacheSize uint) *TransactionResults { - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { + retrieve := func(key string) func(tx *badger.Txn) (flow.TransactionResult, error) { var txResult flow.TransactionResult - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (flow.TransactionResult, error) { - blockID, txID, err := KeyToBlockIDTransactionID(key.(string)) + blockID, txID, err := KeyToBlockIDTransactionID(key) if err != nil { - return nil, fmt.Errorf("could not convert key: %w", err) + return flow.TransactionResult{}, fmt.Errorf("could not convert key: %w", err) } err = operation.RetrieveTransactionResult(blockID, txID, &txResult)(tx) if err != nil { - return nil, handleError(err, flow.TransactionResult{}) + return flow.TransactionResult{}, handleError(err, flow.TransactionResult{}) } return txResult, nil } } - retrieveIndex := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { + retrieveIndex := func(key string) func(tx *badger.Txn) (flow.TransactionResult, error) { var txResult flow.TransactionResult - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (flow.TransactionResult, error) { - blockID, txIndex, err := KeyToBlockIDIndex(key.(string)) + blockID, txIndex, err := KeyToBlockIDIndex(key) if err != nil { - return nil, fmt.Errorf("could not convert index key: %w", err) + return flow.TransactionResult{}, fmt.Errorf("could not convert index key: %w", err) } err = operation.RetrieveTransactionResultByIndex(blockID, txIndex, &txResult)(tx) if err != nil { - return nil, handleError(err, flow.TransactionResult{}) + return flow.TransactionResult{}, handleError(err, flow.TransactionResult{}) } return txResult, nil } } - retrieveForBlock := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { + retrieveForBlock := func(key string) func(tx *badger.Txn) ([]flow.TransactionResult, error) { var txResults []flow.TransactionResult - return func(tx *badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) ([]flow.TransactionResult, error) { - blockID, err := KeyToBlockID(key.(string)) + blockID, err := KeyToBlockID(key) if err != nil { return nil, fmt.Errorf("could not convert index key: %w", err) } @@ -133,19 +132,19 @@ func NewTransactionResults(collector module.CacheMetrics, db *badger.DB, transac } return &TransactionResults{ db: db, - cache: newCache(collector, metrics.ResourceTransactionResults, - withLimit(transactionResultsCacheSize), - withStore(noopStore), - withRetrieve(retrieve), + cache: newCache[string, flow.TransactionResult](collector, metrics.ResourceTransactionResults, + withLimit[string, flow.TransactionResult](transactionResultsCacheSize), + withStore(noopStore[string, flow.TransactionResult]), + withRetrieve[string, flow.TransactionResult](retrieve), ), - indexCache: newCache(collector, metrics.ResourceTransactionResultIndices, - withLimit(transactionResultsCacheSize), - withStore(noopStore), + indexCache: newCache[string, flow.TransactionResult](collector, metrics.ResourceTransactionResultIndices, + withLimit[string, flow.TransactionResult](transactionResultsCacheSize), + withStore(noopStore[string, flow.TransactionResult]), withRetrieve(retrieveIndex), ), - blockCache: newCache(collector, metrics.ResourceTransactionResultIndices, - withLimit(transactionResultsCacheSize), - withStore(noopStore), + blockCache: newCache[string, []flow.TransactionResult](collector, metrics.ResourceTransactionResultIndices, + withLimit[string, []flow.TransactionResult](transactionResultsCacheSize), + withStore(noopStore[string, []flow.TransactionResult]), withRetrieve(retrieveForBlock), ), } @@ -190,14 +189,10 @@ func (tr *TransactionResults) ByBlockIDTransactionID(blockID flow.Identifier, tx tx := tr.db.NewTransaction(false) defer tx.Discard() key := KeyFromBlockIDTransactionID(blockID, txID) - val, err := tr.cache.Get(key)(tx) + transactionResult, err := tr.cache.Get(key)(tx) if err != nil { return nil, err } - transactionResult, ok := val.(flow.TransactionResult) - if !ok { - return nil, fmt.Errorf("could not convert transaction result: %w", err) - } return &transactionResult, nil } @@ -206,14 +201,10 @@ func (tr *TransactionResults) ByBlockIDTransactionIndex(blockID flow.Identifier, tx := tr.db.NewTransaction(false) defer tx.Discard() key := KeyFromBlockIDIndex(blockID, txIndex) - val, err := tr.indexCache.Get(key)(tx) + transactionResult, err := tr.indexCache.Get(key)(tx) if err != nil { return nil, err } - transactionResult, ok := val.(flow.TransactionResult) - if !ok { - return nil, fmt.Errorf("could not convert transaction result: %w", err) - } return &transactionResult, nil } @@ -222,14 +213,10 @@ func (tr *TransactionResults) ByBlockID(blockID flow.Identifier) ([]flow.Transac tx := tr.db.NewTransaction(false) defer tx.Discard() key := KeyFromBlockID(blockID) - val, err := tr.blockCache.Get(key)(tx) + transactionResults, err := tr.blockCache.Get(key)(tx) if err != nil { return nil, err } - transactionResults, ok := val.([]flow.TransactionResult) - if !ok { - return nil, fmt.Errorf("could not convert transaction result: %w", err) - } return transactionResults, nil } diff --git a/storage/badger/transactions.go b/storage/badger/transactions.go index 97cd6e98293..84d02b229af 100644 --- a/storage/badger/transactions.go +++ b/storage/badger/transactions.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -13,21 +12,18 @@ import ( // Transactions ... type Transactions struct { db *badger.DB - cache *Cache + cache *Cache[flow.Identifier, *flow.TransactionBody] } // NewTransactions ... func NewTransactions(cacheMetrics module.CacheMetrics, db *badger.DB) *Transactions { - store := func(key interface{}, val interface{}) func(*transaction.Tx) error { - txID := key.(flow.Identifier) - flowTx := val.(*flow.TransactionBody) - return transaction.WithTx(operation.SkipDuplicates(operation.InsertTransaction(txID, flowTx))) + store := func(txID flow.Identifier, flowTX *flow.TransactionBody) func(*transaction.Tx) error { + return transaction.WithTx(operation.SkipDuplicates(operation.InsertTransaction(txID, flowTX))) } - retrieve := func(key interface{}) func(tx *badger.Txn) (interface{}, error) { - txID := key.(flow.Identifier) - var flowTx flow.TransactionBody - return func(tx *badger.Txn) (interface{}, error) { + retrieve := func(txID flow.Identifier) func(tx *badger.Txn) (*flow.TransactionBody, error) { + return func(tx *badger.Txn) (*flow.TransactionBody, error) { + var flowTx flow.TransactionBody err := operation.RetrieveTransaction(txID, &flowTx)(tx) return &flowTx, err } @@ -35,8 +31,8 @@ func NewTransactions(cacheMetrics module.CacheMetrics, db *badger.DB) *Transacti t := &Transactions{ db: db, - cache: newCache(cacheMetrics, metrics.ResourceTransaction, - withLimit(flow.DefaultTransactionExpiry+100), + cache: newCache[flow.Identifier, *flow.TransactionBody](cacheMetrics, metrics.ResourceTransaction, + withLimit[flow.Identifier, *flow.TransactionBody](flow.DefaultTransactionExpiry+100), withStore(store), withRetrieve(retrieve)), } @@ -66,6 +62,6 @@ func (t *Transactions) retrieveTx(txID flow.Identifier) func(*badger.Txn) (*flow if err != nil { return nil, err } - return val.(*flow.TransactionBody), err + return val, err } } From 02be088f16dc40ff5a6eda0d06b3aa193c0bc52b Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Mon, 17 Jul 2023 16:32:44 +0300 Subject: [PATCH 222/815] Fixed remarks --- .../access/rpc/backend/connection_factory.go | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 114a73307a1..baf25339067 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -69,6 +69,7 @@ type ConnectionFactoryImpl struct { type CachedClient struct { ClientConn *grpc.ClientConn Address string + mutex sync.Mutex timeout time.Duration closeRequested *atomic.Bool wg sync.WaitGroup @@ -90,6 +91,10 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor + // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called + // in the same order during gRPC requests. It is crucial to ensure that the request watcher interceptor is added + // first. This interceptor is responsible for executing necessary request monitoring before passing control to + // subsequent interceptors. if cachedClient != nil { connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) } @@ -125,10 +130,14 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout } cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") - cf.ConnectionsCache.Add(grpcAddress, store) + _ = cf.ConnectionsCache.Add(grpcAddress, store) if cf.AccessMetrics != nil { cf.AccessMetrics.ConnectionAddedToPool() } + cf.mutex.Unlock() + + store.mutex.Lock() + defer store.mutex.Unlock() conn, err := cf.createConnection(grpcAddress, timeout, store) if err != nil { @@ -141,8 +150,6 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout cf.AccessMetrics.TotalConnectionsInPool(uint(cf.ConnectionsCache.Len()), cf.CacheSize) } - cf.mutex.Unlock() - return conn, nil } @@ -237,7 +244,9 @@ func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) // requests to complete before closing the connection. func (s *CachedClient) Close() { // Mark the connection for closure - s.closeRequested.Store(true) + if swapped := s.closeRequested.CompareAndSwap(false, true); !swapped { + return + } // If there are ongoing requests, wait for them to complete asynchronously go func() { @@ -278,16 +287,12 @@ func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClien return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) } - // Increment the request counter to track ongoing requests + // Increment the request counter to track ongoing requests, then + // decrement the request counter before returning cachedClient.wg.Add(1) - + defer cachedClient.wg.Done() // Invoke the actual RPC method - err := invoker(ctx, method, req, reply, cc, opts...) - - // Decrement the request counter - cachedClient.wg.Done() - - return err + return invoker(ctx, method, req, reply, cc, opts...) } return requestWatcherInterceptor From e2547b693eafbad763695f6ea15915f8ade29ea7 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 16:10:23 +0300 Subject: [PATCH 223/815] Added evicting cache client test --- .../rpc/backend/connection_factory_test.go | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 6ddfbe12a2a..fda622992ef 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -462,10 +462,6 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { client, _, err := connectionFactory.GetExecutionAPIClient(clientAddress) assert.NoError(t, err) - result, _ := cache.Get(clientAddress) - clientConn := result.(*CachedClient).ClientConn - clientConn.GetState() - ctx := context.Background() // Generate random number of requests @@ -499,6 +495,49 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { }) } +// TestExecutionEvictingCacheClients +func TestExecutionEvictingCacheClients(t *testing.T) { + // Add createCollNode function to recreate it each time for rapid test + cn := new(collectionNode) + cn.start(t) + defer cn.stop(t) + + // setup the handler mock + req := &access.PingRequest{} + resp := &access.PingResponse{} + cn.handler.On("Ping", testifymock.Anything, req).After(3*time.Second).Return(resp, nil) + + // create the factory + connectionFactory := new(ConnectionFactoryImpl) + // set the execution grpc port + connectionFactory.ExecutionGRPCPort = cn.port + // set the execution grpc client timeout + connectionFactory.ExecutionNodeGRPCTimeout = 10 * time.Second + // set the connection pool cache size + cacheSize := 1 + cache, _ := lru.New(cacheSize) + connectionFactory.ConnectionsCache = cache + connectionFactory.CacheSize = uint(cacheSize) + // set metrics reporting + connectionFactory.AccessMetrics = metrics.NewNoopCollector() + + clientAddress := cn.listener.Addr().String() + // create the execution API client + client, _, err := connectionFactory.GetAccessAPIClient(clientAddress) + assert.NoError(t, err) + + time.AfterFunc(time.Second, func() { + fmt.Println("Function called after 1 seconds") + connectionFactory.InvalidateAccessAPIClient(clientAddress) + }) + + ctx := context.Background() + fmt.Println("Ping started") + _, err = client.Ping(ctx, req) + fmt.Println("Ping finished") + +} + // node mocks a flow node that runs a GRPC server type node struct { server *grpc.Server From dbe364cf52761229f5181e859d07c4469101afe7 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 16:19:25 +0300 Subject: [PATCH 224/815] Change config from pointer to value --- cmd/access/node_builder/access_node_builder.go | 2 +- engine/access/rpc/backend/connection_factory.go | 8 ++++---- engine/access/rpc/backend/connection_factory_test.go | 4 ++-- engine/access/rpc/engine.go | 9 ++------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 1400bf46873..a3bf23df03c 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -160,7 +160,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { FixedExecutionNodeIDs: nil, ArchiveAddressList: nil, MaxMsgSize: grpcutils.DefaultMaxMsgSize, - CircuitBreakerConfig: &backend.CircuitBreakerConfig{ + CircuitBreakerConfig: backend.CircuitBreakerConfig{ Enabled: false, RestoreTimeout: 60 * time.Second, MaxFailures: 5, diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 98e84ddffe2..b5013d4550d 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -72,7 +72,7 @@ type ConnectionFactoryImpl struct { AccessMetrics module.AccessMetrics Log zerolog.Logger mutex sync.Mutex - CircuitBreakerConfig *CircuitBreakerConfig + CircuitBreakerConfig CircuitBreakerConfig } // CircuitBreakerConfig is a configuration struct for the circuit breaker. @@ -116,7 +116,7 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor - if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { + if cf.CircuitBreakerConfig.Enabled { connInterceptors = append(connInterceptors, cf.createCircuitBreakerInterceptor()) } else { connInterceptors = append(connInterceptors, cf.createClientInvalidationInterceptor(address, clientType)) @@ -301,7 +301,7 @@ func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor( address string, clientType clientType, ) grpc.UnaryClientInterceptor { - if cf.CircuitBreakerConfig == nil || !cf.CircuitBreakerConfig.Enabled { + if !cf.CircuitBreakerConfig.Enabled { clientInvalidationInterceptor := func( ctx context.Context, method string, @@ -351,7 +351,7 @@ func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor( // created if the circuit breaker is enabled. All invocations will go through the circuit breaker to be tracked for // success or failure of the call. func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { - if cf.CircuitBreakerConfig != nil && cf.CircuitBreakerConfig.Enabled { + if cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ // The restore timeout is defined to automatically return the circuit breaker to the HalfClose state. Timeout: cf.CircuitBreakerConfig.RestoreTimeout, diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 73a0be84f84..e9f6f1a4819 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -433,7 +433,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { connectionFactory.ExecutionNodeGRPCTimeout = requestTimeout // Set the configuration for the circuit breaker. - connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ + connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, MaxRequests: 1, @@ -516,7 +516,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { connectionFactory.CollectionNodeGRPCTimeout = requestTimeout // Set the configuration for the circuit breaker. - connectionFactory.CircuitBreakerConfig = &CircuitBreakerConfig{ + connectionFactory.CircuitBreakerConfig = CircuitBreakerConfig{ Enabled: true, MaxFailures: 1, MaxRequests: 1, diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 7f9a62809c8..c4b36c263b6 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -48,7 +48,7 @@ type Config struct { PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node - CircuitBreakerConfig *backend.CircuitBreakerConfig // the configuration for circuit breaker + CircuitBreakerConfig backend.CircuitBreakerConfig // the configuration for circuit breaker } // Engine exposes the server with a simplified version of the Access API. @@ -175,11 +175,6 @@ func NewBuilder(log zerolog.Logger, CircuitBreakerConfig: config.CircuitBreakerConfig, } - circuitBreakerEnabled := false - if config.CircuitBreakerConfig != nil { - circuitBreakerEnabled = config.CircuitBreakerConfig.Enabled - } - backend := backend.New(state, collectionRPC, historicalAccessNodes, @@ -199,7 +194,7 @@ func NewBuilder(log zerolog.Logger, log, backend.DefaultSnapshotHistoryLimit, config.ArchiveAddressList, - circuitBreakerEnabled, + config.CircuitBreakerConfig.Enabled, ) finalizedCache, finalizedCacheWorker, err := events.NewFinalizedHeaderCache(state) From 1b2e6dfe88c7858186cf73eadf61fa7630ca52a5 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 16:20:17 +0300 Subject: [PATCH 225/815] Removed unused field --- engine/access/rpc/backend/node_communicator.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 00963457d86..08852180eba 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -12,15 +12,13 @@ const maxFailedRequestCount = 3 // NodeCommunicator is responsible for calling available nodes in the backend. type NodeCommunicator struct { - circuitBreakerEnabled bool - nodeSelectorFactory NodeSelectorFactory + nodeSelectorFactory NodeSelectorFactory } // NewNodeCommunicator creates a new instance of NodeCommunicator. func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { return &NodeCommunicator{ - circuitBreakerEnabled: circuitBreakerEnabled, - nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, + nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, } } From da69a05c7e7ad90be1cf32daa94380e501a5f37e Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 17:37:31 +0300 Subject: [PATCH 226/815] Fixed remarks --- engine/access/rpc/backend/backend.go | 2 +- engine/access/rpc/backend/backend_accounts.go | 2 +- engine/access/rpc/backend/backend_events.go | 2 +- engine/access/rpc/backend/backend_scripts.go | 2 +- engine/access/rpc/backend/backend_test.go | 14 ++--- .../rpc/backend/backend_transactions.go | 24 +++++--- .../access/rpc/backend/connection_factory.go | 11 +++- .../access/rpc/backend/node_communicator.go | 59 ++++++------------ engine/access/rpc/backend/node_selector.go | 61 +++++-------------- 9 files changed, 68 insertions(+), 109 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 2c3fdcc7e0d..d0e88b255a8 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -302,7 +302,7 @@ func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, err return convert.SnapshotToBytes(validSnapshot) } -// executionNodesForBlockID returns upto maxExecutionNodesCnt number of randomly chosen execution node identities +// executionNodesForBlockID returns upto maxNodesCnt number of randomly chosen execution node identities // which have executed the given block ID. // If no such execution node is found, an InsufficientExecutionReceipts error is returned. func executionNodesForBlockID( diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index c1eaa1e98e2..44862c8c2aa 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -108,7 +108,7 @@ func (b *backendAccounts) getAccountAtBlockID( // error aggregating all failures is returned. func (b *backendAccounts) getAccountFromAnyExeNode(ctx context.Context, execNodes flow.IdentityList, req *execproto.GetAccountAtBlockIDRequest) (*execproto.GetAccountAtBlockIDResponse, error) { var resp *execproto.GetAccountAtBlockIDResponse - errToReturn := b.nodeCommunicator.CallAvailableExecutionNode( + errToReturn := b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.Identity) error { var err error diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index 38bd723a077..5bae9d633f1 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -211,7 +211,7 @@ func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { var resp *execproto.GetEventsForBlockIDsResponse var execNode *flow.Identity - errToReturn := b.nodeCommunicator.CallAvailableExecutionNode( + errToReturn := b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.Identity) error { var err error diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index fc615f61c82..7a0b5e2a741 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -96,7 +96,7 @@ func (b *backendScripts) findScriptExecutors( return nil, err } executorAddrs := make([]string, 0, len(executors)) - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(executors) + execNodeSelector := b.nodeSelectorFactory.SelectNodes(executors) for executor := execNodeSelector.Next(); executor != nil; executor = execNodeSelector.Next() { executorAddrs = append(executorAddrs, executor.Address) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index be1024ec4bd..213545a3ab8 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -171,7 +171,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| epochBuilder. BuildEpoch(). CompleteEpoch() @@ -308,7 +308,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| epochBuilder. BuildEpoch(). CompleteEpoch() @@ -373,7 +373,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| epochBuilder.BuildEpoch() // add more blocks to our state in the commit phase, this will allow @@ -2172,14 +2172,14 @@ func (suite *Suite) TestExecutionNodesForBlockID() { require.NoError(suite.T(), err) execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector := execNodeSelectorFactory.SelectExecutionNodes(allExecNodes) + execSelector := execNodeSelectorFactory.SelectNodes(allExecNodes) actualList := flow.IdentityList{} for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { actualList = append(actualList, actual) } - if len(expectedENs) > maxExecutionNodesCnt { + if len(expectedENs) > maxNodesCnt { for _, actual := range actualList { require.Contains(suite.T(), expectedENs, actual) } @@ -2199,14 +2199,14 @@ func (suite *Suite) TestExecutionNodesForBlockID() { require.NoError(suite.T(), err) execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector := execNodeSelectorFactory.SelectExecutionNodes(allExecNodes) + execSelector := execNodeSelectorFactory.SelectNodes(allExecNodes) actualList := flow.IdentityList{} for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { actualList = append(actualList, actual) } - require.Equal(suite.T(), len(actualList), maxExecutionNodesCnt) + require.Equal(suite.T(), len(actualList), maxNodesCnt) }) // if no preferred or fixed ENs are specified, the ExecutionNodesForBlockID function should diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 45e5b6eb1ef..f6706df4874 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -97,13 +97,17 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T defer logAnyError() // try sending the transaction to one of the chosen collection nodes - sendError = b.nodeCommunicator.CallAvailableCollectionNode(collNodes, func(node *flow.Identity) error { - err = b.sendTransactionToCollector(ctx, tx, node.Address) - if err != nil { - return err - } - return nil - }) + sendError = b.nodeCommunicator.CallAvailableNode( + collNodes, + func(node *flow.Identity) error { + err = b.sendTransactionToCollector(ctx, tx, node.Address) + if err != nil { + return err + } + return nil + }, + nil, + ) return sendError } @@ -773,7 +777,7 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( }() var resp *execproto.GetTransactionResultResponse - errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + errToReturn = b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.Identity) error { var err error @@ -835,7 +839,7 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( } var resp *execproto.GetTransactionResultsResponse - errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + errToReturn = b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.Identity) error { var err error @@ -893,7 +897,7 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( } var resp *execproto.GetTransactionResultResponse - errToReturn = b.nodeCommunicator.CallAvailableExecutionNode( + errToReturn = b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.Identity) error { var err error diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index b5013d4550d..8d338525a63 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -116,7 +116,11 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor + // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called + // in the same order during gRPC requests. if cf.CircuitBreakerConfig.Enabled { + // If the circuit breaker interceptor is enabled, it should always be called first before passing control to + // subsequent interceptors. connInterceptors = append(connInterceptors, cf.createCircuitBreakerInterceptor()) } else { connInterceptors = append(connInterceptors, cf.createClientInvalidationInterceptor(address, clientType)) @@ -318,6 +322,8 @@ func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor( cf.InvalidateAccessAPIClient(address) case ExecutionClient: cf.InvalidateExecutionAPIClient(address) + default: + cf.Log.Info().Str("client_invalidation_interceptor", address).Msg(fmt.Sprintf("unexpected client type: %d", clientType)) } } @@ -353,12 +359,15 @@ func (cf *ConnectionFactoryImpl) createClientInvalidationInterceptor( func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryClientInterceptor { if cf.CircuitBreakerConfig.Enabled { circuitBreaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{ - // The restore timeout is defined to automatically return the circuit breaker to the HalfClose state. + // Timeout defines how long the circuit breaker will remain open before transitioning to the HalfClose state. Timeout: cf.CircuitBreakerConfig.RestoreTimeout, + // ReadyToTrip returns true when the circuit breaker should trip and transition to the Open state ReadyToTrip: func(counts gobreaker.Counts) bool { // The number of maximum failures is checked before the circuit breaker goes to the Open state. return counts.ConsecutiveFailures >= cf.CircuitBreakerConfig.MaxFailures }, + // MaxRequests defines the max number of concurrent requests while the circuit breaker is in the HalfClosed + // state. MaxRequests: cf.CircuitBreakerConfig.MaxRequests, }) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 08852180eba..87d0265c182 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -10,6 +10,14 @@ import ( // maxFailedRequestCount represents the maximum number of failed requests before returning errors. const maxFailedRequestCount = 3 +// NodeAction is a callback function type that represents an action to be performed on a node. +// It takes a node as input and returns an error indicating the result of the action. +type NodeAction func(node *flow.Identity) error + +// ErrorTerminator is a callback function that determines whether an error should terminate further execution. +// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. +type ErrorTerminator func(err error) bool + // NodeCommunicator is responsible for calling available nodes in the backend. type NodeCommunicator struct { nodeSelectorFactory NodeSelectorFactory @@ -22,26 +30,26 @@ func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { } } -// CallAvailableExecutionNode calls the provided function on the available execution nodes. -// It iterates through the execution nodes and executes the function. -// If an error occurs, it applies the custom error handler (if provided) and keeps track of the errors. -// If the error occurs in circuit breaker, it continues to the next execution node. +// CallAvailableNode calls the provided function on the available nodes. +// It iterates through the nodes and executes the function. +// If an error occurs, it applies the custom error terminator (if provided) and keeps track of the errors. +// If the error occurs in circuit breaker, it continues to the next node. // If the maximum failed request count is reached, it returns the accumulated errors. -func (b *NodeCommunicator) CallAvailableExecutionNode( +func (b *NodeCommunicator) CallAvailableNode( nodes flow.IdentityList, - call func(node *flow.Identity) error, - customErrorHandler func(err error) bool, + call NodeAction, + shouldTerminateOnError ErrorTerminator, ) error { var errs *multierror.Error - execNodeSelector := b.nodeSelectorFactory.SelectExecutionNodes(nodes) + nodeSelector := b.nodeSelectorFactory.SelectNodes(nodes) - for execNode := execNodeSelector.Next(); execNode != nil; execNode = execNodeSelector.Next() { - err := call(execNode) + for node := nodeSelector.Next(); node != nil; node = nodeSelector.Next() { + err := call(node) if err == nil { return nil } - if customErrorHandler != nil && customErrorHandler(err) { + if shouldTerminateOnError != nil && shouldTerminateOnError(err) { return err } @@ -57,32 +65,3 @@ func (b *NodeCommunicator) CallAvailableExecutionNode( return errs.ErrorOrNil() } - -// CallAvailableCollectionNode calls the provided function on the available collection nodes. -// It iterates through the collection nodes and executes the function. -// If an error occurs, it keeps track of the errors. -// If the error occurs in circuit breaker, it continues to the next collection node. -// If the maximum failed request count is reached, it returns the accumulated errors. -func (b *NodeCommunicator) CallAvailableCollectionNode(nodes flow.IdentityList, call func(node *flow.Identity) error) error { - var errs *multierror.Error - - collNodeSelector := b.nodeSelectorFactory.SelectCollectionNodes(nodes) - - for colNode := collNodeSelector.Next(); colNode != nil; colNode = collNodeSelector.Next() { - err := call(colNode) - if err == nil { - return nil - } - - if err == gobreaker.ErrOpenState { - continue - } - - errs = multierror.Append(errs, err) - if len(errs.Errors) >= maxFailedRequestCount { - return errs.ErrorOrNil() - } - } - - return errs.ErrorOrNil() -} diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index 48831337ba3..c4c1c724c5f 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -2,11 +2,8 @@ package backend import "github.com/onflow/flow-go/model/flow" -// maxExecutionNodesCnt is the maximum number of execution nodes that will be contacted to complete an execution API request. -const maxExecutionNodesCnt = 3 - -// maxCollectionNodesCnt is the maximum number of collection nodes that will be contacted to complete a collection API request. -const maxCollectionNodesCnt = 3 +// maxNodesCnt is the maximum number of nodes that will be contacted to complete an API request. +const maxNodesCnt = 3 // NodeSelector is an interface that represents the ability to select node identities that the access node is trying to reach. // It encapsulates the internal logic of node selection and provides a way to change implementations for different types @@ -21,44 +18,32 @@ type NodeSelectorFactory struct { circuitBreakerEnabled bool } -// SelectExecutionNodes selects the configured number of execution node identities from the provided list of execution nodes -// and returns an execution node selector to iterate through them. -func (n *NodeSelectorFactory) SelectExecutionNodes(executionNodes flow.IdentityList) NodeSelector { +// SelectNodes selects the configured number of node identities from the provided list of nodes +// and returns the node selector to iterate through them. +func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) NodeSelector { // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. if !n.circuitBreakerEnabled { - executionNodes = executionNodes.Sample(maxExecutionNodesCnt) + nodes = nodes.Sample(maxNodesCnt) } - return &ExecutionNodeSelector{ - nodes: executionNodes, + return &MainNodeSelector{ + nodes: nodes, index: 0, } } -// SelectCollectionNodes selects the configured number of collection node identities from the provided list of collection nodes -// and returns a collection node selector to iterate through them. -func (n *NodeSelectorFactory) SelectCollectionNodes(collectionNodes flow.IdentityList) NodeSelector { - // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. - if !n.circuitBreakerEnabled { - collectionNodes = collectionNodes.Sample(maxCollectionNodesCnt) - } +// SelectCollectionNodes - return &CollectionNodeSelector{ - nodes: collectionNodes, - index: 0, - } -} +var _ NodeSelector = (*MainNodeSelector)(nil) -var _ NodeSelector = (*ExecutionNodeSelector)(nil) - -// ExecutionNodeSelector is a specific implementation of an execution node selector. -type ExecutionNodeSelector struct { +// MainNodeSelector is a specific implementation of the node selector. +type MainNodeSelector struct { nodes flow.IdentityList index int } -// Next returns the next execution node in the selector. -func (e *ExecutionNodeSelector) Next() *flow.Identity { +// Next returns the next node in the selector. +func (e *MainNodeSelector) Next() *flow.Identity { if e.index < len(e.nodes) { next := e.nodes[e.index] e.index++ @@ -66,21 +51,3 @@ func (e *ExecutionNodeSelector) Next() *flow.Identity { } return nil } - -var _ NodeSelector = (*CollectionNodeSelector)(nil) - -// CollectionNodeSelector is a specific implementation of a collection node selector. -type CollectionNodeSelector struct { - nodes flow.IdentityList - index int -} - -// Next returns the next collection node in the selector. -func (c *CollectionNodeSelector) Next() *flow.Identity { - if c.index < len(c.nodes) { - next := c.nodes[c.index] - c.index++ - return next - } - return nil -} From cc90ff9e6ae22904ec0108193b202ead7d93c5d2 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 17:56:32 +0300 Subject: [PATCH 227/815] Fixed remarks --- .../access/rpc/backend/connection_factory.go | 22 ++++++++----------- .../rpc/backend/connection_factory_test.go | 5 +++-- .../access/access_circuit_breaker_test.go | 4 ++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 8d338525a63..159f48c969d 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -76,21 +76,17 @@ type ConnectionFactoryImpl struct { } // CircuitBreakerConfig is a configuration struct for the circuit breaker. -// -// Enabled specifies whether the circuit breaker is enabled for collection and execution API clients. -// -// RestoreTimeout specifies the duration after which the circuit breaker will restore the connection to the client -// after closing it due to failures. -// -// MaxFailures specifies the maximum number of failed calls to the client that will cause the circuit breaker -// to close the connection. -// -// MaxRequests specifies the maximum number of requests to check if connection restored after timeout. type CircuitBreakerConfig struct { - Enabled bool + // Enabled specifies whether the circuit breaker is enabled for collection and execution API clients. + Enabled bool + // RestoreTimeout specifies the duration after which the circuit breaker will restore the connection to the client + // after closing it due to failures. RestoreTimeout time.Duration - MaxFailures uint32 - MaxRequests uint32 + // MaxFailures specifies the maximum number of failed calls to the client that will cause the circuit breaker + // to close the connection. + MaxFailures uint32 + // MaxRequests specifies the maximum number of requests to check if connection restored after timeout. + MaxRequests uint32 } type CachedClient struct { diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index e9f6f1a4819..23804a66e02 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -16,6 +16,7 @@ import ( "github.com/sony/gobreaker" "github.com/stretchr/testify/assert" testifymock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -221,7 +222,7 @@ func TestExecutionNodeClientTimeout(t *testing.T) { // create the execution API client client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) - assert.NoError(t, err) + require.NoError(t, err) ctx := context.Background() // make the call to the execution node @@ -453,7 +454,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { // Create the execution API client. client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) - assert.NoError(t, err) + require.NoError(t, err) ctx := context.Background() diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index d99c292465e..aeb37223822 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -120,7 +120,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-max-failures")) accessClient, err := accessContainer.TestnetClient() - assert.NoError(s.T(), err, "failed to get collection node client") + assert.NoError(s.T(), err, "failed to get access node client") latestBlockID, err := accessClient.GetLatestBlockID(ctx) require.NoError(s.T(), err) @@ -176,7 +176,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // Try to send the transaction for the first time. It should wait at least the timeout time and return Unknown error duration, err := sendTransaction(ctx, signedTx) assert.Equal(s.T(), codes.Unknown, status.Code(err)) - assert.GreaterOrEqual(s.T(), requestTimeout, duration) + assert.Greater(s.T(), requestTimeout, duration) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure From 3ea1fd49e0c1d5dae1ecd7a3b8ef91d37f4aeeed Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 20:57:15 +0300 Subject: [PATCH 228/815] Fixed issue with sample after merge. --- engine/access/rpc/backend/backend_scripts.go | 5 ++++- engine/access/rpc/backend/backend_test.go | 6 ++++-- engine/access/rpc/backend/node_communicator.go | 5 ++++- engine/access/rpc/backend/node_selector.go | 16 ++++++++++++---- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 7a0b5e2a741..587bb9f3f3c 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -96,7 +96,10 @@ func (b *backendScripts) findScriptExecutors( return nil, err } executorAddrs := make([]string, 0, len(executors)) - execNodeSelector := b.nodeSelectorFactory.SelectNodes(executors) + execNodeSelector, err := b.nodeSelectorFactory.SelectNodes(executors) + if err != nil { + return nil, err + } for executor := execNodeSelector.Next(); executor != nil; executor = execNodeSelector.Next() { executorAddrs = append(executorAddrs, executor.Address) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 213545a3ab8..60ce82bc440 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -2172,7 +2172,8 @@ func (suite *Suite) TestExecutionNodesForBlockID() { require.NoError(suite.T(), err) execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector := execNodeSelectorFactory.SelectNodes(allExecNodes) + execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) + require.NoError(suite.T(), err) actualList := flow.IdentityList{} for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { @@ -2199,7 +2200,8 @@ func (suite *Suite) TestExecutionNodesForBlockID() { require.NoError(suite.T(), err) execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector := execNodeSelectorFactory.SelectNodes(allExecNodes) + execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) + require.NoError(suite.T(), err) actualList := flow.IdentityList{} for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 87d0265c182..279c281c225 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -41,7 +41,10 @@ func (b *NodeCommunicator) CallAvailableNode( shouldTerminateOnError ErrorTerminator, ) error { var errs *multierror.Error - nodeSelector := b.nodeSelectorFactory.SelectNodes(nodes) + nodeSelector, err := b.nodeSelectorFactory.SelectNodes(nodes) + if err != nil { + return err + } for node := nodeSelector.Next(); node != nil; node = nodeSelector.Next() { err := call(node) diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index c4c1c724c5f..fbe6c1aa1dd 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -1,6 +1,10 @@ package backend -import "github.com/onflow/flow-go/model/flow" +import ( + "fmt" + + "github.com/onflow/flow-go/model/flow" +) // maxNodesCnt is the maximum number of nodes that will be contacted to complete an API request. const maxNodesCnt = 3 @@ -20,16 +24,20 @@ type NodeSelectorFactory struct { // SelectNodes selects the configured number of node identities from the provided list of nodes // and returns the node selector to iterate through them. -func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) NodeSelector { +func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) (NodeSelector, error) { + var err error // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. if !n.circuitBreakerEnabled { - nodes = nodes.Sample(maxNodesCnt) + nodes, err = nodes.Sample(maxNodesCnt) + if err != nil { + return nil, fmt.Errorf("sampling failed: %w", err) + } } return &MainNodeSelector{ nodes: nodes, index: 0, - } + }, nil } // SelectCollectionNodes From 80132f94d2ea55c247589c03c0ce1446e9d6a29b Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 21:41:26 +0300 Subject: [PATCH 229/815] Fixed issue with all nodes unavailable. --- engine/access/rpc/backend/node_communicator.go | 5 +++++ engine/access/rpc/backend/node_selector.go | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 279c281c225..31298d30894 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -3,6 +3,8 @@ package backend import ( "github.com/hashicorp/go-multierror" "github.com/sony/gobreaker" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/onflow/flow-go/model/flow" ) @@ -57,6 +59,9 @@ func (b *NodeCommunicator) CallAvailableNode( } if err == gobreaker.ErrOpenState { + if !nodeSelector.HasNext() && len(errs.Errors) == 0 { + errs = multierror.Append(errs, status.Error(codes.Unavailable, "there are no available nodes")) + } continue } diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index fbe6c1aa1dd..f5682a79ccf 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -12,9 +12,10 @@ const maxNodesCnt = 3 // NodeSelector is an interface that represents the ability to select node identities that the access node is trying to reach. // It encapsulates the internal logic of node selection and provides a way to change implementations for different types // of nodes. Implementations of this interface should define the Next method, which returns the next node identity to be -// selected. +// selected. HasNext checks if there is next node available. type NodeSelector interface { Next() *flow.Identity + HasNext() bool } // NodeSelectorFactory is a factory for creating node selectors based on factory configuration and node type. @@ -50,6 +51,11 @@ type MainNodeSelector struct { index int } +// HasNext returns true if next node is available. +func (e *MainNodeSelector) HasNext() bool { + return e.index < len(e.nodes) +} + // Next returns the next node in the selector. func (e *MainNodeSelector) Next() *flow.Identity { if e.index < len(e.nodes) { From f654460241e554a27c78fe3cd50171a7842ecf95 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 22:03:33 +0300 Subject: [PATCH 230/815] Make timeouts shorter in cf tests. --- engine/access/rpc/backend/connection_factory_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 23804a66e02..aefdfd64488 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -411,8 +411,8 @@ func TestConnectionPoolStale(t *testing.T) { // TestCircuitBreakerExecutionNode tests the circuit breaker state changes for execution nodes. func TestCircuitBreakerExecutionNode(t *testing.T) { - requestTimeout := 1 * time.Second - circuitBreakerRestoreTimeout := 3 * time.Second + requestTimeout := 500 * time.Millisecond + circuitBreakerRestoreTimeout := 1500 * time.Millisecond // Create an execution node for testing. en := new(executionNode) @@ -484,7 +484,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { en.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) // Wait until the circuit breaker transitions to the "HalfOpen" state. - time.Sleep(circuitBreakerRestoreTimeout + time.Second) + time.Sleep(circuitBreakerRestoreTimeout + (500 * time.Millisecond)) // Call and measure the duration for the third invocation (circuit breaker state is now "HalfOpen"). duration, err = callAndMeasurePingDuration() @@ -494,8 +494,8 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { // TestCircuitBreakerCollectionNode tests the circuit breaker state changes for collection nodes. func TestCircuitBreakerCollectionNode(t *testing.T) { - requestTimeout := 1 * time.Second - circuitBreakerRestoreTimeout := 3 * time.Second + requestTimeout := 500 * time.Millisecond + circuitBreakerRestoreTimeout := 1500 * time.Millisecond // Create a collection node for testing. cn := new(collectionNode) @@ -567,7 +567,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { cn.handler.On("Ping", testifymock.Anything, req).Return(resp, nil) // Wait until the circuit breaker transitions to the "HalfOpen" state. - time.Sleep(circuitBreakerRestoreTimeout + time.Second) + time.Sleep(circuitBreakerRestoreTimeout + (500 * time.Millisecond)) // Call and measure the duration for the third invocation (circuit breaker state is now "HalfOpen"). duration, err = callAndMeasurePingDuration() From bc369b2a6ee7fa3b4ea2b0097052298e3dbdf4e5 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 16:40:04 -0400 Subject: [PATCH 231/815] Update network/p2p/tracer/gossipSubMeshTracer.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/p2p/tracer/gossipSubMeshTracer.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index 21bc9e5de6c..c79e12ab14b 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -25,7 +25,10 @@ const ( // MeshLogIntervalWarnMsg is the message logged by the tracer every logInterval if there are unknown peers in the mesh. MeshLogIntervalWarnMsg = "unknown peers in topic mesh peers of local node since last heartbeat" - // defaultLastHighestIHaveRPCSizeResetInterval this default interval should always be equal to the gossipsub heart beat interval. + // defaultLastHighestIHaveRPCSizeResetInterval is the interval that we reset the tracker of max ihave size sent back + // to a default. We use ihave message max size to determine the health of requested iwants from remote peers. However, + // we don't desire an ihave size anomaly to persist forever, hence, we reset it back to a default every minute. + // The choice of the interval to be a minute is in harmony with the GossipSub decay interval. defaultLastHighestIHaveRPCSizeResetInterval = time.Minute ) From a7e305af83d7de0a996c58b9fe5dc2ccc964c6fd Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 18 Jul 2023 23:41:20 +0300 Subject: [PATCH 232/815] Fixed integration test. --- integration/tests/access/access_circuit_breaker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index aeb37223822..d537873136e 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -175,7 +175,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // Try to send the transaction for the first time. It should wait at least the timeout time and return Unknown error duration, err := sendTransaction(ctx, signedTx) - assert.Equal(s.T(), codes.Unknown, status.Code(err)) + assert.Equal(s.T(), codes.Unavailable, status.Code(err)) assert.Greater(s.T(), requestTimeout, duration) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker From 7b52ab18decbfceb530800a79ae21b0255731e46 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 16:43:27 -0400 Subject: [PATCH 233/815] encapsulate tracer-related config parameters --- network/p2p/p2pconf/gossipsub.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pconf/gossipsub.go b/network/p2p/p2pconf/gossipsub.go index b76ff9f4c9b..4578131a6ab 100644 --- a/network/p2p/p2pconf/gossipsub.go +++ b/network/p2p/p2pconf/gossipsub.go @@ -17,6 +17,16 @@ type ResourceManagerConfig struct { type GossipSubConfig struct { // GossipSubRPCInspectorsConfig configuration for all gossipsub RPC control message inspectors. GossipSubRPCInspectorsConfig `mapstructure:",squash"` + + // GossipSubTracerConfig is the configuration for the gossipsub tracer. GossipSub tracer is used to trace the local mesh events and peer scores. + GossipSubTracerConfig `mapstructure:",squash"` + + // PeerScoring is whether to enable GossipSub peer scoring. + PeerScoring bool `mapstructure:"gossipsub-peer-scoring-enabled"` +} + +// GossipSubTracerConfig is the config for the gossipsub tracer. GossipSub tracer is used to trace the local mesh events and peer scores. +type GossipSubTracerConfig struct { // LocalMeshLogInterval is the interval at which the local mesh is logged. LocalMeshLogInterval time.Duration `mapstructure:"gossipsub-local-mesh-logging-interval"` // ScoreTracerInterval is the interval at which the score tracer logs the peer scores. @@ -27,6 +37,4 @@ type GossipSubConfig struct { RPCSentTrackerQueueCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` // RpcSentTrackerNumOfWorkers number of workers for rpc sent tracker worker pool. RpcSentTrackerNumOfWorkers int `mapstructure:"gossipsub-rpc-sent-tracker-workers"` - // PeerScoring is whether to enable GossipSub peer scoring. - PeerScoring bool `mapstructure:"gossipsub-peer-scoring-enabled"` } From b82247df433b790f467f716fd494df77cb9c02ed Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 16:43:34 -0400 Subject: [PATCH 234/815] Update config/default-config.yml Co-authored-by: Yahya Hassanzadeh, Ph.D. --- config/default-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/default-config.yml b/config/default-config.yml index 896668301df..c14f7e13c0b 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -67,7 +67,7 @@ network-config: # Note: this cache size must be large enough to keep a history of sent messages in a reasonable time window of past history. gossipsub-rpc-sent-tracker-cache-size: 1_000_000 # Cache size of the rpc sent tracker queue used for async tracking. - gossipsub-rpc-sent-tracker-queue-cache-size: 1000 + gossipsub-rpc-sent-tracker-queue-cache-size: 100_000 # Number of workers for rpc sent tracker worker pool. gossipsub-rpc-sent-tracker-workers: 5 # Peer scoring is the default value for enabling peer scoring From b8c72cc07ff349c19e6215cc3cf1d06382a84453 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 16:43:50 -0400 Subject: [PATCH 235/815] Update network/p2p/p2pconf/gossipsub.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/p2p/p2pconf/gossipsub.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/p2p/p2pconf/gossipsub.go b/network/p2p/p2pconf/gossipsub.go index b76ff9f4c9b..9ca737e7feb 100644 --- a/network/p2p/p2pconf/gossipsub.go +++ b/network/p2p/p2pconf/gossipsub.go @@ -20,13 +20,13 @@ type GossipSubConfig struct { // LocalMeshLogInterval is the interval at which the local mesh is logged. LocalMeshLogInterval time.Duration `mapstructure:"gossipsub-local-mesh-logging-interval"` // ScoreTracerInterval is the interval at which the score tracer logs the peer scores. - ScoreTracerInterval time.Duration `mapstructure:"gossipsub-score-tracer-interval"` + ScoreTracerInterval time.Duration `validate:"gt=0s" mapstructure:"gossipsub-score-tracer-interval"` // RPCSentTrackerCacheSize cache size of the rpc sent tracker used by the gossipsub mesh tracer. - RPCSentTrackerCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-cache-size"` + RPCSentTrackerCacheSize uint32 `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-cache-size"` // RPCSentTrackerQueueCacheSize cache size of the rpc sent tracker queue used for async tracking. - RPCSentTrackerQueueCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` + RPCSentTrackerQueueCacheSize uint32 `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` // RpcSentTrackerNumOfWorkers number of workers for rpc sent tracker worker pool. - RpcSentTrackerNumOfWorkers int `mapstructure:"gossipsub-rpc-sent-tracker-workers"` + RpcSentTrackerNumOfWorkers int `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-workers"` // PeerScoring is whether to enable GossipSub peer scoring. PeerScoring bool `mapstructure:"gossipsub-peer-scoring-enabled"` } From bea0fff1ef901e0fc2d4e7ca5f6a8a515d4a23f7 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 16:46:22 -0400 Subject: [PATCH 236/815] Update gossipsub.go --- network/p2p/p2pconf/gossipsub.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/network/p2p/p2pconf/gossipsub.go b/network/p2p/p2pconf/gossipsub.go index 4578131a6ab..683dff67fdc 100644 --- a/network/p2p/p2pconf/gossipsub.go +++ b/network/p2p/p2pconf/gossipsub.go @@ -28,13 +28,13 @@ type GossipSubConfig struct { // GossipSubTracerConfig is the config for the gossipsub tracer. GossipSub tracer is used to trace the local mesh events and peer scores. type GossipSubTracerConfig struct { // LocalMeshLogInterval is the interval at which the local mesh is logged. - LocalMeshLogInterval time.Duration `mapstructure:"gossipsub-local-mesh-logging-interval"` + LocalMeshLogInterval time.Duration `validate:"gt=0s" mapstructure:"gossipsub-local-mesh-logging-interval"` // ScoreTracerInterval is the interval at which the score tracer logs the peer scores. - ScoreTracerInterval time.Duration `mapstructure:"gossipsub-score-tracer-interval"` + ScoreTracerInterval time.Duration `validate:"gt=0s" mapstructure:"gossipsub-score-tracer-interval"` // RPCSentTrackerCacheSize cache size of the rpc sent tracker used by the gossipsub mesh tracer. - RPCSentTrackerCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-cache-size"` + RPCSentTrackerCacheSize uint32 `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-cache-size"` // RPCSentTrackerQueueCacheSize cache size of the rpc sent tracker queue used for async tracking. - RPCSentTrackerQueueCacheSize uint32 `mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` + RPCSentTrackerQueueCacheSize uint32 `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-queue-cache-size"` // RpcSentTrackerNumOfWorkers number of workers for rpc sent tracker worker pool. - RpcSentTrackerNumOfWorkers int `mapstructure:"gossipsub-rpc-sent-tracker-workers"` + RpcSentTrackerNumOfWorkers int `validate:"gt=0" mapstructure:"gossipsub-rpc-sent-tracker-workers"` } From 4bacc83da04645ac3950596b8c126831e9227124 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 17:36:36 -0400 Subject: [PATCH 237/815] internally construct metrics for the tracker --- .../node_builder/access_node_builder.go | 18 ++++++------ cmd/observer/node_builder/observer_builder.go | 18 ++++++------ follower/follower_builder.go | 18 ++++++------ network/internal/p2pfixtures/fixtures.go | 18 ++++++------ network/p2p/p2pbuilder/libp2pNodeBuilder.go | 19 ++++++------- network/p2p/tracer/gossipSubMeshTracer.go | 28 ++++++++++--------- .../p2p/tracer/gossipSubMeshTracer_test.go | 17 +++++------ 7 files changed, 67 insertions(+), 69 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 5e0d790af8e..b12ae8f08f8 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1192,15 +1192,15 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: networkMetrics, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + Logger: builder.Logger, + Metrics: networkMetrics, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + NetworkingType: network.PublicNetwork, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 4f3c5ea7738..a869e24332c 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -703,15 +703,15 @@ func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: builder.Metrics.Network, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + Logger: builder.Logger, + Metrics: builder.Metrics.Network, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + NetworkingType: network.PublicNetwork, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 06626555855..27e69ce039c 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -605,15 +605,15 @@ func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: builder.Logger, - Metrics: builder.Metrics.Network, - IDProvider: builder.IdentityProvider, - LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), + Logger: builder.Logger, + Metrics: builder.Metrics.Network, + IDProvider: builder.IdentityProvider, + LoggerInterval: builder.FlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: builder.FlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: builder.FlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + NetworkingType: network.PublicNetwork, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 5a8e60932c8..7b7365d57ab 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -104,15 +104,15 @@ func CreateNode(t *testing.T, networkKey crypto.PrivateKey, sporkID flow.Identif require.NoError(t, err) meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: metrics.NewNoopCollector(), - IDProvider: idProvider, - LoggerInterval: defaultFlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), - RpcSentTrackerCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: defaultFlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.NewNoopCollector(), + Logger: logger, + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: defaultFlowConfig.NetworkConfig.GossipSubConfig.LocalMeshLogInterval, + RpcSentTrackerCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultFlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + NetworkingType: flownet.PublicNetwork, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 2034b9971f6..9c9fa5f6713 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -26,7 +26,6 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/module/metrics" flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/netconf" "github.com/onflow/flow-go/network/p2p" @@ -496,15 +495,15 @@ func DefaultNodeBuilder( } meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: metricsCfg.Metrics, - IDProvider: idProvider, - LoggerInterval: gossipCfg.LocalMeshLogInterval, - RpcSentTrackerCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(metricsCfg.HeroCacheFactory, flownet.PrivateNetwork), - RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: gossipCfg.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: gossipCfg.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(metricsCfg.HeroCacheFactory, flownet.PrivateNetwork), + Logger: logger, + Metrics: metricsCfg.Metrics, + IDProvider: idProvider, + LoggerInterval: gossipCfg.LocalMeshLogInterval, + RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: gossipCfg.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: gossipCfg.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: metricsCfg.HeroCacheFactory, + NetworkingType: flownet.PrivateNetwork, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index c79e12ab14b..232b0eeadb5 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -13,6 +13,8 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/tracer/internal" "github.com/onflow/flow-go/utils/logging" @@ -25,9 +27,9 @@ const ( // MeshLogIntervalWarnMsg is the message logged by the tracer every logInterval if there are unknown peers in the mesh. MeshLogIntervalWarnMsg = "unknown peers in topic mesh peers of local node since last heartbeat" - // defaultLastHighestIHaveRPCSizeResetInterval is the interval that we reset the tracker of max ihave size sent back + // defaultLastHighestIHaveRPCSizeResetInterval is the interval that we reset the tracker of max ihave size sent back // to a default. We use ihave message max size to determine the health of requested iwants from remote peers. However, - // we don't desire an ihave size anomaly to persist forever, hence, we reset it back to a default every minute. + // we don't desire an ihave size anomaly to persist forever, hence, we reset it back to a default every minute. // The choice of the interval to be a minute is in harmony with the GossipSub decay interval. defaultLastHighestIHaveRPCSizeResetInterval = time.Minute ) @@ -56,15 +58,15 @@ type GossipSubMeshTracer struct { var _ p2p.PubSubTracer = (*GossipSubMeshTracer)(nil) type GossipSubMeshTracerConfig struct { - Logger zerolog.Logger - Metrics module.GossipSubLocalMeshMetrics - IDProvider module.IdentityProvider - LoggerInterval time.Duration - RpcSentTrackerCacheCollector module.HeroCacheMetrics - RpcSentTrackerCacheSize uint32 - RpcSentTrackerWorkerQueueCacheCollector module.HeroCacheMetrics - RpcSentTrackerWorkerQueueCacheSize uint32 - RpcSentTrackerNumOfWorkers int + network.NetworkingType + metrics.HeroCacheMetricsFactory + Logger zerolog.Logger + Metrics module.GossipSubLocalMeshMetrics + IDProvider module.IdentityProvider + LoggerInterval time.Duration + RpcSentTrackerCacheSize uint32 + RpcSentTrackerWorkerQueueCacheSize uint32 + RpcSentTrackerNumOfWorkers int } // NewGossipSubMeshTracer creates a new *GossipSubMeshTracer. @@ -77,8 +79,8 @@ func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTra rpcSentTracker := internal.NewRPCSentTracker(&internal.RPCSentTrackerConfig{ Logger: lg, RPCSentCacheSize: config.RpcSentTrackerCacheSize, - RPCSentCacheCollector: config.RpcSentTrackerCacheCollector, - WorkerQueueCacheCollector: config.RpcSentTrackerWorkerQueueCacheCollector, + RPCSentCacheCollector: metrics.GossipSubRPCSentTrackerMetricFactory(config.HeroCacheMetricsFactory, config.NetworkingType), + WorkerQueueCacheCollector: metrics.GossipSubRPCSentTrackerQueueMetricFactory(config.HeroCacheMetricsFactory, config.NetworkingType), WorkerQueueCacheSize: config.RpcSentTrackerWorkerQueueCacheSize, NumOfWorkers: config.RpcSentTrackerNumOfWorkers, LastHighestIhavesSentResetInterval: defaultLastHighestIHaveRPCSizeResetInterval, diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index 6fb4e96316f..2f0d9581a2b 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -14,7 +14,6 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p" @@ -66,15 +65,13 @@ func TestGossipSubMeshTracer(t *testing.T) { // meshTracer logs at 1 second intervals for sake of testing. collector := mockmodule.NewGossipSubLocalMeshMetrics(t) meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: collector, - IDProvider: idProvider, - LoggerInterval: time.Second, - RpcSentTrackerCacheCollector: metrics.NewNoopCollector(), - RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: defaultConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - RpcSentTrackerWorkerQueueCacheCollector: metrics.NewNoopCollector(), + Logger: logger, + Metrics: collector, + IDProvider: idProvider, + LoggerInterval: time.Second, + RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, } meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) tracerNode, tracerId := p2ptest.NodeFixture( From 87c07e07e6d7f8804faa0a91cf1366107fa41fbc Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 17:50:29 -0400 Subject: [PATCH 238/815] remove AddWorkers --- module/component/component.go | 3 +-- .../component/mock/component_manager_builder.go | 16 ---------------- network/p2p/tracer/internal/rpc_sent_tracker.go | 11 ++++++----- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/module/component/component.go b/module/component/component.go index a75534d386a..a887d149270 100644 --- a/module/component/component.go +++ b/module/component/component.go @@ -143,8 +143,7 @@ func NoopWorker(ctx irrecoverable.SignalerContext, ready ReadyFunc) { type ComponentManagerBuilder interface { // AddWorker adds a worker routine for the ComponentManager AddWorker(ComponentWorker) ComponentManagerBuilder - // AddWorkers adds n number of worker routines for the ComponentManager. - AddWorkers(int, ComponentWorker) ComponentManagerBuilder + // Build builds and returns a new ComponentManager instance Build() *ComponentManager } diff --git a/module/component/mock/component_manager_builder.go b/module/component/mock/component_manager_builder.go index 570ecda9269..c414ddc6663 100644 --- a/module/component/mock/component_manager_builder.go +++ b/module/component/mock/component_manager_builder.go @@ -28,22 +28,6 @@ func (_m *ComponentManagerBuilder) AddWorker(_a0 component.ComponentWorker) comp return r0 } -// AddWorkers provides a mock function with given fields: _a0, _a1 -func (_m *ComponentManagerBuilder) AddWorkers(_a0 int, _a1 component.ComponentWorker) component.ComponentManagerBuilder { - ret := _m.Called(_a0, _a1) - - var r0 component.ComponentManagerBuilder - if rf, ok := ret.Get(0).(func(int, component.ComponentWorker) component.ComponentManagerBuilder); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(component.ComponentManagerBuilder) - } - } - - return r0 -} - // Build provides a mock function with given fields: func (_m *ComponentManagerBuilder) Build() *component.ComponentManager { ret := _m.Called() diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 63ae18edb81..0d5fb1a2260 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -76,14 +76,15 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { store, tracker.rpcSent).Build() - tracker.Component = component.NewComponentManagerBuilder(). + builder := component.NewComponentManagerBuilder(). AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { ready() tracker.lastHighestIHaveRPCSizeResetLoop(ctx) - }). - AddWorkers(config.NumOfWorkers, tracker.workerPool.WorkerLogic()). - Build() - + }) + for i := 0; i < config.NumOfWorkers; i++ { + builder.AddWorker(tracker.workerPool.WorkerLogic()) + } + tracker.Component = builder.Build() return tracker } From 13382794a6e431fe4f6e8c4121dde4f811e39d72 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 17:56:15 -0400 Subject: [PATCH 239/815] change debug -> info --- network/p2p/tracer/gossipSubMeshTracer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index 232b0eeadb5..ac4bb60dbf0 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -102,12 +102,12 @@ func NewGossipSubMeshTracer(config *GossipSubMeshTracerConfig) *GossipSubMeshTra }). AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { ready() - lg.Debug().Msg("starting rpc sent tracker") + lg.Info().Msg("starting rpc sent tracker") g.rpcSentTracker.Start(ctx) - lg.Debug().Msg("rpc sent tracker started") + lg.Info().Msg("rpc sent tracker started") <-g.rpcSentTracker.Done() - lg.Debug().Msg("rpc sent tracker stopped") + lg.Info().Msg("rpc sent tracker stopped") }). Build() From bec095c25ce8c5715cd4e1d52932daf4bd6dfc27 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 17:57:21 -0400 Subject: [PATCH 240/815] rename trackRPC -> trackableRPC --- network/p2p/tracer/internal/rpc_sent_tracker.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 0d5fb1a2260..98dfdfaee7e 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -18,9 +18,9 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) -// trackRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed +// trackableRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed // by the *RPCSentTracker. -type trackRPC struct { +type trackableRPC struct { // Nonce prevents deduplication in the hero store Nonce []byte rpc *pubsub.RPC @@ -30,7 +30,7 @@ type trackRPC struct { type RPCSentTracker struct { component.Component cache *rpcSentCache - workerPool *worker.Pool[trackRPC] + workerPool *worker.Pool[trackableRPC] // lastHighestIHaveRPCSize tracks the size of the last largest iHave rpc control message sent. lastHighestIHaveRPCSize *atomic.Int64 lastHighestIHaveRPCSizeResetInterval time.Duration @@ -71,7 +71,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { lastHighestIHaveRPCSize: atomic.NewInt64(0), lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, } - tracker.workerPool = worker.NewWorkerPoolBuilder[trackRPC]( + tracker.workerPool = worker.NewWorkerPoolBuilder[trackableRPC]( config.Logger, store, tracker.rpcSent).Build() @@ -96,12 +96,12 @@ func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) error { if err != nil { return fmt.Errorf("failed to get track rpc work nonce: %w", err) } - t.workerPool.Submit(trackRPC{Nonce: n, rpc: rpc}) + t.workerPool.Submit(trackableRPC{Nonce: n, rpc: rpc}) return nil } // rpcSent tracks control messages sent in *pubsub.RPC. -func (t *RPCSentTracker) rpcSent(work trackRPC) error { +func (t *RPCSentTracker) rpcSent(work trackableRPC) error { switch { case len(work.rpc.GetControl().GetIhave()) > 0: iHave := work.rpc.GetControl().GetIhave() From 119f946a5b8f902eb2e30fd190cff68ba9a94752 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 17:58:24 -0400 Subject: [PATCH 241/815] rename RPCSent -> Track --- network/p2p/tracer/gossipSubMeshTracer.go | 2 +- network/p2p/tracer/internal/rpc_sent_tracker.go | 4 ++-- network/p2p/tracer/internal/rpc_sent_tracker_test.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index ac4bb60dbf0..f0e2d035c59 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -184,7 +184,7 @@ func (t *GossipSubMeshTracer) Prune(p peer.ID, topic string) { // SendRPC is called when a RPC is sent. Currently, the GossipSubMeshTracer tracks iHave RPC messages that have been sent. // This function can be updated to track other control messages in the future as required. func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { - err := t.rpcSentTracker.RPCSent(rpc) + err := t.rpcSentTracker.Track(rpc) if err != nil { t.logger.Err(err).Msg("failed to track sent pubsbub rpc") } diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 98dfdfaee7e..e93ec2c53dc 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -88,10 +88,10 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { return tracker } -// RPCSent submits the control message to the worker queue for async tracking. +// Track submits the control message to the worker queue for async tracking. // Args: // - *pubsub.RPC: the rpc sent. -func (t *RPCSentTracker) RPCSent(rpc *pubsub.RPC) error { +func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { n, err := nonce() if err != nil { return fmt.Errorf("failed to get track rpc work nonce: %w", err) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 5324be9d459..9445391c615 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -61,7 +61,7 @@ func TestRPCSentTracker_IHave(t *testing.T) { } } rpc := rpcFixture(withIhaves(iHaves)) - require.NoError(t, tracker.RPCSent(rpc)) + require.NoError(t, tracker.Track(rpc)) // eventually we should have tracked numOfMsgIds per single topic require.Eventually(t, func() bool { @@ -106,7 +106,7 @@ func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { expectedCacheSize := 0 for _, testCase := range testCases { - require.NoError(t, tracker.RPCSent(testCase.rpcFixture)) + require.NoError(t, tracker.Track(testCase.rpcFixture)) expectedCacheSize += testCase.numOfIhaves } @@ -119,7 +119,7 @@ func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { // after setting sending large RPC lastHighestIHaveRPCSize should reset to 0 after lastHighestIHaveRPCSize reset loop tick largeIhave := 50000 - require.NoError(t, tracker.RPCSent(rpcFixture(withIhaves(mockIHaveFixture(largeIhave, numOfMessageIds))))) + require.NoError(t, tracker.Track(rpcFixture(withIhaves(mockIHaveFixture(largeIhave, numOfMessageIds))))) require.Eventually(t, func() bool { return tracker.LastHighestIHaveRPCSize() == int64(largeIhave) }, 1*time.Second, 100*time.Millisecond) From 23c6cfe60db822d2945041faf701f1f125b4c511 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 18:05:50 -0400 Subject: [PATCH 242/815] document errors as benign --- network/p2p/tracer/internal/rpc_sent_tracker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index e93ec2c53dc..47ba3d16439 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -91,6 +91,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { // Track submits the control message to the worker queue for async tracking. // Args: // - *pubsub.RPC: the rpc sent. +// All errors returned from this function can be considered benign. func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { n, err := nonce() if err != nil { From e1c57c6011024d00d7bbf6e678fe3e67ebdfdf00 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 18:08:08 -0400 Subject: [PATCH 243/815] return error if submit returns false --- network/p2p/tracer/internal/rpc_sent_tracker.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 47ba3d16439..eb860f569bf 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -97,7 +97,9 @@ func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { if err != nil { return fmt.Errorf("failed to get track rpc work nonce: %w", err) } - t.workerPool.Submit(trackableRPC{Nonce: n, rpc: rpc}) + if ok := t.workerPool.Submit(trackableRPC{Nonce: n, rpc: rpc}); !ok { + return fmt.Errorf("failed to track RPC could not submit work to worker pool") + } return nil } From 344b30f2fdf69a2f44da8245751dfa2a4c4d0906 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 18:08:53 -0400 Subject: [PATCH 244/815] rename rpcSent -> rpcSentWorkerLogic --- network/p2p/tracer/internal/rpc_sent_tracker.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index eb860f569bf..27dae125f4a 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -74,7 +74,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { tracker.workerPool = worker.NewWorkerPoolBuilder[trackableRPC]( config.Logger, store, - tracker.rpcSent).Build() + tracker.rpcSentWorkerLogic).Build() builder := component.NewComponentManagerBuilder(). AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { @@ -103,8 +103,8 @@ func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { return nil } -// rpcSent tracks control messages sent in *pubsub.RPC. -func (t *RPCSentTracker) rpcSent(work trackableRPC) error { +// rpcSentWorkerLogic tracks control messages sent in *pubsub.RPC. +func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { switch { case len(work.rpc.GetControl().GetIhave()) > 0: iHave := work.rpc.GetControl().GetIhave() From e6a0a33b3651649bc7806ab87f51fe373b06aa5b Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 18:26:06 -0400 Subject: [PATCH 245/815] add lastUpdate to lastHighestIHaveRPCSize tracking --- .../p2p/tracer/internal/rpc_sent_tracker.go | 47 ++++++------------- .../tracer/internal/rpc_sent_tracker_test.go | 5 +- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 27dae125f4a..c3907208c70 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -13,7 +13,6 @@ import ( "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/mempool/queue" p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) @@ -26,13 +25,18 @@ type trackableRPC struct { rpc *pubsub.RPC } -// RPCSentTracker tracks RPC messages that are sent. +// lastHighestIHaveRPCSize tracks the last highest rpc control message size the time stamp it was last updated. +type lastHighestIHaveRPCSize struct { + *atomic.Int64 + lastUpdate time.Time +} + +// RPCSentTracker tracks RPC messages and the size of the last largest iHave rpc control message sent. type RPCSentTracker struct { component.Component - cache *rpcSentCache - workerPool *worker.Pool[trackableRPC] - // lastHighestIHaveRPCSize tracks the size of the last largest iHave rpc control message sent. - lastHighestIHaveRPCSize *atomic.Int64 + *lastHighestIHaveRPCSize + cache *rpcSentCache + workerPool *worker.Pool[trackableRPC] lastHighestIHaveRPCSizeResetInterval time.Duration } @@ -67,8 +71,8 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.WorkerQueueCacheCollector) tracker := &RPCSentTracker{ + lastHighestIHaveRPCSize: &lastHighestIHaveRPCSize{atomic.NewInt64(0), time.Now()}, cache: newRPCSentCache(cacheConfig), - lastHighestIHaveRPCSize: atomic.NewInt64(0), lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, } tracker.workerPool = worker.NewWorkerPoolBuilder[trackableRPC]( @@ -76,11 +80,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { store, tracker.rpcSentWorkerLogic).Build() - builder := component.NewComponentManagerBuilder(). - AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - ready() - tracker.lastHighestIHaveRPCSizeResetLoop(ctx) - }) + builder := component.NewComponentManagerBuilder() for i := 0; i < config.NumOfWorkers; i++ { builder.AddWorker(tracker.workerPool.WorkerLogic()) } @@ -115,7 +115,8 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { } func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { - if t.lastHighestIHaveRPCSize.Load() < size { + if t.lastHighestIHaveRPCSize.Load() < size || time.Since(t.lastHighestIHaveRPCSize.lastUpdate) > t.lastHighestIHaveRPCSizeResetInterval { + // The last highest ihave RPC size is updated if the new size is larger than the current size, or if the time elapsed since the last update surpasses the reset interval. t.lastHighestIHaveRPCSize.Store(size) } } @@ -148,26 +149,6 @@ func (t *RPCSentTracker) LastHighestIHaveRPCSize() int64 { return t.lastHighestIHaveRPCSize.Load() } -// lastHighestIHaveRPCSizeResetLoop resets the lastHighestIHaveRPCSize to 0 on each interval tick. -func (t *RPCSentTracker) lastHighestIHaveRPCSizeResetLoop(ctx irrecoverable.SignalerContext) { - ticker := time.NewTicker(t.lastHighestIHaveRPCSizeResetInterval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - default: - } - - select { - case <-ctx.Done(): - return - case <-ticker.C: - t.lastHighestIHaveRPCSize.Store(0) - } - } -} - // nonce returns random string that is used to store unique items in herocache. func nonce() ([]byte, error) { b := make([]byte, 16) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 9445391c615..05fce5f8552 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -124,8 +124,11 @@ func TestRPCSentTracker_LastHighestIHaveRPCSize(t *testing.T) { return tracker.LastHighestIHaveRPCSize() == int64(largeIhave) }, 1*time.Second, 100*time.Millisecond) + // we expect lastHighestIHaveRPCSize to be set to the current rpc size being tracked if it hasn't been updated since the configured lastHighestIHaveRPCSizeResetInterval + expectedEventualLastHighest := 8 require.Eventually(t, func() bool { - return tracker.LastHighestIHaveRPCSize() == 0 + require.NoError(t, tracker.Track(rpcFixture(withIhaves(mockIHaveFixture(expectedEventualLastHighest, numOfMessageIds))))) + return tracker.LastHighestIHaveRPCSize() == int64(expectedEventualLastHighest) }, 4*time.Second, 100*time.Millisecond) } From 640f9bfcd9e8e7653652611fd9cd590a55fc518f Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 18 Jul 2023 18:30:24 -0400 Subject: [PATCH 246/815] add KeyNetworkingSecurity when err returned during tracking --- network/p2p/tracer/gossipSubMeshTracer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index f0e2d035c59..fea310a251d 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -186,7 +186,7 @@ func (t *GossipSubMeshTracer) Prune(p peer.ID, topic string) { func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { err := t.rpcSentTracker.Track(rpc) if err != nil { - t.logger.Err(err).Msg("failed to track sent pubsbub rpc") + t.logger.Err(err).Bool(logging.KeyNetworkingSecurity, true).Msg("failed to track sent pubsbub rpc") } } From fbd84467285a91cdb84acfe660a637ccf83dbdd1 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 19 Jul 2023 02:06:40 +0300 Subject: [PATCH 247/815] Added test TestExecutionEvictingCacheClients --- .../rpc/backend/connection_factory_test.go | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index fda622992ef..7f75727f878 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/connectivity" "google.golang.org/grpc/status" "github.com/onflow/flow-go/engine/access/mock" @@ -495,47 +496,81 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { }) } -// TestExecutionEvictingCacheClients +// TestExecutionEvictingCacheClients tests the eviction of cached clients in the execution flow. +// It verifies that when a client is evicted from the cache, subsequent requests are handled correctly. +// +// Test Steps: +// - Call the gRPC method Ping with a delayed response. +// - Invalidate the access API client during the Ping call and verify the expected behavior. +// - Call the gRPC method GetNetworkParameters on the client immediately after eviction and assert the expected +// error response. +// - Wait for the client state to change from "Ready" to "Shutdown", indicating that the client connection was closed. func TestExecutionEvictingCacheClients(t *testing.T) { - // Add createCollNode function to recreate it each time for rapid test + // Create a new collection node for testing cn := new(collectionNode) cn.start(t) defer cn.stop(t) - // setup the handler mock - req := &access.PingRequest{} - resp := &access.PingResponse{} - cn.handler.On("Ping", testifymock.Anything, req).After(3*time.Second).Return(resp, nil) + // Set up mock handlers for Ping and GetNetworkParameters + pingReq := &access.PingRequest{} + pingResp := &access.PingResponse{} + cn.handler.On("Ping", testifymock.Anything, pingReq).After(time.Second).Return(pingResp, nil) - // create the factory + netReq := &access.GetNetworkParametersRequest{} + netResp := &access.GetNetworkParametersResponse{} + cn.handler.On("GetNetworkParameters", testifymock.Anything).Return(netResp, nil) + + // Create the connection factory connectionFactory := new(ConnectionFactoryImpl) - // set the execution grpc port - connectionFactory.ExecutionGRPCPort = cn.port - // set the execution grpc client timeout - connectionFactory.ExecutionNodeGRPCTimeout = 10 * time.Second - // set the connection pool cache size + // Set the gRPC port + connectionFactory.CollectionGRPCPort = cn.port + // Set the gRPC client timeout + connectionFactory.CollectionNodeGRPCTimeout = 5 * time.Second + // Set the connection pool cache size cacheSize := 1 cache, _ := lru.New(cacheSize) connectionFactory.ConnectionsCache = cache connectionFactory.CacheSize = uint(cacheSize) - // set metrics reporting + // Set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() clientAddress := cn.listener.Addr().String() - // create the execution API client + // Create the execution API client client, _, err := connectionFactory.GetAccessAPIClient(clientAddress) - assert.NoError(t, err) + require.NoError(t, err) + + ctx := context.Background() + + // Retrieve the cached client from the cache + result, _ := cache.Get(clientAddress) + cachedClient := result.(*CachedClient) - time.AfterFunc(time.Second, func() { - fmt.Println("Function called after 1 seconds") + // Schedule the invalidation of the access API client after a delay + time.AfterFunc(250*time.Millisecond, func() { + // Invalidate the access API client connectionFactory.InvalidateAccessAPIClient(clientAddress) + + // Assert that the cached client is marked for closure but still waiting for previous request + assert.True(t, cachedClient.closeRequested.Load()) + assert.Equal(t, cachedClient.ClientConn.GetState(), connectivity.Ready) + + // Call a gRPC method on the client, that was already evicted + resp, err := client.GetNetworkParameters(ctx, netReq) + assert.Equal(t, status.Errorf(codes.Unavailable, "the connection to %s was closed", clientAddress), err) + assert.Nil(t, resp) }) - ctx := context.Background() - fmt.Println("Ping started") - _, err = client.Ping(ctx, req) - fmt.Println("Ping finished") + // Call a gRPC method on the client + _, err = client.Ping(ctx, pingReq) + // Check that Ping was called + cn.handler.AssertCalled(t, "Ping", testifymock.Anything, pingReq) + assert.NoError(t, err) + // Wait for the client connection to change state from "Ready" to "Shutdown" as connection was closed. + changed := cachedClient.ClientConn.WaitForStateChange(ctx, connectivity.Ready) + assert.True(t, changed) + assert.Equal(t, connectivity.Shutdown, cachedClient.ClientConn.GetState()) + assert.Equal(t, 0, cache.Len()) } // node mocks a flow node that runs a GRPC server From 43bea66fd948acfc0bee11eb6a25060b21e93244 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Wed, 19 Jul 2023 15:53:19 +0300 Subject: [PATCH 248/815] Return add to retrive connection. --- .../access/rpc/backend/connection_factory.go | 44 +++++++++++++------ .../rpc/backend/connection_factory_test.go | 1 + 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index baf25339067..77fb9dc700f 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -63,13 +63,12 @@ type ConnectionFactoryImpl struct { MaxMsgSize uint AccessMetrics module.AccessMetrics Log zerolog.Logger - mutex sync.Mutex + mutex sync.RWMutex } type CachedClient struct { ClientConn *grpc.ClientConn Address string - mutex sync.Mutex timeout time.Duration closeRequested *atomic.Bool wg sync.WaitGroup @@ -118,9 +117,34 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D return conn, nil } +func (cf *ConnectionFactoryImpl) getClientConnection(grpcAddress string) *grpc.ClientConn { + cf.mutex.RLock() + defer cf.mutex.RUnlock() + + if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { + return res.(*CachedClient).ClientConn + } + return nil +} + func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { + + // 1. attempt to read with Read lock + clientConn := cf.getClientConnection(grpcAddress) + if clientConn != nil { + return clientConn, nil + } + + // 2. if not found acquire write lock cf.mutex.Lock() + defer cf.mutex.Unlock() + + // 3. Check if other goroutine haven't created an entity + if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { + return res.(*CachedClient).ClientConn, nil + } + // 4. Perform initialization in critical section store := &CachedClient{ ClientConn: nil, Address: grpcAddress, @@ -130,27 +154,21 @@ func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout } cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") - _ = cf.ConnectionsCache.Add(grpcAddress, store) - if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionAddedToPool() - } - cf.mutex.Unlock() - - store.mutex.Lock() - defer store.mutex.Unlock() - conn, err := cf.createConnection(grpcAddress, timeout, store) + var err error + store.ClientConn, err = cf.createConnection(grpcAddress, timeout, store) if err != nil { return nil, err } - store.ClientConn = conn + _ = cf.ConnectionsCache.Add(grpcAddress, store) if cf.AccessMetrics != nil { + cf.AccessMetrics.ConnectionAddedToPool() cf.AccessMetrics.NewConnectionEstablished() cf.AccessMetrics.TotalConnectionsInPool(uint(cf.ConnectionsCache.Len()), cf.CacheSize) } - return conn, nil + return store.ClientConn, nil } func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index 7f75727f878..644240a3747 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -388,6 +388,7 @@ func TestConnectionPoolStale(t *testing.T) { // close connection to simulate something "going wrong" with our stored connection res, _ := connectionFactory.ConnectionsCache.Get(proxyConnectionFactory.targetAddress) + connectionFactory.ConnectionsCache.Remove(proxyConnectionFactory.targetAddress) res.(*CachedClient).Close() ctx := context.Background() From 0093a4747e7ce773dc069218b0f02a1c0525968f Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 19 Jul 2023 17:40:23 -0400 Subject: [PATCH 249/815] add iWant control message validation --- config/default-config.yml | 6 + .../validation_inspector_test.go | 2 +- network/netconf/flags.go | 7 ++ .../control_message_validation_inspector.go | 114 +++++++++++++----- network/p2p/inspector/validation/errors.go | 44 +++++-- .../p2p/inspector/validation/errors_test.go | 15 +-- network/p2p/inspector/validation/utils.go | 12 +- .../p2p/p2pconf/gossipsub_rpc_inspectors.go | 15 +++ network/p2p/pubsub.go | 6 + network/p2p/tracer/gossipSubMeshTracer.go | 11 ++ 10 files changed, 178 insertions(+), 54 deletions(-) diff --git a/config/default-config.yml b/config/default-config.yml index c14f7e13c0b..84c3c0837b1 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -106,6 +106,12 @@ network-config: ihave-async-inspection-sample-size-percentage: .10 # Max number of ihave messages in a sample to be inspected ihave-max-sample-size: 100 + + # Max number of iwant messages in a sample to be inspected + gossipsub-rpc-iwant-max-sample-size: 1_000_000 + # The allowed threshold of iWant messages receievd without a corresponding tracked iHave message that was sent + gossipsub-rpc-iwant-cache-miss-threshold: .5 + # RPC metrics observer inspector configs # The number of metrics inspector pool workers gossipsub-rpc-metrics-inspector-workers: 1 diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 3dd873d0e7c..67d63723d21 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -608,7 +608,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) - require.True(t, validation.IsErrDuplicateTopic(notification.Err)) + require.True(t, validation.IsDuplicateFoundErr(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { case p2pmsg.CtrlMsgGraft: diff --git a/network/netconf/flags.go b/network/netconf/flags.go index 045755fde7b..c99c97e87c0 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -59,6 +59,9 @@ const ( metricsInspectorNumberOfWorkers = "gossipsub-rpc-metrics-inspector-workers" metricsInspectorCacheSize = "gossipsub-rpc-metrics-inspector-cache-size" + iwantMaxSampleSize = "gossipsub-rpc-iwant-max-sample-size" + iwantCacheMissThreshold = "gossipsub-rpc-iwant-cache-miss-threshold" + alspDisabled = "alsp-disable-penalty" alspSpamRecordCacheSize = "alsp-spam-record-cache-size" alspSpamRecordQueueSize = "alsp-spam-report-queue-size" @@ -73,6 +76,7 @@ func AllFlagNames() []string { scoreTracerInterval, gossipSubRPCInspectorNotificationCacheSize, validationInspectorNumberOfWorkers, validationInspectorInspectMessageQueueCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheDecay, validationInspectorClusterPrefixHardThreshold, ihaveSyncSampleSizePercentage, ihaveAsyncSampleSizePercentage, ihaveMaxSampleSize, metricsInspectorNumberOfWorkers, metricsInspectorCacheSize, alspDisabled, alspSpamRecordCacheSize, alspSpamRecordQueueSize, alspHearBeatInterval, + iwantMaxSampleSize, iwantCacheMissThreshold, } } @@ -133,6 +137,9 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { flags.Float64(ihaveSyncSampleSizePercentage, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IHaveSyncInspectSampleSizePercentage, "percentage of ihave messages to sample during synchronous validation") flags.Float64(ihaveAsyncSampleSizePercentage, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IHaveAsyncInspectSampleSizePercentage, "percentage of ihave messages to sample during asynchronous validation") flags.Float64(ihaveMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IHaveInspectionMaxSampleSize, "max number of ihaves to sample when performing validation") + + flags.Uint64(iwantMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.MaxSampleSize, "max number of ihaves to sample when performing validation") + flags.Float64(iwantCacheMissThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max number of ihaves to sample when performing validation") } // rpcInspectorValidationLimits utility func that adds flags for each of the validation limits for each control message type. diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 7daf3f38599..1ed22632b71 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -54,6 +54,7 @@ type ControlMsgValidationInspector struct { tracker *cache.ClusterPrefixedMessagesReceivedTracker idProvider module.IdentityProvider rateLimiters map[p2pmsg.ControlMessageType]p2p.BasicRateLimiter + rpcTracker p2p.RPCControlTracking } var _ component.Component = (*ControlMsgValidationInspector)(nil) @@ -196,6 +197,57 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e return nil } +// inspectIWant inspects RPC iWant control messages. This func will sample the iWants and perform validation on each iWant in the sample. +// Ensuring that the following are true: +// - Each iWant corresponds to an iHave that was sent. +// - Each topic in the iWant sample is a valid topic. +// If the number of iWants that do not have a corresponding iHave exceed the configured threshold an error is returned. +// Args: +// - iWant: the list of iWant control messages. +// Returns: +// - DuplicateFoundErr: if there are any duplicate message ids found in across any of the iWants. +// - IWantCacheMissThresholdErr: if the rate of cache misses exceeds the configured allowed threshold. +// - error: if any error occurs while sampling the iWants +func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.ControlIWant) error { + sampleSize := uint(10 * c.rpcTracker.LastHighestIHaveRPCSize()) + if sampleSize == 0 || sampleSize > c.config.IWantRPCInspectionConfig.MaxSampleSize { + sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize + } + + swap := func(i, j uint) { + iWants[i], iWants[j] = iWants[j], iWants[i] + } + err := c.sampleCtrlMessages(uint(len(iWants)), sampleSize, swap) + if err != nil { + return fmt.Errorf("failed to sample iwant messages: %w", err) + } + + tracker := make(duplicateStrTracker) + cacheMisses := float64(0) + totalMessageIDS := float64(0) + for i := uint(0); i < sampleSize; i++ { + iWant := iWants[i] + for _, messageID := range iWant.GetMessageIDs() { + if tracker.isDuplicate(messageID) { + return NewDuplicateFoundErr(fmt.Errorf("duplicate message ID found: %s", messageID)) + } + + if !c.rpcTracker.WasIHaveRPCSent(messageID) { + cacheMisses++ + } + tracker.set(messageID) + totalMessageIDS++ + } + } + + // check cache miss rate + if cacheMisses/totalMessageIDS > c.config.IWantRPCInspectionConfig.CacheMissThreshold { + return NewIWantCacheMissThresholdErr(cacheMisses, totalMessageIDS, c.config.IWantRPCInspectionConfig.CacheMissThreshold) + } + + return nil +} + // Name returns the name of the rpc inspector. func (c *ControlMsgValidationInspector) Name() string { return rpcInspectorComponentName @@ -267,9 +319,16 @@ func (c *ControlMsgValidationInspector) blockingIHaveSamplePreprocessing(from pe // If the RPC control message count exceeds the configured hard threshold we perform synchronous topic validation on a subset // of the control messages. This is used for control message types that do not have an upper bound on the amount of messages a node can send. func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer.ID, validationConfig *p2pconf.CtrlMsgValidationConfig, controlMessage *pubsub_pb.ControlMessage, sampleSize uint) error { - if validationConfig.ControlMsg != p2pmsg.CtrlMsgIHave && validationConfig.ControlMsg != p2pmsg.CtrlMsgIWant { - return fmt.Errorf("unexpected control message type %s encountered during blocking pre-processing sample rpc, expected %s or %s", validationConfig.ControlMsg, p2pmsg.CtrlMsgIHave, p2pmsg.CtrlMsgIWant) + if validationConfig.ControlMsg != p2pmsg.CtrlMsgIHave { + return fmt.Errorf("unexpected control message type %s encountered during blocking pre-processing sample rpc, expected %s", validationConfig.ControlMsg, p2pmsg.CtrlMsgIHave) } + + iHaves := controlMessage.GetIhave() + totalIhaves := uint(len(iHaves)) + swap := func(i, j uint) { + iHaves[i], iHaves[j] = iHaves[j], iHaves[i] + } + activeClusterIDS := c.tracker.GetActiveClusterIds() count := c.getCtrlMsgCount(validationConfig.ControlMsg, controlMessage) lg := c.logger.With(). @@ -280,7 +339,7 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer if count > validationConfig.HardThreshold { // for iHave control message topic validation we only validate a random subset of the messages // shuffle the ihave messages to perform random validation on a subset of size sampleSize - err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIHave, controlMessage, sampleSize) + err := c.sampleCtrlMessages(totalIhaves, sampleSize, swap) if err != nil { return fmt.Errorf("failed to sample ihave messages: %w", err) } @@ -306,7 +365,7 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer // to randomize async validation to avoid data race that can occur when // performing the sampling asynchronously. // for iHave control message topic validation we only validate a random subset of the messages - err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIHave, controlMessage, sampleSize) + err := c.sampleCtrlMessages(totalIhaves, sampleSize, swap) if err != nil { return fmt.Errorf("failed to sample ihave messages: %w", err) } @@ -315,17 +374,10 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer // sampleCtrlMessages performs sampling on the specified control message that will randomize // the items in the control message slice up to index sampleSize-1. -func (c *ControlMsgValidationInspector) sampleCtrlMessages(ctrlMsgType p2pmsg.ControlMessageType, ctrlMsg *pubsub_pb.ControlMessage, sampleSize uint) error { - switch ctrlMsgType { - case p2pmsg.CtrlMsgIHave: - iHaves := ctrlMsg.GetIhave() - swap := func(i, j uint) { - iHaves[i], iHaves[j] = iHaves[j], iHaves[i] - } - err := flowrand.Samples(uint(len(iHaves)), sampleSize, swap) - if err != nil { - return fmt.Errorf("failed to get random sample of ihave control messages: %w", err) - } +func (c *ControlMsgValidationInspector) sampleCtrlMessages(totalSize, sampleSize uint, swap func(i, j uint)) error { + err := flowrand.Samples(totalSize, sampleSize, swap) + if err != nil { + return fmt.Errorf("failed to get random sample of ihave control messages: %w", err) } return nil } @@ -339,6 +391,12 @@ func (c *ControlMsgValidationInspector) processInspectMsgReq(req *InspectMsgRequ c.metrics.AsyncProcessingFinished(req.validationConfig.ControlMsg.String(), time.Since(start)) }() + // iWant validation uses new sample size validation. This will be updated for all other control message types. + switch req.validationConfig.ControlMsg { + case p2pmsg.CtrlMsgIWant: + return c.inspectIWant(req.ctrlMsg.GetIwant()) + } + count := c.getCtrlMsgCount(req.validationConfig.ControlMsg, req.ctrlMsg) lg := c.logger.With(). Str("peer_id", req.Peer.String()). @@ -392,7 +450,7 @@ func (c *ControlMsgValidationInspector) getCtrlMsgCount(ctrlMsgType p2pmsg.Contr // validateTopics ensures all topics in the specified control message are valid flow topic/channel and no duplicate topics exist. // Expected error returns during normal operations: // - channels.InvalidTopicErr: if topic is invalid. -// - ErrDuplicateTopic: if a duplicate topic ID is encountered. +// - DuplicateFoundErr: if a duplicate topic ID is encountered. func (c *ControlMsgValidationInspector) validateTopics(from peer.ID, validationConfig *p2pconf.CtrlMsgValidationConfig, ctrlMsg *pubsub_pb.ControlMessage) error { activeClusterIDS := c.tracker.GetActiveClusterIds() switch validationConfig.ControlMsg { @@ -413,13 +471,13 @@ func (c *ControlMsgValidationInspector) validateTopics(from peer.ID, validationC // validateGrafts performs topic validation on all grafts in the control message using the provided validateTopic func while tracking duplicates. func (c *ControlMsgValidationInspector) validateGrafts(from peer.ID, ctrlMsg *pubsub_pb.ControlMessage, activeClusterIDS flow.ChainIDList) error { - tracker := make(duplicateTopicTracker) + tracker := make(duplicateStrTracker) for _, graft := range ctrlMsg.GetGraft() { topic := channels.Topic(graft.GetTopicID()) - if tracker.isDuplicate(topic) { - return NewDuplicateTopicErr(topic) + if tracker.isDuplicate(topic.String()) { + return NewDuplicateFoundErr(fmt.Errorf("duplicate topic found: %s", topic.String())) } - tracker.set(topic) + tracker.set(topic.String()) err := c.validateTopic(from, topic, activeClusterIDS) if err != nil { return err @@ -430,13 +488,13 @@ func (c *ControlMsgValidationInspector) validateGrafts(from peer.ID, ctrlMsg *pu // validatePrunes performs topic validation on all prunes in the control message using the provided validateTopic func while tracking duplicates. func (c *ControlMsgValidationInspector) validatePrunes(from peer.ID, ctrlMsg *pubsub_pb.ControlMessage, activeClusterIDS flow.ChainIDList) error { - tracker := make(duplicateTopicTracker) + tracker := make(duplicateStrTracker) for _, prune := range ctrlMsg.GetPrune() { topic := channels.Topic(prune.GetTopicID()) - if tracker.isDuplicate(topic) { - return NewDuplicateTopicErr(topic) + if tracker.isDuplicate(topic.String()) { + return NewDuplicateFoundErr(fmt.Errorf("duplicate topic found: %s", topic.String())) } - tracker.set(topic) + tracker.set(topic.String()) err := c.validateTopic(from, topic, activeClusterIDS) if err != nil { return err @@ -455,15 +513,15 @@ func (c *ControlMsgValidationInspector) validateIhaves(from peer.ID, validationC // Sample size ensures liveness of the network when validating messages with no upper bound on the amount of messages that may be received. // All errors returned from this function can be considered benign. func (c *ControlMsgValidationInspector) validateTopicsSample(from peer.ID, validationConfig *p2pconf.CtrlMsgValidationConfig, ctrlMsg *pubsub_pb.ControlMessage, activeClusterIDS flow.ChainIDList, sampleSize uint) error { - tracker := make(duplicateTopicTracker) + tracker := make(duplicateStrTracker) switch validationConfig.ControlMsg { case p2pmsg.CtrlMsgIHave: for i := uint(0); i < sampleSize; i++ { topic := channels.Topic(ctrlMsg.Ihave[i].GetTopicID()) - if tracker.isDuplicate(topic) { - return NewDuplicateTopicErr(topic) + if tracker.isDuplicate(topic.String()) { + return NewDuplicateFoundErr(fmt.Errorf("duplicate topic found: %s", topic.String())) } - tracker.set(topic) + tracker.set(topic.String()) err := c.validateTopic(from, topic, activeClusterIDS) if err != nil { return err diff --git a/network/p2p/inspector/validation/errors.go b/network/p2p/inspector/validation/errors.go index 231fe979498..0b5606d56ea 100644 --- a/network/p2p/inspector/validation/errors.go +++ b/network/p2p/inspector/validation/errors.go @@ -8,6 +8,28 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) +// IWantCacheMissThresholdErr indicates that the amount of cache misses exceeds the allowed threshold. +type IWantCacheMissThresholdErr struct { + misses float64 + totalMessageIDS float64 + threshold float64 +} + +func (e IWantCacheMissThresholdErr) Error() string { + return fmt.Sprintf("%f/%f iWant cache misses exceeds the allowed threshold: %f", e.misses, e.totalMessageIDS, e.threshold) +} + +// NewIWantCacheMissThresholdErr returns a new IWantCacheMissThresholdErr. +func NewIWantCacheMissThresholdErr(misses, totalMessageIDS, threshold float64) IWantCacheMissThresholdErr { + return IWantCacheMissThresholdErr{misses, totalMessageIDS, threshold} +} + +// IsIWantCacheMissThresholdErr returns true if an error is IWantCacheMissThresholdErr +func IsIWantCacheMissThresholdErr(err error) bool { + var e IWantCacheMissThresholdErr + return errors.As(err, &e) +} + // ErrHardThreshold indicates that the amount of RPC messages received exceeds hard threshold. type ErrHardThreshold struct { // controlMsg the control message type. @@ -53,23 +75,23 @@ func IsErrRateLimitedControlMsg(err error) bool { return errors.As(err, &e) } -// ErrDuplicateTopic error that indicates a duplicate topic in control message has been detected. -type ErrDuplicateTopic struct { - topic channels.Topic +// DuplicateFoundErr error that indicates a duplicate has been detected. This can be duplicate topic or message ID tracking. +type DuplicateFoundErr struct { + err error } -func (e ErrDuplicateTopic) Error() string { - return fmt.Errorf("duplicate topic %s", e.topic).Error() +func (e DuplicateFoundErr) Error() string { + return e.err.Error() } -// NewDuplicateTopicErr returns a new ErrDuplicateTopic. -func NewDuplicateTopicErr(topic channels.Topic) ErrDuplicateTopic { - return ErrDuplicateTopic{topic: topic} +// NewDuplicateFoundErr returns a new DuplicateFoundErr. +func NewDuplicateFoundErr(err error) DuplicateFoundErr { + return DuplicateFoundErr{err} } -// IsErrDuplicateTopic returns true if an error is ErrDuplicateTopic. -func IsErrDuplicateTopic(err error) bool { - var e ErrDuplicateTopic +// IsDuplicateFoundErr returns true if an error is DuplicateFoundErr. +func IsDuplicateFoundErr(err error) bool { + var e DuplicateFoundErr return errors.As(err, &e) } diff --git a/network/p2p/inspector/validation/errors_test.go b/network/p2p/inspector/validation/errors_test.go index 9bef259fd41..bb57af601c0 100644 --- a/network/p2p/inspector/validation/errors_test.go +++ b/network/p2p/inspector/validation/errors_test.go @@ -63,19 +63,20 @@ func TestErrRateLimitedControlMsgRoundTrip(t *testing.T) { assert.False(t, IsErrRateLimitedControlMsg(dummyErr), "IsErrRateLimitedControlMsg should return false for non-ErrRateLimitedControlMsg error") } -// TestErrDuplicateTopicRoundTrip ensures correct error formatting for ErrDuplicateTopic. +// TestErrDuplicateTopicRoundTrip ensures correct error formatting for DuplicateFoundErr. func TestErrDuplicateTopicRoundTrip(t *testing.T) { topic := channels.Topic("topic") - err := NewDuplicateTopicErr(topic) + e := fmt.Errorf("duplicate topic found: %s", topic.String()) + err := NewDuplicateFoundErr(e) // tests the error message formatting. - expectedErrMsg := fmt.Errorf("duplicate topic %s", topic).Error() + expectedErrMsg := e.Error() assert.Equal(t, expectedErrMsg, err.Error(), "the error message should be correctly formatted") - // tests the IsErrDuplicateTopic function. - assert.True(t, IsErrDuplicateTopic(err), "IsErrDuplicateTopic should return true for ErrDuplicateTopic error") + // tests the IsDuplicateFoundErr function. + assert.True(t, IsDuplicateFoundErr(err), "IsDuplicateFoundErr should return true for DuplicateFoundErr error") - // test IsErrDuplicateTopic with a different error type. + // test IsDuplicateFoundErr with a different error type. dummyErr := fmt.Errorf("dummy error") - assert.False(t, IsErrDuplicateTopic(dummyErr), "IsErrDuplicateTopic should return false for non-ErrDuplicateTopic error") + assert.False(t, IsDuplicateFoundErr(dummyErr), "IsDuplicateFoundErr should return false for non-DuplicateFoundErr error") } diff --git a/network/p2p/inspector/validation/utils.go b/network/p2p/inspector/validation/utils.go index b15d74548be..0e57a9dd345 100644 --- a/network/p2p/inspector/validation/utils.go +++ b/network/p2p/inspector/validation/utils.go @@ -1,14 +1,12 @@ package validation -import "github.com/onflow/flow-go/network/channels" +type duplicateStrTracker map[string]struct{} -type duplicateTopicTracker map[channels.Topic]struct{} - -func (d duplicateTopicTracker) set(topic channels.Topic) { - d[topic] = struct{}{} +func (d duplicateStrTracker) set(s string) { + d[s] = struct{}{} } -func (d duplicateTopicTracker) isDuplicate(topic channels.Topic) bool { - _, ok := d[topic] +func (d duplicateStrTracker) isDuplicate(s string) bool { + _, ok := d[s] return ok } diff --git a/network/p2p/p2pconf/gossipsub_rpc_inspectors.go b/network/p2p/p2pconf/gossipsub_rpc_inspectors.go index d4f2bd6ccc1..0c42e2b0587 100644 --- a/network/p2p/p2pconf/gossipsub_rpc_inspectors.go +++ b/network/p2p/p2pconf/gossipsub_rpc_inspectors.go @@ -17,6 +17,7 @@ type GossipSubRPCInspectorsConfig struct { // GossipSubRPCValidationInspectorConfigs validation limits used for gossipsub RPC control message inspection. type GossipSubRPCValidationInspectorConfigs struct { ClusterPrefixedMessageConfig `mapstructure:",squash"` + IWantRPCInspectionConfig `mapstructure:",squash"` // NumberOfWorkers number of worker pool workers. NumberOfWorkers int `validate:"gte=1" mapstructure:"gossipsub-rpc-validation-inspector-workers"` // CacheSize size of the queue used by worker pool for the control message validation inspector. @@ -47,6 +48,16 @@ type GossipSubRPCValidationInspectorConfigs struct { IHaveInspectionMaxSampleSize float64 `validate:"gte=100" mapstructure:"ihave-max-sample-size"` } +// IWantRPCInspectionConfig validation configuration for iWANT RPC control messages. +type IWantRPCInspectionConfig struct { + // MaxSampleSize max inspection sample size to use. + MaxSampleSize uint `validate:"gt=0" mapstructure:"gossipsub-rpc-iwant-max-sample-size"` + // CacheMissThreshold the threshold of missing corresponding iHave messages for iWant messages received before an invalid control message notification is disseminated. + CacheMissThreshold float64 `validate:"gt=0" mapstructure:"gossipsub-rpc-iwant-cache-miss-threshold"` +} + +// half of the sample + // GetCtrlMsgValidationConfig returns the CtrlMsgValidationConfig for the specified p2p.ControlMessageType. func (conf *GossipSubRPCValidationInspectorConfigs) GetCtrlMsgValidationConfig(controlMsg p2pmsg.ControlMessageType) (*CtrlMsgValidationConfig, bool) { switch controlMsg { @@ -71,6 +82,10 @@ func (conf *GossipSubRPCValidationInspectorConfigs) GetCtrlMsgValidationConfig(c SafetyThreshold: conf.IHaveLimits.SafetyThreshold, RateLimit: conf.IHaveLimits.RateLimit, }, true + case p2pmsg.CtrlMsgIWant: + return &CtrlMsgValidationConfig{ + ControlMsg: p2pmsg.CtrlMsgIWant, + }, true default: return nil, false } diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index 3e8e030ad8c..bc0a48339ad 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -170,6 +170,12 @@ type SubscriptionFilter interface { type PubSubTracer interface { component.Component pubsub.RawTracer + RPCControlTracking +} + +type RPCControlTracking interface { + LastHighestIHaveRPCSize() int64 + WasIHaveRPCSent(messageID string) bool } // PeerScoreSnapshot is a snapshot of the overall peer score at a given time. diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index fea310a251d..f0b9c96d331 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -190,6 +190,17 @@ func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { } } +// WasIHaveRPCSent returns true if an iHave control message for the messageID was sent, otherwise false. +func (t *GossipSubMeshTracer) WasIHaveRPCSent(messageID string) bool { + // TODO: topic string is obsolete since messageID is globally unique + return t.rpcSentTracker.WasIHaveRPCSent("", messageID) +} + +// LastHighestIHaveRPCSize returns the last highest RPC iHave message sent. +func (t *GossipSubMeshTracer) LastHighestIHaveRPCSize() int64 { + return t.rpcSentTracker.LastHighestIHaveRPCSize() +} + // logLoop logs the mesh peers of the local node for each topic at a regular interval. func (t *GossipSubMeshTracer) logLoop(ctx irrecoverable.SignalerContext) { ticker := time.NewTicker(t.loggerInterval) From 811f83b9be420cd881295e27e0ff1334cd5769df Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:41:21 -0700 Subject: [PATCH 250/815] spike to improve locking in grpc connection factory --- engine/access/rpc/connection/cache.go | 87 +++++++++ engine/access/rpc/connection/connection.go | 143 +++++++++++++++ engine/access/rpc/connection/manager.go | 194 +++++++++++++++++++++ 3 files changed, 424 insertions(+) create mode 100644 engine/access/rpc/connection/cache.go create mode 100644 engine/access/rpc/connection/connection.go create mode 100644 engine/access/rpc/connection/manager.go diff --git a/engine/access/rpc/connection/cache.go b/engine/access/rpc/connection/cache.go new file mode 100644 index 00000000000..149ef2e4430 --- /dev/null +++ b/engine/access/rpc/connection/cache.go @@ -0,0 +1,87 @@ +package connection + +import ( + "sync" + "time" + + lru "github.com/hashicorp/golang-lru" + "go.uber.org/atomic" + "google.golang.org/grpc" +) + +type CachedClient struct { + ClientConn *grpc.ClientConn + Address string + timeout time.Duration + closeRequested *atomic.Bool + wg sync.WaitGroup + mu sync.Mutex +} + +// Close closes the CachedClient connection. It marks the connection for closure and waits asynchronously for ongoing +// requests to complete before closing the connection. +func (s *CachedClient) Close() { + // Mark the connection for closure + if swapped := s.closeRequested.CompareAndSwap(false, true); !swapped { + return + } + + // If there are ongoing requests, wait for them to complete asynchronously + go func() { + s.wg.Wait() + + // Close the connection + s.ClientConn.Close() + }() +} + +type Cache struct { + cache *lru.Cache +} + +func NewCache(cache *lru.Cache) *Cache { + return &Cache{ + cache: cache, + } +} + +func (c *Cache) Get(address string) (*CachedClient, bool) { + val, ok := c.cache.Get(address) + if !ok { + return nil, false + } + return val.(*CachedClient), true +} + +// GetOrAdd atomically gets the CachedClient for the given address from the cache, or adds a new one +// if none existed. +// New entries are added to the cache with their mutex locked. This ensures that the caller gets +// priority when working with the new client, allowing it to create the underlying connection. +// Clients retrieved from the cache are returned without modifying their lock. +func (c *Cache) GetOrAdd(address string, timeout time.Duration) (*CachedClient, bool) { + client := &CachedClient{} + client.mu.Lock() + + val, existed, _ := c.cache.PeekOrAdd(address, client) + if existed { + return val.(*CachedClient), true + } + + client.Address = address + client.timeout = timeout + client.closeRequested = atomic.NewBool(false) + + return client, false +} + +func (c *Cache) Add(address string, client *CachedClient) (evicted bool) { + return c.cache.Add(address, client) +} + +func (c *Cache) Remove(address string) (present bool) { + return c.cache.Remove(address) +} + +func (c *Cache) Len() int { + return c.cache.Len() +} diff --git a/engine/access/rpc/connection/connection.go b/engine/access/rpc/connection/connection.go new file mode 100644 index 00000000000..5d5d91f1e85 --- /dev/null +++ b/engine/access/rpc/connection/connection.go @@ -0,0 +1,143 @@ +package connection + +import ( + "fmt" + "io" + "net" + "time" + + lru "github.com/hashicorp/golang-lru" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/module" +) + +// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node +const DefaultClientTimeout = 3 * time.Second + +// ConnectionFactory is used to create an access api client +type ConnectionFactory interface { + GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) + GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) + InvalidateAccessAPIClient(address string) + GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) + InvalidateExecutionAPIClient(address string) +} + +type ProxyConnectionFactory struct { + ConnectionFactory + targetAddress string +} + +type noopCloser struct{} + +func (c *noopCloser) Close() error { + return nil +} + +func (p *ProxyConnectionFactory) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { + return p.ConnectionFactory.GetAccessAPIClient(p.targetAddress) +} +func (p *ProxyConnectionFactory) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { + return p.ConnectionFactory.GetExecutionAPIClient(p.targetAddress) +} + +type ConnectionFactoryImpl struct { + CollectionGRPCPort uint + ExecutionGRPCPort uint + CollectionNodeGRPCTimeout time.Duration + ExecutionNodeGRPCTimeout time.Duration + ConnectionsCache *lru.Cache + CacheSize uint + MaxMsgSize uint + AccessMetrics module.AccessMetrics + Log zerolog.Logger + + manager *Manager +} + +func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { + return cf.GetAccessAPIClientWithPort(address, cf.CollectionGRPCPort) +} + +func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) { + + grpcAddress, err := getGRPCAddress(address, port) + if err != nil { + return nil, nil, err + } + + conn, closer, err := cf.manager.GetConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) + if err != nil { + return nil, nil, err + } + + return access.NewAccessAPIClient(conn), closer, nil +} + +func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { + if cf.ConnectionsCache == nil { + return + } + + cf.Log.Debug().Str("cached_access_client_invalidated", address).Msg("invalidating cached access client") + cf.invalidateAPIClient(address, cf.CollectionGRPCPort) +} + +func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { + + grpcAddress, err := getGRPCAddress(address, cf.ExecutionGRPCPort) + if err != nil { + return nil, nil, err + } + + conn, closer, err := cf.manager.GetConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) + if err != nil { + return nil, nil, err + } + + return execution.NewExecutionAPIClient(conn), closer, nil +} + +func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { + if cf.ConnectionsCache == nil { + return + } + + cf.Log.Debug().Str("cached_execution_client_invalidated", address).Msg("invalidating cached execution client") + cf.invalidateAPIClient(address, cf.ExecutionGRPCPort) +} + +// invalidateAPIClient invalidates the access API client associated with the given address and port. +// It removes the cached client from the ConnectionsCache and closes the connection. +func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) { + grpcAddress, err := getGRPCAddress(address, port) + if err != nil { + // TODO: return and handle the error + panic(err) + } + + if !cf.manager.Remove(grpcAddress) { + return + } + + if cf.AccessMetrics != nil { + cf.AccessMetrics.ConnectionFromPoolInvalidated() + } +} + +// getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the +// GRPC port from the libp2p port +func getGRPCAddress(address string, grpcPort uint) (string, error) { + // split hostname and port + hostnameOrIP, _, err := net.SplitHostPort(address) + if err != nil { + return "", err + } + // use the hostname from identity list and port number as the one passed in as argument + grpcAddress := fmt.Sprintf("%s:%d", hostnameOrIP, grpcPort) + + return grpcAddress, nil +} diff --git a/engine/access/rpc/connection/manager.go b/engine/access/rpc/connection/manager.go new file mode 100644 index 00000000000..5b4d73c6a80 --- /dev/null +++ b/engine/access/rpc/connection/manager.go @@ -0,0 +1,194 @@ +package connection + +import ( + "context" + "fmt" + "io" + "time" + + "github.com/onflow/flow-go/module" + "github.com/rs/zerolog" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/status" +) + +type Manager struct { + cache *Cache + logger zerolog.Logger + metrics module.AccessMetrics + maxMsgSize uint +} + +func NewManager( + cache *Cache, + logger zerolog.Logger, + metrics module.AccessMetrics, + maxMsgSize uint, +) *Manager { + return &Manager{ + cache: cache, + logger: logger, + metrics: metrics, + maxMsgSize: maxMsgSize, + } +} + +func (m *Manager) GetConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, io.Closer, error) { + if m.cache != nil { + conn, err := m.retrieveConnection(grpcAddress, timeout) + if err != nil { + return nil, nil, err + } + return conn, &noopCloser{}, err + } + + conn, err := m.createConnection(grpcAddress, timeout, nil) + if err != nil { + return nil, nil, err + } + + return conn, io.Closer(conn), nil +} + +func (m *Manager) Remove(grpcAddress string) bool { + if m.cache == nil { + return false + } + + res, ok := m.cache.Get(grpcAddress) + if !ok { + return false + } + + if !m.cache.Remove(grpcAddress) { + return false + } + + // Close the connection only if it is successfully removed from the cache + res.Close() + return true +} + +func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { + + client, ok := m.cache.GetOrAdd(grpcAddress, timeout) + if ok { + // client was from the cache, wait for the lock + client.mu.Lock() + } + defer client.mu.Unlock() + + if client.ClientConn != nil && client.ClientConn.GetState() != connectivity.Shutdown { + return client.ClientConn, nil + } + + conn, err := m.createConnection(grpcAddress, timeout, client) + if err != nil { + return nil, err + } + + client.ClientConn = conn + + return client.ClientConn, nil +} + +// createConnection creates new gRPC connections to remote node +func (m *Manager) createConnection(address string, timeout time.Duration, cachedClient *CachedClient) (*grpc.ClientConn, error) { + + keepaliveParams := keepalive.ClientParameters{ + // how long the client will wait before sending a keepalive to the server if there is no activity + Time: 10 * time.Second, + // how long the client will wait for a response from the keepalive before closing + Timeout: timeout, + } + + var connInterceptors []grpc.UnaryClientInterceptor + + // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called + // in the same order during gRPC requests. It is crucial to ensure that the request watcher interceptor is added + // first. This interceptor is responsible for executing necessary request monitoring before passing control to + // subsequent interceptors. + if cachedClient != nil { + connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) + } + + connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) + + // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached + // The connections should be safe to be persisted and reused + // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams + // https://grpc.io/blog/grpc-on-http2/#keeping-connections-alive + conn, err := grpc.Dial( + address, + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(m.maxMsgSize))), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithKeepaliveParams(keepaliveParams), + grpc.WithChainUnaryInterceptor(connInterceptors...), + ) + if err != nil { + return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) + } + return conn, nil +} + +// WithClientTimeoutOption is a helper function to create a GRPC dial option +// with the specified client timeout interceptor. +func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { + return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) +} + +// createRequestWatcherInterceptor creates a request watcher interceptor to wait for unfinished request before close +func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { + requestWatcherInterceptor := func( + ctx context.Context, + method string, + req interface{}, + reply interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, + ) error { + // Prevent new request from being sent if the connection is marked for closure + if cachedClient.closeRequested.Load() { + return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) + } + + // Increment the request counter to track ongoing requests, then + // decrement the request counter before returning + cachedClient.wg.Add(1) + defer cachedClient.wg.Done() + + // Invoke the actual RPC method + return invoker(ctx, method, req, reply, cc, opts...) + } + + return requestWatcherInterceptor +} + +// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. +func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { + clientTimeoutInterceptor := func( + ctx context.Context, + method string, + req interface{}, + reply interface{}, + cc *grpc.ClientConn, + invoker grpc.UnaryInvoker, + opts ...grpc.CallOption, + ) error { + // Create a context that expires after the specified timeout. + ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // Call the remote GRPC using the short context. + err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) + + return err + } + + return clientTimeoutInterceptor +} From e09ba3273cbe2cd1351064944117caab6a46c327 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:51:24 -0700 Subject: [PATCH 251/815] remove cache from factory --- engine/access/rpc/connection/connection.go | 6 ++---- engine/access/rpc/connection/manager.go | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/engine/access/rpc/connection/connection.go b/engine/access/rpc/connection/connection.go index 5d5d91f1e85..715f9823fde 100644 --- a/engine/access/rpc/connection/connection.go +++ b/engine/access/rpc/connection/connection.go @@ -6,7 +6,6 @@ import ( "net" "time" - lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" @@ -49,7 +48,6 @@ type ConnectionFactoryImpl struct { ExecutionGRPCPort uint CollectionNodeGRPCTimeout time.Duration ExecutionNodeGRPCTimeout time.Duration - ConnectionsCache *lru.Cache CacheSize uint MaxMsgSize uint AccessMetrics module.AccessMetrics @@ -78,7 +76,7 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port } func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { - if cf.ConnectionsCache == nil { + if !cf.manager.HasCache() { return } @@ -102,7 +100,7 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio } func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { - if cf.ConnectionsCache == nil { + if !cf.manager.HasCache() { return } diff --git a/engine/access/rpc/connection/manager.go b/engine/access/rpc/connection/manager.go index 5b4d73c6a80..dfb4fc14497 100644 --- a/engine/access/rpc/connection/manager.go +++ b/engine/access/rpc/connection/manager.go @@ -73,6 +73,10 @@ func (m *Manager) Remove(grpcAddress string) bool { return true } +func (m *Manager) HasCache() bool { + return m.cache != nil +} + func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { client, ok := m.cache.GetOrAdd(grpcAddress, timeout) From 248c4e949530c498ffee4617dcde6c848370226c Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 11:17:26 +0300 Subject: [PATCH 252/815] Revert unnecessary changes --- engine/access/rpc/backend/backend_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 60ce82bc440..ec2d2501a45 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -171,7 +171,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder. BuildEpoch(). CompleteEpoch() @@ -308,7 +308,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder. BuildEpoch(). CompleteEpoch() @@ -373,7 +373,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 // blocks in current state - // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- NodeAction(S_D) |commit| + // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder.BuildEpoch() // add more blocks to our state in the commit phase, this will allow From da821bc79ac6ccee71221bf8c597a7edfd77c16f Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 11:33:58 +0300 Subject: [PATCH 253/815] Fixed test case --- integration/tests/access/access_circuit_breaker_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index d537873136e..d02fc811a6c 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -173,10 +173,10 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { return duration, err } - // Try to send the transaction for the first time. It should wait at least the timeout time and return Unknown error + // Try to send the transaction for the first time. It should wait at least the timeout time and return Unavailable error duration, err := sendTransaction(ctx, signedTx) assert.Equal(s.T(), codes.Unavailable, status.Code(err)) - assert.Greater(s.T(), requestTimeout, duration) + assert.Less(s.T(), duration, requestTimeout) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure From ac8c8b16373dfd1462b2d4a98d6a34f395d58bc5 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 13:25:00 +0300 Subject: [PATCH 254/815] Implemented limitation for scripts execution nodes --- engine/access/rpc/backend/backend.go | 20 +-- engine/access/rpc/backend/backend_scripts.go | 135 ++++++++---------- .../access/rpc/backend/node_communicator.go | 4 +- 3 files changed, 74 insertions(+), 85 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index d0e88b255a8..4552f3f3009 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -124,16 +124,16 @@ func New( state: state, // create the sub-backends backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, - loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, - archivePorts: archivePorts, - nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + state: state, + log: log, + metrics: accessMetrics, + loggedScripts: loggedScripts, + archiveAddressList: archiveAddressList, + archivePorts: archivePorts, + nodeCommunicator: nodeCommunicator, }, backendTransactions: backendTransactions{ staticCollectionRPC: collectionRPC, diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 587bb9f3f3c..103afea02e0 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -9,7 +9,6 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/hashicorp/go-multierror" execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc/codes" @@ -26,16 +25,16 @@ import ( const uniqueScriptLoggingTimeWindow = 10 * time.Minute type backendScripts struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory ConnectionFactory - log zerolog.Logger - metrics module.BackendScriptsMetrics - loggedScripts *lru.Cache - archiveAddressList []string - archivePorts []uint - nodeSelectorFactory NodeSelectorFactory + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory ConnectionFactory + log zerolog.Logger + metrics module.BackendScriptsMetrics + loggedScripts *lru.Cache + archiveAddressList []string + archivePorts []uint + nodeCommunicator *NodeCommunicator } func (b *backendScripts) ExecuteScriptAtLatestBlock( @@ -86,27 +85,6 @@ func (b *backendScripts) ExecuteScriptAtBlockHeight( return b.executeScriptOnExecutor(ctx, blockID, script, arguments) } -func (b *backendScripts) findScriptExecutors( - ctx context.Context, - blockID flow.Identifier, -) ([]string, error) { - executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) - - if err != nil { - return nil, err - } - executorAddrs := make([]string, 0, len(executors)) - execNodeSelector, err := b.nodeSelectorFactory.SelectNodes(executors) - if err != nil { - return nil, err - } - - for executor := execNodeSelector.Next(); executor != nil; executor = execNodeSelector.Next() { - executorAddrs = append(executorAddrs, executor.Address) - } - return executorAddrs, nil -} - // executeScriptOnExecutionNode forwards the request to the execution node using the execution node // grpc client and converts the response back to the access node api response format func (b *backendScripts) executeScriptOnExecutor( @@ -116,7 +94,7 @@ func (b *backendScripts) executeScriptOnExecutor( arguments [][]byte, ) ([]byte, error) { // find few execution nodes which have executed the block earlier and provided an execution receipt for it - scriptExecutors, err := b.findScriptExecutors(ctx, blockID) + executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) if err != nil { return nil, status.Errorf(codes.Internal, "failed to find script executors at blockId %v: %v", blockID.String(), err) } @@ -125,8 +103,8 @@ func (b *backendScripts) executeScriptOnExecutor( // *DO NOT* use this hash for any protocol-related or cryptographic functions. insecureScriptHash := md5.Sum(script) //nolint:gosec - // try execution on Archive nodes first - if len(b.archiveAddressList) > 0 { + // try execution on Archive nodes if there is no execution nodes found + if len(executors) == 0 && len(b.archiveAddressList) > 0 { startTime := time.Now() for idx, rnAddr := range b.archiveAddressList { rnPort := b.archivePorts[idx] @@ -160,54 +138,65 @@ func (b *backendScripts) executeScriptOnExecutor( } } } - // try execution nodes if the script wasn't executed - var errors *multierror.Error + // try to execute the script on one of the execution nodes found - for _, executorAddress := range scriptExecutors { - execStartTime := time.Now() // record start time - result, err := b.tryExecuteScriptOnExecutionNode(ctx, executorAddress, blockID, script, arguments) - if err == nil { - if b.log.GetLevel() == zerolog.DebugLevel { - executionTime := time.Now() - if b.shouldLogScript(executionTime, insecureScriptHash) { - b.log.Debug(). - Str("script_executor_addr", executorAddress). - Hex("block_id", blockID[:]). - Hex("script_hash", insecureScriptHash[:]). - Str("script", string(script)). - Msg("Successfully executed script") - b.loggedScripts.Add(insecureScriptHash, executionTime) + var result []byte + hasInvalidArgument := false + errToReturn := b.nodeCommunicator.CallAvailableNode( + executors, + func(node *flow.Identity) error { + execStartTime := time.Now() + result, err = b.tryExecuteScriptOnExecutionNode(ctx, node.Address, blockID, script, arguments) + if err == nil { + if b.log.GetLevel() == zerolog.DebugLevel { + executionTime := time.Now() + if b.shouldLogScript(executionTime, insecureScriptHash) { + b.log.Debug(). + Str("script_executor_addr", node.Address). + Hex("block_id", blockID[:]). + Hex("script_hash", insecureScriptHash[:]). + Str("script", string(script)). + Msg("Successfully executed script") + b.loggedScripts.Add(insecureScriptHash, executionTime) + } } + + // log execution time + b.metrics.ScriptExecuted( + time.Since(execStartTime), + len(script), + ) + + return nil } - // log execution time - b.metrics.ScriptExecuted( - time.Since(execStartTime), - len(script), - ) + return err + }, + func(node flow.Identity, err error) bool { + hasInvalidArgument = status.Code(err) == codes.InvalidArgument + if hasInvalidArgument { + b.log.Debug().Err(err). + Str("script_executor_addr", node.Address). + Hex("block_id", blockID[:]). + Hex("script_hash", insecureScriptHash[:]). + Str("script", string(script)). + Msg("script failed to execute on the execution node") + } + return hasInvalidArgument + }, + ) - return result, nil - } - // return if it's just a script failure as opposed to an EN/RN failure and skip trying other ENs/RNs - if status.Code(err) == codes.InvalidArgument { - b.log.Debug().Err(err). - Str("script_executor_addr", executorAddress). - Hex("block_id", blockID[:]). - Hex("script_hash", insecureScriptHash[:]). - Str("script", string(script)). - Msg("script failed to execute on the execution node") - return nil, err - } - errors = multierror.Append(errors, err) + if hasInvalidArgument { + return nil, errToReturn } - errToReturn := errors.ErrorOrNil() - if errToReturn != nil { + if errToReturn == nil { b.metrics.ScriptExecutionErrorOnExecutionNode() b.log.Error().Err(err).Msg("script execution failed for execution node internal reasons") + return nil, rpc.ConvertError(errToReturn, "failed to execute script on execution nodes", codes.Internal) + } else { + return result, nil } - - return nil, rpc.ConvertMultiError(errors, "failed to execute script on execution nodes", codes.Internal) } // shouldLogScript checks if the script hash is unique in the time window diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 31298d30894..052b4efa0c9 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -18,7 +18,7 @@ type NodeAction func(node *flow.Identity) error // ErrorTerminator is a callback function that determines whether an error should terminate further execution. // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. -type ErrorTerminator func(err error) bool +type ErrorTerminator func(node flow.Identity, err error) bool // NodeCommunicator is responsible for calling available nodes in the backend. type NodeCommunicator struct { @@ -54,7 +54,7 @@ func (b *NodeCommunicator) CallAvailableNode( return nil } - if shouldTerminateOnError != nil && shouldTerminateOnError(err) { + if shouldTerminateOnError != nil && shouldTerminateOnError(*node, err) { return err } From 3ed6741d85bad7afb50b95c3846f4a65c42894a5 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 14:35:42 +0300 Subject: [PATCH 255/815] Fixed if statement --- cmd/util/cmd/execution-state-extract/export_report.json | 4 ++-- engine/access/rpc/backend/backend_scripts.go | 4 ++-- engine/access/rpc/backend/backend_transactions.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 4c8484e4396..3d4abf5bcf2 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", - "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "PreviousStateCommitment": "0872fc2bbcf573c016e8af58e14db6e1efdc48e8e770115d5962f9225b1fa465", + "CurrentStateCommitment": "0872fc2bbcf573c016e8af58e14db6e1efdc48e8e770115d5962f9225b1fa465", "ReportSucceeded": true } \ No newline at end of file diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 103afea02e0..1f4a03044b2 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -191,11 +191,11 @@ func (b *backendScripts) executeScriptOnExecutor( } if errToReturn == nil { + return result, nil + } else { b.metrics.ScriptExecutionErrorOnExecutionNode() b.log.Error().Err(err).Msg("script execution failed for execution node internal reasons") return nil, rpc.ConvertError(errToReturn, "failed to execute script on execution nodes", codes.Internal) - } else { - return result, nil } } diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index f6706df4874..c4bab57d5c3 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -792,7 +792,7 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( } return err }, - func(err error) bool { + func(_ flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) @@ -853,7 +853,7 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( } return err }, - func(err error) bool { + func(_ flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) @@ -912,7 +912,7 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( } return err }, - func(err error) bool { + func(_ flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) From b779edf1fafc441235c30c01289ef0dbb54a4fa5 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 14:38:30 +0300 Subject: [PATCH 256/815] Added check for argument --- cmd/access/node_builder/access_node_builder.go | 3 +++ cmd/util/cmd/execution-state-extract/export_report.json | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index e2ac124c0ba..3afdf5b313d 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -760,6 +760,9 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { if builder.rpcConf.CircuitBreakerConfig.MaxRequests == 0 { return errors.New("circuit-breaker-max-requests must be greater than 0") } + if builder.rpcConf.CircuitBreakerConfig.RestoreTimeout > 0 { + return errors.New("circuit-breaker-restore-timeout must be greater than 0") + } } return nil diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 3d4abf5bcf2..067d5c44b7f 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "0872fc2bbcf573c016e8af58e14db6e1efdc48e8e770115d5962f9225b1fa465", - "CurrentStateCommitment": "0872fc2bbcf573c016e8af58e14db6e1efdc48e8e770115d5962f9225b1fa465", + "PreviousStateCommitment": "0af51cc7d3d8ac8307b33126a6407ac950e4b64396c55c304b313364c6a0e64d", + "CurrentStateCommitment": "0af51cc7d3d8ac8307b33126a6407ac950e4b64396c55c304b313364c6a0e64d", "ReportSucceeded": true } \ No newline at end of file From ae9c109af206536602a7310433239597778c4546 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 21:18:44 +0300 Subject: [PATCH 257/815] Refactor connection factory --- Makefile | 2 +- .../node_builder/access_node_builder.go | 3 +- .../export_report.json | 4 +- engine/access/apiproxy/access_api_proxy.go | 6 +- engine/access/rpc/backend/backend.go | 5 +- engine/access/rpc/backend/backend_accounts.go | 6 +- engine/access/rpc/backend/backend_events.go | 3 +- engine/access/rpc/backend/backend_scripts.go | 3 +- engine/access/rpc/backend/backend_test.go | 4 +- .../rpc/backend/backend_transactions.go | 3 +- .../access/rpc/backend/connection_factory.go | 347 ------------------ engine/access/rpc/connection/cache.go | 12 +- engine/access/rpc/connection/connection.go | 20 +- .../connection_test.go} | 141 ++++--- engine/access/rpc/connection/manager.go | 25 +- .../rpc/connection/mock/connection_factory.go | 148 ++++++++ engine/access/rpc/engine.go | 9 +- 17 files changed, 316 insertions(+), 425 deletions(-) delete mode 100644 engine/access/rpc/backend/connection_factory.go rename engine/access/rpc/{backend/connection_factory_test.go => connection/connection_test.go} (85%) create mode 100644 engine/access/rpc/connection/mock/connection_factory.go diff --git a/Makefile b/Makefile index 81dd2ce3ce4..d8b37127d1d 100644 --- a/Makefile +++ b/Makefile @@ -193,7 +193,7 @@ generate-mocks: install-mock-generators mockery --name 'API' --dir="./access" --case=underscore --output="./access/mock" --outpkg="mock" mockery --name 'API' --dir="./engine/protocol" --case=underscore --output="./engine/protocol/mock" --outpkg="mock" mockery --name '.*' --dir="./engine/access/state_stream" --case=underscore --output="./engine/access/state_stream/mock" --outpkg="mock" - mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" + mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/connection" --case=underscore --output="./engine/access/rpc/connection/mock" --outpkg="mock" mockery --name '.*' --dir=model/fingerprint --case=underscore --output="./model/fingerprint/mock" --outpkg="mock" mockery --name 'ExecForkActor' --structname 'ExecForkActorMock' --dir=module/mempool/consensus/mock/ --case=underscore --output="./module/mempool/consensus/mock/" --outpkg="mock" mockery --name '.*' --dir=engine/verification/fetcher/ --case=underscore --output="./engine/verification/fetcher/mock" --outpkg="mockfetcher" diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 778cb73dee6..dbd636031d5 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -39,6 +39,7 @@ import ( pingeng "github.com/onflow/flow-go/engine/access/ping" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" + rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/access/state_stream" followereng "github.com/onflow/flow-go/engine/common/follower" "github.com/onflow/flow-go/engine/common/requester" @@ -908,7 +909,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.rpcConf.CollectionAddr, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(builder.rpcConf.MaxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientTimeoutOption(builder.rpcConf.CollectionClientTimeout)) + rpcConnection.WithClientTimeoutOption(builder.rpcConf.CollectionClientTimeout)) if err != nil { return err } diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 4c8484e4396..d5347f21c44 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", - "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "PreviousStateCommitment": "34c4f4a4cc7e0e63115e770c1c79d0183d1cbbd95afe26a1d3c28acf450d4b09", + "CurrentStateCommitment": "34c4f4a4cc7e0e63115e770c1c79d0183d1cbbd95afe26a1d3c28acf450d4b09", "ReportSucceeded": true } \ No newline at end of file diff --git a/engine/access/apiproxy/access_api_proxy.go b/engine/access/apiproxy/access_api_proxy.go index ed47ef167eb..a4468c6cadb 100644 --- a/engine/access/apiproxy/access_api_proxy.go +++ b/engine/access/apiproxy/access_api_proxy.go @@ -17,7 +17,7 @@ import ( "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" - "github.com/onflow/flow-go/engine/access/rpc/backend" + rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/protocol" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -65,7 +65,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(insecure.NewCredentials()), - backend.WithClientTimeoutOption(timeout)) + rpcConnection.WithClientTimeoutOption(timeout)) if err != nil { return err } @@ -79,7 +79,7 @@ func (h *FlowAccessAPIForwarder) reconnectingClient(i int) error { identity.Address, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(h.maxMsgSize))), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), - backend.WithClientTimeoutOption(timeout)) + rpcConnection.WithClientTimeoutOption(timeout)) if err != nil { return fmt.Errorf("cannot connect to %s %w", identity.Address, err) } diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 9cce94f66a3..f8442e7bf74 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" @@ -77,7 +78,7 @@ type Backend struct { chainID flow.ChainID collections storage.Collections executionReceipts storage.ExecutionReceipts - connFactory ConnectionFactory + connFactory connection.ConnectionFactory } func New( @@ -92,7 +93,7 @@ func New( executionResults storage.ExecutionResults, chainID flow.ChainID, accessMetrics module.AccessMetrics, - connFactory ConnectionFactory, + connFactory connection.ConnectionFactory, retryEnabled bool, maxHeightRange uint, preferredExecutionNodeIDs []string, diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index a3a41053c61..a3cf47bb00d 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -5,11 +5,13 @@ import ( "time" "github.com/hashicorp/go-multierror" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" @@ -21,7 +23,7 @@ type backendAccounts struct { state protocol.State headers storage.Headers executionReceipts storage.ExecutionReceipts - connFactory ConnectionFactory + connFactory connection.ConnectionFactory log zerolog.Logger } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index f48ba395947..2745d92bf00 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -13,6 +13,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" @@ -24,7 +25,7 @@ type backendEvents struct { headers storage.Headers executionReceipts storage.ExecutionReceipts state protocol.State - connFactory ConnectionFactory + connFactory connection.ConnectionFactory log zerolog.Logger maxHeightRange uint } diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 0d397d5b4cb..6ac4d2085f6 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -15,6 +15,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -29,7 +30,7 @@ type backendScripts struct { headers storage.Headers executionReceipts storage.ExecutionReceipts state protocol.State - connFactory ConnectionFactory + connFactory connection.ConnectionFactory log zerolog.Logger metrics module.BackendScriptsMetrics loggedScripts *lru.Cache diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 4df2e5fc01e..99969a93fc1 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -6,6 +6,8 @@ import ( "strconv" "testing" + "github.com/onflow/flow-go/engine/access/rpc/connection" + "github.com/dgraph-io/badger/v2" accessproto "github.com/onflow/flow/protobuf/go/flow/access" entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" @@ -2399,7 +2401,7 @@ func (suite *Suite) setupReceipts(block *flow.Block) ([]*flow.ExecutionReceipt, return receipts, ids } -func (suite *Suite) setupConnectionFactory() ConnectionFactory { +func (suite *Suite) setupConnectionFactory() connection.ConnectionFactory { // create a mock connection factory connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index a4f905d8b42..3c0ac616697 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -15,6 +15,7 @@ import ( "google.golang.org/grpc/status" "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/fvm/blueprints" @@ -37,7 +38,7 @@ type backendTransactions struct { transactionMetrics module.TransactionMetrics transactionValidator *access.TransactionValidator retry *Retry - connFactory ConnectionFactory + connFactory connection.ConnectionFactory previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go deleted file mode 100644 index 77fb9dc700f..00000000000 --- a/engine/access/rpc/backend/connection_factory.go +++ /dev/null @@ -1,347 +0,0 @@ -package backend - -import ( - "context" - "fmt" - "io" - "net" - "sync" - "time" - - "go.uber.org/atomic" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - lru "github.com/hashicorp/golang-lru" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/keepalive" - - "github.com/onflow/flow-go/module" -) - -// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node -const DefaultClientTimeout = 3 * time.Second - -// ConnectionFactory is used to create an access api client -type ConnectionFactory interface { - GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) - GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) - InvalidateAccessAPIClient(address string) - GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) - InvalidateExecutionAPIClient(address string) -} - -type ProxyConnectionFactory struct { - ConnectionFactory - targetAddress string -} - -type noopCloser struct{} - -func (c *noopCloser) Close() error { - return nil -} - -func (p *ProxyConnectionFactory) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { - return p.ConnectionFactory.GetAccessAPIClient(p.targetAddress) -} -func (p *ProxyConnectionFactory) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { - return p.ConnectionFactory.GetExecutionAPIClient(p.targetAddress) -} - -type ConnectionFactoryImpl struct { - CollectionGRPCPort uint - ExecutionGRPCPort uint - CollectionNodeGRPCTimeout time.Duration - ExecutionNodeGRPCTimeout time.Duration - ConnectionsCache *lru.Cache - CacheSize uint - MaxMsgSize uint - AccessMetrics module.AccessMetrics - Log zerolog.Logger - mutex sync.RWMutex -} - -type CachedClient struct { - ClientConn *grpc.ClientConn - Address string - timeout time.Duration - closeRequested *atomic.Bool - wg sync.WaitGroup -} - -// createConnection creates new gRPC connections to remote node -func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration, cachedClient *CachedClient) (*grpc.ClientConn, error) { - - if timeout == 0 { - timeout = DefaultClientTimeout - } - - keepaliveParams := keepalive.ClientParameters{ - // how long the client will wait before sending a keepalive to the server if there is no activity - Time: 10 * time.Second, - // how long the client will wait for a response from the keepalive before closing - Timeout: timeout, - } - - var connInterceptors []grpc.UnaryClientInterceptor - - // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called - // in the same order during gRPC requests. It is crucial to ensure that the request watcher interceptor is added - // first. This interceptor is responsible for executing necessary request monitoring before passing control to - // subsequent interceptors. - if cachedClient != nil { - connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) - } - - connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) - - // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached - // The connections should be safe to be persisted and reused - // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams - // https://grpc.io/blog/grpc-on-http2/#keeping-connections-alive - conn, err := grpc.Dial( - address, - grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(cf.MaxMsgSize))), - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithKeepaliveParams(keepaliveParams), - grpc.WithChainUnaryInterceptor(connInterceptors...), - ) - if err != nil { - return nil, fmt.Errorf("failed to connect to address %s: %w", address, err) - } - return conn, nil -} - -func (cf *ConnectionFactoryImpl) getClientConnection(grpcAddress string) *grpc.ClientConn { - cf.mutex.RLock() - defer cf.mutex.RUnlock() - - if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { - return res.(*CachedClient).ClientConn - } - return nil -} - -func (cf *ConnectionFactoryImpl) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { - - // 1. attempt to read with Read lock - clientConn := cf.getClientConnection(grpcAddress) - if clientConn != nil { - return clientConn, nil - } - - // 2. if not found acquire write lock - cf.mutex.Lock() - defer cf.mutex.Unlock() - - // 3. Check if other goroutine haven't created an entity - if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { - return res.(*CachedClient).ClientConn, nil - } - - // 4. Perform initialization in critical section - store := &CachedClient{ - ClientConn: nil, - Address: grpcAddress, - timeout: timeout, - closeRequested: atomic.NewBool(false), - wg: sync.WaitGroup{}, - } - - cf.Log.Debug().Str("cached_client_added", grpcAddress).Msg("adding new cached client to pool") - - var err error - store.ClientConn, err = cf.createConnection(grpcAddress, timeout, store) - if err != nil { - return nil, err - } - - _ = cf.ConnectionsCache.Add(grpcAddress, store) - if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionAddedToPool() - cf.AccessMetrics.NewConnectionEstablished() - cf.AccessMetrics.TotalConnectionsInPool(uint(cf.ConnectionsCache.Len()), cf.CacheSize) - } - - return store.ClientConn, nil -} - -func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { - return cf.GetAccessAPIClientWithPort(address, cf.CollectionGRPCPort) -} - -func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) { - - grpcAddress, err := getGRPCAddress(address, port) - if err != nil { - return nil, nil, err - } - - var conn *grpc.ClientConn - if cf.ConnectionsCache != nil { - conn, err = cf.retrieveConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) - if err != nil { - return nil, nil, err - } - return access.NewAccessAPIClient(conn), &noopCloser{}, err - } - - conn, err = cf.createConnection(grpcAddress, cf.CollectionNodeGRPCTimeout, nil) - if err != nil { - return nil, nil, err - } - - accessAPIClient := access.NewAccessAPIClient(conn) - closer := io.Closer(conn) - return accessAPIClient, closer, nil -} - -func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { - if cf.ConnectionsCache != nil { - cf.Log.Debug().Str("cached_access_client_invalidated", address).Msg("invalidating cached access client") - cf.invalidateAPIClient(address, cf.CollectionGRPCPort) - } -} - -func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { - - grpcAddress, err := getGRPCAddress(address, cf.ExecutionGRPCPort) - if err != nil { - return nil, nil, err - } - - var conn *grpc.ClientConn - if cf.ConnectionsCache != nil { - conn, err = cf.retrieveConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) - if err != nil { - return nil, nil, err - } - return execution.NewExecutionAPIClient(conn), &noopCloser{}, nil - } - - conn, err = cf.createConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout, nil) - if err != nil { - return nil, nil, err - } - - executionAPIClient := execution.NewExecutionAPIClient(conn) - closer := io.Closer(conn) - return executionAPIClient, closer, nil -} - -func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { - if cf.ConnectionsCache != nil { - cf.Log.Debug().Str("cached_execution_client_invalidated", address).Msg("invalidating cached execution client") - cf.invalidateAPIClient(address, cf.ExecutionGRPCPort) - } -} - -// invalidateAPIClient invalidates the access API client associated with the given address and port. -// It removes the cached client from the ConnectionsCache and closes the connection. -func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) { - grpcAddress, _ := getGRPCAddress(address, port) - if res, ok := cf.ConnectionsCache.Get(grpcAddress); ok { - store := res.(*CachedClient) - // Check if it is possible to remove cached client from cache, or it is already removed or being processed - if cf.ConnectionsCache.Remove(grpcAddress) { - // Close the connection only if it is successfully removed from the cache - store.Close() - if cf.AccessMetrics != nil { - cf.AccessMetrics.ConnectionFromPoolInvalidated() - } - } - } -} - -// Close closes the CachedClient connection. It marks the connection for closure and waits asynchronously for ongoing -// requests to complete before closing the connection. -func (s *CachedClient) Close() { - // Mark the connection for closure - if swapped := s.closeRequested.CompareAndSwap(false, true); !swapped { - return - } - - // If there are ongoing requests, wait for them to complete asynchronously - go func() { - s.wg.Wait() - - // Close the connection - s.ClientConn.Close() - }() -} - -// getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the -// GRPC port from the libp2p port -func getGRPCAddress(address string, grpcPort uint) (string, error) { - // split hostname and port - hostnameOrIP, _, err := net.SplitHostPort(address) - if err != nil { - return "", err - } - // use the hostname from identity list and port number as the one passed in as argument - grpcAddress := fmt.Sprintf("%s:%d", hostnameOrIP, grpcPort) - - return grpcAddress, nil -} - -// createRequestWatcherInterceptor creates a request watcher interceptor to wait for unfinished request before close -func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { - requestWatcherInterceptor := func( - ctx context.Context, - method string, - req interface{}, - reply interface{}, - cc *grpc.ClientConn, - invoker grpc.UnaryInvoker, - opts ...grpc.CallOption, - ) error { - // Prevent new request from being sent if the connection is marked for closure - if cachedClient.closeRequested.Load() { - return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) - } - - // Increment the request counter to track ongoing requests, then - // decrement the request counter before returning - cachedClient.wg.Add(1) - defer cachedClient.wg.Done() - // Invoke the actual RPC method - return invoker(ctx, method, req, reply, cc, opts...) - } - - return requestWatcherInterceptor -} - -// createClientTimeoutInterceptor creates a client interceptor with a context that expires after the timeout. -func createClientTimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor { - clientTimeoutInterceptor := func( - ctx context.Context, - method string, - req interface{}, - reply interface{}, - cc *grpc.ClientConn, - invoker grpc.UnaryInvoker, - opts ...grpc.CallOption, - ) error { - // Create a context that expires after the specified timeout. - ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - // Call the remote GRPC using the short context. - err := invoker(ctxWithTimeout, method, req, reply, cc, opts...) - - return err - } - - return clientTimeoutInterceptor -} - -// WithClientTimeoutOption is a helper function to create a GRPC dial option -// with the specified client timeout interceptor. -func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { - return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) -} diff --git a/engine/access/rpc/connection/cache.go b/engine/access/rpc/connection/cache.go index 149ef2e4430..86ce4d5165d 100644 --- a/engine/access/rpc/connection/cache.go +++ b/engine/access/rpc/connection/cache.go @@ -37,11 +37,13 @@ func (s *CachedClient) Close() { type Cache struct { cache *lru.Cache + size int } -func NewCache(cache *lru.Cache) *Cache { +func NewCache(cache *lru.Cache, size int) *Cache { return &Cache{ cache: cache, + size: size, } } @@ -85,3 +87,11 @@ func (c *Cache) Remove(address string) (present bool) { func (c *Cache) Len() int { return c.cache.Len() } + +func (c *Cache) Size() int { + return c.size +} + +func (c *Cache) Contains(address string) (containKey bool) { + return c.cache.Contains(address) +} diff --git a/engine/access/rpc/connection/connection.go b/engine/access/rpc/connection/connection.go index 715f9823fde..12cbb8748c0 100644 --- a/engine/access/rpc/connection/connection.go +++ b/engine/access/rpc/connection/connection.go @@ -13,9 +13,6 @@ import ( "github.com/onflow/flow-go/module" ) -// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node -const DefaultClientTimeout = 3 * time.Second - // ConnectionFactory is used to create an access api client type ConnectionFactory interface { GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) @@ -43,17 +40,16 @@ func (p *ProxyConnectionFactory) GetExecutionAPIClient(address string) (executio return p.ConnectionFactory.GetExecutionAPIClient(p.targetAddress) } +var _ ConnectionFactory = (*ConnectionFactoryImpl)(nil) + type ConnectionFactoryImpl struct { CollectionGRPCPort uint ExecutionGRPCPort uint CollectionNodeGRPCTimeout time.Duration ExecutionNodeGRPCTimeout time.Duration - CacheSize uint - MaxMsgSize uint AccessMetrics module.AccessMetrics Log zerolog.Logger - - manager *Manager + Manager Manager } func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { @@ -67,7 +63,7 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port return nil, nil, err } - conn, closer, err := cf.manager.GetConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) + conn, closer, err := cf.Manager.GetConnection(grpcAddress, cf.CollectionNodeGRPCTimeout) if err != nil { return nil, nil, err } @@ -76,7 +72,7 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port } func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { - if !cf.manager.HasCache() { + if !cf.Manager.HasCache() { return } @@ -91,7 +87,7 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio return nil, nil, err } - conn, closer, err := cf.manager.GetConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) + conn, closer, err := cf.Manager.GetConnection(grpcAddress, cf.ExecutionNodeGRPCTimeout) if err != nil { return nil, nil, err } @@ -100,7 +96,7 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio } func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { - if !cf.manager.HasCache() { + if !cf.Manager.HasCache() { return } @@ -117,7 +113,7 @@ func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) panic(err) } - if !cf.manager.Remove(grpcAddress) { + if !cf.Manager.Remove(grpcAddress) { return } diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/connection/connection_test.go similarity index 85% rename from engine/access/rpc/backend/connection_factory_test.go rename to engine/access/rpc/connection/connection_test.go index 644240a3747..09a2ad4f3ea 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/connection/connection_test.go @@ -1,4 +1,4 @@ -package backend +package connection import ( "context" @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/rs/zerolog" + "go.uber.org/atomic" "pgregory.net/rapid" @@ -45,6 +47,12 @@ func TestProxyAccessAPI(t *testing.T) { connectionFactory.CollectionGRPCPort = cn.port // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + nil, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) proxyConnectionFactory := ProxyConnectionFactory{ ConnectionFactory: connectionFactory, @@ -77,8 +85,15 @@ func TestProxyExecutionAPI(t *testing.T) { connectionFactory := new(ConnectionFactoryImpl) // set the execution grpc port connectionFactory.ExecutionGRPCPort = en.port + // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + nil, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) proxyConnectionFactory := ProxyConnectionFactory{ ConnectionFactory: connectionFactory, @@ -115,10 +130,16 @@ func TestProxyAccessAPIConnectionReuse(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) + // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) proxyConnectionFactory := ProxyConnectionFactory{ ConnectionFactory: connectionFactory, @@ -127,14 +148,14 @@ func TestProxyAccessAPIConnectionReuse(t *testing.T) { // get a collection API client _, closer, err := proxyConnectionFactory.GetAccessAPIClient("foo") - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 1) + assert.Equal(t, connectionCache.Len(), 1) assert.NoError(t, err) assert.Nil(t, closer.Close()) var conn *grpc.ClientConn - res, ok := connectionFactory.ConnectionsCache.Get(proxyConnectionFactory.targetAddress) + res, ok := connectionCache.Get(proxyConnectionFactory.targetAddress) assert.True(t, ok) - conn = res.(*CachedClient).ClientConn + conn = res.ClientConn // check if api client can be rebuilt with retrieved connection accessAPIClient := access.NewAccessAPIClient(conn) @@ -163,10 +184,15 @@ func TestProxyExecutionAPIConnectionReuse(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) proxyConnectionFactory := ProxyConnectionFactory{ ConnectionFactory: connectionFactory, @@ -175,14 +201,14 @@ func TestProxyExecutionAPIConnectionReuse(t *testing.T) { // get an execution API client _, closer, err := proxyConnectionFactory.GetExecutionAPIClient("foo") - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 1) + assert.Equal(t, connectionCache.Len(), 1) assert.NoError(t, err) assert.Nil(t, closer.Close()) var conn *grpc.ClientConn - res, ok := connectionFactory.ConnectionsCache.Get(proxyConnectionFactory.targetAddress) + res, ok := connectionCache.Get(proxyConnectionFactory.targetAddress) assert.True(t, ok) - conn = res.(*CachedClient).ClientConn + conn = res.ClientConn // check if api client can be rebuilt with retrieved connection executionAPIClient := execution.NewExecutionAPIClient(conn) @@ -218,10 +244,15 @@ func TestExecutionNodeClientTimeout(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) // create the execution API client client, _, err := connectionFactory.GetExecutionAPIClient(en.listener.Addr().String()) @@ -261,10 +292,15 @@ func TestCollectionNodeClientTimeout(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) // create the collection API client client, _, err := connectionFactory.GetAccessAPIClient(cn.listener.Addr().String()) @@ -304,31 +340,39 @@ func TestConnectionPoolFull(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) cn1Address := "foo1:123" cn2Address := "foo2:123" cn3Address := "foo3:123" // get a collection API client + // Create and add first client to cache _, _, err := connectionFactory.GetAccessAPIClient(cn1Address) - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 1) + assert.Equal(t, connectionCache.Len(), 1) assert.NoError(t, err) + // Create and add second client to cache _, _, err = connectionFactory.GetAccessAPIClient(cn2Address) - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 2) + assert.Equal(t, connectionCache.Len(), 2) assert.NoError(t, err) + // Peek first client from cache. "recently used"-ness will not be updated, so it will be wiped out first. _, _, err = connectionFactory.GetAccessAPIClient(cn1Address) - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 2) + assert.Equal(t, connectionCache.Len(), 2) assert.NoError(t, err) - // Expecting to replace cn2 because cn1 was accessed more recently + // Create and add third client to cache, firs client will be removed from cache _, _, err = connectionFactory.GetAccessAPIClient(cn3Address) - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 2) + assert.Equal(t, connectionCache.Len(), 2) assert.NoError(t, err) var hostnameOrIP string @@ -342,12 +386,12 @@ func TestConnectionPoolFull(t *testing.T) { assert.NoError(t, err) grpcAddress3 := fmt.Sprintf("%s:%d", hostnameOrIP, connectionFactory.CollectionGRPCPort) - contains1 := connectionFactory.ConnectionsCache.Contains(grpcAddress1) - contains2 := connectionFactory.ConnectionsCache.Contains(grpcAddress2) - contains3 := connectionFactory.ConnectionsCache.Contains(grpcAddress3) + contains1 := connectionCache.Contains(grpcAddress1) + contains2 := connectionCache.Contains(grpcAddress2) + contains3 := connectionCache.Contains(grpcAddress3) - assert.True(t, contains1) - assert.False(t, contains2) + assert.False(t, contains1) + assert.True(t, contains2) assert.True(t, contains3) } @@ -371,10 +415,15 @@ func TestConnectionPoolStale(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) proxyConnectionFactory := ProxyConnectionFactory{ ConnectionFactory: connectionFactory, @@ -383,13 +432,13 @@ func TestConnectionPoolStale(t *testing.T) { // get a collection API client client, _, err := proxyConnectionFactory.GetAccessAPIClient("foo") - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 1) + assert.Equal(t, connectionCache.Len(), 1) assert.NoError(t, err) // close connection to simulate something "going wrong" with our stored connection - res, _ := connectionFactory.ConnectionsCache.Get(proxyConnectionFactory.targetAddress) + res, _ := connectionCache.Get(proxyConnectionFactory.targetAddress) - connectionFactory.ConnectionsCache.Remove(proxyConnectionFactory.targetAddress) - res.(*CachedClient).Close() + connectionCache.Remove(proxyConnectionFactory.targetAddress) + res.Close() ctx := context.Background() // make the call to the collection node (should fail, connection closed) @@ -398,12 +447,12 @@ func TestConnectionPoolStale(t *testing.T) { // re-access, should replace stale connection in cache with new one _, _, _ = proxyConnectionFactory.GetAccessAPIClient("foo") - assert.Equal(t, connectionFactory.ConnectionsCache.Len(), 1) + assert.Equal(t, connectionCache.Len(), 1) var conn *grpc.ClientConn - res, ok := connectionFactory.ConnectionsCache.Get(proxyConnectionFactory.targetAddress) + res, ok := connectionCache.Get(proxyConnectionFactory.targetAddress) assert.True(t, ok) - conn = res.(*CachedClient).ClientConn + conn = res.ClientConn // check if api client can be rebuilt with retrieved connection accessAPIClient := access.NewAccessAPIClient(conn) @@ -454,10 +503,15 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { evictedValue.(*CachedClient).Close() }) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) clientAddress := en.listener.Addr().String() // create the execution API client @@ -530,10 +584,15 @@ func TestExecutionEvictingCacheClients(t *testing.T) { // Set the connection pool cache size cacheSize := 1 cache, _ := lru.New(cacheSize) - connectionFactory.ConnectionsCache = cache - connectionFactory.CacheSize = uint(cacheSize) - // Set metrics reporting + connectionCache := NewCache(cache, cacheSize) + // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() + connectionFactory.Manager = NewManager( + connectionCache, + zerolog.New(zerolog.NewConsoleWriter()), + connectionFactory.AccessMetrics, + 0, + ) clientAddress := cn.listener.Addr().String() // Create the execution API client diff --git a/engine/access/rpc/connection/manager.go b/engine/access/rpc/connection/manager.go index dfb4fc14497..1978da3ed26 100644 --- a/engine/access/rpc/connection/manager.go +++ b/engine/access/rpc/connection/manager.go @@ -6,7 +6,6 @@ import ( "io" "time" - "github.com/onflow/flow-go/module" "github.com/rs/zerolog" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -14,8 +13,13 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/status" + + "github.com/onflow/flow-go/module" ) +// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node +const DefaultClientTimeout = 3 * time.Second + type Manager struct { cache *Cache logger zerolog.Logger @@ -28,8 +32,8 @@ func NewManager( logger zerolog.Logger, metrics module.AccessMetrics, maxMsgSize uint, -) *Manager { - return &Manager{ +) Manager { + return Manager{ cache: cache, logger: logger, metrics: metrics, @@ -78,11 +82,17 @@ func (m *Manager) HasCache() bool { } func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { - client, ok := m.cache.GetOrAdd(grpcAddress, timeout) if ok { // client was from the cache, wait for the lock client.mu.Lock() + if m.metrics != nil { + m.metrics.ConnectionFromPoolReused() + } + } else { + if m.metrics != nil { + m.metrics.ConnectionAddedToPool() + } } defer client.mu.Unlock() @@ -96,12 +106,19 @@ func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) } client.ClientConn = conn + if m.metrics != nil { + m.metrics.NewConnectionEstablished() + m.metrics.TotalConnectionsInPool(uint(m.cache.Len()), uint(m.cache.Size())) + } return client.ClientConn, nil } // createConnection creates new gRPC connections to remote node func (m *Manager) createConnection(address string, timeout time.Duration, cachedClient *CachedClient) (*grpc.ClientConn, error) { + if timeout == 0 { + timeout = DefaultClientTimeout + } keepaliveParams := keepalive.ClientParameters{ // how long the client will wait before sending a keepalive to the server if there is no activity diff --git a/engine/access/rpc/connection/mock/connection_factory.go b/engine/access/rpc/connection/mock/connection_factory.go new file mode 100644 index 00000000000..eebc006485d --- /dev/null +++ b/engine/access/rpc/connection/mock/connection_factory.go @@ -0,0 +1,148 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + access "github.com/onflow/flow/protobuf/go/flow/access" + + execution "github.com/onflow/flow/protobuf/go/flow/execution" + + io "io" + + mock "github.com/stretchr/testify/mock" +) + +// ConnectionFactory is an autogenerated mock type for the ConnectionFactory type +type ConnectionFactory struct { + mock.Mock +} + +// GetAccessAPIClient provides a mock function with given fields: address +func (_m *ConnectionFactory) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { + ret := _m.Called(address) + + var r0 access.AccessAPIClient + var r1 io.Closer + var r2 error + if rf, ok := ret.Get(0).(func(string) (access.AccessAPIClient, io.Closer, error)); ok { + return rf(address) + } + if rf, ok := ret.Get(0).(func(string) access.AccessAPIClient); ok { + r0 = rf(address) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(access.AccessAPIClient) + } + } + + if rf, ok := ret.Get(1).(func(string) io.Closer); ok { + r1 = rf(address) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(io.Closer) + } + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(address) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetAccessAPIClientWithPort provides a mock function with given fields: address, port +func (_m *ConnectionFactory) GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) { + ret := _m.Called(address, port) + + var r0 access.AccessAPIClient + var r1 io.Closer + var r2 error + if rf, ok := ret.Get(0).(func(string, uint) (access.AccessAPIClient, io.Closer, error)); ok { + return rf(address, port) + } + if rf, ok := ret.Get(0).(func(string, uint) access.AccessAPIClient); ok { + r0 = rf(address, port) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(access.AccessAPIClient) + } + } + + if rf, ok := ret.Get(1).(func(string, uint) io.Closer); ok { + r1 = rf(address, port) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(io.Closer) + } + } + + if rf, ok := ret.Get(2).(func(string, uint) error); ok { + r2 = rf(address, port) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetExecutionAPIClient provides a mock function with given fields: address +func (_m *ConnectionFactory) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { + ret := _m.Called(address) + + var r0 execution.ExecutionAPIClient + var r1 io.Closer + var r2 error + if rf, ok := ret.Get(0).(func(string) (execution.ExecutionAPIClient, io.Closer, error)); ok { + return rf(address) + } + if rf, ok := ret.Get(0).(func(string) execution.ExecutionAPIClient); ok { + r0 = rf(address) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(execution.ExecutionAPIClient) + } + } + + if rf, ok := ret.Get(1).(func(string) io.Closer); ok { + r1 = rf(address) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(io.Closer) + } + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(address) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// InvalidateAccessAPIClient provides a mock function with given fields: address +func (_m *ConnectionFactory) InvalidateAccessAPIClient(address string) { + _m.Called(address) +} + +// InvalidateExecutionAPIClient provides a mock function with given fields: address +func (_m *ConnectionFactory) InvalidateExecutionAPIClient(address string) { + _m.Called(address) +} + +type mockConstructorTestingTNewConnectionFactory interface { + mock.TestingT + Cleanup(func()) +} + +// NewConnectionFactory creates a new instance of ConnectionFactory. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewConnectionFactory(t mockConstructorTestingTNewConnectionFactory) *ConnectionFactory { + mock := &ConnectionFactory{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index d4c812df997..30380871257 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/engine/access/rest" "github.com/onflow/flow-go/engine/access/rpc/backend" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -149,7 +150,7 @@ func NewBuilder(log zerolog.Logger, } var err error cache, err = lru.NewWithEvict(int(cacheSize), func(_, evictedValue interface{}) { - store := evictedValue.(*backend.CachedClient) + store := evictedValue.(*connection.CachedClient) store.Close() log.Debug().Str("grpc_conn_evicted", store.Address).Msg("closing grpc connection evicted from pool") if accessMetrics != nil { @@ -161,16 +162,14 @@ func NewBuilder(log zerolog.Logger, } } - connectionFactory := &backend.ConnectionFactoryImpl{ + connectionFactory := &connection.ConnectionFactoryImpl{ CollectionGRPCPort: collectionGRPCPort, ExecutionGRPCPort: executionGRPCPort, CollectionNodeGRPCTimeout: config.CollectionClientTimeout, ExecutionNodeGRPCTimeout: config.ExecutionClientTimeout, - ConnectionsCache: cache, - CacheSize: cacheSize, - MaxMsgSize: config.MaxMsgSize, AccessMetrics: accessMetrics, Log: log, + Manager: connection.NewManager(connection.NewCache(cache, int(cacheSize)), log, accessMetrics, config.MaxMsgSize), } backend := backend.New(state, From 85b17f97baf470297ae3b62f2096a674a49988b2 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 21:37:16 +0300 Subject: [PATCH 258/815] Added comments to new components --- engine/access/rpc/connection/cache.go | 13 ++++++ engine/access/rpc/connection/connection.go | 27 +++++++----- engine/access/rpc/connection/manager.go | 49 +++++++++++++--------- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/engine/access/rpc/connection/cache.go b/engine/access/rpc/connection/cache.go index 86ce4d5165d..20ebd238229 100644 --- a/engine/access/rpc/connection/cache.go +++ b/engine/access/rpc/connection/cache.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc" ) +// CachedClient represents a gRPC client connection that is cached for reuse. type CachedClient struct { ClientConn *grpc.ClientConn Address string @@ -35,11 +36,13 @@ func (s *CachedClient) Close() { }() } +// Cache represents a cache of CachedClient instances with a given maximum size. type Cache struct { cache *lru.Cache size int } +// NewCache creates a new Cache with the specified maximum size and the underlying LRU cache. func NewCache(cache *lru.Cache, size int) *Cache { return &Cache{ cache: cache, @@ -47,6 +50,8 @@ func NewCache(cache *lru.Cache, size int) *Cache { } } +// Get retrieves the CachedClient for the given address from the cache. +// It returns the CachedClient and a boolean indicating whether the entry exists in the cache. func (c *Cache) Get(address string) (*CachedClient, bool) { val, ok := c.cache.Get(address) if !ok { @@ -76,22 +81,30 @@ func (c *Cache) GetOrAdd(address string, timeout time.Duration) (*CachedClient, return client, false } +// Add adds a CachedClient to the cache with the given address. +// It returns a boolean indicating whether an existing entry was evicted. func (c *Cache) Add(address string, client *CachedClient) (evicted bool) { return c.cache.Add(address, client) } +// Remove removes the CachedClient entry from the cache with the given address. +// It returns a boolean indicating whether the entry was present and removed. func (c *Cache) Remove(address string) (present bool) { return c.cache.Remove(address) } +// Len returns the number of CachedClient entries in the cache. func (c *Cache) Len() int { return c.cache.Len() } +// Size returns the maximum size of the cache. func (c *Cache) Size() int { return c.size } +// Contains checks if the cache contains an entry with the given address. +// It returns a boolean indicating whether the address is present in the cache. func (c *Cache) Contains(address string) (containKey bool) { return c.cache.Contains(address) } diff --git a/engine/access/rpc/connection/connection.go b/engine/access/rpc/connection/connection.go index 12cbb8748c0..40cbfe2fc82 100644 --- a/engine/access/rpc/connection/connection.go +++ b/engine/access/rpc/connection/connection.go @@ -13,7 +13,7 @@ import ( "github.com/onflow/flow-go/module" ) -// ConnectionFactory is used to create an access api client +// ConnectionFactory is an interface for creating access and execution API clients. type ConnectionFactory interface { GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) @@ -22,6 +22,7 @@ type ConnectionFactory interface { InvalidateExecutionAPIClient(address string) } +// ProxyConnectionFactory wraps an existing ConnectionFactory and allows getting API clients for a target address. type ProxyConnectionFactory struct { ConnectionFactory targetAddress string @@ -52,12 +53,13 @@ type ConnectionFactoryImpl struct { Manager Manager } +// GetAccessAPIClient gets an access API client for the specified address using the default CollectionGRPCPort. func (cf *ConnectionFactoryImpl) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { return cf.GetAccessAPIClientWithPort(address, cf.CollectionGRPCPort) } +// GetAccessAPIClientWithPort gets an access API client for the specified address and port. func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port uint) (access.AccessAPIClient, io.Closer, error) { - grpcAddress, err := getGRPCAddress(address, port) if err != nil { return nil, nil, err @@ -71,6 +73,8 @@ func (cf *ConnectionFactoryImpl) GetAccessAPIClientWithPort(address string, port return access.NewAccessAPIClient(conn), closer, nil } +// InvalidateAccessAPIClient invalidates the access API client associated with the given address. +// It removes the cached client from the cache and closes the connection if a cache is used. func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { if !cf.Manager.HasCache() { return @@ -80,8 +84,8 @@ func (cf *ConnectionFactoryImpl) InvalidateAccessAPIClient(address string) { cf.invalidateAPIClient(address, cf.CollectionGRPCPort) } +// GetExecutionAPIClient gets an execution API client for the specified address using the default ExecutionGRPCPort. func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (execution.ExecutionAPIClient, io.Closer, error) { - grpcAddress, err := getGRPCAddress(address, cf.ExecutionGRPCPort) if err != nil { return nil, nil, err @@ -95,6 +99,8 @@ func (cf *ConnectionFactoryImpl) GetExecutionAPIClient(address string) (executio return execution.NewExecutionAPIClient(conn), closer, nil } +// InvalidateExecutionAPIClient invalidates the execution API client associated with the given address. +// It removes the cached client from the cache and closes the connection if a cache is used. func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { if !cf.Manager.HasCache() { return @@ -104,13 +110,12 @@ func (cf *ConnectionFactoryImpl) InvalidateExecutionAPIClient(address string) { cf.invalidateAPIClient(address, cf.ExecutionGRPCPort) } -// invalidateAPIClient invalidates the access API client associated with the given address and port. -// It removes the cached client from the ConnectionsCache and closes the connection. +// invalidateAPIClient invalidates the access or execution API client associated with the given address and port. +// It removes the cached client from the ConnectionsCache and closes the connection if a cache is used. func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) { grpcAddress, err := getGRPCAddress(address, port) if err != nil { - // TODO: return and handle the error - panic(err) + panic(err) // TODO: return and handle the error } if !cf.Manager.Remove(grpcAddress) { @@ -122,15 +127,15 @@ func (cf *ConnectionFactoryImpl) invalidateAPIClient(address string, port uint) } } -// getExecutionNodeAddress translates flow.Identity address to the GRPC address of the node by switching the port to the -// GRPC port from the libp2p port +// getGRPCAddress translates the flow.Identity address to the GRPC address of the node by switching the port to the +// GRPC port from the libp2p port. func getGRPCAddress(address string, grpcPort uint) (string, error) { - // split hostname and port + // Split hostname and port hostnameOrIP, _, err := net.SplitHostPort(address) if err != nil { return "", err } - // use the hostname from identity list and port number as the one passed in as argument + // Use the hostname from the identity list and the GRPC port number as the one passed in as an argument. grpcAddress := fmt.Sprintf("%s:%d", hostnameOrIP, grpcPort) return grpcAddress, nil diff --git a/engine/access/rpc/connection/manager.go b/engine/access/rpc/connection/manager.go index 1978da3ed26..ea8878bcb12 100644 --- a/engine/access/rpc/connection/manager.go +++ b/engine/access/rpc/connection/manager.go @@ -17,9 +17,10 @@ import ( "github.com/onflow/flow-go/module" ) -// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node +// DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node. const DefaultClientTimeout = 3 * time.Second +// Manager provides methods for getting and managing gRPC client connections. type Manager struct { cache *Cache logger zerolog.Logger @@ -27,6 +28,7 @@ type Manager struct { maxMsgSize uint } +// NewManager creates a new Manager with the specified parameters. func NewManager( cache *Cache, logger zerolog.Logger, @@ -41,6 +43,9 @@ func NewManager( } } +// GetConnection returns a gRPC client connection for the given grpcAddress and timeout. +// If a cache is used, it retrieves a cached connection, otherwise creates a new connection. +// It returns the client connection and an io.Closer to close the connection when done. func (m *Manager) GetConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, io.Closer, error) { if m.cache != nil { conn, err := m.retrieveConnection(grpcAddress, timeout) @@ -58,6 +63,8 @@ func (m *Manager) GetConnection(grpcAddress string, timeout time.Duration) (*grp return conn, io.Closer(conn), nil } +// Remove removes the gRPC client connection associated with the given grpcAddress from the cache. +// It returns true if the connection was removed successfully, false otherwise. func (m *Manager) Remove(grpcAddress string) bool { if m.cache == nil { return false @@ -77,19 +84,24 @@ func (m *Manager) Remove(grpcAddress string) bool { return true } +// HasCache returns true if the Manager has a cache, false otherwise. func (m *Manager) HasCache() bool { return m.cache != nil } +// retrieveConnection retrieves the CachedClient for the given grpcAddress from the cache or adds a new one if not present. +// If the connection is already cached, it waits for the lock and returns the connection from the cache. +// Otherwise, it creates a new connection and caches it. func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) (*grpc.ClientConn, error) { client, ok := m.cache.GetOrAdd(grpcAddress, timeout) if ok { - // client was from the cache, wait for the lock + // The client was retrieved from the cache, wait for the lock client.mu.Lock() if m.metrics != nil { m.metrics.ConnectionFromPoolReused() } } else { + // The client is new, add it to the cache if m.metrics != nil { m.metrics.ConnectionAddedToPool() } @@ -97,9 +109,11 @@ func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) defer client.mu.Unlock() if client.ClientConn != nil && client.ClientConn.GetState() != connectivity.Shutdown { + // Return the client connection from the cache return client.ClientConn, nil } + // The connection is not cached or is closed, create a new connection and cache it conn, err := m.createConnection(grpcAddress, timeout, client) if err != nil { return nil, err @@ -114,25 +128,24 @@ func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) return client.ClientConn, nil } -// createConnection creates new gRPC connections to remote node +// createConnection creates a new gRPC connection to the remote node at the given address with the specified timeout. +// If the cachedClient is not nil, it means a new entry in the cache is being created, so it's locked to give priority +// to the caller working with the new client, allowing it to create the underlying connection. func (m *Manager) createConnection(address string, timeout time.Duration, cachedClient *CachedClient) (*grpc.ClientConn, error) { if timeout == 0 { timeout = DefaultClientTimeout } keepaliveParams := keepalive.ClientParameters{ - // how long the client will wait before sending a keepalive to the server if there is no activity - Time: 10 * time.Second, - // how long the client will wait for a response from the keepalive before closing - Timeout: timeout, + Time: 10 * time.Second, // How long the client will wait before sending a keepalive to the server if there is no activity. + Timeout: timeout, // How long the client will wait for a response from the keepalive before closing. } var connInterceptors []grpc.UnaryClientInterceptor - // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called - // in the same order during gRPC requests. It is crucial to ensure that the request watcher interceptor is added - // first. This interceptor is responsible for executing necessary request monitoring before passing control to - // subsequent interceptors. + // The order in which interceptors are added to the connInterceptors slice is important as they will be called in + // the same order during gRPC requests. It is crucial to ensure that the request watcher interceptor is added first. + // This interceptor monitors ongoing requests before passing control to subsequent interceptors. if cachedClient != nil { connInterceptors = append(connInterceptors, createRequestWatcherInterceptor(cachedClient)) } @@ -140,7 +153,7 @@ func (m *Manager) createConnection(address string, timeout time.Duration, cached connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached - // The connections should be safe to be persisted and reused + // The connections should be safe to be persisted and reused. // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams // https://grpc.io/blog/grpc-on-http2/#keeping-connections-alive conn, err := grpc.Dial( @@ -156,13 +169,12 @@ func (m *Manager) createConnection(address string, timeout time.Duration, cached return conn, nil } -// WithClientTimeoutOption is a helper function to create a GRPC dial option -// with the specified client timeout interceptor. +// WithClientTimeoutOption is a helper function to create a GRPC dial option with the specified client timeout interceptor. func WithClientTimeoutOption(timeout time.Duration) grpc.DialOption { return grpc.WithUnaryInterceptor(createClientTimeoutInterceptor(timeout)) } -// createRequestWatcherInterceptor creates a request watcher interceptor to wait for unfinished request before close +// createRequestWatcherInterceptor creates a request watcher interceptor to wait for unfinished requests before closing. func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClientInterceptor { requestWatcherInterceptor := func( ctx context.Context, @@ -173,17 +185,16 @@ func createRequestWatcherInterceptor(cachedClient *CachedClient) grpc.UnaryClien invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { - // Prevent new request from being sent if the connection is marked for closure + // Prevent new requests from being sent if the connection is marked for closure. if cachedClient.closeRequested.Load() { return status.Errorf(codes.Unavailable, "the connection to %s was closed", cachedClient.Address) } - // Increment the request counter to track ongoing requests, then - // decrement the request counter before returning + // Increment the request counter to track ongoing requests, then decrement the request counter before returning. cachedClient.wg.Add(1) defer cachedClient.wg.Done() - // Invoke the actual RPC method + // Invoke the actual RPC method. return invoker(ctx, method, req, reply, cc, opts...) } From 1122b8c3a9ad82d08ca9c9341c83139b938b94f7 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 21:41:41 +0300 Subject: [PATCH 259/815] removed unnecessary changes --- cmd/util/cmd/execution-state-extract/export_report.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json deleted file mode 100644 index d5347f21c44..00000000000 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "EpochCounter": 0, - "PreviousStateCommitment": "34c4f4a4cc7e0e63115e770c1c79d0183d1cbbd95afe26a1d3c28acf450d4b09", - "CurrentStateCommitment": "34c4f4a4cc7e0e63115e770c1c79d0183d1cbbd95afe26a1d3c28acf450d4b09", - "ReportSucceeded": true -} \ No newline at end of file From fb4e43f0049ae84d51ff09b9277177548714a555 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Thu, 20 Jul 2023 21:43:48 +0300 Subject: [PATCH 260/815] removed unnecessary changes --- cmd/util/cmd/execution-state-extract/export_report.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json new file mode 100644 index 00000000000..478522206f2 --- /dev/null +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -0,0 +1,6 @@ +{ + "EpochCounter": 0, + "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "ReportSucceeded": true +} From 7dc921bfcfda549b50fe717ab5bc97dd7cadc1d0 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 00:17:54 +0300 Subject: [PATCH 261/815] Apply suggestions from code review Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- engine/access/rpc/backend/node_selector.go | 2 -- integration/tests/access/access_circuit_breaker_test.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index f5682a79ccf..9a68d25769f 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -41,8 +41,6 @@ func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) (NodeSelector }, nil } -// SelectCollectionNodes - var _ NodeSelector = (*MainNodeSelector)(nil) // MainNodeSelector is a specific implementation of the node selector. diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index d02fc811a6c..db9853e9e8e 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -176,7 +176,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // Try to send the transaction for the first time. It should wait at least the timeout time and return Unavailable error duration, err := sendTransaction(ctx, signedTx) assert.Equal(s.T(), codes.Unavailable, status.Code(err)) - assert.Less(s.T(), duration, requestTimeout) + assert.GreaterOrEqual(s.T(), duration, requestTimeout) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure From 384968ad07921c0df86182753d9cb4041b1857dc Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 00:25:29 +0300 Subject: [PATCH 262/815] change error terminator argument type --- engine/access/rpc/backend/backend_scripts.go | 2 +- engine/access/rpc/backend/backend_transactions.go | 6 +++--- engine/access/rpc/backend/node_communicator.go | 4 ++-- integration/tests/access/access_circuit_breaker_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 1f4a03044b2..41ec28ce5ae 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -172,7 +172,7 @@ func (b *backendScripts) executeScriptOnExecutor( return err }, - func(node flow.Identity, err error) bool { + func(node *flow.Identity, err error) bool { hasInvalidArgument = status.Code(err) == codes.InvalidArgument if hasInvalidArgument { b.log.Debug().Err(err). diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index c4bab57d5c3..4622fabff78 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -792,7 +792,7 @@ func (b *backendTransactions) getTransactionResultFromAnyExeNode( } return err }, - func(_ flow.Identity, err error) bool { + func(_ *flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) @@ -853,7 +853,7 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( } return err }, - func(_ flow.Identity, err error) bool { + func(_ *flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) @@ -912,7 +912,7 @@ func (b *backendTransactions) getTransactionResultByIndexFromAnyExeNode( } return err }, - func(_ flow.Identity, err error) bool { + func(_ *flow.Identity, err error) bool { return status.Code(err) == codes.NotFound }, ) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 052b4efa0c9..d75432b0b29 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -18,7 +18,7 @@ type NodeAction func(node *flow.Identity) error // ErrorTerminator is a callback function that determines whether an error should terminate further execution. // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. -type ErrorTerminator func(node flow.Identity, err error) bool +type ErrorTerminator func(node *flow.Identity, err error) bool // NodeCommunicator is responsible for calling available nodes in the backend. type NodeCommunicator struct { @@ -54,7 +54,7 @@ func (b *NodeCommunicator) CallAvailableNode( return nil } - if shouldTerminateOnError != nil && shouldTerminateOnError(*node, err) { + if shouldTerminateOnError != nil && shouldTerminateOnError(node, err) { return err } diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index db9853e9e8e..717a9df98cc 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -181,7 +181,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure duration, err = sendTransaction(ctx, signedTx) - assert.Equal(s.T(), codes.Unknown, status.Code(err)) + assert.Equal(s.T(), codes.Unavailable, status.Code(err)) assert.Greater(s.T(), time.Second, duration) // Reconnect the collection node From 999eb9bdeaf15b420e76b8d76546dfd62f445611 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 20 Jul 2023 18:05:34 -0400 Subject: [PATCH 263/815] add lock for last size and last size update --- .../p2p/tracer/internal/rpc_sent_tracker.go | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index c3907208c70..09b8cf8e48a 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -3,18 +3,17 @@ package internal import ( "crypto/rand" "fmt" + "sync" "time" pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" - "github.com/rs/zerolog" - "go.uber.org/atomic" - "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/mempool/queue" p2pmsg "github.com/onflow/flow-go/network/p2p/message" + "github.com/rs/zerolog" ) // trackableRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed @@ -27,7 +26,8 @@ type trackableRPC struct { // lastHighestIHaveRPCSize tracks the last highest rpc control message size the time stamp it was last updated. type lastHighestIHaveRPCSize struct { - *atomic.Int64 + sync.RWMutex + lastSize int64 lastUpdate time.Time } @@ -71,7 +71,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.WorkerQueueCacheCollector) tracker := &RPCSentTracker{ - lastHighestIHaveRPCSize: &lastHighestIHaveRPCSize{atomic.NewInt64(0), time.Now()}, + lastHighestIHaveRPCSize: &lastHighestIHaveRPCSize{sync.RWMutex{}, 0, time.Now()}, cache: newRPCSentCache(cacheConfig), lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, } @@ -115,9 +115,12 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { } func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { - if t.lastHighestIHaveRPCSize.Load() < size || time.Since(t.lastHighestIHaveRPCSize.lastUpdate) > t.lastHighestIHaveRPCSizeResetInterval { + t.Lock() + defer t.Unlock() + if t.lastSize < size || time.Since(t.lastUpdate) > t.lastHighestIHaveRPCSizeResetInterval { // The last highest ihave RPC size is updated if the new size is larger than the current size, or if the time elapsed since the last update surpasses the reset interval. - t.lastHighestIHaveRPCSize.Store(size) + t.lastSize = size + t.lastUpdate = time.Now() } } @@ -146,7 +149,9 @@ func (t *RPCSentTracker) WasIHaveRPCSent(topicID, messageID string) bool { // LastHighestIHaveRPCSize returns the last highest size of iHaves sent in an rpc. func (t *RPCSentTracker) LastHighestIHaveRPCSize() int64 { - return t.lastHighestIHaveRPCSize.Load() + t.RLock() + defer t.RUnlock() + return t.lastSize } // nonce returns random string that is used to store unique items in herocache. From 65bc05bd5f54ec9bcc6a3c404857bc3bd8f1c9c9 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 20 Jul 2023 18:11:36 -0400 Subject: [PATCH 264/815] remove topic ID from entity ID --- network/p2p/tracer/internal/cache.go | 17 ++++++------- network/p2p/tracer/internal/cache_test.go | 24 ++++++++----------- .../p2p/tracer/internal/rpc_sent_tracker.go | 10 ++++---- .../tracer/internal/rpc_sent_tracker_test.go | 15 +++++------- 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/network/p2p/tracer/internal/cache.go b/network/p2p/tracer/internal/cache.go index b916133b270..655ddf2179f 100644 --- a/network/p2p/tracer/internal/cache.go +++ b/network/p2p/tracer/internal/cache.go @@ -47,26 +47,24 @@ func newRPCSentCache(config *rpcCtrlMsgSentCacheConfig) *rpcSentCache { // add initializes the record cached for the given messageEntityID if it does not exist. // Returns true if the record is initialized, false otherwise (i.e.: the record already exists). // Args: -// - topic: the topic ID. // - messageId: the message ID. // - controlMsgType: the rpc control message type. // Returns: // - bool: true if the record is initialized, false otherwise (i.e.: the record already exists). // Note that if add is called multiple times for the same messageEntityID, the record is initialized only once, and the // subsequent calls return false and do not change the record (i.e.: the record is not re-initialized). -func (r *rpcSentCache) add(topic string, messageId string, controlMsgType p2pmsg.ControlMessageType) bool { - return r.c.Add(newRPCSentEntity(r.rpcSentEntityID(topic, messageId, controlMsgType), controlMsgType)) +func (r *rpcSentCache) add(messageId string, controlMsgType p2pmsg.ControlMessageType) bool { + return r.c.Add(newRPCSentEntity(r.rpcSentEntityID(messageId, controlMsgType), controlMsgType)) } // has checks if the RPC message has been cached indicating it has been sent. // Args: -// - topic: the topic ID. // - messageId: the message ID. // - controlMsgType: the rpc control message type. // Returns: // - bool: true if the RPC has been cache indicating it was sent from the local node. -func (r *rpcSentCache) has(topic string, messageId string, controlMsgType p2pmsg.ControlMessageType) bool { - return r.c.Has(r.rpcSentEntityID(topic, messageId, controlMsgType)) +func (r *rpcSentCache) has(messageId string, controlMsgType p2pmsg.ControlMessageType) bool { + return r.c.Has(r.rpcSentEntityID(messageId, controlMsgType)) } // size returns the number of records in the cache. @@ -74,13 +72,12 @@ func (r *rpcSentCache) size() uint { return r.c.Size() } -// rpcSentEntityID creates an entity ID from the topic, messageID and control message type. +// rpcSentEntityID creates an entity ID from the messageID and control message type. // Args: -// - topic: the topic ID. // - messageId: the message ID. // - controlMsgType: the rpc control message type. // Returns: // - flow.Identifier: the entity ID. -func (r *rpcSentCache) rpcSentEntityID(topic string, messageId string, controlMsgType p2pmsg.ControlMessageType) flow.Identifier { - return flow.MakeIDFromFingerPrint([]byte(fmt.Sprintf("%s%s%s", topic, messageId, controlMsgType))) +func (r *rpcSentCache) rpcSentEntityID(messageId string, controlMsgType p2pmsg.ControlMessageType) flow.Identifier { + return flow.MakeIDFromFingerPrint([]byte(fmt.Sprintf("%s%s", messageId, controlMsgType))) } diff --git a/network/p2p/tracer/internal/cache_test.go b/network/p2p/tracer/internal/cache_test.go index c92b42b5e02..10872b7b7ef 100644 --- a/network/p2p/tracer/internal/cache_test.go +++ b/network/p2p/tracer/internal/cache_test.go @@ -12,7 +12,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/network/channels" p2pmsg "github.com/onflow/flow-go/network/p2p/message" "github.com/onflow/flow-go/utils/unittest" ) @@ -23,24 +22,23 @@ import ( func TestCache_Add(t *testing.T) { cache := cacheFixture(t, 100, zerolog.Nop(), metrics.NewNoopCollector()) controlMsgType := p2pmsg.CtrlMsgIHave - topic := channels.PushBlocks.String() messageID1 := unittest.IdentifierFixture().String() messageID2 := unittest.IdentifierFixture().String() // test initializing a record for an ID that doesn't exist in the cache - initialized := cache.add(topic, messageID1, controlMsgType) + initialized := cache.add(messageID1, controlMsgType) require.True(t, initialized, "expected record to be initialized") - require.True(t, cache.has(topic, messageID1, controlMsgType), "expected record to exist") + require.True(t, cache.has(messageID1, controlMsgType), "expected record to exist") // test initializing a record for an ID that already exists in the cache - initialized = cache.add(topic, messageID1, controlMsgType) + initialized = cache.add(messageID1, controlMsgType) require.False(t, initialized, "expected record not to be initialized") - require.True(t, cache.has(topic, messageID1, controlMsgType), "expected record to exist") + require.True(t, cache.has(messageID1, controlMsgType), "expected record to exist") // test initializing a record for another ID - initialized = cache.add(topic, messageID2, controlMsgType) + initialized = cache.add(messageID2, controlMsgType) require.True(t, initialized, "expected record to be initialized") - require.True(t, cache.has(topic, messageID2, controlMsgType), "expected record to exist") + require.True(t, cache.has(messageID2, controlMsgType), "expected record to exist") } // TestCache_ConcurrentInit tests the concurrent initialization of records. @@ -50,7 +48,6 @@ func TestCache_Add(t *testing.T) { func TestCache_ConcurrentAdd(t *testing.T) { cache := cacheFixture(t, 100, zerolog.Nop(), metrics.NewNoopCollector()) controlMsgType := p2pmsg.CtrlMsgIHave - topic := channels.PushBlocks.String() messageIds := unittest.IdentifierListFixture(10) var wg sync.WaitGroup @@ -59,7 +56,7 @@ func TestCache_ConcurrentAdd(t *testing.T) { for _, id := range messageIds { go func(id flow.Identifier) { defer wg.Done() - cache.add(topic, id.String(), controlMsgType) + cache.add(id.String(), controlMsgType) }(id) } @@ -67,7 +64,7 @@ func TestCache_ConcurrentAdd(t *testing.T) { // ensure that all records are correctly initialized for _, id := range messageIds { - require.True(t, cache.has(topic, id.String(), controlMsgType)) + require.True(t, cache.has(id.String(), controlMsgType)) } } @@ -79,7 +76,6 @@ func TestCache_ConcurrentAdd(t *testing.T) { func TestCache_ConcurrentSameRecordAdd(t *testing.T) { cache := cacheFixture(t, 100, zerolog.Nop(), metrics.NewNoopCollector()) controlMsgType := p2pmsg.CtrlMsgIHave - topic := channels.PushBlocks.String() messageID := unittest.IdentifierFixture().String() const concurrentAttempts = 10 @@ -91,7 +87,7 @@ func TestCache_ConcurrentSameRecordAdd(t *testing.T) { for i := 0; i < concurrentAttempts; i++ { go func() { defer wg.Done() - initSuccess := cache.add(topic, messageID, controlMsgType) + initSuccess := cache.add(messageID, controlMsgType) if initSuccess { successGauge.Inc() } @@ -104,7 +100,7 @@ func TestCache_ConcurrentSameRecordAdd(t *testing.T) { require.Equal(t, int32(1), successGauge.Load()) // ensure that the record is correctly initialized in the cache - require.True(t, cache.has(topic, messageID, controlMsgType)) + require.True(t, cache.has(messageID, controlMsgType)) } // cacheFixture returns a new *RecordCache. diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 09b8cf8e48a..85346125c15 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -130,21 +130,19 @@ func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) { controlMsgType := p2pmsg.CtrlMsgIHave for _, iHave := range iHaves { - topicID := iHave.GetTopicID() for _, messageID := range iHave.GetMessageIDs() { - t.cache.add(topicID, messageID, controlMsgType) + t.cache.add(messageID, controlMsgType) } } } // WasIHaveRPCSent checks if an iHave control message with the provided message ID was sent. // Args: -// - string: the topic ID of the iHave RPC. -// - string: the message ID of the iHave RPC. +// - messageID: the message ID of the iHave RPC. // Returns: // - bool: true if the iHave rpc with the provided message ID was sent. -func (t *RPCSentTracker) WasIHaveRPCSent(topicID, messageID string) bool { - return t.cache.has(topicID, messageID, p2pmsg.CtrlMsgIHave) +func (t *RPCSentTracker) WasIHaveRPCSent(messageID string) bool { + return t.cache.has(messageID, p2pmsg.CtrlMsgIHave) } // LastHighestIHaveRPCSize returns the last highest size of iHaves sent in an rpc. diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 05fce5f8552..5ee97e9a80e 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -13,7 +13,6 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/utils/unittest" ) @@ -38,25 +37,23 @@ func TestRPCSentTracker_IHave(t *testing.T) { }() t.Run("WasIHaveRPCSent should return false for iHave message Id that has not been tracked", func(t *testing.T) { - require.False(t, tracker.WasIHaveRPCSent("topic_id", "message_id")) + require.False(t, tracker.WasIHaveRPCSent("message_id")) }) t.Run("WasIHaveRPCSent should return true for iHave message after it is tracked with iHaveRPCSent", func(t *testing.T) { numOfMsgIds := 100 testCases := []struct { - topic string messageIDS []string }{ - {channels.PushBlocks.String(), unittest.IdentifierListFixture(numOfMsgIds).Strings()}, - {channels.ReceiveApprovals.String(), unittest.IdentifierListFixture(numOfMsgIds).Strings()}, - {channels.SyncCommittee.String(), unittest.IdentifierListFixture(numOfMsgIds).Strings()}, - {channels.RequestChunks.String(), unittest.IdentifierListFixture(numOfMsgIds).Strings()}, + {unittest.IdentifierListFixture(numOfMsgIds).Strings()}, + {unittest.IdentifierListFixture(numOfMsgIds).Strings()}, + {unittest.IdentifierListFixture(numOfMsgIds).Strings()}, + {unittest.IdentifierListFixture(numOfMsgIds).Strings()}, } iHaves := make([]*pb.ControlIHave, len(testCases)) for i, testCase := range testCases { testCase := testCase iHaves[i] = &pb.ControlIHave{ - TopicID: &testCase.topic, MessageIDs: testCase.messageIDS, } } @@ -70,7 +67,7 @@ func TestRPCSentTracker_IHave(t *testing.T) { for _, testCase := range testCases { for _, messageID := range testCase.messageIDS { - require.True(t, tracker.WasIHaveRPCSent(testCase.topic, messageID)) + require.True(t, tracker.WasIHaveRPCSent(messageID)) } } }) From 7c5daef4ab8ea91ab7cb03e51a1820198a9583c5 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 20 Jul 2023 19:17:16 -0400 Subject: [PATCH 265/815] add test cases for concurrent tracking and duplicate RPC tracking --- .../p2p/tracer/internal/rpc_sent_tracker.go | 7 ++ .../tracer/internal/rpc_sent_tracker_test.go | 80 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 85346125c15..12ba32c4915 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -16,6 +16,10 @@ import ( "github.com/rs/zerolog" ) +const ( + iHaveRPCTrackedLog = "ihave rpc tracked successfully" +) + // trackableRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed // by the *RPCSentTracker. type trackableRPC struct { @@ -35,6 +39,7 @@ type lastHighestIHaveRPCSize struct { type RPCSentTracker struct { component.Component *lastHighestIHaveRPCSize + logger zerolog.Logger cache *rpcSentCache workerPool *worker.Pool[trackableRPC] lastHighestIHaveRPCSizeResetInterval time.Duration @@ -71,6 +76,7 @@ func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { config.WorkerQueueCacheCollector) tracker := &RPCSentTracker{ + logger: config.Logger.With().Str("component", "rpc_sent_tracker").Logger(), lastHighestIHaveRPCSize: &lastHighestIHaveRPCSize{sync.RWMutex{}, 0, time.Now()}, cache: newRPCSentCache(cacheConfig), lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, @@ -110,6 +116,7 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { iHave := work.rpc.GetControl().GetIhave() t.iHaveRPCSent(iHave) t.updateLastHighestIHaveRPCSize(int64(len(iHave))) + t.logger.Info().Int("size", len(iHave)).Msg(iHaveRPCTrackedLog) } return nil } diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 5ee97e9a80e..9b571e3d5a6 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -2,6 +2,7 @@ package internal import ( "context" + "os" "testing" "time" @@ -9,6 +10,7 @@ import ( pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "go.uber.org/atomic" "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/module/irrecoverable" @@ -71,6 +73,84 @@ func TestRPCSentTracker_IHave(t *testing.T) { } } }) + +} + +// TestRPCSentTracker_DuplicateMessageID ensures the worker pool of the RPC tracker processes req with the same message ID but different nonce. +func TestRPCSentTracker_DuplicateMessageID(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + processedWorkLogs := atomic.NewInt64(0) + hook := zerolog.HookFunc(func(e *zerolog.Event, level zerolog.Level, message string) { + if level == zerolog.InfoLevel { + if message == iHaveRPCTrackedLog { + processedWorkLogs.Inc() + } + } + }) + logger := zerolog.New(os.Stdout).Level(zerolog.InfoLevel).Hook(hook) + + tracker := mockTracker(t, time.Minute) + require.NotNil(t, tracker) + tracker.logger = logger + tracker.Start(signalerCtx) + defer func() { + cancel() + unittest.RequireComponentsDoneBefore(t, time.Second, tracker) + }() + + messageID := unittest.IdentifierFixture().String() + rpc := rpcFixture(withIhaves([]*pb.ControlIHave{{ + MessageIDs: []string{messageID}, + }})) + // track duplicate RPC's each will be processed by a worker + require.NoError(t, tracker.Track(rpc)) + require.NoError(t, tracker.Track(rpc)) + + // eventually we should have processed both RPCs + require.Eventually(t, func() bool { + return processedWorkLogs.Load() == 2 + }, time.Second, 100*time.Millisecond) +} + +// TestRPCSentTracker_ConcurrentTracking ensures that all message IDs in RPC's are tracked as expected when tracked concurrently. +func TestRPCSentTracker_ConcurrentTracking(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + tracker := mockTracker(t, time.Minute) + require.NotNil(t, tracker) + + tracker.Start(signalerCtx) + defer func() { + cancel() + unittest.RequireComponentsDoneBefore(t, time.Second, tracker) + }() + + numOfMsgIds := 100 + numOfRPCs := 100 + rpcs := make([]*pubsub.RPC, numOfRPCs) + for i := 0; i < numOfRPCs; i++ { + i := i + go func() { + rpc := rpcFixture(withIhaves([]*pb.ControlIHave{{MessageIDs: unittest.IdentifierListFixture(numOfMsgIds).Strings()}})) + require.NoError(t, tracker.Track(rpc)) + rpcs[i] = rpc + }() + } + + // eventually we should have tracked numOfMsgIds per single topic + require.Eventually(t, func() bool { + return tracker.cache.size() == uint(numOfRPCs*numOfMsgIds) + }, time.Second, 100*time.Millisecond) + + for _, rpc := range rpcs { + ihaves := rpc.GetControl().GetIhave() + for _, messageID := range ihaves[0].GetMessageIDs() { + require.True(t, tracker.WasIHaveRPCSent(messageID)) + } + } } // TestRPCSentTracker_IHave ensures *RPCSentTracker tracks the last largest iHave size as expected. From c1d3b2e0922e62309f67db7615671e24155de1ed Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 20 Jul 2023 19:17:57 -0400 Subject: [PATCH 266/815] remove add workers func --- module/component/component.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/module/component/component.go b/module/component/component.go index a887d149270..07fc387e077 100644 --- a/module/component/component.go +++ b/module/component/component.go @@ -166,14 +166,6 @@ func (c *componentManagerBuilderImpl) AddWorker(worker ComponentWorker) Componen return c } -// AddWorkers adds n number of workers for the ComponentManager. -func (c *componentManagerBuilderImpl) AddWorkers(n int, worker ComponentWorker) ComponentManagerBuilder { - for i := 0; i < n; i++ { - c.workers = append(c.workers, worker) - } - return c -} - // Build returns a new ComponentManager instance with the configured workers // Build may be called multiple times to create multiple individual ComponentManagers. This will // result in the worker routines being called multiple times. If this is unsafe, do not call it From cc4c03ce77877b52305a821e95e1baf8fb6c7490 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 15:22:11 +0300 Subject: [PATCH 267/815] Fixed broken test --- .../node_builder/access_node_builder.go | 2 +- integration/testnet/client.go | 24 +++++++++---------- .../access/access_circuit_breaker_test.go | 18 +++++++------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 29ca72a720a..61cae1f468c 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -762,7 +762,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { if builder.rpcConf.CircuitBreakerConfig.MaxRequests == 0 { return errors.New("circuit-breaker-max-requests must be greater than 0") } - if builder.rpcConf.CircuitBreakerConfig.RestoreTimeout > 0 { + if builder.rpcConf.CircuitBreakerConfig.RestoreTimeout <= 0 { return errors.New("circuit-breaker-restore-timeout must be greater than 0") } } diff --git a/integration/testnet/client.go b/integration/testnet/client.go index ab2eb0b751e..941c83a8fbd 100644 --- a/integration/testnet/client.go +++ b/integration/testnet/client.go @@ -79,18 +79,18 @@ func NewClient(addr string, chain flow.Chain) (*Client, error) { } // Uncomment for debugging keys - //json, err := key.MarshalJSON() - //if err != nil { - // return nil, fmt.Errorf("cannot marshal key json: %w", err) - //} - //public := key.PublicKey(1000) - //publicJson, err := public.MarshalJSON() - //if err != nil { - // return nil, fmt.Errorf("cannot marshal key json: %w", err) - //} - - //fmt.Printf("New client with private key: \n%s\n", json) - //fmt.Printf("and public key: \n%s\n", publicJson) + json, err := key.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("cannot marshal key json: %w", err) + } + public := key.PublicKey(1000) + publicJson, err := public.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("cannot marshal key json: %w", err) + } + + fmt.Printf("New client with private key: \n%s\n", json) + fmt.Printf("and public key: \n%s\n", publicJson) return NewClientWithKey(addr, sdk.Address(chain.ServiceAddress()), privateKey, chain) } diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index 717a9df98cc..d2721e41284 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -62,6 +62,7 @@ func (s *AccessCircuitBreakerSuite) SetupTest() { testnet.WithLogLevel(zerolog.InfoLevel), testnet.WithAdditionalFlag("--circuit-breaker-enabled=true"), testnet.WithAdditionalFlag(fmt.Sprintf("--circuit-breaker-restore-timeout=%s", cbRestoreTimeout.String())), + testnet.WithAdditionalFlag("--circuit-breaker-max-requests=1"), testnet.WithAdditionalFlag("--circuit-breaker-max-failures=1"), testnet.WithAdditionalFlag(fmt.Sprintf("--collection-client-timeout=%s", requestTimeout.String())), ), @@ -105,9 +106,6 @@ func (s *AccessCircuitBreakerSuite) SetupTest() { // 3. Connect the collection node to the network and wait for the circuit breaker restore time. // 4. Successfully send a transaction. func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { - ctx, cancel := context.WithCancel(s.ctx) - defer cancel() - // 1. Get the collection node collectionContainer := s.net.ContainerByName("collection_1") @@ -117,12 +115,14 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { // Check if access node was created with circuit breaker flags require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-enabled")) require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-restore-timeout")) + require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-max-requests")) require.True(s.T(), accessContainer.IsFlagSet("circuit-breaker-max-failures")) accessClient, err := accessContainer.TestnetClient() - assert.NoError(s.T(), err, "failed to get access node client") + require.NoError(s.T(), err, "failed to get access node client") + require.NotNil(s.T(), accessClient, "failed to get access node client") - latestBlockID, err := accessClient.GetLatestBlockID(ctx) + latestBlockID, err := accessClient.GetLatestBlockID(s.ctx) require.NoError(s.T(), err) // Create a new account to deploy Counter to @@ -153,7 +153,7 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { SetGasLimit(9999) // Sign the transaction - childCtx, cancel := context.WithTimeout(ctx, time.Second*10) + childCtx, cancel := context.WithTimeout(s.ctx, time.Second*10) signedTx, err := accessClient.SignTransaction(createAccountTx) require.NoError(s.T(), err) cancel() @@ -174,13 +174,13 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { } // Try to send the transaction for the first time. It should wait at least the timeout time and return Unavailable error - duration, err := sendTransaction(ctx, signedTx) + duration, err := sendTransaction(s.ctx, signedTx) assert.Equal(s.T(), codes.Unavailable, status.Code(err)) assert.GreaterOrEqual(s.T(), duration, requestTimeout) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure - duration, err = sendTransaction(ctx, signedTx) + duration, err = sendTransaction(s.ctx, signedTx) assert.Equal(s.T(), codes.Unavailable, status.Code(err)) assert.Greater(s.T(), time.Second, duration) @@ -192,6 +192,6 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { time.Sleep(cbRestoreTimeout) // Try to send the transaction for the third time. The transaction should be sent successfully - _, err = sendTransaction(ctx, signedTx) + _, err = sendTransaction(s.ctx, signedTx) require.NoError(s.T(), err, "transaction should be sent") } From 695ff8c07660124ad997f5b7a01af2e17def0ec3 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 16:28:01 +0300 Subject: [PATCH 268/815] Fixed review remarks --- engine/access/rpc/connection/cache.go | 4 +-- engine/access/rpc/connection/connection.go | 6 ----- .../access/rpc/connection/connection_test.go | 26 +++++++++---------- engine/access/rpc/connection/manager.go | 14 ++++++++-- engine/access/rpc/engine.go | 7 ++++- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/engine/access/rpc/connection/cache.go b/engine/access/rpc/connection/cache.go index 20ebd238229..9aa53d3d251 100644 --- a/engine/access/rpc/connection/cache.go +++ b/engine/access/rpc/connection/cache.go @@ -98,8 +98,8 @@ func (c *Cache) Len() int { return c.cache.Len() } -// Size returns the maximum size of the cache. -func (c *Cache) Size() int { +// MaxSize returns the maximum size of the cache. +func (c *Cache) MaxSize() int { return c.size } diff --git a/engine/access/rpc/connection/connection.go b/engine/access/rpc/connection/connection.go index 40cbfe2fc82..f1ac3270654 100644 --- a/engine/access/rpc/connection/connection.go +++ b/engine/access/rpc/connection/connection.go @@ -28,12 +28,6 @@ type ProxyConnectionFactory struct { targetAddress string } -type noopCloser struct{} - -func (c *noopCloser) Close() error { - return nil -} - func (p *ProxyConnectionFactory) GetAccessAPIClient(address string) (access.AccessAPIClient, io.Closer, error) { return p.ConnectionFactory.GetAccessAPIClient(p.targetAddress) } diff --git a/engine/access/rpc/connection/connection_test.go b/engine/access/rpc/connection/connection_test.go index 09a2ad4f3ea..75239a075ae 100644 --- a/engine/access/rpc/connection/connection_test.go +++ b/engine/access/rpc/connection/connection_test.go @@ -10,8 +10,6 @@ import ( "testing" "time" - "github.com/rs/zerolog" - "go.uber.org/atomic" "pgregory.net/rapid" @@ -49,7 +47,7 @@ func TestProxyAccessAPI(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( nil, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -90,7 +88,7 @@ func TestProxyExecutionAPI(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( nil, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -136,7 +134,7 @@ func TestProxyAccessAPIConnectionReuse(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -189,7 +187,7 @@ func TestProxyExecutionAPIConnectionReuse(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -249,7 +247,7 @@ func TestExecutionNodeClientTimeout(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -297,7 +295,7 @@ func TestCollectionNodeClientTimeout(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -345,7 +343,7 @@ func TestConnectionPoolFull(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -420,7 +418,7 @@ func TestConnectionPoolStale(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -508,7 +506,7 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) @@ -583,13 +581,15 @@ func TestExecutionEvictingCacheClients(t *testing.T) { connectionFactory.CollectionNodeGRPCTimeout = 5 * time.Second // Set the connection pool cache size cacheSize := 1 - cache, _ := lru.New(cacheSize) + cache, err := lru.New(cacheSize) + require.NoError(t, err) + connectionCache := NewCache(cache, cacheSize) // set metrics reporting connectionFactory.AccessMetrics = metrics.NewNoopCollector() connectionFactory.Manager = NewManager( connectionCache, - zerolog.New(zerolog.NewConsoleWriter()), + unittest.Logger(), connectionFactory.AccessMetrics, 0, ) diff --git a/engine/access/rpc/connection/manager.go b/engine/access/rpc/connection/manager.go index ea8878bcb12..8fac0108139 100644 --- a/engine/access/rpc/connection/manager.go +++ b/engine/access/rpc/connection/manager.go @@ -20,6 +20,12 @@ import ( // DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node. const DefaultClientTimeout = 3 * time.Second +type noopCloser struct{} + +func (c *noopCloser) Close() error { + return nil +} + // Manager provides methods for getting and managing gRPC client connections. type Manager struct { cache *Cache @@ -79,6 +85,10 @@ func (m *Manager) Remove(grpcAddress string) bool { return false } + // Obtain the lock here to ensure that ClientConn was initialized, avoiding a situation with a nil ClientConn. + res.mu.Lock() + defer res.mu.Unlock() + // Close the connection only if it is successfully removed from the cache res.Close() return true @@ -101,7 +111,7 @@ func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) m.metrics.ConnectionFromPoolReused() } } else { - // The client is new, add it to the cache + // The client is new, lock is already held if m.metrics != nil { m.metrics.ConnectionAddedToPool() } @@ -122,7 +132,7 @@ func (m *Manager) retrieveConnection(grpcAddress string, timeout time.Duration) client.ClientConn = conn if m.metrics != nil { m.metrics.NewConnectionEstablished() - m.metrics.TotalConnectionsInPool(uint(m.cache.Len()), uint(m.cache.Size())) + m.metrics.TotalConnectionsInPool(uint(m.cache.Len()), uint(m.cache.MaxSize())) } return client.ClientConn, nil diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index 30380871257..aa4f937ecb9 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -162,6 +162,11 @@ func NewBuilder(log zerolog.Logger, } } + var connCache *connection.Cache + if cache != nil { + connCache = connection.NewCache(cache, int(cacheSize)) + } + connectionFactory := &connection.ConnectionFactoryImpl{ CollectionGRPCPort: collectionGRPCPort, ExecutionGRPCPort: executionGRPCPort, @@ -169,7 +174,7 @@ func NewBuilder(log zerolog.Logger, ExecutionNodeGRPCTimeout: config.ExecutionClientTimeout, AccessMetrics: accessMetrics, Log: log, - Manager: connection.NewManager(connection.NewCache(cache, int(cacheSize)), log, accessMetrics, config.MaxMsgSize), + Manager: connection.NewManager(connCache, log, accessMetrics, config.MaxMsgSize), } backend := backend.New(state, From eb6b627804c53d92d2770934a25029f29ef93b28 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 21:32:19 +0300 Subject: [PATCH 269/815] Fixed conflicts --- cmd/observer/node_builder/observer_builder.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index b2001021158..a5c978b43af 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -902,6 +902,11 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { return nil, fmt.Errorf("could not initialize backend cache: %w", err) } + var connBackendCache *rpcConnection.Cache + if backendCache != nil { + connBackendCache = rpcConnection.NewCache(backendCache, int(cacheSize)) + } + connFactory := &rpcConnection.ConnectionFactoryImpl{ CollectionGRPCPort: 0, ExecutionGRPCPort: 0, @@ -909,6 +914,12 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { ExecutionNodeGRPCTimeout: backendConfig.ExecutionClientTimeout, AccessMetrics: accessMetrics, Log: node.Logger, + Manager: rpcConnection.NewManager( + connBackendCache, + node.Logger, + accessMetrics, + config.MaxMsgSize, + ), } accessBackend := backend.New( From 30ac5aa58bd3cf33da1114857d87ff2681a43df7 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 21:44:41 +0300 Subject: [PATCH 270/815] Apply suggestions from code review Co-authored-by: Yurii Oleksyshyn --- engine/access/rpc/backend/node_selector.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index 9a68d25769f..69a8f3e11aa 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -19,6 +19,9 @@ type NodeSelector interface { } // NodeSelectorFactory is a factory for creating node selectors based on factory configuration and node type. +// Supported configurations: +// circuitBreakerEnabled = true - nodes will be pseudo-randomly sampled and picked in-order. +// circuitBreakerEnabled = false - nodes will be picked from proposed list in-order without any changes. type NodeSelectorFactory struct { circuitBreakerEnabled bool } @@ -44,6 +47,7 @@ func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) (NodeSelector var _ NodeSelector = (*MainNodeSelector)(nil) // MainNodeSelector is a specific implementation of the node selector. +// Which performs in-order node selection using fixed list of pre-defined nodes. type MainNodeSelector struct { nodes flow.IdentityList index int From 82f8cbaf90cc19b966674337c0b50fdd628e1be3 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 21:59:04 +0300 Subject: [PATCH 271/815] Fixed review remarks --- engine/access/rpc/backend/connection_factory.go | 1 + .../access/rpc/backend/connection_factory_test.go | 10 ++-------- engine/access/rpc/backend/node_selector.go | 13 +++++++------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index 159f48c969d..bb22af8c249 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -27,6 +27,7 @@ import ( // DefaultClientTimeout is used when making a GRPC request to a collection node or an execution node const DefaultClientTimeout = 3 * time.Second +// clientType is an enumeration type used to differentiate between different types of gRPC clients. type clientType int const ( diff --git a/engine/access/rpc/backend/connection_factory_test.go b/engine/access/rpc/backend/connection_factory_test.go index aefdfd64488..bd4393332db 100644 --- a/engine/access/rpc/backend/connection_factory_test.go +++ b/engine/access/rpc/backend/connection_factory_test.go @@ -443,10 +443,7 @@ func TestCircuitBreakerExecutionNode(t *testing.T) { // Set the connection pool cache size. cacheSize := 1 - cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { - evictedValue.(*CachedClient).Close() - }) - connectionFactory.ConnectionsCache = cache + connectionFactory.ConnectionsCache, _ = lru.New(cacheSize) connectionFactory.CacheSize = uint(cacheSize) // Set metrics reporting. @@ -526,10 +523,7 @@ func TestCircuitBreakerCollectionNode(t *testing.T) { // Set the connection pool cache size. cacheSize := 1 - cache, _ := lru.NewWithEvict(cacheSize, func(_, evictedValue interface{}) { - evictedValue.(*CachedClient).Close() - }) - connectionFactory.ConnectionsCache = cache + connectionFactory.ConnectionsCache, _ = lru.New(cacheSize) connectionFactory.CacheSize = uint(cacheSize) // Set metrics reporting. diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index 69a8f3e11aa..f90f8271b2d 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -38,14 +38,9 @@ func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentityList) (NodeSelector } } - return &MainNodeSelector{ - nodes: nodes, - index: 0, - }, nil + return NewMainNodeSelector(nodes), nil } -var _ NodeSelector = (*MainNodeSelector)(nil) - // MainNodeSelector is a specific implementation of the node selector. // Which performs in-order node selection using fixed list of pre-defined nodes. type MainNodeSelector struct { @@ -53,6 +48,12 @@ type MainNodeSelector struct { index int } +var _ NodeSelector = (*MainNodeSelector)(nil) + +func NewMainNodeSelector(nodes flow.IdentityList) *MainNodeSelector { + return &MainNodeSelector{nodes: nodes, index: 0} +} + // HasNext returns true if next node is available. func (e *MainNodeSelector) HasNext() bool { return e.index < len(e.nodes) From 0d5a2c48d6379d9cbf3ac0623cbd74359a975476 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 21 Jul 2023 22:41:39 +0300 Subject: [PATCH 272/815] Fixed integrarion test. Removed timeouts as it is invalid check there. --- .../access/rpc/backend/connection_factory.go | 9 +++---- .../access/access_circuit_breaker_test.go | 27 +++++-------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/engine/access/rpc/backend/connection_factory.go b/engine/access/rpc/backend/connection_factory.go index bb22af8c249..957cccf7dfc 100644 --- a/engine/access/rpc/backend/connection_factory.go +++ b/engine/access/rpc/backend/connection_factory.go @@ -99,7 +99,6 @@ type CachedClient struct { // createConnection creates new gRPC connections to remote node func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.Duration, clientType clientType) (*grpc.ClientConn, error) { - if timeout == 0 { timeout = DefaultClientTimeout } @@ -113,8 +112,11 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D var connInterceptors []grpc.UnaryClientInterceptor + connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) + // The order in which interceptors are added to the `connInterceptors` slice is important since they will be called - // in the same order during gRPC requests. + // in the opposite order during gRPC requests. See documentation for more info: + // https://grpc.io/blog/grpc-web-interceptor/#binding-interceptors if cf.CircuitBreakerConfig.Enabled { // If the circuit breaker interceptor is enabled, it should always be called first before passing control to // subsequent interceptors. @@ -123,8 +125,6 @@ func (cf *ConnectionFactoryImpl) createConnection(address string, timeout time.D connInterceptors = append(connInterceptors, cf.createClientInvalidationInterceptor(address, clientType)) } - connInterceptors = append(connInterceptors, createClientTimeoutInterceptor(timeout)) - // ClientConn's default KeepAlive on connections is indefinite, assuming the timeout isn't reached // The connections should be safe to be persisted and reused // https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams @@ -386,7 +386,6 @@ func (cf *ConnectionFactoryImpl) createCircuitBreakerInterceptor() grpc.UnaryCli // the "StateClosed" and handles invocations as usual. _, err := circuitBreaker.Execute(func() (interface{}, error) { err := invoker(ctx, method, req, reply, cc, opts...) - return nil, err }) return err diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index d2721e41284..a0d38f79b5c 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -38,8 +38,8 @@ type AccessCircuitBreakerSuite struct { net *testnet.FlowNetwork } -var requestTimeout = 3 * time.Second -var cbRestoreTimeout = 6 * time.Second +var requestTimeout = 1500 * time.Millisecond +var cbRestoreTimeout = 3 * time.Second func (s *AccessCircuitBreakerSuite) TearDownTest() { s.log.Info().Msg("================> Start TearDownTest") @@ -153,36 +153,23 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { SetGasLimit(9999) // Sign the transaction - childCtx, cancel := context.WithTimeout(s.ctx, time.Second*10) signedTx, err := accessClient.SignTransaction(createAccountTx) require.NoError(s.T(), err) - cancel() // 3. Disconnect the collection node from the network to activate the Circuit Breaker err = collectionContainer.Disconnect() require.NoError(s.T(), err, "failed to pause connection node") // 4. Send a couple of transactions to test if the circuit breaker opens correctly - sendTransaction := func(ctx context.Context, tx *sdk.Transaction) (time.Duration, error) { - childCtx, cancel = context.WithTimeout(ctx, time.Second*10) - start := time.Now() - err := accessClient.SendTransaction(childCtx, tx) - duration := time.Since(start) - defer cancel() - - return duration, err - } - // Try to send the transaction for the first time. It should wait at least the timeout time and return Unavailable error - duration, err := sendTransaction(s.ctx, signedTx) + err = accessClient.SendTransaction(s.ctx, signedTx) assert.Equal(s.T(), codes.Unavailable, status.Code(err)) - assert.GreaterOrEqual(s.T(), duration, requestTimeout) // Try to send the transaction for the second time. It should wait less than a second because the circuit breaker // is configured to break after the first failure - duration, err = sendTransaction(s.ctx, signedTx) - assert.Equal(s.T(), codes.Unavailable, status.Code(err)) - assert.Greater(s.T(), time.Second, duration) + err = accessClient.SendTransaction(s.ctx, signedTx) + //Here we catch the codes.Unknown error, as this is the one that comes from the Circuit Breaker when the state is Open. + assert.Equal(s.T(), codes.Unknown, status.Code(err)) // Reconnect the collection node err = collectionContainer.Connect() @@ -192,6 +179,6 @@ func (s *AccessCircuitBreakerSuite) TestCircuitBreaker() { time.Sleep(cbRestoreTimeout) // Try to send the transaction for the third time. The transaction should be sent successfully - _, err = sendTransaction(s.ctx, signedTx) + err = accessClient.SendTransaction(s.ctx, signedTx) require.NoError(s.T(), err, "transaction should be sent") } From 554071ee8ba0be0944aed0c6b8805500aed59151 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 23 Jul 2023 21:31:46 +0100 Subject: [PATCH 273/815] Generic related refactoring based on code review feedback --- storage/badger/cache.go | 1 - storage/badger/events.go | 2 +- storage/badger/headers.go | 4 ++-- storage/badger/transaction_results.go | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/storage/badger/cache.go b/storage/badger/cache.go index 0ac36a63b29..3941ba3c111 100644 --- a/storage/badger/cache.go +++ b/storage/badger/cache.go @@ -101,7 +101,6 @@ func (c *Cache[K, V]) Get(key K) func(*badger.Txn) (V, error) { // get it from the database resource, err := c.retrieve(key)(tx) if err != nil { - if errors.Is(err, storage.ErrNotFound) { c.metrics.CacheNotFound(c.resource) } diff --git a/storage/badger/events.go b/storage/badger/events.go index eb9478af07c..c92c5a0fd2b 100644 --- a/storage/badger/events.go +++ b/storage/badger/events.go @@ -156,7 +156,7 @@ func NewServiceEvents(collector module.CacheMetrics, db *badger.DB) *ServiceEven db: db, cache: newCache[flow.Identifier, []flow.Event](collector, metrics.ResourceEvents, withStore(noopStore[flow.Identifier, []flow.Event]), - withRetrieve[flow.Identifier, []flow.Event](retrieve)), + withRetrieve(retrieve)), } } diff --git a/storage/badger/headers.go b/storage/badger/headers.go index 17a1f7feaa8..78fb4ed49fb 100644 --- a/storage/badger/headers.go +++ b/storage/badger/headers.go @@ -58,8 +58,8 @@ func NewHeaders(collector module.CacheMetrics, db *badger.DB) *Headers { heightCache: newCache[uint64, flow.Identifier](collector, metrics.ResourceFinalizedHeight, withLimit[uint64, flow.Identifier](4*flow.DefaultTransactionExpiry), - withStore[uint64, flow.Identifier](storeHeight), - withRetrieve[uint64, flow.Identifier](retrieveHeight)), + withStore(storeHeight), + withRetrieve(retrieveHeight)), } return h diff --git a/storage/badger/transaction_results.go b/storage/badger/transaction_results.go index 6057b210127..9f9faedc38a 100644 --- a/storage/badger/transaction_results.go +++ b/storage/badger/transaction_results.go @@ -135,7 +135,7 @@ func NewTransactionResults(collector module.CacheMetrics, db *badger.DB, transac cache: newCache[string, flow.TransactionResult](collector, metrics.ResourceTransactionResults, withLimit[string, flow.TransactionResult](transactionResultsCacheSize), withStore(noopStore[string, flow.TransactionResult]), - withRetrieve[string, flow.TransactionResult](retrieve), + withRetrieve(retrieve), ), indexCache: newCache[string, flow.TransactionResult](collector, metrics.ResourceTransactionResultIndices, withLimit[string, flow.TransactionResult](transactionResultsCacheSize), From b4c749c1bb75be70b5a3a7091c1612299b073767 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Mon, 24 Jul 2023 15:08:44 +0300 Subject: [PATCH 274/815] CHanged timeouts --- integration/tests/access/access_circuit_breaker_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/access/access_circuit_breaker_test.go b/integration/tests/access/access_circuit_breaker_test.go index a0d38f79b5c..a96e9cf2ab7 100644 --- a/integration/tests/access/access_circuit_breaker_test.go +++ b/integration/tests/access/access_circuit_breaker_test.go @@ -38,8 +38,8 @@ type AccessCircuitBreakerSuite struct { net *testnet.FlowNetwork } -var requestTimeout = 1500 * time.Millisecond -var cbRestoreTimeout = 3 * time.Second +var requestTimeout = 3 * time.Second +var cbRestoreTimeout = 6 * time.Second func (s *AccessCircuitBreakerSuite) TearDownTest() { s.log.Info().Msg("================> Start TearDownTest") From 359c8ceb2234f1e6cfd735946ccca66a94a3528d Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 24 Jul 2023 13:43:08 +0100 Subject: [PATCH 275/815] Added sample benchmark of cache test generic vs interface with sample output --- storage/badger/benchmarks/generic_bench.txt | 8 ++ storage/badger/cache_bench_generic_test.go | 37 ++++++ storage/badger/cache_interface.go | 131 ++++++++++++++++++++ storage/badger/generic_bench.txt | 8 ++ 4 files changed, 184 insertions(+) create mode 100644 storage/badger/benchmarks/generic_bench.txt create mode 100644 storage/badger/cache_bench_generic_test.go create mode 100644 storage/badger/cache_interface.go create mode 100644 storage/badger/generic_bench.txt diff --git a/storage/badger/benchmarks/generic_bench.txt b/storage/badger/benchmarks/generic_bench.txt new file mode 100644 index 00000000000..afc71aa8438 --- /dev/null +++ b/storage/badger/benchmarks/generic_bench.txt @@ -0,0 +1,8 @@ +goos: darwin +goarch: amd64 +pkg: github.com/onflow/flow-go/storage/badger +cpu: VirtualApple @ 2.50GHz +BenchmarkInterface-8 1625692 695.4 ns/op +BenchmarkGenerics-8 2015852 595.0 ns/op +PASS +ok github.com/onflow/flow-go/storage/badger 12.342s diff --git a/storage/badger/cache_bench_generic_test.go b/storage/badger/cache_bench_generic_test.go new file mode 100644 index 00000000000..27fb37f9063 --- /dev/null +++ b/storage/badger/cache_bench_generic_test.go @@ -0,0 +1,37 @@ +package badger + +import ( + "testing" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/utils/unittest" +) + +func BenchmarkInterface(B *testing.B) { + key := unittest.IdentifierFixture() + cache := newCacheIFace(metrics.NewNoopCollector(), "test") + + for i := 0; i < B.N; i++ { + // insert, then remove the item + cache.Insert(key, unittest.RandomBytes(128)) + cache.Remove(key) + + _ = cache.IsCached(key) + + } +} + +func BenchmarkGenerics(B *testing.B) { + key := unittest.IdentifierFixture() + cache := newCache[flow.Identifier, any](metrics.NewNoopCollector(), "test") + + for i := 0; i < B.N; i++ { + // insert, then remove the item + cache.Insert(key, unittest.RandomBytes(128)) + cache.Remove(key) + + _ = cache.IsCached(key) + + } +} diff --git a/storage/badger/cache_interface.go b/storage/badger/cache_interface.go new file mode 100644 index 00000000000..b370f4ffaca --- /dev/null +++ b/storage/badger/cache_interface.go @@ -0,0 +1,131 @@ +package badger + +import ( + "errors" + "fmt" + + "github.com/dgraph-io/badger/v2" + lru "github.com/hashicorp/golang-lru" + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/storage/badger/transaction" +) + +type storeFuncIFace func(key interface{}, val interface{}) func(*transaction.Tx) error + +func withStoreIFace(store storeFuncIFace) func(face *CacheIFace) { + return func(c *CacheIFace) { + c.store = store + } +} + +func noStoreIFace(key interface{}, val interface{}) func(*transaction.Tx) error { + return func(tx *transaction.Tx) error { + return fmt.Errorf("no store function for cache put available") + } +} + +type retrieveFuncIFace func(key interface{}) func(*badger.Txn) (interface{}, error) + +func noRetrieveIFace(key interface{}) func(*badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (interface{}, error) { + return nil, fmt.Errorf("no retrieve function for cache get available") + } +} + +type CacheIFace struct { + metrics module.CacheMetrics + limit uint + store storeFuncIFace + retrieve retrieveFuncIFace + resource string + cache *lru.Cache +} + +func newCacheIFace(collector module.CacheMetrics, resourceName string, options ...func(*CacheIFace)) *CacheIFace { + c := CacheIFace{ + metrics: collector, + limit: 1000, + store: noStoreIFace, + retrieve: noRetrieveIFace, + resource: resourceName, + } + for _, option := range options { + option(&c) + } + c.cache, _ = lru.New(int(c.limit)) + c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) + return &c +} + +// IsCached returns true if the key exists in the cache. +// It DOES NOT check whether the key exists in the underlying data store. +func (c *CacheIFace) IsCached(key any) bool { + exists := c.cache.Contains(key) + return exists +} + +// Get will try to retrieve the resource from cache first, and then from the +// injected. During normal operations, the following error returns are expected: +// - `storage.ErrNotFound` if key is unknown. +func (c *CacheIFace) Get(key interface{}) func(*badger.Txn) (interface{}, error) { + return func(tx *badger.Txn) (interface{}, error) { + + // check if we have it in the cache + resource, cached := c.cache.Get(key) + if cached { + c.metrics.CacheHit(c.resource) + return resource, nil + } + + // get it from the database + resource, err := c.retrieve(key)(tx) + if err != nil { + if errors.Is(err, storage.ErrNotFound) { + c.metrics.CacheNotFound(c.resource) + } + return nil, fmt.Errorf("could not retrieve resource: %w", err) + } + + c.metrics.CacheMiss(c.resource) + + // cache the resource and eject least recently used one if we reached limit + evicted := c.cache.Add(key, resource) + if !evicted { + c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) + } + + return resource, nil + } +} + +func (c *CacheIFace) Remove(key interface{}) { + c.cache.Remove(key) +} + +// Insert will add a resource directly to the cache with the given ID +func (c *CacheIFace) Insert(key interface{}, resource interface{}) { + // cache the resource and eject least recently used one if we reached limit + evicted := c.cache.Add(key, resource) + if !evicted { + c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) + } +} + +// PutTx will return tx which adds a resource to the cache with the given ID. +func (c *CacheIFace) PutTx(key interface{}, resource interface{}) func(*transaction.Tx) error { + storeOps := c.store(key, resource) // assemble DB operations to store resource (no execution) + + return func(tx *transaction.Tx) error { + err := storeOps(tx) // execute operations to store resource + if err != nil { + return fmt.Errorf("could not store resource: %w", err) + } + + tx.OnSucceed(func() { + c.Insert(key, resource) + }) + + return nil + } +} diff --git a/storage/badger/generic_bench.txt b/storage/badger/generic_bench.txt new file mode 100644 index 00000000000..521cc35ed2b --- /dev/null +++ b/storage/badger/generic_bench.txt @@ -0,0 +1,8 @@ +goos: darwin +goarch: amd64 +pkg: github.com/onflow/flow-go/storage/badger +cpu: VirtualApple @ 2.50GHz +BenchmarkInterface-8 1653637 700.5 ns/op +BenchmarkGenerics-8 2020924 595.7 ns/op +PASS +ok github.com/onflow/flow-go/storage/badger 11.619s From 46d10fb67bcf6375238a501eb2a0f9821910d163 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Tue, 25 Jul 2023 00:56:46 +0300 Subject: [PATCH 276/815] Removed work around --- engine/access/rpc/backend/backend.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index d492892feba..37277c4dc1f 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -225,13 +225,6 @@ func NewCache( var cache *lru.Cache cacheSize := connectionPoolSize if cacheSize > 0 { - // TODO: remove this fallback after fixing issues with evictions - // It was observed that evictions cause connection errors for in flight requests. This works around - // the issue by forcing hte pool size to be greater than the number of ENs + LNs - if cacheSize < DefaultConnectionPoolSize { - log.Warn().Msg("connection pool size below threshold, setting pool size to default value ") - cacheSize = DefaultConnectionPoolSize - } var err error cache, err = lru.NewWithEvict(int(cacheSize), func(_, evictedValue interface{}) { store := evictedValue.(*connection.CachedClient) From 41ef40baaaae30168e99eab9acc5a986a46d2b9c Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 25 Jul 2023 09:27:29 -0400 Subject: [PATCH 277/815] Update component.go --- module/component/component.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/module/component/component.go b/module/component/component.go index a887d149270..34f8f61cf14 100644 --- a/module/component/component.go +++ b/module/component/component.go @@ -157,23 +157,15 @@ func NewComponentManagerBuilder() ComponentManagerBuilder { return &componentManagerBuilderImpl{} } +// AddWorker adds a ComponentWorker closure to the ComponentManagerBuilder // All worker functions will be run in parallel when the ComponentManager is started. // Note: AddWorker is not concurrency-safe, and should only be called on an individual builder -// within a single goroutine.// AddWorker adds a ComponentWorker closure to the ComponentManagerBuilder - +// within a single goroutine. func (c *componentManagerBuilderImpl) AddWorker(worker ComponentWorker) ComponentManagerBuilder { c.workers = append(c.workers, worker) return c } -// AddWorkers adds n number of workers for the ComponentManager. -func (c *componentManagerBuilderImpl) AddWorkers(n int, worker ComponentWorker) ComponentManagerBuilder { - for i := 0; i < n; i++ { - c.workers = append(c.workers, worker) - } - return c -} - // Build returns a new ComponentManager instance with the configured workers // Build may be called multiple times to create multiple individual ComponentManagers. This will // result in the worker routines being called multiple times. If this is unsafe, do not call it From abf501bf591c17af1a7fe89e580ed5c5df887e36 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 25 Jul 2023 18:43:20 +0100 Subject: [PATCH 278/815] Remove benchmark file, refactor benchmark test --- storage/badger/benchmarks/generic_bench.txt | 8 -------- storage/badger/cache_bench_generic_test.go | 9 ++++----- 2 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 storage/badger/benchmarks/generic_bench.txt diff --git a/storage/badger/benchmarks/generic_bench.txt b/storage/badger/benchmarks/generic_bench.txt deleted file mode 100644 index afc71aa8438..00000000000 --- a/storage/badger/benchmarks/generic_bench.txt +++ /dev/null @@ -1,8 +0,0 @@ -goos: darwin -goarch: amd64 -pkg: github.com/onflow/flow-go/storage/badger -cpu: VirtualApple @ 2.50GHz -BenchmarkInterface-8 1625692 695.4 ns/op -BenchmarkGenerics-8 2015852 595.0 ns/op -PASS -ok github.com/onflow/flow-go/storage/badger 12.342s diff --git a/storage/badger/cache_bench_generic_test.go b/storage/badger/cache_bench_generic_test.go index 27fb37f9063..58212ad5741 100644 --- a/storage/badger/cache_bench_generic_test.go +++ b/storage/badger/cache_bench_generic_test.go @@ -11,10 +11,10 @@ import ( func BenchmarkInterface(B *testing.B) { key := unittest.IdentifierFixture() cache := newCacheIFace(metrics.NewNoopCollector(), "test") - + value := unittest.RandomBytes(128) for i := 0; i < B.N; i++ { // insert, then remove the item - cache.Insert(key, unittest.RandomBytes(128)) + cache.Insert(key, value) cache.Remove(key) _ = cache.IsCached(key) @@ -25,10 +25,9 @@ func BenchmarkInterface(B *testing.B) { func BenchmarkGenerics(B *testing.B) { key := unittest.IdentifierFixture() cache := newCache[flow.Identifier, any](metrics.NewNoopCollector(), "test") - + value := unittest.RandomBytes(128) for i := 0; i < B.N; i++ { - // insert, then remove the item - cache.Insert(key, unittest.RandomBytes(128)) + cache.Insert(key, value) cache.Remove(key) _ = cache.IsCached(key) From f90bea0a505d48078c55abd3cf4cadc32fe418f3 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 25 Jul 2023 18:59:43 +0100 Subject: [PATCH 279/815] Update go.mod, remove benchmark files --- go.mod | 2 +- storage/badger/cache_bench_generic_test.go | 36 ------ storage/badger/cache_interface.go | 131 --------------------- 3 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 storage/badger/cache_bench_generic_test.go delete mode 100644 storage/badger/cache_interface.go diff --git a/go.mod b/go.mod index b4b7cbe7f62..ed9abda52b2 100644 --- a/go.mod +++ b/go.mod @@ -100,6 +100,7 @@ require ( require ( github.com/coreos/go-semver v0.3.0 github.com/go-playground/validator/v10 v10.14.1 + github.com/hashicorp/golang-lru/v2 v2.0.2 github.com/mitchellh/mapstructure v1.5.0 github.com/onflow/wal v0.0.0-20230529184820-bc9f8244608d github.com/slok/go-http-metrics v0.10.0 @@ -167,7 +168,6 @@ require ( github.com/googleapis/gax-go/v2 v2.7.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect diff --git a/storage/badger/cache_bench_generic_test.go b/storage/badger/cache_bench_generic_test.go deleted file mode 100644 index 58212ad5741..00000000000 --- a/storage/badger/cache_bench_generic_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package badger - -import ( - "testing" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/utils/unittest" -) - -func BenchmarkInterface(B *testing.B) { - key := unittest.IdentifierFixture() - cache := newCacheIFace(metrics.NewNoopCollector(), "test") - value := unittest.RandomBytes(128) - for i := 0; i < B.N; i++ { - // insert, then remove the item - cache.Insert(key, value) - cache.Remove(key) - - _ = cache.IsCached(key) - - } -} - -func BenchmarkGenerics(B *testing.B) { - key := unittest.IdentifierFixture() - cache := newCache[flow.Identifier, any](metrics.NewNoopCollector(), "test") - value := unittest.RandomBytes(128) - for i := 0; i < B.N; i++ { - cache.Insert(key, value) - cache.Remove(key) - - _ = cache.IsCached(key) - - } -} diff --git a/storage/badger/cache_interface.go b/storage/badger/cache_interface.go deleted file mode 100644 index b370f4ffaca..00000000000 --- a/storage/badger/cache_interface.go +++ /dev/null @@ -1,131 +0,0 @@ -package badger - -import ( - "errors" - "fmt" - - "github.com/dgraph-io/badger/v2" - lru "github.com/hashicorp/golang-lru" - "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/storage" - "github.com/onflow/flow-go/storage/badger/transaction" -) - -type storeFuncIFace func(key interface{}, val interface{}) func(*transaction.Tx) error - -func withStoreIFace(store storeFuncIFace) func(face *CacheIFace) { - return func(c *CacheIFace) { - c.store = store - } -} - -func noStoreIFace(key interface{}, val interface{}) func(*transaction.Tx) error { - return func(tx *transaction.Tx) error { - return fmt.Errorf("no store function for cache put available") - } -} - -type retrieveFuncIFace func(key interface{}) func(*badger.Txn) (interface{}, error) - -func noRetrieveIFace(key interface{}) func(*badger.Txn) (interface{}, error) { - return func(tx *badger.Txn) (interface{}, error) { - return nil, fmt.Errorf("no retrieve function for cache get available") - } -} - -type CacheIFace struct { - metrics module.CacheMetrics - limit uint - store storeFuncIFace - retrieve retrieveFuncIFace - resource string - cache *lru.Cache -} - -func newCacheIFace(collector module.CacheMetrics, resourceName string, options ...func(*CacheIFace)) *CacheIFace { - c := CacheIFace{ - metrics: collector, - limit: 1000, - store: noStoreIFace, - retrieve: noRetrieveIFace, - resource: resourceName, - } - for _, option := range options { - option(&c) - } - c.cache, _ = lru.New(int(c.limit)) - c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) - return &c -} - -// IsCached returns true if the key exists in the cache. -// It DOES NOT check whether the key exists in the underlying data store. -func (c *CacheIFace) IsCached(key any) bool { - exists := c.cache.Contains(key) - return exists -} - -// Get will try to retrieve the resource from cache first, and then from the -// injected. During normal operations, the following error returns are expected: -// - `storage.ErrNotFound` if key is unknown. -func (c *CacheIFace) Get(key interface{}) func(*badger.Txn) (interface{}, error) { - return func(tx *badger.Txn) (interface{}, error) { - - // check if we have it in the cache - resource, cached := c.cache.Get(key) - if cached { - c.metrics.CacheHit(c.resource) - return resource, nil - } - - // get it from the database - resource, err := c.retrieve(key)(tx) - if err != nil { - if errors.Is(err, storage.ErrNotFound) { - c.metrics.CacheNotFound(c.resource) - } - return nil, fmt.Errorf("could not retrieve resource: %w", err) - } - - c.metrics.CacheMiss(c.resource) - - // cache the resource and eject least recently used one if we reached limit - evicted := c.cache.Add(key, resource) - if !evicted { - c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) - } - - return resource, nil - } -} - -func (c *CacheIFace) Remove(key interface{}) { - c.cache.Remove(key) -} - -// Insert will add a resource directly to the cache with the given ID -func (c *CacheIFace) Insert(key interface{}, resource interface{}) { - // cache the resource and eject least recently used one if we reached limit - evicted := c.cache.Add(key, resource) - if !evicted { - c.metrics.CacheEntries(c.resource, uint(c.cache.Len())) - } -} - -// PutTx will return tx which adds a resource to the cache with the given ID. -func (c *CacheIFace) PutTx(key interface{}, resource interface{}) func(*transaction.Tx) error { - storeOps := c.store(key, resource) // assemble DB operations to store resource (no execution) - - return func(tx *transaction.Tx) error { - err := storeOps(tx) // execute operations to store resource - if err != nil { - return fmt.Errorf("could not store resource: %w", err) - } - - tx.OnSucceed(func() { - c.Insert(key, resource) - }) - - return nil - } -} From 6a4e69be1a80023e4a4815663b5010a751cf6bfb Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 25 Jul 2023 19:10:58 +0100 Subject: [PATCH 280/815] Fix imports --- storage/badger/approvals.go | 1 + storage/badger/cache.go | 1 + storage/badger/cache_test.go | 3 ++- storage/badger/chunkDataPacks.go | 1 + storage/badger/cluster_payloads.go | 1 + storage/badger/commits.go | 1 + storage/badger/dkg_state.go | 1 + storage/badger/epoch_commits.go | 1 + storage/badger/epoch_setups.go | 1 + storage/badger/epoch_statuses.go | 1 + storage/badger/events.go | 1 + storage/badger/guarantees.go | 1 + storage/badger/headers.go | 1 + storage/badger/index.go | 1 + storage/badger/my_receipts.go | 1 + storage/badger/qcs.go | 1 + storage/badger/receipts.go | 1 + storage/badger/results.go | 1 + storage/badger/seals.go | 1 + storage/badger/transaction_results.go | 1 + storage/badger/transactions.go | 1 + 21 files changed, 22 insertions(+), 1 deletion(-) diff --git a/storage/badger/approvals.go b/storage/badger/approvals.go index c50a4455301..eb3cf4ae820 100644 --- a/storage/badger/approvals.go +++ b/storage/badger/approvals.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/cache.go b/storage/badger/cache.go index 3941ba3c111..67bc0b7f055 100644 --- a/storage/badger/cache.go +++ b/storage/badger/cache.go @@ -6,6 +6,7 @@ import ( "github.com/dgraph-io/badger/v2" lru "github.com/hashicorp/golang-lru/v2" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/transaction" diff --git a/storage/badger/cache_test.go b/storage/badger/cache_test.go index 048ba359b69..76ea7ce18bc 100644 --- a/storage/badger/cache_test.go +++ b/storage/badger/cache_test.go @@ -3,10 +3,11 @@ package badger import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/utils/unittest" - "github.com/stretchr/testify/assert" ) // TestCache_Exists tests existence checking items in the cache. diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index 1f44627ccce..2a5c733f60e 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/cluster_payloads.go b/storage/badger/cluster_payloads.go index 2073d5a8662..ff7034dd35e 100644 --- a/storage/badger/cluster_payloads.go +++ b/storage/badger/cluster_payloads.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" diff --git a/storage/badger/commits.go b/storage/badger/commits.go index 6417d10e679..11a4e4aa8e2 100644 --- a/storage/badger/commits.go +++ b/storage/badger/commits.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/dkg_state.go b/storage/badger/dkg_state.go index 9052999c484..73e2b3e8133 100644 --- a/storage/badger/dkg_state.go +++ b/storage/badger/dkg_state.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" diff --git a/storage/badger/epoch_commits.go b/storage/badger/epoch_commits.go index 6a302cc6009..20dadaccdba 100644 --- a/storage/badger/epoch_commits.go +++ b/storage/badger/epoch_commits.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/epoch_setups.go b/storage/badger/epoch_setups.go index 38f0139313f..24757067f8f 100644 --- a/storage/badger/epoch_setups.go +++ b/storage/badger/epoch_setups.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/epoch_statuses.go b/storage/badger/epoch_statuses.go index 7fb6e93629c..2d64fcfea8f 100644 --- a/storage/badger/epoch_statuses.go +++ b/storage/badger/epoch_statuses.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/events.go b/storage/badger/events.go index c92c5a0fd2b..f2d0242c9e7 100644 --- a/storage/badger/events.go +++ b/storage/badger/events.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/guarantees.go b/storage/badger/guarantees.go index 9087c5463f3..b7befd342b6 100644 --- a/storage/badger/guarantees.go +++ b/storage/badger/guarantees.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/headers.go b/storage/badger/headers.go index 78fb4ed49fb..bfdaaa320df 100644 --- a/storage/badger/headers.go +++ b/storage/badger/headers.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/index.go b/storage/badger/index.go index 7c8dcffbd7b..49d87b928da 100644 --- a/storage/badger/index.go +++ b/storage/badger/index.go @@ -4,6 +4,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/my_receipts.go b/storage/badger/my_receipts.go index fa1f3130837..ff1584f44d6 100644 --- a/storage/badger/my_receipts.go +++ b/storage/badger/my_receipts.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/qcs.go b/storage/badger/qcs.go index 51e01afd82f..6e512b0a272 100644 --- a/storage/badger/qcs.go +++ b/storage/badger/qcs.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/receipts.go b/storage/badger/receipts.go index a6b0cf14d43..b92c3961048 100644 --- a/storage/badger/receipts.go +++ b/storage/badger/receipts.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/results.go b/storage/badger/results.go index f6be4f6318b..d4d1a4525b0 100644 --- a/storage/badger/results.go +++ b/storage/badger/results.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/seals.go b/storage/badger/seals.go index c5772ea371a..5ae5cbe71af 100644 --- a/storage/badger/seals.go +++ b/storage/badger/seals.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/transaction_results.go b/storage/badger/transaction_results.go index 9f9faedc38a..1869d5d5aea 100644 --- a/storage/badger/transaction_results.go +++ b/storage/badger/transaction_results.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" diff --git a/storage/badger/transactions.go b/storage/badger/transactions.go index 84d02b229af..eeca9c9477e 100644 --- a/storage/badger/transactions.go +++ b/storage/badger/transactions.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" From ab2ee74147e47cd50a61e368249355d742f31997 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 25 Jul 2023 14:22:45 -0400 Subject: [PATCH 281/815] Update rpc_sent_tracker.go --- network/p2p/tracer/internal/rpc_sent_tracker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 12ba32c4915..9551693af13 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -8,12 +8,13 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" + "github.com/rs/zerolog" + "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/mempool/queue" p2pmsg "github.com/onflow/flow-go/network/p2p/message" - "github.com/rs/zerolog" ) const ( From 41f7d5c348caf4a31a7c6ff14c323ecc3ba36fa4 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 25 Jul 2023 14:30:17 -0400 Subject: [PATCH 282/815] Update rpc_sent_tracker.go --- network/p2p/tracer/internal/rpc_sent_tracker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 9551693af13..5c2c842a48c 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -9,7 +9,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/rs/zerolog" - + "github.com/onflow/flow-go/engine/common/worker" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" From f1b7e5cc746047763e50d0e750cb483466dfa533 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 25 Jul 2023 14:54:34 -0400 Subject: [PATCH 283/815] Update gossipSubMeshTracer_test.go --- network/p2p/tracer/gossipSubMeshTracer_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index 2f0d9581a2b..c8a680df53e 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p" @@ -69,6 +70,7 @@ func TestGossipSubMeshTracer(t *testing.T) { Metrics: collector, IDProvider: idProvider, LoggerInterval: time.Second, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, RpcSentTrackerWorkerQueueCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, RpcSentTrackerNumOfWorkers: defaultConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, From e39b63fc5290b1209b9fb5b87eb777dff4ab1e42 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 26 Jul 2023 12:28:31 +0100 Subject: [PATCH 284/815] Handle code review feedback, remove benchmark file, comments and rename variable --- storage/badger/cluster_payloads.go | 5 ++--- storage/badger/generic_bench.txt | 8 -------- storage/badger/qcs.go | 2 -- 3 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 storage/badger/generic_bench.txt diff --git a/storage/badger/cluster_payloads.go b/storage/badger/cluster_payloads.go index ff7034dd35e..b8090aa73db 100644 --- a/storage/badger/cluster_payloads.go +++ b/storage/badger/cluster_payloads.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -25,10 +24,10 @@ func NewClusterPayloads(cacheMetrics module.CacheMetrics, db *badger.DB) *Cluste return transaction.WithTx(procedure.InsertClusterPayload(blockID, payload)) } - retrieve := func(key flow.Identifier) func(tx *badger.Txn) (*cluster.Payload, error) { + retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*cluster.Payload, error) { var payload cluster.Payload return func(tx *badger.Txn) (*cluster.Payload, error) { - err := procedure.RetrieveClusterPayload(key, &payload)(tx) + err := procedure.RetrieveClusterPayload(blockID, &payload)(tx) return &payload, err } } diff --git a/storage/badger/generic_bench.txt b/storage/badger/generic_bench.txt deleted file mode 100644 index 521cc35ed2b..00000000000 --- a/storage/badger/generic_bench.txt +++ /dev/null @@ -1,8 +0,0 @@ -goos: darwin -goarch: amd64 -pkg: github.com/onflow/flow-go/storage/badger -cpu: VirtualApple @ 2.50GHz -BenchmarkInterface-8 1653637 700.5 ns/op -BenchmarkGenerics-8 2020924 595.7 ns/op -PASS -ok github.com/onflow/flow-go/storage/badger 11.619s diff --git a/storage/badger/qcs.go b/storage/badger/qcs.go index 6e512b0a272..e7ae81fdfc7 100644 --- a/storage/badger/qcs.go +++ b/storage/badger/qcs.go @@ -2,7 +2,6 @@ package badger import ( "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" @@ -23,7 +22,6 @@ var _ storage.QuorumCertificates = (*QuorumCertificates)(nil) // which supports storing, caching and retrieving by block ID. func NewQuorumCertificates(collector module.CacheMetrics, db *badger.DB, cacheSize uint) *QuorumCertificates { store := func(_ flow.Identifier, qc *flow.QuorumCertificate) func(*transaction.Tx) error { - //qc := val.(*flow.QuorumCertificate) return transaction.WithTx(operation.InsertQuorumCertificate(qc)) } From 9d686584efe40c3081991277a18807b17d62e2c7 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 26 Jul 2023 22:21:40 +0100 Subject: [PATCH 285/815] Fix goimports --- storage/badger/cluster_payloads.go | 1 + storage/badger/qcs.go | 1 + 2 files changed, 2 insertions(+) diff --git a/storage/badger/cluster_payloads.go b/storage/badger/cluster_payloads.go index b8090aa73db..0fc3ba3ee28 100644 --- a/storage/badger/cluster_payloads.go +++ b/storage/badger/cluster_payloads.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" diff --git a/storage/badger/qcs.go b/storage/badger/qcs.go index e7ae81fdfc7..856595184d4 100644 --- a/storage/badger/qcs.go +++ b/storage/badger/qcs.go @@ -2,6 +2,7 @@ package badger import ( "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/metrics" From 4f572a2111a33958f02aaa3a9af1c6e493ee9f77 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Tue, 25 Jul 2023 21:28:54 +0200 Subject: [PATCH 286/815] FVM flaky tests fix --- .gitignore | 1 + engine/execution/computation/computer/computer_test.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 472cc944ee4..1be2e18a99f 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ go.work.sum # Ledger checkpoint status files **/checkpoint_status.json +**/export_report.json # Local testing result files tps-results*.json diff --git a/engine/execution/computation/computer/computer_test.go b/engine/execution/computation/computer/computer_test.go index 4428f06465f..3f025fa3646 100644 --- a/engine/execution/computation/computer/computer_test.go +++ b/engine/execution/computation/computer/computer_test.go @@ -1261,6 +1261,8 @@ func Test_ExecutingSystemCollection(t *testing.T) { me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). Return(nil, nil) + constRandomSource := make([]byte, 32) + exe, err := computer.NewBlockComputer( vm, execCtx, @@ -1271,7 +1273,7 @@ func Test_ExecutingSystemCollection(t *testing.T) { me, prov, nil, - testutil.ProtocolStateWithSourceFixture(nil), + testutil.ProtocolStateWithSourceFixture(constRandomSource), testMaxConcurrency) require.NoError(t, err) From 731a26c6d47a1c5d79341b25f4b09c094ee06db2 Mon Sep 17 00:00:00 2001 From: Andrii Slisarchuk Date: Fri, 28 Jul 2023 01:30:45 +0300 Subject: [PATCH 287/815] Fixed remarks --- .../export_report.json | 6 +- engine/access/rpc/backend/backend_scripts.go | 4 +- .../access/rpc/connection/connection_test.go | 70 +++---------------- integration/testnet/client.go | 24 +++---- 4 files changed, 27 insertions(+), 77 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index bf70c636e61..f33cbf40cb9 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "524d1b0bb8c8826b42298a04c327b34f113628c617cf396e4fcd1405bdf7c70f", - "CurrentStateCommitment": "524d1b0bb8c8826b42298a04c327b34f113628c617cf396e4fcd1405bdf7c70f", + "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", "ReportSucceeded": true -} \ No newline at end of file +} diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 3651450b472..62d32c56211 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -104,8 +104,8 @@ func (b *backendScripts) executeScriptOnExecutor( // *DO NOT* use this hash for any protocol-related or cryptographic functions. insecureScriptHash := md5.Sum(script) //nolint:gosec - // try execution on Archive nodes if there is no execution nodes found - if len(executors) == 0 && len(b.archiveAddressList) > 0 { + // try execution on Archive nodes + if len(b.archiveAddressList) > 0 { startTime := time.Now() for idx, rnAddr := range b.archiveAddressList { rnPort := b.archivePorts[idx] diff --git a/engine/access/rpc/connection/connection_test.go b/engine/access/rpc/connection/connection_test.go index cbfea58c3d6..a961816605e 100644 --- a/engine/access/rpc/connection/connection_test.go +++ b/engine/access/rpc/connection/connection_test.go @@ -52,12 +52,7 @@ func TestProxyAccessAPI(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) proxyConnectionFactory := ProxyConnectionFactory{ @@ -99,12 +94,7 @@ func TestProxyExecutionAPI(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) proxyConnectionFactory := ProxyConnectionFactory{ @@ -151,12 +141,7 @@ func TestProxyAccessAPIConnectionReuse(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) proxyConnectionFactory := ProxyConnectionFactory{ @@ -210,12 +195,7 @@ func TestProxyExecutionAPIConnectionReuse(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) proxyConnectionFactory := ProxyConnectionFactory{ @@ -276,12 +256,7 @@ func TestExecutionNodeClientTimeout(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) // create the execution API client @@ -330,12 +305,7 @@ func TestCollectionNodeClientTimeout(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) // create the collection API client @@ -384,12 +354,7 @@ func TestConnectionPoolFull(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) cn1Address := "foo1:123" @@ -465,12 +430,7 @@ func TestConnectionPoolStale(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) proxyConnectionFactory := ProxyConnectionFactory{ @@ -559,12 +519,7 @@ func TestExecutionNodeClientClosedGracefully(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) clientAddress := en.listener.Addr().String() @@ -648,12 +603,7 @@ func TestExecutionEvictingCacheClients(t *testing.T) { unittest.Logger(), connectionFactory.AccessMetrics, 0, - CircuitBreakerConfig{ - Enabled: false, - MaxFailures: 0, - MaxRequests: 0, - RestoreTimeout: 0, - }, + CircuitBreakerConfig{}, ) clientAddress := cn.listener.Addr().String() diff --git a/integration/testnet/client.go b/integration/testnet/client.go index 941c83a8fbd..51026702085 100644 --- a/integration/testnet/client.go +++ b/integration/testnet/client.go @@ -79,18 +79,18 @@ func NewClient(addr string, chain flow.Chain) (*Client, error) { } // Uncomment for debugging keys - json, err := key.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("cannot marshal key json: %w", err) - } - public := key.PublicKey(1000) - publicJson, err := public.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("cannot marshal key json: %w", err) - } - - fmt.Printf("New client with private key: \n%s\n", json) - fmt.Printf("and public key: \n%s\n", publicJson) + //json, err := key.MarshalJSON() + //if err != nil { + // return nil, fmt.Errorf("cannot marshal key json: %w", err) + //} + //public := key.PublicKey(1000) + //publicJson, err := public.MarshalJSON() + //if err != nil { + // return nil, fmt.Errorf("cannot marshal key json: %w", err) + //} + // + //fmt.Printf("New client with private key: \n%s\n", json) + //fmt.Printf("and public key: \n%s\n", publicJson) return NewClientWithKey(addr, sdk.Address(chain.ServiceAddress()), privateKey, chain) } From e0527bc1d8f9cd08167a5bd5a8fbb76f9409c718 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Tue, 11 Jul 2023 17:36:03 +0200 Subject: [PATCH 288/815] add workflow to validate git tags --- .github/workflows/semver-tags.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/semver-tags.yaml diff --git a/.github/workflows/semver-tags.yaml b/.github/workflows/semver-tags.yaml new file mode 100644 index 00000000000..41ebe625744 --- /dev/null +++ b/.github/workflows/semver-tags.yaml @@ -0,0 +1,22 @@ +name: Verify Tag + +on: + push: + tags: + - '*' + +jobs: + SemVer-Check: + runs-on: ubuntu-latest + steps: + - name: Check if tag is SemVer compliant + # the tag should be in semver format, but can optionally be prepended by "any_text_with_slashes/" and "v" + # valid examples crypto/v0.24.5-fvm, tools/flaky_test_monitor/v0.23.5, v0.23.5, 0.23.5-fvm + run: | + TAG_NAME=${GITHUB_REF#refs/tags/} + if [[ "${TAG_NAME}" =~ ^(.+\/)*v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]]; then + echo "Tag $TAG_NAME is SemVer compliant" + else + echo "Tag $TAG_NAME is not SemVer compliant" + exit 1 + fi From 9d1c0665809f4831f8f573116565d102ba9a2d6a Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Tue, 11 Jul 2023 20:05:33 +0200 Subject: [PATCH 289/815] make the node fail to start on invalid version --- .github/workflows/ci.yml | 5 ++++- cmd/bootstrap/transit/cmd/utils.go | 2 +- cmd/build/version.go | 21 +++++++++++-------- cmd/build/version_test.go | 2 +- cmd/execution_builder.go | 17 ++++++++------- cmd/scaffold.go | 8 +++---- .../export_report.json | 6 ------ engine/access/access_test.go | 2 +- .../rest/routes/node_version_info_test.go | 2 +- engine/access/rpc/backend/backend.go | 2 +- engine/access/rpc/backend/backend_network.go | 2 +- engine/testutil/nodes.go | 6 ++++-- integration/localnet/builder/bootstrap.go | 2 +- 13 files changed, 41 insertions(+), 36 deletions(-) delete mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26d14496fb5..e41e747e0e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ on: env: GO_VERSION: "1.20" -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true @@ -203,6 +203,9 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + with: + # all tags are needed for integration tests + fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v3 with: diff --git a/cmd/bootstrap/transit/cmd/utils.go b/cmd/bootstrap/transit/cmd/utils.go index 380646671c0..1f8f2f7920b 100644 --- a/cmd/bootstrap/transit/cmd/utils.go +++ b/cmd/bootstrap/transit/cmd/utils.go @@ -36,7 +36,7 @@ var ( // commit and semver vars commit = build.Commit() - semver = build.Semver() + semver = build.Version() ) // readNodeID reads the NodeID file diff --git a/cmd/build/version.go b/cmd/build/version.go index 238385846fc..d089c27f3fb 100644 --- a/cmd/build/version.go +++ b/cmd/build/version.go @@ -7,6 +7,7 @@ package build import ( + "fmt" "strings" smv "github.com/coreos/go-semver/semver" @@ -21,8 +22,8 @@ var ( commit string ) -// Semver returns the semantic version of this build. -func Semver() string { +// Version returns the raw version string of this build. +func Version() string { return semver } @@ -48,24 +49,26 @@ func init() { } } -// SemverV2 returns the semantic version of this build as a semver.Version -// if it is defined, or nil otherwise. +var UndefinedVersionError = fmt.Errorf("version is undefined") + +// Semver returns the semantic version of this build as a semver.Version +// if it is defined, or UndefinedVersionError otherwise. // The version string is converted to a semver compliant one if it isn't already // but this might fail if the version string is still not semver compliant. In that // case, an error is returned. -func SemverV2() (*smv.Version, error) { +func Semver() (*smv.Version, error) { if !IsDefined(semver) { - return nil, nil + return nil, UndefinedVersionError } - ver, err := smv.NewVersion(makeSemverV2Compliant(semver)) + ver, err := smv.NewVersion(makeSemverCompliant(semver)) return ver, err } -// makeSemverV2Compliant converts a non-semver version string to a semver compliant one. +// makeSemverCompliant converts a non-semver version string to a semver compliant one. // This removes the leading 'v'. // In the past we sometimes omitted the patch version, e.g. v1.0.0 became v1.0 so this // also adds a 0 patch version if there's no patch version. -func makeSemverV2Compliant(version string) string { +func makeSemverCompliant(version string) string { if !IsDefined(version) { return version } diff --git a/cmd/build/version_test.go b/cmd/build/version_test.go index 3e130cadde8..4f3232b56d2 100644 --- a/cmd/build/version_test.go +++ b/cmd/build/version_test.go @@ -17,7 +17,7 @@ func TestMakeSemverV2Compliant(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - output := makeSemverV2Compliant(tc.input) + output := makeSemverCompliant(tc.input) if output != tc.expected { t.Errorf("Got %s; expected %s", output, tc.expected) } diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 90186b5aa62..871ab90b8f6 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -666,15 +666,18 @@ func (exeNode *ExecutionNode) LoadStopControl( module.ReadyDoneAware, error, ) { - ver, err := build.SemverV2() + ver, err := build.Semver() if err != nil { - ver = nil - // TODO: In the future we want to error here, but for now we just log a warning. - // This is because we currently have no strong guarantee that then node version - // tag is semver compliant. - exeNode.builder.Logger.Warn(). + err = fmt.Errorf("could not set semver version for stop control. "+ + "version %s is not semver compliant: %w", build.Version(), err) + + // The node would not know its own version. Without this the node would not know + // how to reach to version boundaries. + exeNode.builder.Logger. Err(err). - Msg("could not set semver version for stop control") + Msg("error starting stop control") + + return nil, err } latestFinalizedBlock, err := node.State.Final().Head() diff --git a/cmd/scaffold.go b/cmd/scaffold.go index e6043fd1801..07e2d63011e 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -202,7 +202,7 @@ func (fnb *FlowNodeBuilder) EnqueuePingService() { // setup the Ping provider to return the software version and the sealed block height pingInfoProvider := &ping.InfoProvider{ SoftwareVersionFun: func() string { - return build.Semver() + return build.Version() }, SealedBlockHeightFun: func() (uint64, error) { head, err := node.State.Sealed().Head() @@ -610,7 +610,7 @@ func (fnb *FlowNodeBuilder) ValidateFlags(f func() error) NodeBuilder { } func (fnb *FlowNodeBuilder) PrintBuildVersionDetails() { - fnb.Logger.Info().Str("version", build.Semver()).Str("commit", build.Commit()).Msg("build details") + fnb.Logger.Info().Str("version", build.Version()).Str("commit", build.Commit()).Msg("build details") } func (fnb *FlowNodeBuilder) initNodeInfo() error { @@ -733,7 +733,7 @@ func (fnb *FlowNodeBuilder) initMetrics() error { if err != nil { return fmt.Errorf("could not query root snapshoot protocol version: %w", err) } - nodeInfoMetrics.NodeInfo(build.Semver(), build.Commit(), nodeConfig.SporkID.String(), protocolVersion) + nodeInfoMetrics.NodeInfo(build.Version(), build.Commit(), nodeConfig.SporkID.String(), protocolVersion) return nil }) } @@ -761,7 +761,7 @@ func (fnb *FlowNodeBuilder) createGCEProfileUploader(client *gcemd.Client, opts ProjectID: projectID, ChainID: chainID, Role: fnb.NodeConfig.NodeRole, - Version: build.Semver(), + Version: build.Version(), Commit: build.Commit(), Instance: instance, } diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json deleted file mode 100644 index f33cbf40cb9..00000000000 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "EpochCounter": 0, - "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", - "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", - "ReportSucceeded": true -} diff --git a/engine/access/access_test.go b/engine/access/access_test.go index b6d4051e9ba..b3979979fb9 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -1142,7 +1142,7 @@ func (suite *Suite) TestAPICallNodeVersionInfo() { respNodeVersionInfo := resp.Info suite.Require().Equal(respNodeVersionInfo, &entitiesproto.NodeVersionInfo{ - Semver: build.Semver(), + Semver: build.Version(), Commit: build.Commit(), SporkId: sporkId[:], ProtocolVersion: uint64(protocolVersion), diff --git a/engine/access/rest/routes/node_version_info_test.go b/engine/access/rest/routes/node_version_info_test.go index 25f19ae1f3c..179d339f94f 100644 --- a/engine/access/rest/routes/node_version_info_test.go +++ b/engine/access/rest/routes/node_version_info_test.go @@ -29,7 +29,7 @@ func TestGetNodeVersionInfo(t *testing.T) { req := getNodeVersionInfoRequest(t) params := &access.NodeVersionInfo{ - Semver: build.Semver(), + Semver: build.Version(), Commit: build.Commit(), SporkId: unittest.IdentifierFixture(), ProtocolVersion: unittest.Uint64InRange(10, 30), diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index b6720242717..9b10cc5b539 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -303,7 +303,7 @@ func (b *Backend) GetNodeVersionInfo(ctx context.Context) (*access.NodeVersionIn } return &access.NodeVersionInfo{ - Semver: build.Semver(), + Semver: build.Version(), Commit: build.Commit(), SporkId: sporkId, ProtocolVersion: uint64(protocolVersion), diff --git a/engine/access/rpc/backend/backend_network.go b/engine/access/rpc/backend/backend_network.go index d88c36db070..577ebaa8a84 100644 --- a/engine/access/rpc/backend/backend_network.go +++ b/engine/access/rpc/backend/backend_network.go @@ -57,7 +57,7 @@ func (b *backendNetwork) GetNodeVersionInfo(ctx context.Context) (*access.NodeVe } return &access.NodeVersionInfo{ - Semver: build.Semver(), + Semver: build.Version(), Commit: build.Commit(), SporkId: sporkId, ProtocolVersion: uint64(protocolVersion), diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index fc5df47f25d..8639e1ee1f7 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/coreos/go-semver/semver" "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" @@ -687,8 +688,9 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity *flow.Identity, identit // disabled by default uploader := uploader.NewManager(node.Tracer) - ver, err := build.SemverV2() - require.NoError(t, err, "failed to parse semver version from build info") + _, err = build.Semver() + require.ErrorIs(t, err, build.UndefinedVersionError) + ver := semver.New("0.0.0") latestFinalizedBlock, err := node.State.Final().Head() require.NoError(t, err) diff --git a/integration/localnet/builder/bootstrap.go b/integration/localnet/builder/bootstrap.go index dc54dba8cdb..2a42963461f 100644 --- a/integration/localnet/builder/bootstrap.go +++ b/integration/localnet/builder/bootstrap.go @@ -518,7 +518,7 @@ func defaultService(name, role, dataDir, profilerDir string, i int) Service { Dockerfile: "cmd/Dockerfile", Args: map[string]string{ "TARGET": fmt.Sprintf("./cmd/%s", role), - "VERSION": build.Semver(), + "VERSION": build.Version(), "COMMIT": build.Commit(), "GOARCH": runtime.GOARCH, }, From b5972880f2122dc4d858d90efc1a0d4337093fed Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 5 Jun 2023 15:30:49 +0200 Subject: [PATCH 290/815] Added size to a list struct, removed some isUndefined() and setUndefined(), tests pool_test and cache_test pass-a --- .../herocache/backdata/heropool/linkedlist.go | 10 +++ .../herocache/backdata/heropool/pool.go | 70 +++++++++++++++---- .../herocache/backdata/heropool/pool_test.go | 4 ++ 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 4c72931c630..27d603201bf 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -3,12 +3,22 @@ package heropool // link represents a slice-based doubly linked-list node that // consists of a next and previous poolIndex. type link struct { + // As we dont have from now on an invalid index we need either to make next/prev point to itself + // in order to show that its invalid + // Other option is to say that if a link is a tail or head then respectively its prev or next should not be used + // if tail and head concide then neither of them is to be used + // Both solutions defacto would reintroduce isDefined, we would need to check if next != current Index + // of if this is head or tail ... of one of the lists. For this reason I am not sure that this idea of removing 0 as undefined wont backfire + + // lets start with head and tail check next poolIndex prev poolIndex } // state represents a doubly linked-list by its head and tail pool indices. type state struct { + //those might now coincide rather than point to 0 head poolIndex tail poolIndex + size int } diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 92f926c2239..950d642def7 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,5 +1,13 @@ package heropool +//TODO +// 1. introduce size into linked list struct and make sure that it is correcly maintained +// by adding checks to existing unit tests. The are several options to do that , bt for now lets maintain size in basic interf method +// like Add, Remove, init +// 2. start replacing checks for undifined with checks for size , run tests +// 3. finally remove the consept of 0 index as being undefined null ptr index + +// TBH I woould decouple lists and poolEntity. import ( "github.com/rs/zerolog" @@ -48,7 +56,9 @@ func (p PoolEntity) Entity() flow.Entity { } type Pool struct { - logger zerolog.Logger + logger zerolog.Logger + // This size is a size of used linked list. As we will have it now as a part of + // linked list it can be removed size uint32 free state // keeps track of free slots. used state // keeps track of allocated slots to cachedEntities. @@ -61,10 +71,16 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log free: state{ head: poolIndex{index: 0}, tail: poolIndex{index: 0}, + // It's a bit redundant to have a size of free as it can be deduced by + // len(poolEntities) - used.size. However in task it is specified + // "Adding a size attribute to track the number of items in the linked list." + // As there are 2 linked list looks like they suggested to have a size per list. + size: 0, }, used: state{ head: poolIndex{index: 0}, tail: poolIndex{index: 0}, + size: 0, }, poolEntities: make([]poolEntity, sizeLimit), ejectionMode: ejectionMode, @@ -76,8 +92,16 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log return l } +// can be negative +func (p *Pool) modifyUsedBy(incrementBy int) { + p.used.size += incrementBy + p.free.size = len(p.poolEntities) - p.used.size +} + +// DONE size updated // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { + p.free.head.setPoolIndex(0) p.free.tail.setPoolIndex(0) @@ -87,6 +111,7 @@ func (p *Pool) initFreeEntities() { // and updates its tail p.free.tail.setPoolIndex(EIndex(i)) } + p.free.size = len(p.poolEntities) } // Add writes given entity into a poolEntity on the underlying entities linked-list. @@ -96,6 +121,8 @@ func (p *Pool) initFreeEntities() { // // If the pool has no available slots and an ejection is set, ejection occurs when adding a new entity. // If an ejection occurred, ejectedEntity holds the ejected entity. + +// done func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( entityIndex EIndex, slotAvailable bool, ejectedEntity flow.Entity) { entityIndex, slotAvailable, ejectedEntity = p.sliceIndexForEntity() @@ -103,24 +130,35 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner + // p.poolEntities[entityIndex].node.next.setUndefined() p.poolEntities[entityIndex].node.prev.setUndefined() - if p.used.head.isUndefined() { + if p.used.size == 0 { // used list is empty, hence setting head of used list to current entityIndex. p.used.head.setPoolIndex(entityIndex) - p.poolEntities[p.used.head.getSliceIndex()].node.prev.setUndefined() - } - - if !p.used.tail.isUndefined() { - // links new entity to the tail + // as size gonna be non zero tail has to point somewhere and it cant point to 0 anylonger as 0 now + // is legitim. Lets then make tail and head concide. + p.used.tail.setPoolIndex(entityIndex) + // we treat both as undefined prev and next if this node is tail and head so nothing to do + //p.poolEntities[p.used.head.getSliceIndex()].node.prev.setUndefined() + } else { + // if used is non empty then connect to its tail, we expect that eviction conserved valid list p.connect(p.used.tail, entityIndex) + // TODO will it work for corner cases like when capasity of pool is 1 or 2 etc ... + // since we are appending to the used list, entityIndex also acts as tail of the list. + p.used.tail.setPoolIndex(entityIndex) } - - // since we are appending to the used list, entityIndex also acts as tail of the list. - p.used.tail.setPoolIndex(entityIndex) - + /* + if !p.used.tail.isUndefined() { + // links new entity to the tail + p.connect(p.used.tail, entityIndex) + }*/ + + // not sure why here it is incremented as p.sliceIndexForEntity() couldve evict one element + // may be check for ejectedEntity ? p.size++ + p.modifyUsedBy(1) } return entityIndex, slotAvailable, ejectedEntity @@ -147,8 +185,8 @@ func (p *Pool) All() []PoolEntity { // Head returns the head of used items. Assuming no ejection happened and pool never goes beyond limit, Head returns // the first inserted element. -func (p *Pool) Head() (flow.Entity, bool) { - if p.used.head.isUndefined() { +func (p Pool) Head() (flow.Entity, bool) { + if p.used.size == 0 { return nil, false } e := p.poolEntities[p.used.head.getSliceIndex()] @@ -162,6 +200,7 @@ func (p *Pool) Head() (flow.Entity, bool) { // // Ejection happens if there is no available slot, and there is an ejection mode set. // If an ejection occurred, ejectedEntity holds the ejected entity. +// TODO update size here func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEntity flow.Entity) { lruEject := func() (EIndex, bool, flow.Entity) { // LRU ejection @@ -240,6 +279,7 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { // invalidateUsedHead moves current used head forward by one node. It // also removes the entity the invalidated head is presenting and appends the // node represented by the used head to the tail of the free list. +// TODO update size here func (p *Pool) invalidateUsedHead() flow.Entity { headSliceIndex := p.used.head.getSliceIndex() return p.invalidateEntityAtIndex(headSliceIndex) @@ -247,6 +287,7 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // claimFreeHead moves the free head forward, and returns the slice index of the // old free head to host a new entity. +// TODO update size here func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head.getSliceIndex() // moves head forward @@ -273,6 +314,7 @@ func (p *Pool) claimFreeHead() EIndex { } // Remove removes entity corresponding to given getSliceIndex from the list. +// TODO update size here func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { return p.invalidateEntityAtIndex(sliceIndex) } @@ -280,6 +322,7 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // invalidateEntityAtIndex invalidates the given getSliceIndex in the linked list by // removing its corresponding linked-list node from the used linked list, and appending // it to the tail of the free list. It also removes the entity that the invalidated node is presenting. +// TODO update size here func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] prev := poolEntity.node.prev @@ -329,6 +372,7 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // decrements Size p.size-- + p.modifyUsedBy(-1) return invalidatedEntity } diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 9b8b15bea3a..2d0d3c6f031 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -446,6 +446,10 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // adding each element must be successful. entityIndex, slotAvailable, ejectedEntity := pool.Add(e.ID(), e, uint64(i)) + if pool.Size() > 30 { + fmt.Println("may be opver limit debug") + } + if i < len(pool.poolEntities) { // in case of no over limit, size of entities linked list should be incremented by each addition. require.Equal(t, pool.Size(), uint32(i+1)) From 6bc29b821a71234033882ffbf217457d68916ea7 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 5 Jun 2023 17:14:33 +0200 Subject: [PATCH 291/815] Removed some more checks for undefined --- .../herocache/backdata/heropool/pool.go | 67 ++++++++++++++++--- .../herocache/backdata/heropool/pool_test.go | 44 ++++++------ 2 files changed, 80 insertions(+), 31 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 950d642def7..233a65e58e7 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -9,6 +9,9 @@ package heropool // TBH I woould decouple lists and poolEntity. import ( + "fmt" + "math/rand" + "github.com/rs/zerolog" "github.com/onflow/flow-go/model/flow" @@ -150,10 +153,10 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.used.tail.setPoolIndex(entityIndex) } /* - if !p.used.tail.isUndefined() { - // links new entity to the tail - p.connect(p.used.tail, entityIndex) - }*/ + if !p.used.tail.isUndefined() { + // links new entity to the tail + p.connect(p.used.tail, entityIndex) + }*/ // not sure why here it is incremented as p.sliceIndexForEntity() couldve evict one element // may be check for ejectedEntity ? @@ -245,11 +248,12 @@ func (p *Pool) Size() uint32 { // getHeads returns entities corresponding to the used and free heads. func (p *Pool) getHeads() (*poolEntity, *poolEntity) { var usedHead, freeHead *poolEntity - if !p.used.head.isUndefined() { + + if p.used.size != 0 { usedHead = &p.poolEntities[p.used.head.getSliceIndex()] } - if !p.free.head.isUndefined() { + if p.free.size != 0 { freeHead = &p.poolEntities[p.free.head.getSliceIndex()] } @@ -259,11 +263,11 @@ func (p *Pool) getHeads() (*poolEntity, *poolEntity) { // getTails returns entities corresponding to the used and free tails. func (p *Pool) getTails() (*poolEntity, *poolEntity) { var usedTail, freeTail *poolEntity - if !p.used.tail.isUndefined() { + if p.used.size != 0 { usedTail = &p.poolEntities[p.used.tail.getSliceIndex()] } - if !p.free.tail.isUndefined() { + if p.free.size != 0 { freeTail = &p.poolEntities[p.free.tail.getSliceIndex()] } @@ -288,14 +292,14 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // claimFreeHead moves the free head forward, and returns the slice index of the // old free head to host a new entity. // TODO update size here -func (p *Pool) claimFreeHead() EIndex { +func (p *Pool) claimFreeHead2() EIndex { oldFreeHeadIndex := p.free.head.getSliceIndex() // moves head forward p.free.head = p.poolEntities[oldFreeHeadIndex].node.next // new head should point to an undefined prev, // but we first check if list is not empty, i.e., // head itself is not undefined. - if !p.free.head.isUndefined() { + if p.free.size != 0 { p.poolEntities[p.free.head.getSliceIndex()].node.prev.setUndefined() } @@ -312,6 +316,45 @@ func (p *Pool) claimFreeHead() EIndex { return oldFreeHeadIndex } +func (p *Pool) claimFreeHead() EIndex { + oldFreeHeadIndex := p.free.head.getSliceIndex() + // moves head forward + + if p.free.size == 0 { + fmt.Println("debug shouldnt happen") + } + + if p.free.size == 1 { + p.free.size = 0 + } + + if p.free.size > 1 { + p.free.head = p.poolEntities[oldFreeHeadIndex].node.next + + } + + // new head should point to an undefined prev, + // but we first check if list is not empty, i.e., + // head itself is not undefined. + // irrelevant a heads prev is undefined by convention + //if p.free.size != 0 { + // p.poolEntities[p.free.head.getSliceIndex()].node.prev.setUndefined() + //} + + // also, we check if the old head and tail are aligned and, if so, update the + // tail as well. This happens when we claim the only existing + // node of the free list. + //same not needed as size set to 0 + //if p.free.tail.getSliceIndex() == oldFreeHeadIndex { + // p.free.tail.setUndefined() + //} + + // clears pointers of claimed head + //p.poolEntities[oldFreeHeadIndex].node.next.setUndefined() + //p.poolEntities[oldFreeHeadIndex].node.prev.setUndefined() + + return oldFreeHeadIndex +} // Remove removes entity corresponding to given getSliceIndex from the list. // TODO update size here @@ -379,7 +422,7 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // appendToFreeList appends linked-list node represented by getSliceIndex to tail of free list. func (p *Pool) appendToFreeList(sliceIndex EIndex) { - if p.free.head.isUndefined() { + if p.free.size == 0 { // free list is empty p.free.head.setPoolIndex(sliceIndex) p.free.tail.setPoolIndex(sliceIndex) @@ -389,6 +432,8 @@ func (p *Pool) appendToFreeList(sliceIndex EIndex) { // appends to the tail, and updates the tail p.connect(p.free.tail, sliceIndex) p.free.tail.setPoolIndex(sliceIndex) + // it's gonna be reupdated but its a good practice to maintain size in sync + p.free.size++ } // isInvalidated returns true if linked-list node represented by getSliceIndex does not contain diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 2d0d3c6f031..f80296b2367 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -299,8 +299,8 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // pointer indices must be undefined. require.Nil(t, usedHead) require.Nil(t, usedTail) - require.True(t, pool.used.tail.isUndefined()) - require.True(t, pool.used.head.isUndefined()) + require.True(t, pool.used.size == 0) + //require.True(t, pool.used.head.isUndefined()) } } } @@ -385,8 +385,8 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // pointer indices must be undefined. require.Nil(t, usedHead) require.Nil(t, usedTail) - require.True(t, pool.used.tail.isUndefined()) - require.True(t, pool.used.head.isUndefined()) + require.True(t, pool.used.size == 0) + //require.True(t, pool.used.head.isUndefined()) } } } @@ -394,15 +394,17 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // testInitialization evaluates the state of an initialized pool before adding any element to it. func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { // head and tail of "used" linked-list must be undefined at initialization time, since we have no elements in the list. - require.True(t, pool.used.head.isUndefined()) - require.True(t, pool.used.tail.isUndefined()) + require.True(t, pool.used.size == 0) + //require.True(t, pool.used.tail.isUndefined()) for i := 0; i < len(pool.poolEntities); i++ { if i == 0 { // head of "free" linked-list should point to index 0 of entities slice. require.Equal(t, EIndex(i), pool.free.head.getSliceIndex()) // previous element of head must be undefined (linked-list head feature). - require.True(t, pool.poolEntities[i].node.prev.isUndefined()) + + // TODO this is more tricky to change , leaving it for later. + //require.True(t, pool.poolEntities[i].node.prev.isUndefined()) } if i != 0 { @@ -419,7 +421,8 @@ func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { // tail of "free" linked-list should point to the last index in entities slice. require.Equal(t, EIndex(i), pool.free.tail.getSliceIndex()) // next element of tail must be undefined. - require.True(t, pool.poolEntities[i].node.next.isUndefined()) + //TODO + //require.True(t, pool.poolEntities[i].node.next.isUndefined()) } } } @@ -446,10 +449,6 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // adding each element must be successful. entityIndex, slotAvailable, ejectedEntity := pool.Add(e.ID(), e, uint64(i)) - if pool.Size() > 30 { - fmt.Println("may be opver limit debug") - } - if i < len(pool.poolEntities) { // in case of no over limit, size of entities linked list should be incremented by each addition. require.Equal(t, pool.Size(), uint32(i+1)) @@ -519,21 +518,24 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. } require.Equal(t, pool.poolEntities[expectedUsedHead].entity, usedHead.entity) // head must be healthy and point back to undefined. - require.True(t, usedHead.node.prev.isUndefined()) + // This is not needed anymore as head's prev is now ignored + //require.True(t, usedHead.node.prev.isUndefined()) } if ejectionMode != NoEjection || i < len(pool.poolEntities) { // new entity must be successfully added to tail of used linked-list require.Equal(t, entitiesToBeAdded[i], usedTail.entity) // used tail must be healthy and point back to undefined. - require.True(t, usedTail.node.next.isUndefined()) + // This is not needed anymore as tail's next is now ignored + //require.True(t, usedTail.node.next.isUndefined()) } if ejectionMode == NoEjection && i >= len(pool.poolEntities) { // used tail must not move require.Equal(t, entitiesToBeAdded[len(pool.poolEntities)-1], usedTail.entity) // used tail must be healthy and point back to undefined. - require.True(t, usedTail.node.next.isUndefined()) + // This is not needed anymore as tail's next is now ignored + //require.True(t, usedTail.node.next.isUndefined()) } // free head @@ -542,7 +544,8 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // should move to i+1 element. require.Equal(t, EIndex(i+1), pool.free.head.getSliceIndex()) // head must be healthy and point back to undefined. - require.True(t, freeHead.node.prev.isUndefined()) + // This is not needed anymore as head's prev is now ignored + //require.True(t, freeHead.node.prev.isUndefined()) } else { // once we go beyond limit, // we run out of free slots, @@ -558,7 +561,8 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // updated). require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.free.tail.getSliceIndex()) // head tail be healthy and point next to undefined. - require.True(t, freeTail.node.next.isUndefined()) + // This is not needed anymore as tail's next is now ignored + //require.True(t, freeTail.node.next.isUndefined()) } else { // once we go beyond limit, we run out of free slots, and // free tail must be kept at undefined. @@ -652,7 +656,7 @@ func withTestScenario(t *testing.T, pool := NewHeroPool(limit, ejectionMode, unittest.Logger()) // head on underlying linked-list value should be uninitialized - require.True(t, pool.used.head.isUndefined()) + require.True(t, pool.used.size == 0) require.Equal(t, pool.Size(), uint32(0)) entities := unittest.EntityListFixture(uint(entityCount)) @@ -676,8 +680,8 @@ func tailAccessibleFromHead(t *testing.T, headSliceIndex EIndex, tailSliceIndex require.NotEqual(t, tailSliceIndex, index, "tail visited in less expected steps (potential inconsistency)", i, steps) _, ok := seen[index] require.False(t, ok, "duplicate identifiers found") - - require.False(t, pool.poolEntities[index].node.next.isUndefined(), "tail not found, and reached end of list") + //TODO + //require.False(t, pool.poolEntities[index].node.next.isUndefined(), "tail not found, and reached end of list") index = pool.poolEntities[index].node.next.getSliceIndex() } } From b135031bf68521d8cc2e327d0f2ea197f8dad787 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 5 Jun 2023 18:00:41 +0200 Subject: [PATCH 292/815] Milestone: Removed isUndefined from code, tests do pass --- .../herocache/backdata/heropool/pool.go | 64 ++++++++++--------- .../herocache/backdata/heropool/poolIndex.go | 6 +- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 233a65e58e7..76b4d0ebcaf 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -292,30 +292,7 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // claimFreeHead moves the free head forward, and returns the slice index of the // old free head to host a new entity. // TODO update size here -func (p *Pool) claimFreeHead2() EIndex { - oldFreeHeadIndex := p.free.head.getSliceIndex() - // moves head forward - p.free.head = p.poolEntities[oldFreeHeadIndex].node.next - // new head should point to an undefined prev, - // but we first check if list is not empty, i.e., - // head itself is not undefined. - if p.free.size != 0 { - p.poolEntities[p.free.head.getSliceIndex()].node.prev.setUndefined() - } - - // also, we check if the old head and tail are aligned and, if so, update the - // tail as well. This happens when we claim the only existing - // node of the free list. - if p.free.tail.getSliceIndex() == oldFreeHeadIndex { - p.free.tail.setUndefined() - } - // clears pointers of claimed head - p.poolEntities[oldFreeHeadIndex].node.next.setUndefined() - p.poolEntities[oldFreeHeadIndex].node.prev.setUndefined() - - return oldFreeHeadIndex -} func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head.getSliceIndex() // moves head forward @@ -366,12 +343,34 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // removing its corresponding linked-list node from the used linked list, and appending // it to the tail of the free list. It also removes the entity that the invalidated node is presenting. // TODO update size here + func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] prev := poolEntity.node.prev next := poolEntity.node.next invalidatedEntity := poolEntity.entity + if p.used.size == 0 { + fmt.Println("Debug shouldnt happen") + //this function works only when called on nonempty ued list. would be nice to have + // panic like a debug assert later + return invalidatedEntity + + } + if p.used.size == 1 { + // decrements Size + //se could set here p.ued.head.prev and next to 0s but its not needed + p.poolEntities[sliceIndex].id = flow.ZeroID + p.poolEntities[sliceIndex].entity = nil + p.appendToFreeList(sliceIndex) + p.size-- + p.modifyUsedBy(-1) + + return invalidatedEntity + } + // here size > 1 + + // 3 cases index is middle, head or last of used if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { // links next and prev elements for non-head and non-tail element p.connect(prev, next.getSliceIndex()) @@ -385,10 +384,12 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // new head should point to an undefined prev, // but we first check if list is not empty, i.e., // head itself is not undefined. - if !p.used.head.isUndefined() { - usedHead, _ := p.getHeads() - usedHead.node.prev.setUndefined() - } + // if !p.used.head.isUndefined() + //not needed anymore + //if !p.used.head.isUndefined() { + // usedHead, _ := p.getHeads() + // usedHead.node.prev.setUndefined() + //} } if sliceIndex == p.used.tail.getSliceIndex() { @@ -399,10 +400,11 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // new head should point tail to an undefined next, // but we first check if list is not empty, i.e., // tail itself is not undefined. - if !p.used.tail.isUndefined() { - usedTail, _ := p.getTails() - usedTail.node.next.setUndefined() - } + //not needed + //if !p.used.tail.isUndefined() { + // usedTail, _ := p.getTails() + // usedTail.node.next.setUndefined() + //} } // invalidates entity and adds it to free entities. diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index 46d356faeea..bcb9815006a 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -13,9 +13,9 @@ type poolIndex struct { // isUndefined returns true if this poolIndex is set to zero. An undefined // poolIndex is equivalent to a nil address-based one. -func (p poolIndex) isUndefined() bool { - return p.index == uint32(0) -} +//func (p poolIndex) isUndefined() bool { +// return p.index == uint32(0) +//} // setUndefined sets poolIndex to its undefined (i.e., nil equivalent) value. func (p *poolIndex) setUndefined() { From 50ad01047db95390f50189a5fa130e0fda6fc27b Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 5 Jun 2023 18:05:52 +0200 Subject: [PATCH 293/815] Milestone: setUndifined is removed --- module/mempool/herocache/backdata/heropool/pool.go | 8 ++++---- module/mempool/herocache/backdata/heropool/poolIndex.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 76b4d0ebcaf..913408a0511 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -134,8 +134,8 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner // - p.poolEntities[entityIndex].node.next.setUndefined() - p.poolEntities[entityIndex].node.prev.setUndefined() + //p.poolEntities[entityIndex].node.next.setUndefined() + //p.poolEntities[entityIndex].node.prev.setUndefined() if p.used.size == 0 { // used list is empty, hence setting head of used list to current entityIndex. @@ -410,8 +410,8 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // invalidates entity and adds it to free entities. p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.poolEntities[sliceIndex].node.next.setUndefined() - p.poolEntities[sliceIndex].node.prev.setUndefined() + //p.poolEntities[sliceIndex].node.next.setUndefined() + //p.poolEntities[sliceIndex].node.prev.setUndefined() p.appendToFreeList(sliceIndex) diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index bcb9815006a..3f2cfb61d23 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -18,9 +18,9 @@ type poolIndex struct { //} // setUndefined sets poolIndex to its undefined (i.e., nil equivalent) value. -func (p *poolIndex) setUndefined() { - p.index = uint32(0) -} +//func (p *poolIndex) setUndefined() { +// p.index = uint32(0) +//} // getSliceIndex returns the slice-index equivalent of the poolIndex. func (p poolIndex) getSliceIndex() EIndex { From 0235526c87ba2ce56297db1c3ae5694a7e4bc2b9 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 5 Jun 2023 18:09:49 +0200 Subject: [PATCH 294/815] Removed +1 amd -1 shifts of index --- module/mempool/herocache/backdata/heropool/poolIndex.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index 3f2cfb61d23..b1eb0f81be5 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -24,11 +24,11 @@ type poolIndex struct { // getSliceIndex returns the slice-index equivalent of the poolIndex. func (p poolIndex) getSliceIndex() EIndex { - return EIndex(p.index) - 1 + return EIndex(p.index) } // setPoolIndex converts the input slice-based index into a pool index and // sets the underlying poolIndex. func (p *poolIndex) setPoolIndex(sliceIndex EIndex) { - p.index = uint32(sliceIndex + 1) + p.index = uint32(sliceIndex) } From 31533681243a315f83ee703c2e2a71f3ab45ac6c Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 13:40:03 +0200 Subject: [PATCH 295/815] Removed setPoolIndex --- .../herocache/backdata/heropool/pool.go | 21 +++++++++---------- .../herocache/backdata/heropool/poolIndex.go | 8 +++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 913408a0511..033e4e75774 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -105,14 +105,14 @@ func (p *Pool) modifyUsedBy(incrementBy int) { // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { - p.free.head.setPoolIndex(0) - p.free.tail.setPoolIndex(0) + p.free.head.index = EIndex(0) + p.free.tail.index = EIndex(0) for i := 1; i < len(p.poolEntities); i++ { // appends slice index i to tail of free linked list p.connect(p.free.tail, EIndex(i)) // and updates its tail - p.free.tail.setPoolIndex(EIndex(i)) + p.free.tail.index = EIndex(i) } p.free.size = len(p.poolEntities) } @@ -139,10 +139,10 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( if p.used.size == 0 { // used list is empty, hence setting head of used list to current entityIndex. - p.used.head.setPoolIndex(entityIndex) + p.used.head.index = entityIndex // as size gonna be non zero tail has to point somewhere and it cant point to 0 anylonger as 0 now // is legitim. Lets then make tail and head concide. - p.used.tail.setPoolIndex(entityIndex) + p.used.tail.index = entityIndex // we treat both as undefined prev and next if this node is tail and head so nothing to do //p.poolEntities[p.used.head.getSliceIndex()].node.prev.setUndefined() } else { @@ -150,7 +150,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.connect(p.used.tail, entityIndex) // TODO will it work for corner cases like when capasity of pool is 1 or 2 etc ... // since we are appending to the used list, entityIndex also acts as tail of the list. - p.used.tail.setPoolIndex(entityIndex) + p.used.tail.index = entityIndex } /* if !p.used.tail.isUndefined() { @@ -276,7 +276,7 @@ func (p *Pool) getTails() (*poolEntity, *poolEntity) { // connect links the prev and next nodes as the adjacent nodes in the double-linked list. func (p *Pool) connect(prev poolIndex, next EIndex) { - p.poolEntities[prev.getSliceIndex()].node.next.setPoolIndex(next) + p.poolEntities[prev.getSliceIndex()].node.next.index = next p.poolEntities[next].node.prev = prev } @@ -307,7 +307,6 @@ func (p *Pool) claimFreeHead() EIndex { if p.free.size > 1 { p.free.head = p.poolEntities[oldFreeHeadIndex].node.next - } // new head should point to an undefined prev, @@ -426,14 +425,14 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { func (p *Pool) appendToFreeList(sliceIndex EIndex) { if p.free.size == 0 { // free list is empty - p.free.head.setPoolIndex(sliceIndex) - p.free.tail.setPoolIndex(sliceIndex) + p.free.head.index = sliceIndex + p.free.tail.index = sliceIndex return } // appends to the tail, and updates the tail p.connect(p.free.tail, sliceIndex) - p.free.tail.setPoolIndex(sliceIndex) + p.free.tail.index = sliceIndex // it's gonna be reupdated but its a good practice to maintain size in sync p.free.size++ } diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index b1eb0f81be5..3e114eb7212 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -8,7 +8,7 @@ package heropool // poolIndex also furnished with methods to convert a "poolIndex" value to // a slice index, and vice versa. type poolIndex struct { - index uint32 + index EIndex } // isUndefined returns true if this poolIndex is set to zero. An undefined @@ -29,6 +29,6 @@ func (p poolIndex) getSliceIndex() EIndex { // setPoolIndex converts the input slice-based index into a pool index and // sets the underlying poolIndex. -func (p *poolIndex) setPoolIndex(sliceIndex EIndex) { - p.index = uint32(sliceIndex) -} +//func (p *poolIndex) setPoolIndex(sliceIndex EIndex) { +// p.index = uint32(sliceIndex) +//} From bc768cb80e2b362c02bd56b822cd54d7332b11d5 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 13:48:05 +0200 Subject: [PATCH 296/815] Cleaned up from superfluous comments --- .../herocache/backdata/heropool/pool.go | 75 +------------------ .../herocache/backdata/heropool/poolIndex.go | 6 -- 2 files changed, 1 insertion(+), 80 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 033e4e75774..1354e5c1cf5 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,16 +1,7 @@ package heropool -//TODO -// 1. introduce size into linked list struct and make sure that it is correcly maintained -// by adding checks to existing unit tests. The are several options to do that , bt for now lets maintain size in basic interf method -// like Add, Remove, init -// 2. start replacing checks for undifined with checks for size , run tests -// 3. finally remove the consept of 0 index as being undefined null ptr index - -// TBH I woould decouple lists and poolEntity. import ( "fmt" - "math/rand" "github.com/rs/zerolog" @@ -74,10 +65,6 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log free: state{ head: poolIndex{index: 0}, tail: poolIndex{index: 0}, - // It's a bit redundant to have a size of free as it can be deduced by - // len(poolEntities) - used.size. However in task it is specified - // "Adding a size attribute to track the number of items in the linked list." - // As there are 2 linked list looks like they suggested to have a size per list. size: 0, }, used: state{ @@ -101,7 +88,6 @@ func (p *Pool) modifyUsedBy(incrementBy int) { p.free.size = len(p.poolEntities) - p.used.size } -// DONE size updated // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { @@ -133,9 +119,6 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - // - //p.poolEntities[entityIndex].node.next.setUndefined() - //p.poolEntities[entityIndex].node.prev.setUndefined() if p.used.size == 0 { // used list is empty, hence setting head of used list to current entityIndex. @@ -152,11 +135,6 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( // since we are appending to the used list, entityIndex also acts as tail of the list. p.used.tail.index = entityIndex } - /* - if !p.used.tail.isUndefined() { - // links new entity to the tail - p.connect(p.used.tail, entityIndex) - }*/ // not sure why here it is incremented as p.sliceIndexForEntity() couldve evict one element // may be check for ejectedEntity ? @@ -203,7 +181,6 @@ func (p Pool) Head() (flow.Entity, bool) { // // Ejection happens if there is no available slot, and there is an ejection mode set. // If an ejection occurred, ejectedEntity holds the ejected entity. -// TODO update size here func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEntity flow.Entity) { lruEject := func() (EIndex, bool, flow.Entity) { // LRU ejection @@ -283,7 +260,6 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { // invalidateUsedHead moves current used head forward by one node. It // also removes the entity the invalidated head is presenting and appends the // node represented by the used head to the tail of the free list. -// TODO update size here func (p *Pool) invalidateUsedHead() flow.Entity { headSliceIndex := p.used.head.getSliceIndex() return p.invalidateEntityAtIndex(headSliceIndex) @@ -291,11 +267,8 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // claimFreeHead moves the free head forward, and returns the slice index of the // old free head to host a new entity. -// TODO update size here - func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head.getSliceIndex() - // moves head forward if p.free.size == 0 { fmt.Println("debug shouldnt happen") @@ -309,31 +282,10 @@ func (p *Pool) claimFreeHead() EIndex { p.free.head = p.poolEntities[oldFreeHeadIndex].node.next } - // new head should point to an undefined prev, - // but we first check if list is not empty, i.e., - // head itself is not undefined. - // irrelevant a heads prev is undefined by convention - //if p.free.size != 0 { - // p.poolEntities[p.free.head.getSliceIndex()].node.prev.setUndefined() - //} - - // also, we check if the old head and tail are aligned and, if so, update the - // tail as well. This happens when we claim the only existing - // node of the free list. - //same not needed as size set to 0 - //if p.free.tail.getSliceIndex() == oldFreeHeadIndex { - // p.free.tail.setUndefined() - //} - - // clears pointers of claimed head - //p.poolEntities[oldFreeHeadIndex].node.next.setUndefined() - //p.poolEntities[oldFreeHeadIndex].node.prev.setUndefined() - return oldFreeHeadIndex } // Remove removes entity corresponding to given getSliceIndex from the list. -// TODO update size here func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { return p.invalidateEntityAtIndex(sliceIndex) } @@ -341,8 +293,6 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // invalidateEntityAtIndex invalidates the given getSliceIndex in the linked list by // removing its corresponding linked-list node from the used linked list, and appending // it to the tail of the free list. It also removes the entity that the invalidated node is presenting. -// TODO update size here - func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] prev := poolEntity.node.prev @@ -367,9 +317,8 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { return invalidatedEntity } - // here size > 1 + // here size guaranteed > 1 - // 3 cases index is middle, head or last of used if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { // links next and prev elements for non-head and non-tail element p.connect(prev, next.getSliceIndex()) @@ -380,37 +329,15 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // moves head forward oldUsedHead, _ := p.getHeads() p.used.head = oldUsedHead.node.next - // new head should point to an undefined prev, - // but we first check if list is not empty, i.e., - // head itself is not undefined. - // if !p.used.head.isUndefined() - //not needed anymore - //if !p.used.head.isUndefined() { - // usedHead, _ := p.getHeads() - // usedHead.node.prev.setUndefined() - //} } if sliceIndex == p.used.tail.getSliceIndex() { - // invalidating used tail - // moves tail backward oldUsedTail, _ := p.getTails() p.used.tail = oldUsedTail.node.prev - // new head should point tail to an undefined next, - // but we first check if list is not empty, i.e., - // tail itself is not undefined. - //not needed - //if !p.used.tail.isUndefined() { - // usedTail, _ := p.getTails() - // usedTail.node.next.setUndefined() - //} } - // invalidates entity and adds it to free entities. p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - //p.poolEntities[sliceIndex].node.next.setUndefined() - //p.poolEntities[sliceIndex].node.prev.setUndefined() p.appendToFreeList(sliceIndex) diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index 3e114eb7212..1d2945705cd 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -26,9 +26,3 @@ type poolIndex struct { func (p poolIndex) getSliceIndex() EIndex { return EIndex(p.index) } - -// setPoolIndex converts the input slice-based index into a pool index and -// sets the underlying poolIndex. -//func (p *poolIndex) setPoolIndex(sliceIndex EIndex) { -// p.index = uint32(sliceIndex) -//} From 0d6628fa573f466421d1a2099649eb8f9f742276 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 14:30:42 +0200 Subject: [PATCH 297/815] Fixed tests --- .../herocache/backdata/heropool/pool_test.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index f80296b2367..224bc920e07 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -393,18 +393,14 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // testInitialization evaluates the state of an initialized pool before adding any element to it. func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { - // head and tail of "used" linked-list must be undefined at initialization time, since we have no elements in the list. + // "used" linked-list must have a zero size, since we have no elements in the list. require.True(t, pool.used.size == 0) - //require.True(t, pool.used.tail.isUndefined()) for i := 0; i < len(pool.poolEntities); i++ { if i == 0 { // head of "free" linked-list should point to index 0 of entities slice. + // previous element of head must is always undefined by convention and may hold any value. require.Equal(t, EIndex(i), pool.free.head.getSliceIndex()) - // previous element of head must be undefined (linked-list head feature). - - // TODO this is more tricky to change , leaving it for later. - //require.True(t, pool.poolEntities[i].node.prev.isUndefined()) } if i != 0 { @@ -419,10 +415,8 @@ func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { if i == len(pool.poolEntities)-1 { // tail of "free" linked-list should point to the last index in entities slice. + // next element of tail is always undefined by convention and may hold any value. require.Equal(t, EIndex(i), pool.free.tail.getSliceIndex()) - // next element of tail must be undefined. - //TODO - //require.True(t, pool.poolEntities[i].node.next.isUndefined()) } } } @@ -680,8 +674,6 @@ func tailAccessibleFromHead(t *testing.T, headSliceIndex EIndex, tailSliceIndex require.NotEqual(t, tailSliceIndex, index, "tail visited in less expected steps (potential inconsistency)", i, steps) _, ok := seen[index] require.False(t, ok, "duplicate identifiers found") - //TODO - //require.False(t, pool.poolEntities[index].node.next.isUndefined(), "tail not found, and reached end of list") index = pool.poolEntities[index].node.next.getSliceIndex() } } From 640a689011e5adfcd7d697e1b0a52cadf679c6a9 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 18:05:19 +0200 Subject: [PATCH 298/815] Refactored methods to lists --- .../herocache/backdata/heropool/linkedlist.go | 18 +++- .../herocache/backdata/heropool/pool.go | 97 ++++++------------- 2 files changed, 47 insertions(+), 68 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 27d603201bf..a57be659638 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -2,6 +2,8 @@ package heropool // link represents a slice-based doubly linked-list node that // consists of a next and previous poolIndex. +// if a link doesn't belong to any state it's next and prev may have any values, +// but those are treated as invalid and should not be used. type link struct { // As we dont have from now on an invalid index we need either to make next/prev point to itself // in order to show that its invalid @@ -16,9 +18,23 @@ type link struct { } // state represents a doubly linked-list by its head and tail pool indices. +// If satte has 0 size, its tail's and head's prev and next are treated as invalid. +// Moreover head's prev and tails next are always treated as invalid and may hold any values. type state struct { //those might now coincide rather than point to 0 head poolIndex tail poolIndex - size int + size uint32 +} + +// Adds element to the tail of the list or creates first element +func (s *state) addElement(p *Pool, element EIndex) { + if s.size == 0 { + s.head.index = element + s.tail.index = element + s.size = 1 + return + } + p.connect2(s.tail, element, s) + s.tail.index = element } diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 1354e5c1cf5..9c81266d91c 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -82,25 +82,11 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log return l } -// can be negative -func (p *Pool) modifyUsedBy(incrementBy int) { - p.used.size += incrementBy - p.free.size = len(p.poolEntities) - p.used.size -} - // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { - - p.free.head.index = EIndex(0) - p.free.tail.index = EIndex(0) - - for i := 1; i < len(p.poolEntities); i++ { - // appends slice index i to tail of free linked list - p.connect(p.free.tail, EIndex(i)) - // and updates its tail - p.free.tail.index = EIndex(i) + for i := 0; i < len(p.poolEntities); i++ { + p.free.addElement(p, EIndex(i)) } - p.free.size = len(p.poolEntities) } // Add writes given entity into a poolEntity on the underlying entities linked-list. @@ -119,27 +105,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - - if p.used.size == 0 { - // used list is empty, hence setting head of used list to current entityIndex. - p.used.head.index = entityIndex - // as size gonna be non zero tail has to point somewhere and it cant point to 0 anylonger as 0 now - // is legitim. Lets then make tail and head concide. - p.used.tail.index = entityIndex - // we treat both as undefined prev and next if this node is tail and head so nothing to do - //p.poolEntities[p.used.head.getSliceIndex()].node.prev.setUndefined() - } else { - // if used is non empty then connect to its tail, we expect that eviction conserved valid list - p.connect(p.used.tail, entityIndex) - // TODO will it work for corner cases like when capasity of pool is 1 or 2 etc ... - // since we are appending to the used list, entityIndex also acts as tail of the list. - p.used.tail.index = entityIndex - } - - // not sure why here it is incremented as p.sliceIndexForEntity() couldve evict one element - // may be check for ejectedEntity ? - p.size++ - p.modifyUsedBy(1) + p.used.addElement(p, entityIndex) } return entityIndex, slotAvailable, ejectedEntity @@ -151,11 +117,11 @@ func (p *Pool) Get(entityIndex EIndex) (flow.Identifier, flow.Entity, uint64) { } // All returns all stored entities in this pool. -func (p *Pool) All() []PoolEntity { - all := make([]PoolEntity, p.size) +func (p Pool) All() []PoolEntity { + all := make([]PoolEntity, p.used.size) next := p.used.head - for i := uint32(0); i < p.size; i++ { + for i := uint32(0); i < p.used.size; i++ { e := p.poolEntities[next.getSliceIndex()] all[i] = e.PoolEntity next = e.node.next @@ -218,8 +184,8 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn } // Size returns total number of entities that this list maintains. -func (p *Pool) Size() uint32 { - return p.size +func (p Pool) Size() uint32 { + return p.used.size } // getHeads returns entities corresponding to the used and free heads. @@ -257,6 +223,20 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { p.poolEntities[next].node.prev = prev } +// connect 2 move to state and call it add , enccapsulate together with size ==0 case +func (p *Pool) connect2(prev poolIndex, next EIndex, s *state) { + p.poolEntities[prev.getSliceIndex()].node.next.index = next + p.poolEntities[next].node.prev = prev + s.size++ +} + +// removes element +func (p *Pool) connect3(prev poolIndex, next EIndex, s *state) { + p.poolEntities[prev.getSliceIndex()].node.next.index = next + p.poolEntities[next].node.prev = prev + s.size-- +} + // invalidateUsedHead moves current used head forward by one node. It // also removes the entity the invalidated head is presenting and appends the // node represented by the used head to the tail of the free list. @@ -280,6 +260,7 @@ func (p *Pool) claimFreeHead() EIndex { if p.free.size > 1 { p.free.head = p.poolEntities[oldFreeHeadIndex].node.next + p.free.size = p.free.size - 1 } return oldFreeHeadIndex @@ -311,9 +292,8 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { //se could set here p.ued.head.prev and next to 0s but its not needed p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.appendToFreeList(sliceIndex) - p.size-- - p.modifyUsedBy(-1) + p.free.addElement(p, EIndex(sliceIndex)) + p.used.size-- return invalidatedEntity } @@ -321,7 +301,8 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { // links next and prev elements for non-head and non-tail element - p.connect(prev, next.getSliceIndex()) + p.connect3(prev, next.getSliceIndex(), &p.used) + //p.connect(prev, next.getSliceIndex()) } if sliceIndex == p.used.head.getSliceIndex() { @@ -329,41 +310,23 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { // moves head forward oldUsedHead, _ := p.getHeads() p.used.head = oldUsedHead.node.next + p.used.size-- } if sliceIndex == p.used.tail.getSliceIndex() { oldUsedTail, _ := p.getTails() p.used.tail = oldUsedTail.node.prev + p.used.size-- } p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.appendToFreeList(sliceIndex) - - // decrements Size - p.size-- - p.modifyUsedBy(-1) + p.free.addElement(p, EIndex(sliceIndex)) return invalidatedEntity } -// appendToFreeList appends linked-list node represented by getSliceIndex to tail of free list. -func (p *Pool) appendToFreeList(sliceIndex EIndex) { - if p.free.size == 0 { - // free list is empty - p.free.head.index = sliceIndex - p.free.tail.index = sliceIndex - return - } - - // appends to the tail, and updates the tail - p.connect(p.free.tail, sliceIndex) - p.free.tail.index = sliceIndex - // it's gonna be reupdated but its a good practice to maintain size in sync - p.free.size++ -} - // isInvalidated returns true if linked-list node represented by getSliceIndex does not contain // a valid entity. func (p *Pool) isInvalidated(sliceIndex EIndex) bool { From c532cfb6961569417350dc7dd7315f99c55fee64 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 18:24:17 +0200 Subject: [PATCH 299/815] Improve method names --- .../herocache/backdata/heropool/linkedlist.go | 11 ++-- .../herocache/backdata/heropool/pool.go | 56 ++++++++++++++++--- .../herocache/backdata/heropool/poolIndex.go | 16 ------ 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index a57be659638..b60ade3e555 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -28,13 +28,14 @@ type state struct { } // Adds element to the tail of the list or creates first element -func (s *state) addElement(p *Pool, element EIndex) { +func (s *state) addElement(p *Pool, entity EIndex) { if s.size == 0 { - s.head.index = element - s.tail.index = element + s.head.index = entity + s.tail.index = entity s.size = 1 return } - p.connect2(s.tail, element, s) - s.tail.index = element + p.connect(s.tail, entity) + s.size++ + s.tail.index = entity } diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 9c81266d91c..d6f8329b76e 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -223,13 +223,6 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { p.poolEntities[next].node.prev = prev } -// connect 2 move to state and call it add , enccapsulate together with size ==0 case -func (p *Pool) connect2(prev poolIndex, next EIndex, s *state) { - p.poolEntities[prev.getSliceIndex()].node.next.index = next - p.poolEntities[next].node.prev = prev - s.size++ -} - // removes element func (p *Pool) connect3(prev poolIndex, next EIndex, s *state) { p.poolEntities[prev.getSliceIndex()].node.next.index = next @@ -280,6 +273,55 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { next := poolEntity.node.next invalidatedEntity := poolEntity.entity + if p.used.size == 0 { + panic("Removing entity from an empty list") + } + + if p.used.size == 1 { + // decrements Size + //se could set here p.ued.head.prev and next to 0s but its not needed + p.poolEntities[sliceIndex].id = flow.ZeroID + p.poolEntities[sliceIndex].entity = nil + p.free.addElement(p, EIndex(sliceIndex)) + p.used.size-- + + return invalidatedEntity + } + // here size guaranteed > 1 + + if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { + // links next and prev elements for non-head and non-tail element + p.connect3(prev, next.getSliceIndex(), &p.used) + //p.connect(prev, next.getSliceIndex()) + } + + if sliceIndex == p.used.head.getSliceIndex() { + // invalidating used head + // moves head forward + oldUsedHead, _ := p.getHeads() + p.used.head = oldUsedHead.node.next + p.used.size-- + } + + if sliceIndex == p.used.tail.getSliceIndex() { + oldUsedTail, _ := p.getTails() + p.used.tail = oldUsedTail.node.prev + p.used.size-- + } + + p.poolEntities[sliceIndex].id = flow.ZeroID + p.poolEntities[sliceIndex].entity = nil + + p.free.addElement(p, EIndex(sliceIndex)) + + return invalidatedEntity +} +func (p *Pool) invalidateEntityAtIndex2(sliceIndex EIndex) flow.Entity { + poolEntity := p.poolEntities[sliceIndex] + prev := poolEntity.node.prev + next := poolEntity.node.next + invalidatedEntity := poolEntity.entity + if p.used.size == 0 { fmt.Println("Debug shouldnt happen") //this function works only when called on nonempty ued list. would be nice to have diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go index 1d2945705cd..f425bf2a51e 100644 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ b/module/mempool/herocache/backdata/heropool/poolIndex.go @@ -2,26 +2,10 @@ package heropool // poolIndex represents a slice-based linked list pointer. Instead of pointing // to a memory address, this pointer points to a slice index. -// -// Note: an "undefined" (i.e., nil) notion for this poolIndex corresponds to the -// value of uint32(0). Hence, legit "index" poolEntities start from uint32(1). -// poolIndex also furnished with methods to convert a "poolIndex" value to -// a slice index, and vice versa. type poolIndex struct { index EIndex } -// isUndefined returns true if this poolIndex is set to zero. An undefined -// poolIndex is equivalent to a nil address-based one. -//func (p poolIndex) isUndefined() bool { -// return p.index == uint32(0) -//} - -// setUndefined sets poolIndex to its undefined (i.e., nil equivalent) value. -//func (p *poolIndex) setUndefined() { -// p.index = uint32(0) -//} - // getSliceIndex returns the slice-index equivalent of the poolIndex. func (p poolIndex) getSliceIndex() EIndex { return EIndex(p.index) From 286c00d4efc6b9242bbabd3a69565bd5a1af4fcc Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 18:25:25 +0200 Subject: [PATCH 300/815] Improve method names and saved --- .../herocache/backdata/heropool/linkedlist.go | 2 +- module/mempool/herocache/backdata/heropool/pool.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index b60ade3e555..cccafe543cb 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -28,7 +28,7 @@ type state struct { } // Adds element to the tail of the list or creates first element -func (s *state) addElement(p *Pool, entity EIndex) { +func (s *state) appendEntity(p *Pool, entity EIndex) { if s.size == 0 { s.head.index = entity s.tail.index = entity diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index d6f8329b76e..71995c411b2 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -85,7 +85,7 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { for i := 0; i < len(p.poolEntities); i++ { - p.free.addElement(p, EIndex(i)) + p.free.appendEntity(p, EIndex(i)) } } @@ -105,7 +105,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - p.used.addElement(p, entityIndex) + p.used.appendEntity(p, entityIndex) } return entityIndex, slotAvailable, ejectedEntity @@ -282,7 +282,7 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { //se could set here p.ued.head.prev and next to 0s but its not needed p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.free.addElement(p, EIndex(sliceIndex)) + p.free.appendEntity(p, EIndex(sliceIndex)) p.used.size-- return invalidatedEntity @@ -312,7 +312,7 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.free.addElement(p, EIndex(sliceIndex)) + p.free.appendEntity(p, EIndex(sliceIndex)) return invalidatedEntity } @@ -334,7 +334,7 @@ func (p *Pool) invalidateEntityAtIndex2(sliceIndex EIndex) flow.Entity { //se could set here p.ued.head.prev and next to 0s but its not needed p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.free.addElement(p, EIndex(sliceIndex)) + p.free.appendEntity(p, EIndex(sliceIndex)) p.used.size-- return invalidatedEntity @@ -364,7 +364,7 @@ func (p *Pool) invalidateEntityAtIndex2(sliceIndex EIndex) flow.Entity { p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.free.addElement(p, EIndex(sliceIndex)) + p.free.appendEntity(p, EIndex(sliceIndex)) return invalidatedEntity } From e06fb766a798e85f3d5b42f28ae04c348e35c5c6 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 19:10:10 +0200 Subject: [PATCH 301/815] Refactored out connect3 --- .../herocache/backdata/heropool/linkedlist.go | 39 +++++-- .../herocache/backdata/heropool/pool.go | 100 +----------------- 2 files changed, 34 insertions(+), 105 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index cccafe543cb..b973dc84c5b 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -27,15 +27,42 @@ type state struct { size uint32 } -// Adds element to the tail of the list or creates first element -func (s *state) appendEntity(p *Pool, entity EIndex) { +// Adds entity to the tail of the list or creates first element +func (s *state) appendEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { - s.head.index = entity - s.tail.index = entity + s.head.index = entityIndex + s.tail.index = entityIndex s.size = 1 return } - p.connect(s.tail, entity) + p.connect(s.tail, entityIndex) s.size++ - s.tail.index = entity + s.tail.index = entityIndex +} + +func (s *state) removeEntity(p *Pool, entityIndex EIndex) { + if s.size == 0 { + panic("Removing entity from an empty list") + } + if s.size == 1 { + s.size-- + return + } + node := p.poolEntities[entityIndex].node + + if entityIndex != s.head.getSliceIndex() && entityIndex != s.tail.getSliceIndex() { + // links next and prev elements for non-head and non-tail element + p.connect(node.prev, node.next.getSliceIndex()) + } + + if entityIndex == s.head.getSliceIndex() { + // moves head forward + s.head = node.next + } + + if entityIndex == s.tail.getSliceIndex() { + // moves tail backwards + s.tail = node.prev + } + s.size-- } diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 71995c411b2..255b259e021 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -223,13 +223,6 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { p.poolEntities[next].node.prev = prev } -// removes element -func (p *Pool) connect3(prev poolIndex, next EIndex, s *state) { - p.poolEntities[prev.getSliceIndex()].node.next.index = next - p.poolEntities[next].node.prev = prev - s.size-- -} - // invalidateUsedHead moves current used head forward by one node. It // also removes the entity the invalidated head is presenting and appends the // node represented by the used head to the tail of the free list. @@ -269,101 +262,10 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // it to the tail of the free list. It also removes the entity that the invalidated node is presenting. func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] - prev := poolEntity.node.prev - next := poolEntity.node.next invalidatedEntity := poolEntity.entity - - if p.used.size == 0 { - panic("Removing entity from an empty list") - } - - if p.used.size == 1 { - // decrements Size - //se could set here p.ued.head.prev and next to 0s but its not needed - p.poolEntities[sliceIndex].id = flow.ZeroID - p.poolEntities[sliceIndex].entity = nil - p.free.appendEntity(p, EIndex(sliceIndex)) - p.used.size-- - - return invalidatedEntity - } - // here size guaranteed > 1 - - if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { - // links next and prev elements for non-head and non-tail element - p.connect3(prev, next.getSliceIndex(), &p.used) - //p.connect(prev, next.getSliceIndex()) - } - - if sliceIndex == p.used.head.getSliceIndex() { - // invalidating used head - // moves head forward - oldUsedHead, _ := p.getHeads() - p.used.head = oldUsedHead.node.next - p.used.size-- - } - - if sliceIndex == p.used.tail.getSliceIndex() { - oldUsedTail, _ := p.getTails() - p.used.tail = oldUsedTail.node.prev - p.used.size-- - } - + p.used.removeEntity(p, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - - p.free.appendEntity(p, EIndex(sliceIndex)) - - return invalidatedEntity -} -func (p *Pool) invalidateEntityAtIndex2(sliceIndex EIndex) flow.Entity { - poolEntity := p.poolEntities[sliceIndex] - prev := poolEntity.node.prev - next := poolEntity.node.next - invalidatedEntity := poolEntity.entity - - if p.used.size == 0 { - fmt.Println("Debug shouldnt happen") - //this function works only when called on nonempty ued list. would be nice to have - // panic like a debug assert later - return invalidatedEntity - - } - if p.used.size == 1 { - // decrements Size - //se could set here p.ued.head.prev and next to 0s but its not needed - p.poolEntities[sliceIndex].id = flow.ZeroID - p.poolEntities[sliceIndex].entity = nil - p.free.appendEntity(p, EIndex(sliceIndex)) - p.used.size-- - - return invalidatedEntity - } - // here size guaranteed > 1 - - if sliceIndex != p.used.head.getSliceIndex() && sliceIndex != p.used.tail.getSliceIndex() { - // links next and prev elements for non-head and non-tail element - p.connect3(prev, next.getSliceIndex(), &p.used) - //p.connect(prev, next.getSliceIndex()) - } - - if sliceIndex == p.used.head.getSliceIndex() { - // invalidating used head - // moves head forward - oldUsedHead, _ := p.getHeads() - p.used.head = oldUsedHead.node.next - p.used.size-- - } - - if sliceIndex == p.used.tail.getSliceIndex() { - oldUsedTail, _ := p.getTails() - p.used.tail = oldUsedTail.node.prev - p.used.size-- - } - - p.poolEntities[sliceIndex].id = flow.ZeroID - p.poolEntities[sliceIndex].entity = nil - p.free.appendEntity(p, EIndex(sliceIndex)) return invalidatedEntity From cfbf2224e0fc3c1ba816b5ae3ca1e5d57ae94b22 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 19:23:33 +0200 Subject: [PATCH 302/815] Refactored all lit methods --- .../herocache/backdata/heropool/linkedlist.go | 1 + .../mempool/herocache/backdata/heropool/pool.go | 17 +---------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index b973dc84c5b..f5a8b98925b 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -40,6 +40,7 @@ func (s *state) appendEntity(p *Pool, entityIndex EIndex) { s.tail.index = entityIndex } +// Removes an entity from the list func (s *state) removeEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { panic("Removing entity from an empty list") diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 255b259e021..696c22d23ea 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,8 +1,6 @@ package heropool import ( - "fmt" - "github.com/rs/zerolog" "github.com/onflow/flow-go/model/flow" @@ -235,20 +233,7 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // old free head to host a new entity. func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head.getSliceIndex() - - if p.free.size == 0 { - fmt.Println("debug shouldnt happen") - } - - if p.free.size == 1 { - p.free.size = 0 - } - - if p.free.size > 1 { - p.free.head = p.poolEntities[oldFreeHeadIndex].node.next - p.free.size = p.free.size - 1 - } - + p.free.removeEntity(p, oldFreeHeadIndex) return oldFreeHeadIndex } From a168d690375482aef3faba49f81588a5e3a9a00f Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 19:28:37 +0200 Subject: [PATCH 303/815] Removed the need for pool index --- .../herocache/backdata/heropool/linkedlist.go | 22 ++--- .../herocache/backdata/heropool/pool.go | 28 +++--- .../herocache/backdata/heropool/pool_test.go | 86 +++++++++---------- 3 files changed, 68 insertions(+), 68 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index f5a8b98925b..b6101173bae 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -13,8 +13,8 @@ type link struct { // of if this is head or tail ... of one of the lists. For this reason I am not sure that this idea of removing 0 as undefined wont backfire // lets start with head and tail check - next poolIndex - prev poolIndex + next EIndex + prev EIndex } // state represents a doubly linked-list by its head and tail pool indices. @@ -22,22 +22,22 @@ type link struct { // Moreover head's prev and tails next are always treated as invalid and may hold any values. type state struct { //those might now coincide rather than point to 0 - head poolIndex - tail poolIndex + head EIndex + tail EIndex size uint32 } // Adds entity to the tail of the list or creates first element func (s *state) appendEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { - s.head.index = entityIndex - s.tail.index = entityIndex + s.head = entityIndex + s.tail = entityIndex s.size = 1 return } p.connect(s.tail, entityIndex) s.size++ - s.tail.index = entityIndex + s.tail = entityIndex } // Removes an entity from the list @@ -51,17 +51,17 @@ func (s *state) removeEntity(p *Pool, entityIndex EIndex) { } node := p.poolEntities[entityIndex].node - if entityIndex != s.head.getSliceIndex() && entityIndex != s.tail.getSliceIndex() { + if entityIndex != s.head && entityIndex != s.tail { // links next and prev elements for non-head and non-tail element - p.connect(node.prev, node.next.getSliceIndex()) + p.connect(node.prev, node.next) } - if entityIndex == s.head.getSliceIndex() { + if entityIndex == s.head { // moves head forward s.head = node.next } - if entityIndex == s.tail.getSliceIndex() { + if entityIndex == s.tail { // moves tail backwards s.tail = node.prev } diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 696c22d23ea..e303510f18b 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -61,13 +61,13 @@ type Pool struct { func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { l := &Pool{ free: state{ - head: poolIndex{index: 0}, - tail: poolIndex{index: 0}, + head: 0, + tail: 0, size: 0, }, used: state{ - head: poolIndex{index: 0}, - tail: poolIndex{index: 0}, + head: 0, + tail: 0, size: 0, }, poolEntities: make([]poolEntity, sizeLimit), @@ -120,7 +120,7 @@ func (p Pool) All() []PoolEntity { next := p.used.head for i := uint32(0); i < p.used.size; i++ { - e := p.poolEntities[next.getSliceIndex()] + e := p.poolEntities[next] all[i] = e.PoolEntity next = e.node.next } @@ -134,7 +134,7 @@ func (p Pool) Head() (flow.Entity, bool) { if p.used.size == 0 { return nil, false } - e := p.poolEntities[p.used.head.getSliceIndex()] + e := p.poolEntities[p.used.head] return e.Entity(), true } @@ -191,11 +191,11 @@ func (p *Pool) getHeads() (*poolEntity, *poolEntity) { var usedHead, freeHead *poolEntity if p.used.size != 0 { - usedHead = &p.poolEntities[p.used.head.getSliceIndex()] + usedHead = &p.poolEntities[p.used.head] } if p.free.size != 0 { - freeHead = &p.poolEntities[p.free.head.getSliceIndex()] + freeHead = &p.poolEntities[p.free.head] } return usedHead, freeHead @@ -205,19 +205,19 @@ func (p *Pool) getHeads() (*poolEntity, *poolEntity) { func (p *Pool) getTails() (*poolEntity, *poolEntity) { var usedTail, freeTail *poolEntity if p.used.size != 0 { - usedTail = &p.poolEntities[p.used.tail.getSliceIndex()] + usedTail = &p.poolEntities[p.used.tail] } if p.free.size != 0 { - freeTail = &p.poolEntities[p.free.tail.getSliceIndex()] + freeTail = &p.poolEntities[p.free.tail] } return usedTail, freeTail } // connect links the prev and next nodes as the adjacent nodes in the double-linked list. -func (p *Pool) connect(prev poolIndex, next EIndex) { - p.poolEntities[prev.getSliceIndex()].node.next.index = next +func (p *Pool) connect(prev EIndex, next EIndex) { + p.poolEntities[prev].node.next = next p.poolEntities[next].node.prev = prev } @@ -225,14 +225,14 @@ func (p *Pool) connect(prev poolIndex, next EIndex) { // also removes the entity the invalidated head is presenting and appends the // node represented by the used head to the tail of the free list. func (p *Pool) invalidateUsedHead() flow.Entity { - headSliceIndex := p.used.head.getSliceIndex() + headSliceIndex := p.used.head return p.invalidateEntityAtIndex(headSliceIndex) } // claimFreeHead moves the free head forward, and returns the slice index of the // old free head to host a new entity. func (p *Pool) claimFreeHead() EIndex { - oldFreeHeadIndex := p.free.head.getSliceIndex() + oldFreeHeadIndex := p.free.head p.free.removeEntity(p, oldFreeHeadIndex) return oldFreeHeadIndex } diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 224bc920e07..1280b72c725 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -232,17 +232,17 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // size of list should be decremented after each invalidation. require.Equal(t, uint32(totalEntitiesStored-i-1), pool.Size()) // invalidated head should be appended to free entities - require.Equal(t, pool.free.tail.getSliceIndex(), EIndex(i)) + require.Equal(t, pool.free.tail, EIndex(i)) if freeListInitialSize != 0 { // number of entities is below limit, hence free list is not empty. // invalidating used head must not change the free head. - require.Equal(t, EIndex(totalEntitiesStored), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(totalEntitiesStored), pool.free.head) } else { // number of entities is greater than or equal to limit, hence free list is empty. // free head must be updated to the first invalidated head (index 0), // and must be kept there for entire test (as we invalidate head not tail). - require.Equal(t, EIndex(0), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(0), pool.free.head) } // except when the list is empty, head must be updated after invalidation, @@ -251,14 +251,14 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt if i != totalEntitiesStored-1 { // used linked-list tailAccessibleFromHead(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, pool.Size()) headAccessibleFromTail(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, pool.Size()) @@ -266,14 +266,14 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // // after invalidating each item, size of free linked-list is incremented by one. tailAccessibleFromHead(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, uint32(i+1+freeListInitialSize)) headAccessibleFromTail(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, uint32(i+1+freeListInitialSize)) } @@ -287,12 +287,12 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // used tail should point to the last element in pool, since we are // invalidating head. require.Equal(t, entities[totalEntitiesStored-1].ID(), usedTail.id) - require.Equal(t, EIndex(totalEntitiesStored-1), pool.used.tail.getSliceIndex()) + require.Equal(t, EIndex(totalEntitiesStored-1), pool.used.tail) // used head must point to the next element in the pool, // i.e., invalidating head moves it forward. require.Equal(t, entities[i+1].ID(), usedHead.id) - require.Equal(t, EIndex(i+1), pool.used.head.getSliceIndex()) + require.Equal(t, EIndex(i+1), pool.used.head) } else { // pool is empty // used head and tail must be nil and their corresponding @@ -311,25 +311,25 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt offset := len(pool.poolEntities) - size for i := 0; i < size; i++ { // invalidates tail index - tailIndex := pool.used.tail.getSliceIndex() + tailIndex := pool.used.tail require.Equal(t, EIndex(size-1-i), tailIndex) pool.invalidateEntityAtIndex(tailIndex) // old head index must be invalidated require.True(t, pool.isInvalidated(tailIndex)) // unclaimed head should be appended to free entities - require.Equal(t, pool.free.tail.getSliceIndex(), tailIndex) + require.Equal(t, pool.free.tail, tailIndex) if offset != 0 { // number of entities is below limit // free must head keeps pointing to first empty index after // adding all entities. - require.Equal(t, EIndex(size), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(size), pool.free.head) } else { // number of entities is greater than or equal to limit // free head must be updated to last element in the pool (size - 1), // and must be kept there for entire test (as we invalidate tail not head). - require.Equal(t, EIndex(size-1), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(size-1), pool.free.head) } // size of pool should be shrunk after each invalidation. @@ -342,27 +342,27 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // used linked-list tailAccessibleFromHead(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, pool.Size()) headAccessibleFromTail(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, pool.Size()) // free linked-list tailAccessibleFromHead(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, uint32(i+1+offset)) headAccessibleFromTail(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, uint32(i+1+offset)) } @@ -374,11 +374,11 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // // used tail should move backward after each invalidation require.Equal(t, entities[size-i-2].ID(), usedTail.id) - require.Equal(t, EIndex(size-i-2), pool.used.tail.getSliceIndex()) + require.Equal(t, EIndex(size-i-2), pool.used.tail) // used head must point to the first element in the pool, require.Equal(t, entities[0].ID(), usedHead.id) - require.Equal(t, EIndex(0), pool.used.head.getSliceIndex()) + require.Equal(t, EIndex(0), pool.used.head) } else { // pool is empty // used head and tail must be nil and their corresponding @@ -400,23 +400,23 @@ func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { if i == 0 { // head of "free" linked-list should point to index 0 of entities slice. // previous element of head must is always undefined by convention and may hold any value. - require.Equal(t, EIndex(i), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(i), pool.free.head) } if i != 0 { // except head, any element should point back to its previous index in slice. - require.Equal(t, EIndex(i-1), pool.poolEntities[i].node.prev.getSliceIndex()) + require.Equal(t, EIndex(i-1), pool.poolEntities[i].node.prev) } if i != len(pool.poolEntities)-1 { // except tail, any element should point forward to its next index in slice. - require.Equal(t, EIndex(i+1), pool.poolEntities[i].node.next.getSliceIndex()) + require.Equal(t, EIndex(i+1), pool.poolEntities[i].node.next) } if i == len(pool.poolEntities)-1 { // tail of "free" linked-list should point to the last index in entities slice. // next element of tail is always undefined by convention and may hold any value. - require.Equal(t, EIndex(i), pool.free.tail.getSliceIndex()) + require.Equal(t, EIndex(i), pool.free.tail) } } } @@ -536,7 +536,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. if i < len(pool.poolEntities)-1 { // as long as we are below limit, after adding i element, free head // should move to i+1 element. - require.Equal(t, EIndex(i+1), pool.free.head.getSliceIndex()) + require.Equal(t, EIndex(i+1), pool.free.head) // head must be healthy and point back to undefined. // This is not needed anymore as head's prev is now ignored //require.True(t, freeHead.node.prev.isUndefined()) @@ -553,7 +553,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // must keep pointing to last index of the array-based linked-list. In other // words, adding element must not change free tail (since only free head is // updated). - require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.free.tail.getSliceIndex()) + require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.free.tail) // head tail be healthy and point next to undefined. // This is not needed anymore as tail's next is now ignored //require.True(t, freeTail.node.next.isUndefined()) @@ -574,13 +574,13 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. usedTraverseStep = uint32(len(pool.poolEntities)) } tailAccessibleFromHead(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, usedTraverseStep) headAccessibleFromTail(t, - pool.used.head.getSliceIndex(), - pool.used.tail.getSliceIndex(), + pool.used.head, + pool.used.tail, pool, usedTraverseStep) @@ -598,13 +598,13 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. freeTraverseStep = uint32(0) } tailAccessibleFromHead(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, freeTraverseStep) headAccessibleFromTail(t, - pool.free.head.getSliceIndex(), - pool.free.tail.getSliceIndex(), + pool.free.head, + pool.free.tail, pool, freeTraverseStep) } @@ -674,7 +674,7 @@ func tailAccessibleFromHead(t *testing.T, headSliceIndex EIndex, tailSliceIndex require.NotEqual(t, tailSliceIndex, index, "tail visited in less expected steps (potential inconsistency)", i, steps) _, ok := seen[index] require.False(t, ok, "duplicate identifiers found") - index = pool.poolEntities[index].node.next.getSliceIndex() + index = pool.poolEntities[index].node.next } } @@ -693,6 +693,6 @@ func headAccessibleFromTail(t *testing.T, headSliceIndex EIndex, tailSliceIndex _, ok := seen[index] require.False(t, ok, "duplicate identifiers found") - index = pool.poolEntities[index].node.prev.getSliceIndex() + index = pool.poolEntities[index].node.prev } } From 9a6c363c6c46543a6cf232838e509516d9d0ee0f Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 19:34:18 +0200 Subject: [PATCH 304/815] Removed poll index file --- module/mempool/herocache/backdata/heropool/pool.go | 1 - .../mempool/herocache/backdata/heropool/poolIndex.go | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 module/mempool/herocache/backdata/heropool/poolIndex.go diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index e303510f18b..3be4acf58f9 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -207,7 +207,6 @@ func (p *Pool) getTails() (*poolEntity, *poolEntity) { if p.used.size != 0 { usedTail = &p.poolEntities[p.used.tail] } - if p.free.size != 0 { freeTail = &p.poolEntities[p.free.tail] } diff --git a/module/mempool/herocache/backdata/heropool/poolIndex.go b/module/mempool/herocache/backdata/heropool/poolIndex.go deleted file mode 100644 index f425bf2a51e..00000000000 --- a/module/mempool/herocache/backdata/heropool/poolIndex.go +++ /dev/null @@ -1,12 +0,0 @@ -package heropool - -// poolIndex represents a slice-based linked list pointer. Instead of pointing -// to a memory address, this pointer points to a slice index. -type poolIndex struct { - index EIndex -} - -// getSliceIndex returns the slice-index equivalent of the poolIndex. -func (p poolIndex) getSliceIndex() EIndex { - return EIndex(p.index) -} From 88dc3b9620aae54dc11e5df0a0f15d7696f345d2 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 19:38:53 +0200 Subject: [PATCH 305/815] Removed obsolet comments --- .../herocache/backdata/heropool/linkedlist.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index b6101173bae..d7f7c6dc8f5 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -5,14 +5,6 @@ package heropool // if a link doesn't belong to any state it's next and prev may have any values, // but those are treated as invalid and should not be used. type link struct { - // As we dont have from now on an invalid index we need either to make next/prev point to itself - // in order to show that its invalid - // Other option is to say that if a link is a tail or head then respectively its prev or next should not be used - // if tail and head concide then neither of them is to be used - // Both solutions defacto would reintroduce isDefined, we would need to check if next != current Index - // of if this is head or tail ... of one of the lists. For this reason I am not sure that this idea of removing 0 as undefined wont backfire - - // lets start with head and tail check next EIndex prev EIndex } @@ -27,7 +19,7 @@ type state struct { size uint32 } -// Adds entity to the tail of the list or creates first element +// adds entity to the tail of the list or creates first element func (s *state) appendEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { s.head = entityIndex @@ -40,7 +32,7 @@ func (s *state) appendEntity(p *Pool, entityIndex EIndex) { s.tail = entityIndex } -// Removes an entity from the list +// removes an entity from the list func (s *state) removeEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { panic("Removing entity from an empty list") From d2587e8d5daf61bde305cac05c98b0c41d79d756 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 20:42:18 +0200 Subject: [PATCH 306/815] removed obsolete line --- module/mempool/herocache/backdata/heropool/pool_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 1280b72c725..91447de02ed 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -300,7 +300,6 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt require.Nil(t, usedHead) require.Nil(t, usedTail) require.True(t, pool.used.size == 0) - //require.True(t, pool.used.head.isUndefined()) } } } From 92aa83fc4725edaf22d945ccb98ae6e4660ff779 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 22:10:26 +0200 Subject: [PATCH 307/815] removed superfluous comment --- module/mempool/herocache/backdata/heropool/linkedlist.go | 1 - 1 file changed, 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index d7f7c6dc8f5..f9aafd1ff6e 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -13,7 +13,6 @@ type link struct { // If satte has 0 size, its tail's and head's prev and next are treated as invalid. // Moreover head's prev and tails next are always treated as invalid and may hold any values. type state struct { - //those might now coincide rather than point to 0 head EIndex tail EIndex size uint32 From e8bd105716bf9420b83e9d51659f40f40577eb9c Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 9 Jun 2023 22:22:15 +0200 Subject: [PATCH 308/815] fixed typo --- module/mempool/herocache/backdata/heropool/linkedlist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index f9aafd1ff6e..02416583ff6 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -10,7 +10,7 @@ type link struct { } // state represents a doubly linked-list by its head and tail pool indices. -// If satte has 0 size, its tail's and head's prev and next are treated as invalid. +// If state has 0 size, its tail's and head's prev and next are treated as invalid. // Moreover head's prev and tails next are always treated as invalid and may hold any values. type state struct { head EIndex From 547c133e3d221d39ec792438ace7f1f1a4118b57 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 12 Jun 2023 20:46:44 +0200 Subject: [PATCH 309/815] Added a unit test for linked list --- .../herocache/backdata/heropool/linkedlist.go | 2 +- .../backdata/heropool/linkedlist_test.go | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 module/mempool/herocache/backdata/heropool/linkedlist_test.go diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 02416583ff6..945e7f86a74 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -34,7 +34,7 @@ func (s *state) appendEntity(p *Pool, entityIndex EIndex) { // removes an entity from the list func (s *state) removeEntity(p *Pool, entityIndex EIndex) { if s.size == 0 { - panic("Removing entity from an empty list") + panic("Removing an entity from the empty list") } if s.size == 1 { s.size-- diff --git a/module/mempool/herocache/backdata/heropool/linkedlist_test.go b/module/mempool/herocache/backdata/heropool/linkedlist_test.go new file mode 100644 index 00000000000..deee7bba069 --- /dev/null +++ b/module/mempool/herocache/backdata/heropool/linkedlist_test.go @@ -0,0 +1,76 @@ +package heropool + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/utils/unittest" +) + +// TestStoreAndRetrieval_BelowLimit checks health of heroPool for storing and retrieval scenarios that +// do not involve ejection. +// The test involves cases for testing the pool below its limit, and also up to its limit. However, it never gets beyond +// the limit, so no ejection will kick-in. +func TestAddElementsToList(t *testing.T) { + limit_capacity := uint32(4) + number_of_entities := uint32(4) + t.Run(fmt.Sprintf("%d-limit-%d-entities", limit_capacity, number_of_entities), func(t *testing.T) { + withTestScenario(t, limit_capacity, number_of_entities, LRUEjection, []func(*testing.T, *Pool, []*unittest.MockEntity){ + func(t *testing.T, pool *Pool, entities []*unittest.MockEntity) { + testAddingElementsToList(t, pool, entities, LRUEjection) + }, + }..., + ) + }) +} + +func checkList(t *testing.T, expectedList []EIndex, pool *Pool, list *state) { + node_index := list.head + i := 0 + // check size + require.Equal(t, uint32(len(expectedList)), list.size) + // check links + for ; node_index != pool.free.tail; i++ { + require.Equal(t, expectedList[i], node_index) + node_index_next := pool.poolEntities[node_index].node.next + require.Equal(t, expectedList[i], pool.poolEntities[node_index_next].node.prev) + node_index = node_index_next + } + //check the tail + require.Equal(t, expectedList[i], node_index) +} + +func testAddingElementsToList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { + + //check entities are initalized + expectedList := [4]EIndex{0, 1, 2, 3} + checkList(t, expectedList[:], pool, &pool.free) + + // remove middle + expectedListNoMiddle := [3]EIndex{0, 1, 3} + pool.free.removeEntity(pool, 2) + checkList(t, expectedListNoMiddle[:], pool, &pool.free) + + // remove head + pool.free.removeEntity(pool, 0) + checkList(t, expectedListNoMiddle[1:], pool, &pool.free) + + // remove tail + pool.free.removeEntity(pool, 3) + checkList(t, expectedListNoMiddle[1:2], pool, &pool.free) + + // remove the last element and ty to remove from an empty list + pool.free.removeEntity(pool, 1) + + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } else { + require.Equal(t, r, "Removing an entity from the empty list") + } + }() + + pool.free.removeEntity(pool, 1) +} From f081841c54c804a507062899a99e95c8c92d4d82 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 12 Jun 2023 20:56:40 +0200 Subject: [PATCH 310/815] Fixed comments --- .../herocache/backdata/heropool/linkedlist_test.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist_test.go b/module/mempool/herocache/backdata/heropool/linkedlist_test.go index deee7bba069..fd7bd579d82 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist_test.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist_test.go @@ -9,17 +9,15 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestStoreAndRetrieval_BelowLimit checks health of heroPool for storing and retrieval scenarios that -// do not involve ejection. -// The test involves cases for testing the pool below its limit, and also up to its limit. However, it never gets beyond -// the limit, so no ejection will kick-in. -func TestAddElementsToList(t *testing.T) { +// TestRemovingElementsFromList first insures that lit ha been initialized correclty, +// then removes element and tests list consistency +func TestRemovingElementsFromList(t *testing.T) { limit_capacity := uint32(4) number_of_entities := uint32(4) t.Run(fmt.Sprintf("%d-limit-%d-entities", limit_capacity, number_of_entities), func(t *testing.T) { withTestScenario(t, limit_capacity, number_of_entities, LRUEjection, []func(*testing.T, *Pool, []*unittest.MockEntity){ func(t *testing.T, pool *Pool, entities []*unittest.MockEntity) { - testAddingElementsToList(t, pool, entities, LRUEjection) + testRemovingElementsFromList(t, pool, entities, LRUEjection) }, }..., ) @@ -42,7 +40,7 @@ func checkList(t *testing.T, expectedList []EIndex, pool *Pool, list *state) { require.Equal(t, expectedList[i], node_index) } -func testAddingElementsToList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { +func testRemovingElementsFromList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { //check entities are initalized expectedList := [4]EIndex{0, 1, 2, 3} From 4dc40231f6922e395a19c39408e22e50ffd826ba Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 12 Jun 2023 20:59:07 +0200 Subject: [PATCH 311/815] Improved comment --- module/mempool/herocache/backdata/heropool/linkedlist_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist_test.go b/module/mempool/herocache/backdata/heropool/linkedlist_test.go index fd7bd579d82..d3bc3aa5597 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist_test.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist_test.go @@ -9,8 +9,8 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestRemovingElementsFromList first insures that lit ha been initialized correclty, -// then removes element and tests list consistency +// TestRemovingElementsFromList - first insures that list has been initialized correclty, +// then removes elements and tests lists consistency. func TestRemovingElementsFromList(t *testing.T) { limit_capacity := uint32(4) number_of_entities := uint32(4) From 1b78ad043a54b0a7a7d930f4de652ace88d4357c Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 16 Jun 2023 11:17:46 +0200 Subject: [PATCH 312/815] Added a unit test for append --- .../herocache/backdata/heropool/linkedlist_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist_test.go b/module/mempool/herocache/backdata/heropool/linkedlist_test.go index d3bc3aa5597..5db4394adb5 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist_test.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist_test.go @@ -72,3 +72,17 @@ func testRemovingElementsFromList(t *testing.T, pool *Pool, entitiesToBeAdded [] pool.free.removeEntity(pool, 1) } + +func testAppendingElementsToList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { + + //check entities are initalized + expectedList := [4]EIndex{0, 1, 2, 3} + checkList(t, expectedList[:], pool, &pool.free) + + // add Elements + expectedAfterAppendList := [3]EIndex{3, 0, 1} + pool.used.appendEntity(pool, 3) + pool.used.appendEntity(pool, 0) + pool.used.appendEntity(pool, 1) + checkList(t, expectedAfterAppendList[:], pool, &pool.used) +} From 51ae75288699a7441e512e6900736b73f047b3cb Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Wed, 21 Jun 2023 14:04:10 +0200 Subject: [PATCH 313/815] Implemented -1 as an invalid index-a --- .../herocache/backdata/heropool/linkedlist.go | 41 --------- .../backdata/heropool/linkedlist_test.go | 88 ------------------- .../herocache/backdata/heropool/pool.go | 71 +++++++++++++-- .../herocache/backdata/heropool/pool_test.go | 21 +++-- 4 files changed, 74 insertions(+), 147 deletions(-) delete mode 100644 module/mempool/herocache/backdata/heropool/linkedlist_test.go diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 945e7f86a74..1abd255bcd5 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -17,44 +17,3 @@ type state struct { tail EIndex size uint32 } - -// adds entity to the tail of the list or creates first element -func (s *state) appendEntity(p *Pool, entityIndex EIndex) { - if s.size == 0 { - s.head = entityIndex - s.tail = entityIndex - s.size = 1 - return - } - p.connect(s.tail, entityIndex) - s.size++ - s.tail = entityIndex -} - -// removes an entity from the list -func (s *state) removeEntity(p *Pool, entityIndex EIndex) { - if s.size == 0 { - panic("Removing an entity from the empty list") - } - if s.size == 1 { - s.size-- - return - } - node := p.poolEntities[entityIndex].node - - if entityIndex != s.head && entityIndex != s.tail { - // links next and prev elements for non-head and non-tail element - p.connect(node.prev, node.next) - } - - if entityIndex == s.head { - // moves head forward - s.head = node.next - } - - if entityIndex == s.tail { - // moves tail backwards - s.tail = node.prev - } - s.size-- -} diff --git a/module/mempool/herocache/backdata/heropool/linkedlist_test.go b/module/mempool/herocache/backdata/heropool/linkedlist_test.go deleted file mode 100644 index 5db4394adb5..00000000000 --- a/module/mempool/herocache/backdata/heropool/linkedlist_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package heropool - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/utils/unittest" -) - -// TestRemovingElementsFromList - first insures that list has been initialized correclty, -// then removes elements and tests lists consistency. -func TestRemovingElementsFromList(t *testing.T) { - limit_capacity := uint32(4) - number_of_entities := uint32(4) - t.Run(fmt.Sprintf("%d-limit-%d-entities", limit_capacity, number_of_entities), func(t *testing.T) { - withTestScenario(t, limit_capacity, number_of_entities, LRUEjection, []func(*testing.T, *Pool, []*unittest.MockEntity){ - func(t *testing.T, pool *Pool, entities []*unittest.MockEntity) { - testRemovingElementsFromList(t, pool, entities, LRUEjection) - }, - }..., - ) - }) -} - -func checkList(t *testing.T, expectedList []EIndex, pool *Pool, list *state) { - node_index := list.head - i := 0 - // check size - require.Equal(t, uint32(len(expectedList)), list.size) - // check links - for ; node_index != pool.free.tail; i++ { - require.Equal(t, expectedList[i], node_index) - node_index_next := pool.poolEntities[node_index].node.next - require.Equal(t, expectedList[i], pool.poolEntities[node_index_next].node.prev) - node_index = node_index_next - } - //check the tail - require.Equal(t, expectedList[i], node_index) -} - -func testRemovingElementsFromList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { - - //check entities are initalized - expectedList := [4]EIndex{0, 1, 2, 3} - checkList(t, expectedList[:], pool, &pool.free) - - // remove middle - expectedListNoMiddle := [3]EIndex{0, 1, 3} - pool.free.removeEntity(pool, 2) - checkList(t, expectedListNoMiddle[:], pool, &pool.free) - - // remove head - pool.free.removeEntity(pool, 0) - checkList(t, expectedListNoMiddle[1:], pool, &pool.free) - - // remove tail - pool.free.removeEntity(pool, 3) - checkList(t, expectedListNoMiddle[1:2], pool, &pool.free) - - // remove the last element and ty to remove from an empty list - pool.free.removeEntity(pool, 1) - - defer func() { - if r := recover(); r == nil { - t.Errorf("The code did not panic") - } else { - require.Equal(t, r, "Removing an entity from the empty list") - } - }() - - pool.free.removeEntity(pool, 1) -} - -func testAppendingElementsToList(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest.MockEntity, ejectionMode EjectionMode) { - - //check entities are initalized - expectedList := [4]EIndex{0, 1, 2, 3} - checkList(t, expectedList[:], pool, &pool.free) - - // add Elements - expectedAfterAppendList := [3]EIndex{3, 0, 1} - pool.used.appendEntity(pool, 3) - pool.used.appendEntity(pool, 0) - pool.used.appendEntity(pool, 1) - checkList(t, expectedAfterAppendList[:], pool, &pool.used) -} diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 3be4acf58f9..b592a1c5d7f 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -16,7 +16,9 @@ const ( ) // EIndex is data type representing an entity index in Pool. -type EIndex uint32 +type EIndex int64 + +const InvalidIndex EIndex = -1 // poolEntity represents the data type that is maintained by type poolEntity struct { @@ -83,7 +85,7 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { for i := 0; i < len(p.poolEntities); i++ { - p.free.appendEntity(p, EIndex(i)) + p.appendEntity(&p.free, EIndex(i)) } } @@ -103,7 +105,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - p.used.appendEntity(p, entityIndex) + p.appendEntity(&p.used, entityIndex) } return entityIndex, slotAvailable, ejectedEntity @@ -158,7 +160,12 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn switch p.ejectionMode { case NoEjection: // pool is set for no ejection, hence, no slice index is selected, abort immediately. - return 0, false, nil + return InvalidIndex, false, nil + case LRUEjection: + // LRU ejection + // the used head is the oldest entity, so we turn the used head to a free head here. + invalidatedEntity := p.invalidateUsedHead() + return p.claimFreeHead(), true, invalidatedEntity case RandomEjection: // we only eject randomly when the pool is full and random ejection is on. random, err := rand.Uint32n(p.size) @@ -232,7 +239,7 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // old free head to host a new entity. func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head - p.free.removeEntity(p, oldFreeHeadIndex) + p.removeEntity(&p.free, oldFreeHeadIndex) return oldFreeHeadIndex } @@ -247,10 +254,10 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] invalidatedEntity := poolEntity.entity - p.used.removeEntity(p, sliceIndex) + p.removeEntity(&p.used, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.free.appendEntity(p, EIndex(sliceIndex)) + p.appendEntity(&p.free, EIndex(sliceIndex)) return invalidatedEntity } @@ -268,3 +275,53 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { return true } + +// utility method that removes an entity from one of the states. +// NOTE: removed entity has to be added to another state. +func (p *Pool) removeEntity(s *state, entityIndex EIndex) { + if s.size == 0 { + panic("Removing an entity from the empty list") + } + if s.size == 1 { + s.head = InvalidIndex + s.tail = InvalidIndex + s.size-- + return + } + node := p.poolEntities[entityIndex].node + + if entityIndex != s.head && entityIndex != s.tail { + // links next and prev elements for non-head and non-tail element + p.connect(node.prev, node.next) + } + + if entityIndex == s.head { + // moves head forward + s.head = node.next + p.poolEntities[s.head].node.prev = InvalidIndex + } + + if entityIndex == s.tail { + // moves tail backwards + s.tail = node.prev + p.poolEntities[s.tail].node.next = InvalidIndex + } + s.size-- +} + +// appends an entity to the tail of the state or creates a first element. +// NOTE: entity should not be in any list before this method is applied +func (p *Pool) appendEntity(s *state, entityIndex EIndex) { + if s.size == 0 { + s.head = entityIndex + s.tail = entityIndex + p.poolEntities[s.head].node.prev = InvalidIndex + p.poolEntities[s.tail].node.next = InvalidIndex + s.size = 1 + return + } + p.connect(s.tail, entityIndex) + s.size++ + s.tail = entityIndex + p.poolEntities[s.tail].node.next = InvalidIndex +} diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 91447de02ed..36e2ed214db 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -300,6 +300,8 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt require.Nil(t, usedHead) require.Nil(t, usedTail) require.True(t, pool.used.size == 0) + require.Equal(t, pool.used.tail, InvalidIndex) + require.Equal(t, pool.used.head, InvalidIndex) } } } @@ -385,7 +387,8 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt require.Nil(t, usedHead) require.Nil(t, usedTail) require.True(t, pool.used.size == 0) - //require.True(t, pool.used.head.isUndefined()) + require.Equal(t, pool.used.head, InvalidIndex) + require.Equal(t, pool.used.tail, InvalidIndex) } } } @@ -488,7 +491,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. if i >= len(pool.poolEntities) { require.False(t, slotAvailable) require.Nil(t, ejectedEntity) - require.Equal(t, entityIndex, EIndex(0)) + require.Equal(t, entityIndex, InvalidIndex) // when pool is full and with NoEjection, the head must keep pointing to the first added element. headEntity, headExists := pool.Head() @@ -511,16 +514,14 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. } require.Equal(t, pool.poolEntities[expectedUsedHead].entity, usedHead.entity) // head must be healthy and point back to undefined. - // This is not needed anymore as head's prev is now ignored - //require.True(t, usedHead.node.prev.isUndefined()) + require.Equal(t, usedHead.node.prev, InvalidIndex) } if ejectionMode != NoEjection || i < len(pool.poolEntities) { // new entity must be successfully added to tail of used linked-list require.Equal(t, entitiesToBeAdded[i], usedTail.entity) // used tail must be healthy and point back to undefined. - // This is not needed anymore as tail's next is now ignored - //require.True(t, usedTail.node.next.isUndefined()) + require.Equal(t, usedTail.node.next, InvalidIndex) } if ejectionMode == NoEjection && i >= len(pool.poolEntities) { @@ -528,7 +529,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. require.Equal(t, entitiesToBeAdded[len(pool.poolEntities)-1], usedTail.entity) // used tail must be healthy and point back to undefined. // This is not needed anymore as tail's next is now ignored - //require.True(t, usedTail.node.next.isUndefined()) + require.Equal(t, usedTail.node.next, InvalidIndex) } // free head @@ -537,8 +538,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // should move to i+1 element. require.Equal(t, EIndex(i+1), pool.free.head) // head must be healthy and point back to undefined. - // This is not needed anymore as head's prev is now ignored - //require.True(t, freeHead.node.prev.isUndefined()) + require.Equal(t, freeHead.node.prev, InvalidIndex) } else { // once we go beyond limit, // we run out of free slots, @@ -554,8 +554,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // updated). require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.free.tail) // head tail be healthy and point next to undefined. - // This is not needed anymore as tail's next is now ignored - //require.True(t, freeTail.node.next.isUndefined()) + require.Equal(t, freeTail.node.next, InvalidIndex) } else { // once we go beyond limit, we run out of free slots, and // free tail must be kept at undefined. From 9f363dc1bc385b524c280102093294a72ace9fa2 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Wed, 21 Jun 2023 14:59:59 +0200 Subject: [PATCH 314/815] Put back all checks for the invalid index --- module/mempool/herocache/backdata/heropool/pool.go | 8 ++++---- .../mempool/herocache/backdata/heropool/pool_test.go | 12 +++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index b592a1c5d7f..4435b4fa301 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -63,13 +63,13 @@ type Pool struct { func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { l := &Pool{ free: state{ - head: 0, - tail: 0, + head: InvalidIndex, + tail: InvalidIndex, size: 0, }, used: state{ - head: 0, - tail: 0, + head: InvalidIndex, + tail: InvalidIndex, size: 0, }, poolEntities: make([]poolEntity, sizeLimit), diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 36e2ed214db..70a1176c33c 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -397,12 +397,15 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { // "used" linked-list must have a zero size, since we have no elements in the list. require.True(t, pool.used.size == 0) + require.Equal(t, pool.used.head, InvalidIndex) + require.Equal(t, pool.used.tail, InvalidIndex) for i := 0; i < len(pool.poolEntities); i++ { if i == 0 { - // head of "free" linked-list should point to index 0 of entities slice. - // previous element of head must is always undefined by convention and may hold any value. + // head of "free" linked-list should point to InvalidIndex of entities slice. require.Equal(t, EIndex(i), pool.free.head) + // previous element of head must be undefined (linked-list head feature). + require.Equal(t, pool.poolEntities[i].node.prev, InvalidIndex) } if i != 0 { @@ -417,8 +420,9 @@ func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { if i == len(pool.poolEntities)-1 { // tail of "free" linked-list should point to the last index in entities slice. - // next element of tail is always undefined by convention and may hold any value. require.Equal(t, EIndex(i), pool.free.tail) + // next element of tail must be undefined. + require.Equal(t, pool.poolEntities[i].node.next, InvalidIndex) } } } @@ -672,6 +676,8 @@ func tailAccessibleFromHead(t *testing.T, headSliceIndex EIndex, tailSliceIndex require.NotEqual(t, tailSliceIndex, index, "tail visited in less expected steps (potential inconsistency)", i, steps) _, ok := seen[index] require.False(t, ok, "duplicate identifiers found") + + require.NotEqual(t, pool.poolEntities[index].node.next, InvalidIndex, "tail not found, and reached end of list") index = pool.poolEntities[index].node.next } } From 9612f30e970683b7ffcd42fa63b9faa60ba35467 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Wed, 21 Jun 2023 16:54:52 +0200 Subject: [PATCH 315/815] Added a consistency list check --- .../herocache/backdata/heropool/pool_test.go | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 70a1176c33c..705c3e6079d 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -610,6 +610,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. pool, freeTraverseStep) } + checkEachEntityIsInFreeOrUsedState(t, pool) } // testRetrievingEntitiesFrom evaluates that all entities starting from given index are retrievable from pool. @@ -700,3 +701,26 @@ func headAccessibleFromTail(t *testing.T, headSliceIndex EIndex, tailSliceIndex index = pool.poolEntities[index].node.prev } } + +func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { + pool_capacity := len(pool.poolEntities) + // check size + require.Equal(t, int(pool.free.size+pool.used.size), pool_capacity, "Pool capacity is not equal to the sum of used and free sizes") + // check elelments + nodesInFree := discoverNodesBelongingToStateList(t, pool, &pool.free) + nodesInUsed := discoverNodesBelongingToStateList(t, pool, &pool.used) + for i := 0; i < pool_capacity; i++ { + require.False(t, !nodesInFree[i] && !nodesInUsed[i], "Node is not in any state list") + require.False(t, nodesInFree[i] && nodesInUsed[i], "Node is in two state lists at the same time") + } +} + +func discoverNodesBelongingToStateList(t *testing.T, pool *Pool, s *state) []bool { + result := make([]bool, len(pool.poolEntities)) + for node_index := s.head; node_index != InvalidIndex; { + require.False(t, result[node_index], "A node is present two times in the same state list") + result[node_index] = true + node_index = pool.poolEntities[node_index].node.next + } + return result +} From 3224d2d8cc1d72980985a50caa04d97ff8836cee Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Wed, 21 Jun 2023 17:25:31 +0200 Subject: [PATCH 316/815] Improved documentation --- module/mempool/herocache/backdata/heropool/pool_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 705c3e6079d..99bde7e8564 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -702,20 +702,22 @@ func headAccessibleFromTail(t *testing.T, headSliceIndex EIndex, tailSliceIndex } } +// checks if each entity in the pool belongs exactly to one of the sate lists func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { pool_capacity := len(pool.poolEntities) // check size require.Equal(t, int(pool.free.size+pool.used.size), pool_capacity, "Pool capacity is not equal to the sum of used and free sizes") // check elelments - nodesInFree := discoverNodesBelongingToStateList(t, pool, &pool.free) - nodesInUsed := discoverNodesBelongingToStateList(t, pool, &pool.used) + nodesInFree := discoverEntitiesBelongingToStateList(t, pool, &pool.free) + nodesInUsed := discoverEntitiesBelongingToStateList(t, pool, &pool.used) for i := 0; i < pool_capacity; i++ { require.False(t, !nodesInFree[i] && !nodesInUsed[i], "Node is not in any state list") require.False(t, nodesInFree[i] && nodesInUsed[i], "Node is in two state lists at the same time") } } -func discoverNodesBelongingToStateList(t *testing.T, pool *Pool, s *state) []bool { +// discovers all entities in the pool that belong to the given list +func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, s *state) []bool { result := make([]bool, len(pool.poolEntities)) for node_index := s.head; node_index != InvalidIndex; { require.False(t, result[node_index], "A node is present two times in the same state list") From 9503bd68407e50a3b4013fe3517741d4bfb79019 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Wed, 21 Jun 2023 18:25:33 +0200 Subject: [PATCH 317/815] Added more checks for pool consictensy --- module/mempool/herocache/backdata/heropool/pool_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 99bde7e8564..1c35fed910e 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -303,6 +303,7 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt require.Equal(t, pool.used.tail, InvalidIndex) require.Equal(t, pool.used.head, InvalidIndex) } + checkEachEntityIsInFreeOrUsedState(t, pool) } } @@ -390,6 +391,7 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt require.Equal(t, pool.used.head, InvalidIndex) require.Equal(t, pool.used.tail, InvalidIndex) } + checkEachEntityIsInFreeOrUsedState(t, pool) } } @@ -609,8 +611,9 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. pool.free.tail, pool, freeTraverseStep) + + checkEachEntityIsInFreeOrUsedState(t, pool) } - checkEachEntityIsInFreeOrUsedState(t, pool) } // testRetrievingEntitiesFrom evaluates that all entities starting from given index are retrievable from pool. From 9c8e431fa9a430df90f3d6e3492a68bf89629ea4 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 22 Jun 2023 11:20:01 +0200 Subject: [PATCH 318/815] Changed undefined value to max uint --- module/mempool/herocache/backdata/heropool/pool.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 4435b4fa301..beff7099948 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -3,6 +3,7 @@ package heropool import ( "github.com/rs/zerolog" + "github.com/ethereum/go-ethereum/common/math" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/rand" ) @@ -16,9 +17,9 @@ const ( ) // EIndex is data type representing an entity index in Pool. -type EIndex int64 +type EIndex uint32 -const InvalidIndex EIndex = -1 +const InvalidIndex EIndex = math.MaxUint32 // poolEntity represents the data type that is maintained by type poolEntity struct { @@ -277,15 +278,18 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { } // utility method that removes an entity from one of the states. -// NOTE: removed entity has to be added to another state. +// NOTE: a removed entity has to be added to another state. func (p *Pool) removeEntity(s *state, entityIndex EIndex) { if s.size == 0 { panic("Removing an entity from the empty list") } if s.size == 1 { + // here set to InvalidIndex s.head = InvalidIndex s.tail = InvalidIndex s.size-- + p.poolEntities[entityIndex].node.next = InvalidIndex + p.poolEntities[entityIndex].node.prev = InvalidIndex return } node := p.poolEntities[entityIndex].node @@ -307,6 +311,8 @@ func (p *Pool) removeEntity(s *state, entityIndex EIndex) { p.poolEntities[s.tail].node.next = InvalidIndex } s.size-- + p.poolEntities[entityIndex].node.next = InvalidIndex + p.poolEntities[entityIndex].node.prev = InvalidIndex } // appends an entity to the tail of the state or creates a first element. From 418a33dc6974cabb437281e42870f976a9c353a3 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 19:24:59 +0200 Subject: [PATCH 319/815] Added remove add tests --- .../herocache/backdata/heropool/pool.go | 9 ++ .../herocache/backdata/heropool/pool_test.go | 100 ++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index beff7099948..90cc0639212 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -78,11 +78,20 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log logger: logger, } + l.setDefaultNodeLinkValues() l.initFreeEntities() return l } +// setDefaultNodeLinkValues sets nodes prev and nextt to InvalidIndex for all cached entity poolEntities. +func (p *Pool) setDefaultNodeLinkValues() { + for i := 0; i < len(p.poolEntities); i++ { + p.poolEntities[i].node.next = InvalidIndex + p.poolEntities[i].node.prev = InvalidIndex + } +} + // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { for i := 0; i < len(p.poolEntities); i++ { diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 1c35fed910e..409fcfdc987 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -10,6 +10,18 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) +type OperatioType int + +const ( + Add OperatioType = iota + Remove +) + +type OperationAndIndex struct { + operation OperatioType + index uint +} + // TestStoreAndRetrieval_BelowLimit checks health of heroPool for storing and retrieval scenarios that // do not involve ejection. // The test involves cases for testing the pool below its limit, and also up to its limit. However, it never gets beyond @@ -215,6 +227,94 @@ func TestInvalidateEntity(t *testing.T) { } } +// TestAddAndRemoveEntities checks health of heroPool for storing and removing scenarios. LRU, NoEjection and RandomEjection are tested. +func TestAddAndRemoveEntities(t *testing.T) { + for _, tc := range []struct { + limit uint32 // capacity of pool + entityCount uint32 // total entities to be stored + ejectionMode EjectionMode // ejection mode + operationsIndexes []OperationAndIndex // operation to perform on an entity stored by index + finalEntitiesinThePool []uint // indexes of entities residing in the pool at the end of the test + }{ + { + limit: 2, + entityCount: 5, + ejectionMode: LRUEjection, + operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 2}, {Add, 2}, {Remove, 2}}, + finalEntitiesinThePool: []uint{3}, + }, + { + limit: 2, + entityCount: 5, + ejectionMode: NoEjection, + operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 0}, {Remove, 1}, {Add, 4}, {Add, 3}}, + finalEntitiesinThePool: []uint{3, 4}, + }, + { + limit: 5, + entityCount: 6, + ejectionMode: RandomEjection, + operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Remove, 1}, {Add, 2}, {Remove, 0}, {Add, 5}, {Add, 4}, {Add, 1}, {Add, 3}, {Add, 0}}, + finalEntitiesinThePool: nil, + }, + } { + t.Run(fmt.Sprintf("%d-limit-%d-entities", tc.limit, tc.entityCount), func(t *testing.T) { + testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.operationsIndexes, tc.finalEntitiesinThePool) + }) + } +} + +// testAddRemoveEntities allows to add or remove entities in an order given by operationsIndexes. Each OperationAndIndex consists of an operaton to perform +// on an entity stored under a corresponding index. +func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsIndexes []OperationAndIndex, finalEntitiesinThePool []uint) { + pool := NewHeroPool(limit, ejectionMode) + + entities := unittest.EntityListFixture(uint(entityCount)) + + // this map maintains entities inserted into the pool + var insertedEntities map[flow.Identifier]EIndex + insertedEntities = make(map[flow.Identifier]EIndex) + + for _, operationAndIndex := range operationsIndexes { + operation := operationAndIndex.operation + index := operationAndIndex.index + switch operation { + case Add: + indexInThePool, _, ejectedEntity := pool.Add(entities[index].ID(), entities[index], uint64(index)) + if indexInThePool != InvalidIndex { + insertedEntities[entities[index].ID()] = indexInThePool + } + if ejectedEntity != nil { + delete(insertedEntities, ejectedEntity.ID()) + } + case Remove: + indexInPool, found := insertedEntities[entities[index].ID()] + if found { + removedEntity := pool.Remove(indexInPool) + require.Equal(t, entities[index].ID(), removedEntity.ID(), "Removed wrong entity") + delete(insertedEntities, entities[index].ID()) + } else { + panic("Ill constructed test, trying to remove an element that has not been inserted") + } + default: + panic("Unknown pool operation") + } + checkEachEntityIsInFreeOrUsedState(t, pool) + } + require.Equal(t, uint32(len(insertedEntities)), pool.Size(), "Pool size doesnt correspond to a number of inserted entities") + for id, indexInThePool := range insertedEntities { + flowIndentifier, _, _ := pool.Get(indexInThePool) + require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") + } + if finalEntitiesinThePool != nil { + for _, idInEntities := range finalEntitiesinThePool { + flowIndentifier, _, owner := pool.Get(insertedEntities[entities[idInEntities].ID()]) + require.Equal(t, flowIndentifier, entities[idInEntities].ID(), "Pool contains an unexpected entity") + require.Equal(t, uint64(idInEntities), owner, "Entity is stored with a wrong owner") + } + } +} + // testInvalidatingHead keeps invalidating the head and evaluates the linked-list keeps updating its head // and remains connected. func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEntity) { From 57216495b3b3600d96b373b81f76c2a0a6c1768f Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 19:30:34 +0200 Subject: [PATCH 320/815] Changed variable name --- module/mempool/herocache/backdata/heropool/pool_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 409fcfdc987..5756fb49169 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -266,7 +266,8 @@ func TestAddAndRemoveEntities(t *testing.T) { // testAddRemoveEntities allows to add or remove entities in an order given by operationsIndexes. Each OperationAndIndex consists of an operaton to perform // on an entity stored under a corresponding index. -func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsIndexes []OperationAndIndex, finalEntitiesinThePool []uint) { +func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsAndIndexes []OperationAndIndex, finalEntitiesinThePool []uint) { + pool := NewHeroPool(limit, ejectionMode) entities := unittest.EntityListFixture(uint(entityCount)) @@ -275,7 +276,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject var insertedEntities map[flow.Identifier]EIndex insertedEntities = make(map[flow.Identifier]EIndex) - for _, operationAndIndex := range operationsIndexes { + for _, operationAndIndex := range operationsAndIndexes { operation := operationAndIndex.operation index := operationAndIndex.index switch operation { @@ -301,7 +302,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject } checkEachEntityIsInFreeOrUsedState(t, pool) } - require.Equal(t, uint32(len(insertedEntities)), pool.Size(), "Pool size doesnt correspond to a number of inserted entities") + require.Equal(t, uint32(len(insertedEntities)), pool.Size(), "Pool size doesn't correspond to a number of inserted entities") for id, indexInThePool := range insertedEntities { flowIndentifier, _, _ := pool.Get(indexInThePool) require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") From 2c2fd7b9f8f4c173140b05e99b83b7283d028c6d Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 20:21:19 +0200 Subject: [PATCH 321/815] Added some go doc descriptions --- module/mempool/herocache/backdata/heropool/pool_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 5756fb49169..cb4ca793fa1 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -10,13 +10,17 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) +// A type of operation to perform on a pool. type OperatioType int const ( + // Add - interpreteded as a command to add an element to a pool. Add OperatioType = iota + // Remove - interpreteded as a command to remove an element from a pool. Remove ) +// OperationAndIndex contains an operation to be perfomed on an antity stored under the specified index of an array of entities. type OperationAndIndex struct { operation OperatioType index uint From e126956a035862130d8eb5281f38d349696ba857 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 20:27:45 +0200 Subject: [PATCH 322/815] Fixed comments and import --- module/mempool/herocache/backdata/heropool/linkedlist.go | 6 ++---- module/mempool/herocache/backdata/heropool/pool.go | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 1abd255bcd5..f5696e42457 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -2,16 +2,14 @@ package heropool // link represents a slice-based doubly linked-list node that // consists of a next and previous poolIndex. -// if a link doesn't belong to any state it's next and prev may have any values, -// but those are treated as invalid and should not be used. +// if a link doesn't belong to any state it's next and prev should hold InvalidIndex. type link struct { next EIndex prev EIndex } // state represents a doubly linked-list by its head and tail pool indices. -// If state has 0 size, its tail's and head's prev and next are treated as invalid. -// Moreover head's prev and tails next are always treated as invalid and may hold any values. +// If state has 0 size, its tail's and head's prev and next are treated as invalid and should hole InvalidIndex values. type state struct { head EIndex tail EIndex diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 90cc0639212..1ebdb30ab33 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,9 +1,10 @@ package heropool import ( + "math" + "github.com/rs/zerolog" - "github.com/ethereum/go-ethereum/common/math" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/rand" ) From f3d0bae32a261920b7a0d780e0091b0c2ff0721f Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 20:48:15 +0200 Subject: [PATCH 323/815] Fixed variable names --- .../herocache/backdata/heropool/pool.go | 2 +- .../herocache/backdata/heropool/pool_test.go | 38 ++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 1ebdb30ab33..a28eb325c1c 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -85,7 +85,7 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log return l } -// setDefaultNodeLinkValues sets nodes prev and nextt to InvalidIndex for all cached entity poolEntities. +// setDefaultNodeLinkValues sets nodes prev and next to InvalidIndex for all cached entity poolEntities. func (p *Pool) setDefaultNodeLinkValues() { for i := 0; i < len(p.poolEntities); i++ { p.poolEntities[i].node.next = InvalidIndex diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index cb4ca793fa1..e29415a4d45 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -20,7 +20,7 @@ const ( Remove ) -// OperationAndIndex contains an operation to be perfomed on an antity stored under the specified index of an array of entities. +// OperationAndIndex contains an operation to be perfomed on an entity stored under the specified index of an array of entities. type OperationAndIndex struct { operation OperatioType index uint @@ -231,52 +231,54 @@ func TestInvalidateEntity(t *testing.T) { } } -// TestAddAndRemoveEntities checks health of heroPool for storing and removing scenarios. LRU, NoEjection and RandomEjection are tested. +// TestAddAndRemoveEntities checks health of heroPool for scenario where entitites are stored and removed in a predetermined order. +// LRUEjection, NoEjection and RandomEjection are tested. RandomEjection doesn't allow to provide a final state of the pool to check. func TestAddAndRemoveEntities(t *testing.T) { for _, tc := range []struct { - limit uint32 // capacity of pool - entityCount uint32 // total entities to be stored - ejectionMode EjectionMode // ejection mode - operationsIndexes []OperationAndIndex // operation to perform on an entity stored by index - finalEntitiesinThePool []uint // indexes of entities residing in the pool at the end of the test + limit uint32 // capacity of the pool + entityCount uint32 // total entities to be stored + ejectionMode EjectionMode // ejection mode + operationsIndexes []OperationAndIndex // operation to perform on an entity stored under the given index, + // where index ranges from 0 to entityCount + finalEntitiesInThePool []uint // indexes of entities residing in the pool at the end of the test }{ { limit: 2, entityCount: 5, ejectionMode: LRUEjection, operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 2}, {Add, 2}, {Remove, 2}}, - finalEntitiesinThePool: []uint{3}, + finalEntitiesInThePool: []uint{3}, }, { limit: 2, entityCount: 5, ejectionMode: NoEjection, operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 0}, {Remove, 1}, {Add, 4}, {Add, 3}}, - finalEntitiesinThePool: []uint{3, 4}, + finalEntitiesInThePool: []uint{3, 4}, }, { limit: 5, entityCount: 6, ejectionMode: RandomEjection, operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Remove, 1}, {Add, 2}, {Remove, 0}, {Add, 5}, {Add, 4}, {Add, 1}, {Add, 3}, {Add, 0}}, - finalEntitiesinThePool: nil, + finalEntitiesInThePool: nil, }, } { t.Run(fmt.Sprintf("%d-limit-%d-entities", tc.limit, tc.entityCount), func(t *testing.T) { - testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.operationsIndexes, tc.finalEntitiesinThePool) + testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.operationsIndexes, tc.finalEntitiesInThePool) }) } } // testAddRemoveEntities allows to add or remove entities in an order given by operationsIndexes. Each OperationAndIndex consists of an operaton to perform -// on an entity stored under a corresponding index. -func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsAndIndexes []OperationAndIndex, finalEntitiesinThePool []uint) { +// on an entity stored under a corresponding index. Index range from 0 to entityCount. +func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsAndIndexes []OperationAndIndex, finalEntitiesInThePool []uint) { pool := NewHeroPool(limit, ejectionMode) entities := unittest.EntityListFixture(uint(entityCount)) - // this map maintains entities inserted into the pool + // this map maintains entities currently stored in the pool var insertedEntities map[flow.Identifier]EIndex insertedEntities = make(map[flow.Identifier]EIndex) @@ -311,8 +313,8 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject flowIndentifier, _, _ := pool.Get(indexInThePool) require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") } - if finalEntitiesinThePool != nil { - for _, idInEntities := range finalEntitiesinThePool { + if finalEntitiesInThePool != nil { + for _, idInEntities := range finalEntitiesInThePool { flowIndentifier, _, owner := pool.Get(insertedEntities[entities[idInEntities].ID()]) require.Equal(t, flowIndentifier, entities[idInEntities].ID(), "Pool contains an unexpected entity") require.Equal(t, uint64(idInEntities), owner, "Entity is stored with a wrong owner") @@ -810,7 +812,7 @@ func headAccessibleFromTail(t *testing.T, headSliceIndex EIndex, tailSliceIndex } } -// checks if each entity in the pool belongs exactly to one of the sate lists +// checkEachEntityIsInFreeOrUsedState checks if each entity in the pool belongs exactly to one of the state lists. func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { pool_capacity := len(pool.poolEntities) // check size @@ -824,7 +826,7 @@ func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { } } -// discovers all entities in the pool that belong to the given list +// discoverEntitiesBelongingToStateList discovers all entities in the pool that belong to the given list. func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, s *state) []bool { result := make([]bool, len(pool.poolEntities)) for node_index := s.head; node_index != InvalidIndex; { From 5e7c93c628b84ed355010490a498725df7a94b5d Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 23 Jun 2023 21:02:17 +0200 Subject: [PATCH 324/815] Add a comment to invalid index --- module/mempool/herocache/backdata/heropool/pool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index a28eb325c1c..74225de91b2 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -20,6 +20,7 @@ const ( // EIndex is data type representing an entity index in Pool. type EIndex uint32 +// InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil adress. const InvalidIndex EIndex = math.MaxUint32 // poolEntity represents the data type that is maintained by From dbb1a064dc03a8ab88e2f3504f708eef41586ddf Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 30 Jun 2023 13:05:25 +0200 Subject: [PATCH 325/815] Intoduced stateUsed and stateFree --- .../herocache/backdata/heropool/pool.go | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 74225de91b2..32e0245493e 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -23,6 +23,15 @@ type EIndex uint32 // InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil adress. const InvalidIndex EIndex = math.MaxUint32 +// A type dedicated to describe possible states of placeholders for entities in the pool. +type StateType string + +// A placeholder in a free state can be used to store an entity. +const stateFree StateType = "free-state" + +// A placeholder in a used state stores currently an entity. +const stateUsed StateType = "used-state" + // poolEntity represents the data type that is maintained by type poolEntity struct { PoolEntity @@ -97,7 +106,7 @@ func (p *Pool) setDefaultNodeLinkValues() { // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { for i := 0; i < len(p.poolEntities); i++ { - p.appendEntity(&p.free, EIndex(i)) + p.appendEntity(stateFree, EIndex(i)) } } @@ -117,7 +126,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - p.appendEntity(&p.used, entityIndex) + p.appendEntity(stateUsed, entityIndex) } return entityIndex, slotAvailable, ejectedEntity @@ -251,7 +260,7 @@ func (p *Pool) invalidateUsedHead() flow.Entity { // old free head to host a new entity. func (p *Pool) claimFreeHead() EIndex { oldFreeHeadIndex := p.free.head - p.removeEntity(&p.free, oldFreeHeadIndex) + p.removeEntity(stateFree, oldFreeHeadIndex) return oldFreeHeadIndex } @@ -266,10 +275,10 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { poolEntity := p.poolEntities[sliceIndex] invalidatedEntity := poolEntity.entity - p.removeEntity(&p.used, sliceIndex) + p.removeEntity(stateUsed, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.appendEntity(&p.free, EIndex(sliceIndex)) + p.appendEntity(stateFree, EIndex(sliceIndex)) return invalidatedEntity } @@ -288,9 +297,24 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { return true } +// a helper method that allows to get an adress fo the state form the state type. +func (p *Pool) getStateFromType(stateType StateType) *state { + var s *state = nil + switch stateType { + case stateFree: + s = &p.free + case stateUsed: + s = &p.used + default: + panic("Unknown state type") + } + return s +} + // utility method that removes an entity from one of the states. // NOTE: a removed entity has to be added to another state. -func (p *Pool) removeEntity(s *state, entityIndex EIndex) { +func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { + var s *state = p.getStateFromType(stateType) if s.size == 0 { panic("Removing an entity from the empty list") } @@ -328,7 +352,9 @@ func (p *Pool) removeEntity(s *state, entityIndex EIndex) { // appends an entity to the tail of the state or creates a first element. // NOTE: entity should not be in any list before this method is applied -func (p *Pool) appendEntity(s *state, entityIndex EIndex) { +func (p *Pool) appendEntity(stateType StateType, entityIndex EIndex) { + var s *state = p.getStateFromType(stateType) + if s.size == 0 { s.head = entityIndex s.tail = entityIndex From 08ca9a13725c597432c93d412010a152d627a0fa Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 30 Jun 2023 23:59:02 +0200 Subject: [PATCH 326/815] Modified test to include randoim operations --- .../herocache/backdata/heropool/linkedlist.go | 2 +- .../herocache/backdata/heropool/pool_test.go | 164 +++++++++++++----- .../heropool/pool_test_operations.txt | 1 + .../heropool/pool_test_random_numbers.txt | 1 + 4 files changed, 119 insertions(+), 49 deletions(-) create mode 100644 module/mempool/herocache/backdata/heropool/pool_test_operations.txt create mode 100644 module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index f5696e42457..99823070d69 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -9,7 +9,7 @@ type link struct { } // state represents a doubly linked-list by its head and tail pool indices. -// If state has 0 size, its tail's and head's prev and next are treated as invalid and should hole InvalidIndex values. +// If state has 0 size, its tail's and head's prev and next are treated as invalid and should hold InvalidIndex values. type state struct { head EIndex tail EIndex diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index e29415a4d45..4ae03e2ccd6 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -1,7 +1,11 @@ package heropool import ( + "errors" "fmt" + "io/ioutil" + "strconv" + "strings" "testing" "github.com/stretchr/testify/require" @@ -11,18 +15,18 @@ import ( ) // A type of operation to perform on a pool. -type OperatioType int +type OperationType int const ( // Add - interpreteded as a command to add an element to a pool. - Add OperatioType = iota + Add OperationType = iota // Remove - interpreteded as a command to remove an element from a pool. Remove ) // OperationAndIndex contains an operation to be perfomed on an entity stored under the specified index of an array of entities. type OperationAndIndex struct { - operation OperatioType + operation OperationType index uint } @@ -231,48 +235,102 @@ func TestInvalidateEntity(t *testing.T) { } } +// a helper function that converts an operation encoded as 0 or 1 to Add or Remove. +func stringToOperationType(s string) (OperationType, error) { + switch s { + case "0": + return Add, nil + case "1": + return Remove, nil + default: + return Add, errors.New("Unknown operation") + } +} + +// reads random number used as ownerId and operation to perform on flow enitites. +func readAndParseData() ([]uint, []OperationAndIndex, error) { + fileRandomNumbers := "pool_test_random_numbers.txt" + contentNumbers, err := ioutil.ReadFile(fileRandomNumbers) + + if err != nil { + return nil, nil, err + } + + ownerRandomNumbersStr := strings.Fields(string(contentNumbers)) + ownerRandomNumbersUint := make([]uint, len(ownerRandomNumbersStr), len(ownerRandomNumbersStr)) + for i, numberStr := range ownerRandomNumbersStr { + numberInt, err := strconv.Atoi(numberStr) + if err != nil { + return nil, nil, err + } + ownerRandomNumbersUint[i] = uint(numberInt) + } + + fileOperations := "pool_test_operations.txt" + content_operations, err := ioutil.ReadFile(fileOperations) + + operations := strings.Fields(string(content_operations)) + operationsOnEntity := make([]OperationAndIndex, len(operations), len(operations)) + for i, operation := range operations { + operationAndIndexSplit := strings.Split(operation, ",") + indexEntity, err := strconv.Atoi(operationAndIndexSplit[1]) + if err != nil { + return nil, nil, err + } + operation, err := stringToOperationType(operationAndIndexSplit[0]) + if err != nil { + return nil, nil, err + } + operationsOnEntity[i] = OperationAndIndex{operation: operation, index: uint(indexEntity)} + } + return ownerRandomNumbersUint, operationsOnEntity, nil +} + // TestAddAndRemoveEntities checks health of heroPool for scenario where entitites are stored and removed in a predetermined order. // LRUEjection, NoEjection and RandomEjection are tested. RandomEjection doesn't allow to provide a final state of the pool to check. func TestAddAndRemoveEntities(t *testing.T) { + ownerIds, operationsAndIndex, err := readAndParseData() + require.Nil(t, err) + for _, tc := range []struct { - limit uint32 // capacity of the pool - entityCount uint32 // total entities to be stored - ejectionMode EjectionMode // ejection mode + limit uint32 // capacity of the pool + entityCount uint32 // total entities to be stored + ejectionMode EjectionMode // ejection mode + ownerIds []uint operationsIndexes []OperationAndIndex // operation to perform on an entity stored under the given index, - // where index ranges from 0 to entityCount - finalEntitiesInThePool []uint // indexes of entities residing in the pool at the end of the test + // where index ranges from 0 to the entityCount }{ { - limit: 2, - entityCount: 5, - ejectionMode: LRUEjection, - operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 2}, {Add, 2}, {Remove, 2}}, - finalEntitiesInThePool: []uint{3}, + limit: 500, + entityCount: 1000, + ejectionMode: LRUEjection, + ownerIds: ownerIds, + operationsIndexes: operationsAndIndex[0:1000], }, { - limit: 2, - entityCount: 5, - ejectionMode: NoEjection, - operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Add, 2}, {Add, 3}, {Remove, 0}, {Remove, 1}, {Add, 4}, {Add, 3}}, - finalEntitiesInThePool: []uint{3, 4}, + limit: 500, + entityCount: 1000, + ejectionMode: NoEjection, + ownerIds: ownerIds, + operationsIndexes: operationsAndIndex[0:1000], }, { - limit: 5, - entityCount: 6, - ejectionMode: RandomEjection, - operationsIndexes: []OperationAndIndex{{Add, 0}, {Add, 1}, {Remove, 1}, {Add, 2}, {Remove, 0}, {Add, 5}, {Add, 4}, {Add, 1}, {Add, 3}, {Add, 0}}, - finalEntitiesInThePool: nil, + limit: 500, + entityCount: 1000, + ejectionMode: RandomEjection, + ownerIds: ownerIds, + operationsIndexes: operationsAndIndex[0:1000], }, } { t.Run(fmt.Sprintf("%d-limit-%d-entities", tc.limit, tc.entityCount), func(t *testing.T) { - testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.operationsIndexes, tc.finalEntitiesInThePool) + testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.ownerIds, tc.operationsIndexes) }) } } // testAddRemoveEntities allows to add or remove entities in an order given by operationsIndexes. Each OperationAndIndex consists of an operaton to perform // on an entity stored under a corresponding index. Index range from 0 to entityCount. -func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, operationsAndIndexes []OperationAndIndex, finalEntitiesInThePool []uint) { +func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, ownerIds []uint, operationsAndIndexes []OperationAndIndex) { pool := NewHeroPool(limit, ejectionMode) @@ -282,43 +340,52 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject var insertedEntities map[flow.Identifier]EIndex insertedEntities = make(map[flow.Identifier]EIndex) + // this map maps a random owner id to an entity index in an array of entities + var ownerIdToIndex map[uint64]uint + ownerIdToIndex = make(map[uint64]uint) + for i := 0; i < int(entityCount); i++ { + ownerIdToIndex[uint64(ownerIds[i])] = uint(i) + } + for _, operationAndIndex := range operationsAndIndexes { + operation := operationAndIndex.operation - index := operationAndIndex.index + entityIndex := operationAndIndex.index + switch operation { case Add: - indexInThePool, _, ejectedEntity := pool.Add(entities[index].ID(), entities[index], uint64(index)) - if indexInThePool != InvalidIndex { - insertedEntities[entities[index].ID()] = indexInThePool - } - if ejectedEntity != nil { - delete(insertedEntities, ejectedEntity.ID()) + _, found := insertedEntities[entities[entityIndex].ID()] + if !found { + indexInThePool, _, ejectedEntity := pool.Add(entities[entityIndex].ID(), entities[entityIndex], uint64(ownerIds[entityIndex])) + if indexInThePool != InvalidIndex { + insertedEntities[entities[entityIndex].ID()] = indexInThePool + } + if ejectedEntity != nil { + delete(insertedEntities, ejectedEntity.ID()) + } } case Remove: - indexInPool, found := insertedEntities[entities[index].ID()] + indexInPool, found := insertedEntities[entities[entityIndex].ID()] if found { removedEntity := pool.Remove(indexInPool) - require.Equal(t, entities[index].ID(), removedEntity.ID(), "Removed wrong entity") - delete(insertedEntities, entities[index].ID()) - } else { - panic("Ill constructed test, trying to remove an element that has not been inserted") + require.Equal(t, entities[entityIndex].ID(), removedEntity.ID(), "Removed wrong entity") + delete(insertedEntities, entities[entityIndex].ID()) } default: - panic("Unknown pool operation") + require.True(t, false, "Unknown pool operation") } checkEachEntityIsInFreeOrUsedState(t, pool) } + require.Equal(t, uint32(len(insertedEntities)), pool.Size(), "Pool size doesn't correspond to a number of inserted entities") for id, indexInThePool := range insertedEntities { - flowIndentifier, _, _ := pool.Get(indexInThePool) + flowIndentifier, _, ownerIdActual := pool.Get(indexInThePool) require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") - } - if finalEntitiesInThePool != nil { - for _, idInEntities := range finalEntitiesInThePool { - flowIndentifier, _, owner := pool.Get(insertedEntities[entities[idInEntities].ID()]) - require.Equal(t, flowIndentifier, entities[idInEntities].ID(), "Pool contains an unexpected entity") - require.Equal(t, uint64(idInEntities), owner, "Entity is stored with a wrong owner") + tmp := ownerIdToIndex[ownerIdActual] + if entities[tmp].Identifier != id { + fmt.Print(" ") } + require.Equal(t, entities[ownerIdToIndex[ownerIdActual]].Identifier, id, "Pool contains an entity with an unexpected owner id") } } @@ -818,8 +885,8 @@ func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { // check size require.Equal(t, int(pool.free.size+pool.used.size), pool_capacity, "Pool capacity is not equal to the sum of used and free sizes") // check elelments - nodesInFree := discoverEntitiesBelongingToStateList(t, pool, &pool.free) - nodesInUsed := discoverEntitiesBelongingToStateList(t, pool, &pool.used) + nodesInFree := discoverEntitiesBelongingToStateList(t, pool, stateFree) + nodesInUsed := discoverEntitiesBelongingToStateList(t, pool, stateUsed) for i := 0; i < pool_capacity; i++ { require.False(t, !nodesInFree[i] && !nodesInUsed[i], "Node is not in any state list") require.False(t, nodesInFree[i] && nodesInUsed[i], "Node is in two state lists at the same time") @@ -827,7 +894,8 @@ func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { } // discoverEntitiesBelongingToStateList discovers all entities in the pool that belong to the given list. -func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, s *state) []bool { +func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, stateType StateType) []bool { + s := pool.getStateFromType(stateType) result := make([]bool, len(pool.poolEntities)) for node_index := s.head; node_index != InvalidIndex; { require.False(t, result[node_index], "A node is present two times in the same state list") diff --git a/module/mempool/herocache/backdata/heropool/pool_test_operations.txt b/module/mempool/herocache/backdata/heropool/pool_test_operations.txt new file mode 100644 index 00000000000..9d583e742cc --- /dev/null +++ b/module/mempool/herocache/backdata/heropool/pool_test_operations.txt @@ -0,0 +1 @@ +1,777 0,587 0,711 0,184 1,932 0,254 1,980 1,613 1,868 0,632 0,402 0,665 1,494 1,171 0,84 0,257 0,608 1,793 0,883 0,131 1,706 1,647 0,430 0,530 1,819 1,674 0,135 0,420 0,944 1,399 1,551 0,372 0,62 0,676 1,608 1,123 0,321 0,221 0,379 0,660 1,398 1,188 0,842 1,119 0,637 0,315 1,750 1,678 1,791 1,346 0,879 0,499 1,862 0,413 0,338 1,477 1,998 0,897 0,187 1,693 0,578 0,490 0,606 0,105 1,936 0,848 1,742 1,621 0,109 1,226 1,728 0,76 1,908 1,754 0,432 0,560 1,710 1,486 0,123 0,45 0,888 1,862 0,518 0,40 0,795 1,576 0,979 1,511 0,617 1,379 0,155 0,991 0,982 1,864 0,201 1,99 1,909 1,459 0,414 1,525 0,3 1,122 1,752 1,646 1,307 0,937 0,910 1,531 0,124 0,141 0,374 0,741 0,587 0,346 1,726 1,21 0,590 1,133 0,172 0,69 0,133 1,652 0,905 0,103 0,294 1,991 0,166 1,301 0,37 1,739 1,763 1,289 0,529 0,13 0,612 1,537 0,31 1,867 0,280 0,920 1,609 0,399 0,754 0,472 1,19 1,322 1,251 1,354 1,903 0,412 1,497 0,288 0,940 1,684 0,589 1,911 0,728 0,439 0,791 1,193 1,454 0,789 1,866 0,970 1,862 0,66 0,754 1,233 0,751 0,962 1,844 1,947 0,224 0,919 0,6 0,626 0,436 0,911 0,252 1,99 0,413 1,869 1,917 0,872 0,952 1,350 1,773 0,185 0,133 1,197 0,672 0,641 1,752 0,126 0,430 1,675 1,89 1,141 0,899 0,101 0,891 1,304 0,704 0,243 1,914 0,846 0,975 1,656 0,11 0,657 1,364 0,160 1,736 1,88 0,839 0,908 1,731 1,566 0,530 0,826 0,439 1,272 0,918 1,819 0,721 1,359 0,765 0,661 1,685 1,107 1,644 0,527 1,240 1,488 1,702 1,485 1,575 0,857 0,707 0,749 1,340 0,446 1,611 0,16 0,220 1,567 0,131 0,460 0,864 0,343 0,670 0,223 1,369 1,388 0,792 0,107 1,259 1,961 0,904 0,670 0,494 0,377 0,395 0,724 1,313 1,109 1,167 1,531 0,528 1,443 1,884 0,525 0,872 1,260 1,526 1,466 0,908 0,664 1,682 0,832 0,925 1,483 1,263 1,576 1,818 0,622 1,577 0,50 0,251 1,825 1,610 1,943 1,218 1,227 0,799 1,491 0,644 1,772 1,818 1,799 1,308 1,751 0,573 1,15 1,622 1,486 0,549 1,924 0,782 0,366 1,641 0,490 0,362 1,943 0,872 0,421 1,577 1,967 0,101 0,667 0,528 0,269 1,542 1,380 0,957 0,218 1,673 0,188 0,835 0,556 0,544 1,987 0,96 0,966 0,197 0,884 1,917 1,908 0,677 1,280 1,753 1,79 1,558 1,11 1,246 0,626 1,235 0,203 0,747 0,173 1,772 0,507 0,92 0,896 1,945 0,921 0,106 1,957 0,240 0,748 0,840 1,354 1,630 1,318 0,842 0,787 0,230 1,884 1,125 0,807 0,934 1,124 1,55 1,323 1,158 1,208 0,197 0,756 1,425 0,716 0,541 0,454 0,550 1,641 0,318 0,336 1,547 0,142 0,57 1,184 0,218 0,112 1,286 0,308 1,344 0,894 1,643 0,242 0,190 1,687 0,198 1,160 0,309 1,207 0,609 0,149 1,258 1,730 1,152 0,643 1,76 1,264 1,688 1,812 0,53 1,287 1,601 0,60 1,582 0,59 1,18 0,190 1,915 0,421 0,267 0,487 1,947 0,976 1,893 0,84 1,420 1,622 0,191 0,324 0,618 1,627 0,287 0,301 0,656 0,292 0,197 0,69 1,356 0,963 0,437 1,726 1,932 0,771 1,538 1,683 0,116 1,586 0,476 0,52 0,901 0,884 1,953 1,262 1,947 1,716 0,600 0,25 0,849 1,160 1,594 0,297 0,110 1,354 0,164 1,158 0,885 0,134 0,301 1,388 1,834 1,721 1,433 1,496 1,338 1,765 1,289 0,466 1,922 0,594 1,668 1,915 1,587 0,881 1,857 0,685 1,627 1,690 0,248 0,732 1,849 1,955 0,40 1,485 1,346 0,898 1,341 1,614 0,222 1,478 1,248 1,122 0,86 1,492 0,397 0,274 1,1 0,970 1,752 0,711 1,866 0,221 1,544 0,523 0,808 1,462 1,130 0,215 1,71 1,490 0,750 1,760 0,515 1,134 0,225 0,460 0,725 0,876 0,878 1,664 0,79 1,150 1,342 1,487 0,24 0,31 1,869 1,264 1,437 1,332 0,261 1,541 1,38 1,746 0,179 1,475 1,929 1,72 1,570 0,889 0,919 0,923 0,567 1,872 1,341 0,207 0,15 1,901 1,155 0,283 0,849 0,238 0,126 0,784 0,638 1,688 0,543 1,4 1,332 1,965 1,949 0,270 0,739 1,717 0,37 1,984 1,700 1,144 1,679 0,966 1,629 1,556 0,621 0,280 1,423 1,439 1,929 1,142 0,54 1,228 1,97 0,705 0,628 1,275 0,571 0,963 1,252 1,329 0,990 0,105 1,864 0,12 0,912 0,794 1,679 0,258 0,487 0,857 0,335 0,573 0,57 0,384 0,869 0,824 1,149 1,928 0,554 1,572 1,617 1,828 1,910 0,28 0,279 0,375 1,187 0,635 1,832 1,46 1,279 0,934 0,341 0,630 1,600 0,676 0,311 1,199 1,343 1,398 0,43 1,447 0,526 1,479 1,638 0,870 0,772 0,58 1,310 0,88 1,209 1,470 0,868 0,511 1,156 0,681 0,249 1,766 1,491 1,292 0,498 0,207 1,454 1,249 1,589 0,950 0,813 1,169 0,339 0,236 1,368 0,621 1,537 0,741 0,937 1,404 1,992 1,17 0,517 0,830 0,131 0,171 0,247 1,650 0,825 1,918 0,230 0,806 0,145 0,529 0,726 1,480 0,992 0,99 0,671 0,50 1,991 0,623 1,181 1,848 0,679 0,131 1,413 0,529 1,186 0,388 0,806 1,630 1,69 0,367 1,327 1,980 0,5 1,118 1,625 0,844 0,537 0,888 1,673 1,935 1,582 0,212 1,61 1,246 0,979 1,487 1,393 1,862 1,291 1,127 0,848 0,573 0,278 1,573 0,894 1,255 0,575 1,943 0,689 1,985 0,194 0,299 1,599 1,908 0,760 0,996 0,741 1,977 0,956 1,394 0,165 0,778 0,622 0,724 0,866 0,452 1,759 0,566 0,622 1,690 1,385 0,820 1,748 1,828 1,977 0,384 0,119 0,371 1,545 1,537 1,510 1,381 0,594 1,95 0,979 0,262 0,777 1,928 1,919 0,914 0,370 0,164 1,78 0,961 0,633 0,687 1,939 0,160 0,644 0,128 0,350 1,400 1,643 1,399 0,605 1,688 1,233 0,116 1,999 1,717 0,101 1,508 1,54 1,704 1,578 0,91 0,761 1,14 0,303 0,539 1,692 1,548 1,238 1,217 1,88 1,844 1,257 1,911 0,919 1,632 1,189 1,297 0,627 1,126 0,162 1,700 1,662 0,205 0,128 1,993 0,196 0,454 1,307 1,686 1,382 1,682 0,849 0,445 1,374 1,13 0,495 1,885 0,81 0,42 0,583 0,750 0,374 0,991 0,239 1,841 1,281 1,160 0,415 1,821 1,216 1,124 1,298 0,461 1,58 0,949 1,729 1,383 1,343 0,285 0,146 1,248 0,220 1,248 1,193 0,185 0,807 0,69 0,450 1,773 1,179 1,703 1,554 1,721 0,448 0,697 1,789 1,26 1,747 0,527 0,974 1,230 1,648 0,851 1,337 1,845 1,207 0,588 0,128 1,638 1,265 1,398 1,217 0,634 1,155 0,389 0,529 0,502 0,360 1,45 1,308 0,63 1,64 1,843 1,815 1,202 1,641 1,22 0,342 1,973 1,385 1,865 0,703 1,884 1,95 1,282 1,28 1,781 0,745 1,141 0,17 0,512 0,199 0,421 1,21 0,291 1,833 1,333 0,882 1,949 0,196 0,322 0,283 0,483 0,952 0,591 1,850 1,917 1,641 1,348 1,131 0,628 1,320 0,748 1,770 0,884 0,852 1,880 0,767 0,675 0,138 1,419 0,127 1,843 1,779 0,958 1,575 0,585 0,986 0,839 0,260 0,807 1,608 0,575 0,876 0,290 1,167 0,443 0,596 1,809 0,623 0,675 1,406 1,656 0,507 1,846 1,418 0,636 1,350 0,539 1,125 0,744 1,931 1,479 1,697 1,352 0,717 0,352 1,973 1,191 1,883 1,377 0,745 1,385 0,884 1,243 1,772 1,558 1,124 1,867 0,218 0,750 1,715 1,284 0,901 1,545 1,461 0,230 0,615 0,592 0,774 1,814 0,950 0,418 1,562 0,436 0,327 1,547 0,417 1,566 0,784 1,555 1,465 1,12 1,437 0,367 1,515 1,378 0,564 1,393 1,865 1,72 0,650 1,447 0,565 1,749 0,683 1,99 0,918 1,544 1,849 0,732 0,228 1,203 1,256 1,634 0,29 1,703 1,858 0,381 1,938 0,547 1,551 0,21 0,552 1,41 0,632 1,959 1,563 1,705 1,379 0,698 0,152 0,430 0,217 0,272 0,325 1,331 1,458 0,231 0,867 1,997 0,813 0,630 0,519 0,744 0,527 1,984 0,792 1,695 1,591 0,323 0,285 0,479 1,763 0,807 1,245 1,503 1,603 1,773 1,647 1,321 0,19 0,295 0,767 1,799 0,510 1,935 0,739 0,395 1,532 0,902 0,631 0,295 0,544 0,922 0,362 1,56 0,581 1,80 1,738 0,243 0,351 0,74 1,200 0,254 0,828 0,831 1,860 1,743 1,273 1,734 1,202 0,604 0,257 1,459 1,378 0,887 1,421 0,858 0,538 1,521 1,849 1,271 0,979 0,701 1,110 1,149 0,853 0,595 0,177 1,449 1,354 0,583 0,878 1,254 0,621 1,152 1,158 0,558 1,752 0,718 0,26 1,674 1,172 0,143 0,644 1,681 0,546 1,393 1,458 1,823 0,677 0,498 1,969 1,377 1,923 0,485 1,786 1,19 1,682 1,118 0,200 0,879 1,111 0,336 1,207 1,857 1,823 0,847 1,880 0,388 0,953 0,371 0,448 1,474 0,828 1,330 1,512 1,604 1,306 1,284 1,858 1,556 0,338 0,380 1,249 0,995 0,417 0,118 0,603 1,255 0,221 0,984 1,222 1,573 1,221 0,979 0,280 0,425 1,486 1,710 1,465 0,675 1,745 1,912 1,578 0,968 1,218 1,630 0,131 0,400 0,831 0,429 0,888 1,998 0,924 1,563 1,325 1,913 0,135 1,548 0,56 1,125 0,93 1,89 0,465 1,492 0,779 1,240 0,503 1,475 0,730 0,399 1,81 0,715 1,874 0,243 1,248 1,682 1,872 0,701 1,847 0,509 1,108 0,768 0,722 1,152 1,992 0,923 0,147 0,218 0,160 1,144 1,545 0,537 0,381 0,735 1,162 1,663 0,844 0,533 1,507 0,182 1,472 0,279 0,369 0,961 1,464 0,540 0,119 0,689 1,875 1,240 1,520 1,881 1,983 0,708 0,889 1,905 0,138 0,163 0,396 0,137 0,857 1,709 1,275 0,579 1,103 1,781 0,294 1,595 0,35 1,394 1,294 1,787 0,296 1,595 1,392 0,740 0,442 0,98 1,373 1,323 1,315 1,332 0,605 1,933 1,326 1,973 1,893 1,698 1,966 0,928 0,786 1,361 1,683 1,412 1,137 1,655 1,704 1,708 1,972 1,850 1,287 1,266 0,119 0,819 1,952 1,342 1,243 0,978 0,49 1,100 0,631 0,522 1,173 1,677 0,778 0,567 0,436 0,830 0,849 0,581 0,998 0,176 0,965 1,545 1,454 0,343 0,456 1,603 1,566 1,228 0,889 0,704 0,143 0,763 1,300 1,232 0,403 0,633 1,850 1,66 0,892 0,114 1,445 1,710 0,172 0,188 0,247 1,110 1,989 1,310 1,299 0,902 1,446 0,298 0,180 0,823 0,620 1,696 0,182 0,15 0,44 1,467 1,506 1,737 0,473 1,994 1,229 1,739 0,145 0,847 1,133 0,836 1,15 0,797 1,689 1,843 0,115 0,210 1,652 0,887 0,366 0,989 1,342 0,592 0,152 1,662 1,18 1,270 0,757 0,902 0,763 0,62 1,940 1,743 1,142 1,42 1,5 0,781 0,230 1,131 0,751 1,177 1,786 1,820 0,309 1,564 0,719 0,13 0,815 0,451 1,510 0,77 0,844 1,860 1,373 0,470 0,453 1,747 1,942 1,703 0,673 0,945 1,552 0,111 0,433 1,610 0,63 0,386 1,836 1,765 1,246 1,935 1,576 0,946 0,463 0,608 1,679 1,811 1,308 1,658 0,160 0,172 1,828 0,954 1,818 1,568 1,103 0,546 1,283 1,386 0,249 1,834 0,613 0,478 1,564 1,342 0,400 0,3 1,635 1,382 1,798 0,42 1,38 0,776 1,228 0,905 0,309 1,230 1,312 1,582 1,189 0,665 1,524 0,768 1,421 0,696 1,422 1,293 0,421 0,109 0,888 1,265 0,779 0,139 1,164 1,321 0,367 1,344 0,886 0,212 1,361 0,558 1,845 0,836 0,840 0,821 1,573 1,9 0,607 0,924 1,439 0,993 0,818 0,966 1,42 1,806 1,339 0,622 1,871 1,639 0,891 1,697 1,255 1,68 0,423 1,852 1,539 0,425 1,182 1,473 0,323 1,855 1,277 1,25 1,726 0,653 0,803 0,841 1,972 0,275 0,967 0,301 0,436 0,596 1,415 0,304 0,937 0,727 1,123 1,708 1,349 0,738 1,129 1,569 0,520 1,599 1,311 0,504 0,999 1,731 0,844 1,585 1,809 0,449 0,99 0,5 1,271 0,812 1,82 1,313 0,746 1,577 1,465 0,384 1,183 1,568 0,68 0,373 1,959 0,177 0,910 0,852 0,268 1,828 0,590 0,578 0,525 0,793 0,993 0,814 0,784 1,41 0,811 1,176 1,687 0,171 1,209 1,470 1,262 0,212 0,979 0,567 0,804 0,794 0,109 1,343 0,874 1,811 1,779 1,788 0,362 0,244 1,473 0,450 0,315 0,734 0,121 0,81 1,425 1,760 1,882 1,621 1,757 1,404 0,759 0,228 1,590 1,201 0,107 0,979 0,656 1,376 0,553 1,878 0,238 1,962 1,934 0,238 0,879 0,817 0,488 1,446 1,920 1,902 0,168 1,190 1,143 1,139 0,83 0,509 1,313 1,135 1,362 1,338 0,96 1,12 0,138 0,511 0,752 1,794 1,62 0,875 1,35 1,149 1,162 0,992 1,168 1,373 1,515 1,634 1,343 0,250 0,472 0,68 0,720 1,160 0,686 1,698 1,865 1,861 0,161 1,749 1,992 1,810 0,107 1,97 0,510 0,271 0,853 0,446 1,367 1,660 1,624 1,524 0,954 0,332 0,526 0,305 0,179 1,115 0,972 1,271 0,118 1,68 0,164 1,269 0,697 1,370 0,862 1,420 0,692 0,732 0,877 1,297 0,369 0,693 0,510 1,984 1,733 1,282 1,902 0,737 1,551 1,233 1,111 1,367 1,774 1,247 1,922 1,724 0,33 1,460 0,859 1,574 0,910 0,290 0,292 1,132 1,122 1,957 0,258 1,257 0,847 1,435 0,845 0,731 1,387 0,967 1,579 0,285 1,347 0,631 1,546 1,215 0,391 1,762 1,485 0,489 0,483 0,887 0,469 1,993 0,657 1,827 1,564 0,867 1,88 0,979 1,846 1,711 1,671 1,752 1,451 1,461 1,102 1,742 1,9 1,736 0,685 1,984 0,487 1,784 1,33 0,274 0,555 1,856 1,462 0,235 0,786 1,397 1,441 1,84 0,11 1,151 1,457 0,450 1,942 1,94 1,447 1,90 1,345 1,557 1,699 0,403 0,320 1,108 1,400 1,359 0,368 0,109 1,846 0,583 1,555 0,282 0,150 1,833 0,885 1,44 1,36 0,98 0,839 0,279 0,683 0,798 1,945 1,898 1,904 0,376 0,619 0,665 1,832 0,930 1,451 0,253 0,647 0,332 1,347 1,453 0,993 1,150 0,196 1,904 1,119 1,84 0,81 0,413 1,596 0,995 0,918 1,415 0,963 0,180 0,765 1,100 1,708 0,257 1,801 0,442 0,840 0,150 1,128 1,965 1,748 0,625 1,478 0,683 0,676 0,55 0,291 0,304 1,230 1,396 0,278 0,45 1,951 1,126 0,762 1,90 0,430 0,31 0,236 1,458 0,75 0,901 1,117 0,78 0,907 0,585 1,139 0,438 1,702 1,866 1,510 0,233 1,710 1,620 0,935 0,870 1,481 0,673 1,690 1,455 0,884 0,211 1,203 0,908 0,312 1,383 0,595 0,88 1,601 0,157 1,901 0,449 1,315 0,407 1,372 0,974 1,433 1,781 0,689 0,718 0,253 1,445 1,956 1,157 0,340 1,17 1,83 1,883 1,669 1,415 1,272 1,130 0,739 0,880 1,818 0,201 1,295 1,262 0,344 0,438 1,372 1,824 0,534 0,385 0,795 0,333 0,430 0,382 1,45 1,857 1,435 0,553 0,170 1,160 0,907 1,949 1,657 0,938 0,117 1,773 0,497 0,807 0,322 0,22 1,913 0,458 1,599 0,623 1,602 1,774 0,846 1,995 0,772 0,959 1,43 1,244 1,32 1,460 0,395 1,644 0,640 0,819 0,656 1,983 1,371 1,661 0,480 1,295 1,480 0,604 0,384 0,861 0,342 0,401 0,449 0,837 0,60 1,932 1,528 0,950 1,358 1,302 1,633 1,331 0,371 1,3 0,221 1,860 1,72 1,420 1,281 1,89 1,108 0,566 0,407 1,343 1,531 1,232 0,961 0,298 0,739 0,76 1,529 0,869 0,70 0,192 1,178 1,663 1,918 1,292 0,36 1,980 0,820 0,943 0,654 0,883 0,690 0,769 1,402 0,768 0,635 1,675 0,709 0,326 1,679 1,324 1,940 0,163 1,760 0,989 1,892 0,771 1,930 0,811 1,880 0,583 0,935 1,164 1,954 1,200 0,481 0,705 1,853 0,455 0,70 0,166 1,851 0,570 1,237 0,117 0,891 0,208 0,741 0,259 0,360 0,300 0,965 0,776 1,465 0,840 1,208 1,961 0,856 1,601 1,856 0,322 0,18 0,132 1,440 1,80 0,872 1,291 0,468 0,483 1,536 0,208 0,21 0,976 1,992 1,238 0,410 1,851 0,952 0,28 1,154 0,107 0,625 0,517 1,679 0,335 0,621 0,258 0,707 0,399 1,85 0,621 0,489 1,491 0,988 1,810 1,558 0,275 0,371 1,183 1,113 0,495 0,365 0,302 0,950 0,289 1,955 0,715 1,653 0,596 0,207 1,765 1,602 1,888 0,728 1,561 0,898 1,875 0,918 1,493 1,772 1,364 1,858 0,744 0,128 0,663 1,90 0,120 0,627 1,938 0,984 1,787 0,988 1,954 1,311 0,839 1,140 1,834 0,944 0,949 0,865 1,49 0,402 0,898 0,738 1,558 1,748 0,110 0,793 0,250 1,109 1,919 1,730 0,44 0,989 0,644 0,703 1,245 1,222 0,26 1,105 0,864 0,123 1,564 0,850 1,342 0,155 1,349 1,999 0,123 0,935 1,422 0,870 1,279 1,189 0,462 1,369 1,237 1,718 1,974 1,224 0,29 1,493 0,398 0,690 0,262 1,861 0,846 0,159 1,339 1,983 1,722 1,705 0,589 1,462 0,666 0,535 1,68 0,753 1,630 1,591 0,874 0,57 1,811 1,940 0,938 0,541 0,362 1,593 0,857 1,375 0,519 1,646 0,999 0,732 0,304 1,337 1,48 0,709 0,646 1,668 0,332 1,142 0,535 0,839 0,721 0,113 1,456 1,788 1,95 0,154 1,59 0,543 0,635 1,30 0,838 0,27 0,919 1,7 1,637 1,816 1,509 0,867 1,167 1,356 0,435 1,884 1,554 0,618 0,243 1,336 0,587 0,568 1,848 1,169 0,102 1,963 0,632 0,985 0,325 0,497 1,612 1,504 1,230 0,784 1,352 0,565 1,25 1,690 1,459 0,830 1,838 1,914 0,699 1,973 1,417 1,845 0,751 1,907 0,936 0,152 1,607 0,798 1,84 1,118 1,351 0,299 0,247 0,745 0,455 0,325 1,287 1,271 0,620 0,446 1,929 1,360 0,426 1,912 0,487 0,869 0,706 1,531 0,661 0,574 0,484 0,256 1,899 1,868 0,996 0,5 1,332 0,583 1,979 1,121 0,25 1,333 1,952 1,361 1,534 0,142 0,119 0,411 0,87 1,643 1,126 1,13 1,104 0,790 0,955 0,343 0,504 0,803 0,668 0,621 0,551 0,752 1,873 1,185 1,374 1,212 0,678 1,995 1,313 0,78 0,768 1,93 0,45 1,47 0,721 1,200 0,379 1,723 1,483 0,610 1,309 0,355 0,41 0,844 1,781 0,538 0,565 1,397 1,586 0,339 1,112 1,922 0,909 0,738 0,16 1,192 1,415 1,935 1,705 0,585 1,73 0,451 1,211 1,29 1,151 0,984 1,268 0,704 0,507 1,78 0,174 1,394 1,823 1,818 0,185 0,423 0,252 0,494 0,952 1,790 0,1 1,561 0,707 0,373 1,445 1,666 0,141 1,882 1,611 0,250 0,143 1,325 0,291 0,252 0,915 1,750 1,155 0,704 1,887 1,710 0,950 0,651 1,692 0,983 1,756 1,354 0,701 1,950 0,301 1,502 1,356 1,373 0,117 1,209 1,459 0,827 1,928 1,621 1,41 0,239 0,132 1,653 1,508 0,548 0,438 0,345 1,513 1,798 1,715 1,941 1,285 1,388 1,178 1,862 1,548 0,554 1,421 1,456 0,393 0,935 1,332 1,148 0,509 0,124 0,649 1,588 0,396 0,30 1,738 0,496 1,505 0,805 0,913 0,168 0,1 0,620 0,326 1,720 0,133 0,720 0,520 0,746 1,992 1,571 1,638 0,232 1,617 0,369 0,105 1,558 1,789 0,395 1,813 1,522 1,156 0,208 1,615 0,735 1,178 1,417 0,491 1,712 0,485 0,112 1,454 0,654 0,476 1,906 0,376 0,442 1,12 0,240 0,657 1,146 1,781 1,581 1,445 1,446 1,746 0,976 1,786 0,572 0,229 1,246 0,940 1,478 1,531 0,285 1,742 1,123 1,477 0,180 1,539 1,554 0,896 0,241 0,626 0,867 0,267 1,494 0,0 1,508 1,948 0,747 1,294 1,247 0,503 0,668 0,636 1,250 1,168 1,337 0,22 1,534 0,989 0,143 0,392 0,619 1,128 0,412 0,398 1,840 1,265 1,135 1,670 0,604 0,222 1,168 1,806 0,714 0,607 0,136 0,764 1,551 1,69 1,394 0,320 1,420 1,514 0,543 0,904 0,696 1,396 0,563 1,271 0,267 0,846 0,752 0,512 0,464 1,38 0,512 1,64 1,968 1,760 0,736 1,626 1,120 0,865 1,243 0,161 0,765 0,245 1,618 0,949 1,252 1,832 0,853 0,565 0,984 1,369 0,200 1,95 0,96 0,530 0,111 1,653 0,111 1,511 1,389 1,444 0,347 0,911 1,145 1,85 0,615 0,564 1,476 1,337 1,140 1,309 0,236 0,686 1,951 1,327 1,524 1,49 1,534 1,314 1,262 0,716 1,524 1,709 1,905 0,470 1,959 1,794 0,684 1,649 1,354 0,720 0,948 0,84 1,779 0,19 1,884 0,658 0,267 1,579 0,646 1,406 1,794 0,888 0,597 0,5 0,640 0,6 0,367 0,55 1,596 1,498 1,485 1,263 1,605 1,826 1,852 1,989 0,613 1,161 0,845 1,28 1,854 0,331 1,718 0,972 1,605 0,615 1,944 0,200 1,71 0,584 1,389 0,974 0,999 0,116 0,234 1,190 1,933 0,25 1,3 0,215 1,331 0,556 1,948 1,570 0,955 0,775 0,984 1,199 0,126 0,401 1,708 1,459 1,362 0,290 1,174 0,527 0,261 0,52 1,232 0,990 1,354 1,652 0,568 0,567 0,723 1,158 1,410 1,974 1,885 0,132 0,837 0,906 0,891 0,269 0,257 1,179 1,560 1,623 0,115 1,222 0,750 0,38 0,119 0,120 0,958 0,245 0,607 1,435 0,365 1,986 1,300 1,736 1,9 0,494 0,253 1,326 0,68 0,644 0,674 0,566 0,726 0,277 0,785 0,898 0,846 1,453 0,328 1,440 1,851 0,913 1,583 1,435 0,288 1,450 0,441 1,674 0,403 0,147 1,124 0,361 1,709 1,476 1,158 1,563 0,416 0,44 1,938 0,576 0,191 1,405 1,908 1,291 0,967 0,860 0,382 1,47 0,352 1,11 1,303 1,414 0,497 0,542 1,240 1,801 0,558 1,148 0,61 1,654 1,725 1,900 1,94 1,607 1,56 1,303 1,497 0,871 1,806 1,904 0,741 0,761 1,749 1,48 1,451 1,991 1,695 1,860 0,406 1,847 0,302 0,382 0,915 0,431 1,72 1,411 0,352 0,184 0,733 1,326 1,964 0,663 1,81 0,636 1,117 0,923 0,84 0,217 1,24 0,348 0,94 1,66 1,153 0,294 0,621 1,452 1,769 0,230 0,172 1,983 1,173 0,975 1,411 0,889 0,944 1,298 1,975 0,893 0,115 1,7 1,967 0,817 0,641 1,680 0,545 1,542 1,482 1,612 1,600 0,254 0,393 0,161 1,684 0,182 1,656 1,48 0,837 0,800 1,248 1,387 0,640 1,292 1,928 1,250 1,128 0,947 0,457 1,457 1,406 0,52 0,388 0,942 1,794 0,837 1,198 0,510 1,836 0,896 0,22 0,66 0,173 1,458 1,764 0,310 0,112 1,762 0,962 1,836 0,748 1,187 0,487 0,945 1,844 0,696 1,547 0,160 0,881 1,840 1,136 0,132 1,431 0,560 0,256 0,471 1,273 0,223 1,745 0,176 0,306 1,362 0,649 0,844 1,140 0,967 0,931 1,549 1,240 0,882 0,543 1,710 1,747 0,175 0,369 0,76 1,0 1,764 0,806 0,392 1,986 1,191 1,966 1,269 0,419 0,84 0,456 1,344 1,118 0,24 0,701 0,779 1,830 1,85 1,520 0,620 0,242 0,873 0,977 1,891 1,472 1,116 1,376 0,427 0,937 1,214 0,16 1,584 1,11 1,659 1,53 1,602 1,240 1,331 1,582 0,965 1,383 0,741 0,797 1,135 0,37 0,726 1,862 0,378 0,545 0,543 1,8 1,24 0,772 0,831 1,663 0,487 1,669 0,500 0,45 0,662 0,170 0,476 1,790 0,363 0,458 0,15 1,327 0,371 1,801 0,613 0,267 1,899 1,215 0,116 1,653 0,634 0,652 1,831 1,777 0,862 1,557 0,549 0,757 1,893 0,847 0,302 0,410 0,214 1,431 1,521 1,783 0,159 1,335 1,897 0,641 1,929 0,503 1,44 0,691 0,11 0,419 0,272 0,767 0,759 1,127 1,532 1,353 1,41 0,524 1,273 0,469 1,240 0,267 1,737 0,849 1,910 0,189 1,448 0,485 1,265 0,419 0,887 0,34 1,540 1,727 0,344 1,938 1,14 1,409 0,468 1,390 1,108 0,365 0,717 0,545 1,247 0,572 0,522 1,791 0,495 1,290 0,510 1,627 0,30 1,667 1,600 0,670 1,445 1,296 1,991 0,545 1,717 1,242 0,206 0,990 0,528 0,566 0,898 0,104 1,145 1,703 1,992 0,70 0,538 1,514 1,999 1,269 1,581 0,191 1,337 0,653 0,897 0,603 1,533 0,480 1,95 1,955 0,587 1,701 1,304 0,209 1,635 0,452 1,151 1,996 1,910 0,439 0,805 0,918 0,321 1,43 1,353 1,561 1,293 1,862 0,742 0,164 1,503 1,965 1,911 1,93 0,638 0,872 0,314 1,90 1,983 1,639 0,468 1,789 1,439 1,174 1,739 1,530 1,913 1,870 0,951 1,198 0,159 1,749 0,510 0,301 0,206 0,862 1,625 1,590 1,570 0,243 1,721 1,341 1,996 0,722 1,131 1,796 0,850 1,233 0,235 1,317 0,283 1,411 1,285 0,515 0,938 1,419 1,369 0,39 1,682 1,958 1,942 0,697 0,779 1,758 0,878 1,382 1,129 0,395 0,743 1,829 1,456 1,535 0,478 0,959 0,598 1,120 1,616 0,431 1,440 1,578 1,715 0,364 1,516 1,851 1,664 1,772 0,830 0,438 0,659 0,391 1,647 1,797 0,869 1,121 0,968 1,890 1,43 1,711 0,729 1,670 0,186 1,454 1,791 0,968 1,10 0,755 1,152 1,64 1,514 0,404 1,532 0,479 1,255 0,686 0,582 0,210 0,465 1,621 1,472 0,930 1,708 1,473 0,279 1,596 0,184 0,287 1,102 0,17 0,991 1,332 0,103 0,267 0,717 0,586 1,88 1,656 0,677 1,694 0,911 0,23 1,948 0,828 1,146 0,76 0,856 0,344 1,506 0,258 0,694 1,587 1,13 1,646 0,225 1,934 0,355 0,705 1,152 1,180 0,571 1,789 0,308 0,926 0,665 0,614 1,661 1,894 1,699 1,700 0,455 0,384 0,134 0,860 0,78 0,135 0,851 1,39 1,838 1,114 1,904 0,143 0,679 1,292 0,395 0,237 1,401 1,91 1,605 1,112 0,601 0,44 1,985 0,13 1,281 1,514 0,533 1,516 0,327 1,537 1,266 0,772 1,979 1,121 1,826 0,377 1,419 1,913 1,679 1,209 0,917 1,630 1,468 0,51 1,221 0,836 1,245 0,533 0,430 1,943 0,41 1,133 1,798 1,782 1,921 0,296 0,884 1,901 1,998 1,54 0,322 0,227 0,926 1,716 1,459 1,151 1,776 1,979 0,455 0,948 1,255 1,645 1,321 1,963 0,294 1,139 1,959 1,478 0,296 1,146 0,905 1,214 0,728 1,5 0,191 1,330 0,11 1,231 1,980 1,980 0,598 1,813 0,340 1,146 0,857 0,252 0,743 0,256 0,914 1,156 0,861 1,59 1,112 0,457 1,943 1,789 1,431 1,634 0,553 1,552 1,944 0,6 0,125 1,625 1,443 1,760 0,645 1,542 0,857 0,210 1,827 1,900 1,908 0,492 1,618 1,448 0,734 1,235 1,452 1,900 1,251 0,421 0,756 1,806 1,342 1,64 1,885 0,381 0,928 0,549 0,271 0,599 1,412 0,936 0,4 1,982 1,22 0,630 0,248 1,466 0,332 0,152 0,951 0,994 1,666 0,401 0,734 0,52 0,921 0,925 1,98 0,563 1,998 0,240 1,714 1,653 0,616 0,653 0,864 0,742 0,333 0,335 0,424 0,852 1,59 0,339 0,806 1,107 0,499 1,748 1,367 0,510 1,528 1,224 1,791 1,59 0,510 0,142 0,363 0,916 1,470 0,844 1,354 0,7 1,397 0,101 0,36 0,160 0,336 1,314 0,859 1,612 1,158 1,268 1,390 1,625 0,307 0,278 1,927 0,628 0,913 0,44 0,558 1,151 1,655 1,503 1,796 1,526 0,313 0,971 1,253 0,658 1,567 1,919 0,153 0,482 0,335 0,371 0,792 1,784 0,449 0,266 1,534 0,512 0,118 1,76 0,360 0,916 0,1 1,266 0,558 0,128 0,775 0,21 0,907 1,941 0,62 1,349 0,604 0,765 0,64 0,474 1,774 0,193 0,772 0,64 0,779 0,327 0,670 0,939 0,316 1,146 0,516 1,672 0,868 1,104 0,249 0,702 1,452 0,443 1,588 1,256 1,904 1,870 0,708 1,114 0,745 0,374 1,451 0,708 0,166 0,383 0,490 1,124 0,681 1,230 0,677 1,94 0,163 1,618 0,993 0,201 0,784 0,624 1,721 0,174 1,234 1,421 1,556 0,357 1,317 1,971 1,567 0,142 0,917 1,116 1,198 0,743 1,648 0,804 0,451 0,827 0,173 0,428 0,117 1,629 1,773 1,182 0,866 0,789 0,510 1,30 0,481 1,115 0,271 1,298 0,17 1,746 0,38 0,794 0,99 1,481 0,199 0,971 1,895 0,392 1,672 1,878 1,525 1,484 0,602 1,340 1,348 0,484 0,289 1,841 0,154 1,168 0,580 1,218 1,404 1,169 1,701 0,258 1,365 0,543 0,165 0,621 1,401 1,281 0,425 1,37 1,801 1,434 0,170 1,320 0,988 1,901 1,881 0,965 1,258 0,225 1,301 0,361 0,380 0,294 1,557 1,502 1,737 1,279 0,776 0,830 0,712 0,363 0,587 0,272 0,687 1,816 1,615 0,115 0,982 1,214 0,270 0,637 1,439 0,483 1,232 0,146 0,675 0,214 1,866 1,165 1,357 1,855 1,98 1,487 1,677 0,536 1,336 1,877 1,12 0,472 1,502 1,626 0,314 1,807 0,516 0,691 1,616 0,121 1,561 0,112 0,499 0,875 0,911 0,705 1,538 1,174 0,661 1,34 1,601 0,155 1,751 0,939 0,51 1,82 1,262 0,766 1,549 0,173 0,186 0,11 0,407 0,761 1,758 0,990 1,122 1,232 1,181 0,890 1,234 1,402 1,905 1,646 1,526 0,374 0,470 1,14 1,613 1,556 1,838 1,244 1,766 0,941 1,290 0,296 1,648 0,84 0,658 1,974 0,795 0,743 1,630 0,309 0,183 0,241 1,402 1,139 1,227 1,798 1,159 0,888 1,640 0,148 1,637 1,434 1,580 0,662 1,385 0,670 0,724 0,217 0,436 1,461 1,272 1,232 0,80 0,695 0,789 1,90 1,418 1,377 0,99 0,112 0,271 0,671 0,813 1,174 1,520 1,417 1,962 0,122 1,571 1,436 0,542 1,23 0,723 1,306 0,818 1,422 0,876 1,253 1,74 1,795 0,981 1,970 1,357 1,246 0,417 1,444 0,321 0,166 1,741 0,280 1,456 1,416 1,573 1,815 0,308 1,748 0,231 1,66 0,340 0,866 1,602 1,692 0,501 1,195 1,329 0,86 1,742 0,424 0,702 1,352 0,185 0,828 1,740 0,591 0,228 1,159 0,574 1,244 0,384 1,513 0,430 0,135 0,462 1,535 1,468 1,842 0,894 1,116 1,808 1,75 1,664 1,373 0,664 1,647 0,700 0,599 0,819 0,698 1,57 1,532 1,532 1,348 0,84 1,424 1,713 1,71 1,846 1,101 0,626 1,207 1,421 0,789 1,468 1,651 1,281 1,176 1,574 1,183 0,109 1,945 0,157 1,911 0,256 0,360 0,210 1,643 0,759 1,626 0,470 1,864 0,850 1,569 0,563 1,761 0,688 1,34 1,899 1,349 1,217 1,606 1,548 0,777 0,744 0,605 0,18 0,688 0,279 0,724 1,857 1,677 0,51 1,36 0,101 0,462 1,117 0,300 0,741 0,989 0,363 0,215 0,271 0,584 1,185 1,558 1,483 1,634 0,733 0,752 0,586 0,28 0,27 1,853 0,605 0,385 1,88 1,781 1,458 0,557 1,898 0,58 1,127 0,586 0,418 0,208 0,998 0,79 1,113 0,19 0,491 1,21 0,734 0,317 1,229 1,540 1,487 0,883 0,969 0,592 1,919 1,718 1,118 1,739 1,249 0,623 1,935 1,128 0,529 0,107 0,704 0,663 1,592 0,960 1,471 1,517 1,860 1,108 0,656 0,517 1,671 1,392 0,204 0,578 0,594 1,669 1,398 1,965 1,106 1,356 1,828 1,62 1,767 0,893 1,156 0,450 0,955 1,370 1,896 0,96 1,335 0,833 1,660 1,575 0,212 1,247 1,602 1,969 1,844 0,339 1,926 1,526 0,626 1,272 1,14 1,735 0,182 0,471 0,582 1,295 0,526 0,22 1,311 1,143 1,960 0,294 0,843 0,776 0,330 1,591 1,833 1,742 0,106 1,499 0,865 1,687 1,671 0,462 1,30 0,465 0,388 0,746 1,164 0,305 1,773 0,216 0,225 1,724 1,486 0,819 0,848 0,978 0,173 0,101 1,550 1,67 1,57 1,559 1,936 0,612 0,508 0,794 1,160 1,459 0,197 0,598 1,47 0,41 1,691 0,301 1,236 0,255 0,832 0,876 1,747 0,397 0,969 0,979 0,160 0,593 1,853 0,283 1,372 0,423 1,924 1,823 1,848 0,16 0,601 0,502 0,680 0,262 0,457 0,560 1,962 1,873 0,915 1,324 0,602 0,349 1,719 0,619 1,477 0,265 0,403 1,425 0,70 0,891 0,827 0,514 1,182 1,240 1,550 1,847 0,114 1,875 1,753 1,130 0,134 0,270 1,986 1,670 1,935 1,136 1,697 1,648 1,879 0,545 0,397 0,904 0,602 1,487 1,29 1,981 1,683 0,684 0,407 0,453 0,444 0,766 0,486 1,80 0,252 1,708 1,223 0,790 1,315 1,758 0,230 0,517 1,487 1,702 0,692 1,79 1,618 1,631 0,618 0,213 0,308 1,645 0,925 1,378 1,868 1,958 0,874 1,186 1,20 0,538 1,60 0,878 0,427 1,869 0,201 1,482 0,518 1,922 1,557 1,345 0,258 1,932 1,748 1,748 0,77 0,710 0,683 0,243 0,676 1,971 1,900 1,520 0,678 0,263 0,452 1,847 1,176 0,57 0,572 0,309 0,657 1,81 0,896 0,508 1,913 1,674 1,682 1,215 1,359 0,780 1,302 1,147 0,656 1,103 0,201 1,754 0,717 0,969 1,999 1,502 1,370 0,353 0,375 1,713 0,146 0,711 1,188 0,389 1,50 1,467 0,6 0,373 1,573 0,700 1,549 1,251 0,384 1,839 1,896 0,500 0,985 0,512 1,172 1,638 0,395 0,398 0,363 1,214 0,536 1,513 0,651 1,68 0,616 0,558 1,36 0,441 1,858 1,447 0,421 1,449 1,199 1,264 0,644 0,101 1,416 1,302 1,50 1,638 1,733 1,746 1,651 0,95 1,752 1,641 0,988 1,960 0,783 1,530 0,951 0,152 1,738 0,587 1,573 0,808 1,49 0,225 1,17 1,564 0,727 0,410 1,84 0,218 0,279 1,410 1,513 0,314 1,580 0,970 0,721 1,365 1,912 1,906 1,403 0,616 1,52 0,147 1,33 1,868 1,742 1,211 1,607 0,138 0,772 1,335 1,835 0,333 1,421 1,772 0,729 1,581 0,655 1,347 1,378 0,344 1,252 0,572 0,18 1,785 0,237 0,246 1,698 0,771 0,194 1,843 0,250 1,181 0,164 0,621 0,943 1,876 1,395 0,976 0,555 0,726 1,764 1,404 0,651 0,880 1,508 0,890 0,897 1,60 1,693 0,867 0,838 0,936 0,813 1,797 1,400 1,156 0,320 1,363 0,688 0,138 0,887 1,782 0,784 0,86 0,471 0,223 1,193 1,761 1,283 0,592 0,771 0,516 1,852 0,324 0,12 0,107 1,860 0,162 0,273 0,80 1,816 1,496 1,519 1,738 0,926 1,429 0,918 1,120 0,488 1,693 0,969 1,846 0,249 0,33 0,938 1,740 0,374 1,958 1,113 1,557 0,151 1,250 1,465 0,503 0,809 1,366 1,243 1,308 0,185 1,184 1,549 0,527 1,819 0,961 1,300 0,886 1,736 1,510 0,573 0,294 0,743 1,306 0,344 0,968 1,943 0,241 0,414 0,638 1,913 0,639 0,79 1,41 1,482 0,216 1,645 1,22 1,684 0,102 1,389 1,820 1,495 0,512 0,157 0,622 1,988 0,841 0,829 0,17 0,37 0,774 0,72 0,114 1,972 0,147 0,544 1,490 1,835 0,636 0,750 1,314 1,495 1,370 0,487 1,564 1,584 0,843 0,955 1,639 1,676 0,227 0,422 0,960 0,14 0,487 0,823 1,584 1,187 0,64 1,698 1,615 1,830 0,254 1,826 1,912 1,740 0,628 1,592 1,499 1,462 0,288 1,765 1,493 0,811 1,391 1,155 0,482 1,831 1,830 0,406 1,511 1,160 1,532 1,834 1,348 0,752 1,389 1,531 1,751 1,751 1,592 1,133 1,294 1,821 1,821 0,62 1,882 0,269 0,479 0,961 0,672 1,979 0,579 1,707 0,775 0,128 0,310 0,569 0,49 1,99 1,614 0,90 0,952 1,12 1,248 1,589 0,637 0,872 0,812 1,742 0,847 1,786 1,75 1,582 0,1 1,362 0,169 1,299 0,460 1,808 1,989 1,248 0,618 0,173 1,618 0,807 1,473 1,205 0,706 0,404 0,5 1,333 0,744 0,744 0,131 0,665 1,295 0,835 1,352 1,308 0,193 1,939 0,654 0,814 1,654 1,774 0,349 1,477 1,219 1,848 0,944 1,513 1,754 0,242 0,73 1,833 1,313 0,329 1,969 1,839 0,359 1,754 1,935 1,898 0,326 1,517 0,624 0,168 0,0 1,343 1,964 0,310 0,418 1,963 1,872 1,21 1,928 0,575 0,65 0,12 0,402 1,330 0,178 1,85 1,157 0,210 1,973 1,277 0,351 1,364 1,760 1,655 0,649 0,761 1,610 0,289 0,986 1,325 0,809 1,360 1,511 0,914 1,931 0,324 0,685 0,870 0,588 1,496 0,93 1,515 0,779 1,979 1,7 1,419 0,937 0,52 1,154 1,27 0,174 1,781 1,840 1,485 0,359 0,592 1,594 1,18 1,725 0,162 0,512 1,986 1,249 0,831 0,673 1,335 1,36 1,264 1,46 1,193 1,551 0,697 0,676 0,6 0,720 0,720 0,873 0,49 0,703 1,81 1,18 1,939 0,620 0,848 1,656 0,594 0,843 1,897 1,404 0,900 1,160 1,893 0,778 1,757 1,305 0,84 1,943 1,4 0,628 1,334 1,282 1,443 0,932 0,695 1,137 0,65 0,310 0,211 1,45 1,842 0,665 0,144 1,75 0,857 0,85 0,983 1,87 1,634 1,349 0,43 1,713 0,187 0,617 0,696 0,593 0,292 0,687 0,191 0,130 1,338 0,621 1,537 0,410 1,15 1,610 0,372 1,186 1,69 0,820 0,580 1,777 1,336 1,222 0,132 0,82 0,803 0,308 1,563 0,628 1,487 1,705 1,542 0,434 1,878 1,32 1,921 1,957 1,934 1,533 0,278 1,128 0,463 0,253 0,340 1,224 1,242 1,995 0,319 0,934 1,519 0,919 1,532 1,393 0,221 0,493 0,82 1,464 1,649 1,367 0,64 0,611 1,686 0,856 0,906 1,821 1,593 1,858 1,947 0,54 0,716 0,612 0,981 1,392 1,102 0,863 1,623 1,178 1,957 0,402 0,383 1,659 0,239 1,78 1,586 0,588 0,197 0,273 1,329 0,59 1,496 1,81 0,230 0,443 1,923 0,485 1,684 0,90 1,951 0,509 1,178 1,241 1,822 0,530 1,416 0,505 1,234 1,800 0,178 1,759 1,522 1,193 0,515 0,486 1,386 1,851 0,324 1,111 1,700 0,501 1,933 0,834 0,477 0,39 0,847 0,410 1,625 1,636 1,490 1,72 0,132 1,137 1,175 0,561 1,237 0,143 0,801 1,792 0,775 0,983 1,105 1,243 0,254 0,129 1,606 1,226 1,201 0,594 0,439 0,730 0,609 1,561 1,458 1,850 1,501 0,512 1,27 0,746 0,860 0,111 1,586 0,920 1,372 1,704 1,939 0,692 0,573 0,18 1,742 0,693 0,681 0,350 0,274 0,547 1,765 1,320 0,466 1,738 1,661 1,373 0,396 0,779 1,153 1,885 1,122 1,174 1,204 1,659 0,357 0,119 0,564 0,906 1,792 0,39 0,158 1,882 1,724 0,914 0,145 1,66 1,352 0,722 1,940 1,384 1,311 0,76 1,130 0,318 1,892 0,931 0,829 1,622 0,574 1,462 1,827 0,916 0,822 0,313 0,460 1,198 1,246 0,879 0,393 1,84 1,846 1,57 0,574 1,803 1,554 0,759 1,15 0,668 0,315 1,270 0,870 1,741 1,857 0,741 1,545 1,405 1,603 0,398 0,615 1,411 1,268 0,509 0,701 1,333 0,630 0,746 1,276 1,694 0,962 1,52 0,902 1,524 1,885 0,738 1,133 0,975 1,703 0,960 1,398 1,92 0,915 0,659 0,52 0,425 1,325 1,947 1,119 1,76 1,816 1,245 1,654 0,55 1,393 1,205 1,327 0,432 1,849 0,572 0,489 0,303 0,25 0,22 0,991 0,5 1,602 1,428 1,118 1,279 1,392 0,785 0,863 1,159 1,571 0,36 1,291 1,392 1,446 0,904 0,556 1,548 0,46 0,55 0,509 1,466 1,914 0,825 1,263 1,320 0,222 1,45 1,310 0,982 1,996 0,760 1,601 1,109 0,971 0,102 1,14 0,634 0,109 1,70 1,45 1,354 0,696 0,33 1,140 0,628 0,608 1,378 1,600 1,32 0,220 0,755 1,963 1,930 1,890 1,426 0,250 1,561 0,774 1,528 1,406 1,511 1,781 0,699 1,495 0,422 1,845 0,597 0,756 1,391 1,275 0,616 1,672 0,754 0,196 0,642 1,754 1,421 0,528 0,773 0,572 0,41 1,712 0,396 1,465 1,834 1,31 0,37 1,829 0,338 0,48 0,254 0,746 0,851 0,119 0,319 0,931 1,535 1,599 0,72 0,991 1,994 1,915 0,176 0,361 0,667 0,781 1,473 1,901 1,23 0,573 0,477 0,260 0,489 1,744 0,364 0,950 0,897 1,359 0,902 1,984 0,899 0,770 0,275 0,618 1,428 1,55 1,721 1,239 0,698 1,960 1,415 0,635 1,910 0,582 0,447 1,274 1,840 1,905 1,674 0,970 1,49 0,451 0,212 1,630 1,681 1,179 1,672 1,692 1,905 1,192 1,117 1,125 0,434 1,447 1,122 0,390 1,90 1,715 0,620 0,309 0,402 0,761 0,836 0,125 0,292 0,898 1,313 1,294 0,742 1,630 1,34 1,369 0,506 1,70 0,556 0,798 1,529 1,177 0,83 1,348 0,955 1,932 0,534 1,538 0,70 1,960 1,167 1,258 0,543 1,244 0,908 0,266 1,388 1,937 1,608 0,499 0,939 0,982 1,244 1,924 0,986 1,906 1,385 0,20 0,961 0,385 0,700 1,899 0,106 0,881 0,407 1,422 0,673 0,687 0,460 0,664 0,813 1,992 1,520 0,486 1,568 0,340 0,35 1,638 1,130 0,721 1,503 0,214 1,982 1,996 1,302 0,584 1,218 0,668 0,568 1,861 1,110 1,905 0,361 1,740 1,297 0,629 1,223 0,863 1,276 1,148 1,362 1,801 1,478 0,968 0,828 1,88 0,557 1,974 0,998 0,7 0,125 0,559 0,550 1,496 1,122 1,276 0,604 1,202 1,897 0,839 0,509 0,734 0,809 1,703 1,793 0,326 0,397 0,253 0,631 0,456 1,269 1,199 0,614 1,797 1,569 0,510 0,711 0,625 0,119 0,399 1,515 1,927 1,93 0,907 1,884 0,119 0,426 0,159 0,863 0,992 0,310 0,829 0,30 0,429 0,185 1,496 1,605 1,912 0,123 1,163 0,89 1,471 1,954 0,438 0,797 1,808 1,117 0,955 0,745 1,214 0,236 0,298 0,331 0,227 0,385 1,287 1,497 0,736 0,713 0,777 0,630 1,349 0,435 1,976 0,943 0,176 1,396 1,419 0,708 1,320 1,694 1,206 1,966 0,827 0,712 0,588 0,849 0,672 0,968 0,772 0,79 1,972 1,434 1,203 1,548 0,122 0,950 1,532 0,740 1,662 1,859 1,767 0,470 1,183 1,448 1,604 0,727 0,871 1,685 0,928 1,638 1,321 1,434 1,818 0,449 0,616 0,272 1,233 0,599 1,807 0,964 1,529 0,898 1,342 0,925 0,95 1,132 0,718 1,857 1,644 0,933 0,316 0,734 1,371 1,262 0,959 1,480 1,851 1,20 1,721 0,416 1,847 1,383 1,230 0,346 1,323 0,396 0,399 0,611 0,127 1,237 0,878 0,4 0,375 1,583 0,24 1,856 1,251 1,0 1,199 0,852 1,954 0,153 0,342 1,425 0,501 0,613 0,239 0,45 1,925 1,878 0,935 1,32 1,693 0,999 0,821 0,42 0,588 0,645 0,19 1,409 0,806 1,996 1,503 1,943 1,782 0,669 1,663 1,127 0,405 1,971 1,623 1,311 0,799 1,439 0,508 0,660 0,114 0,682 0,495 1,788 1,660 1,198 0,874 1,0 0,969 0,502 1,268 0,217 0,412 1,933 0,175 0,255 1,367 1,112 0,360 1,601 1,483 1,669 0,279 1,372 1,279 0,739 0,607 1,899 0,770 0,672 0,96 0,971 1,638 0,369 0,383 0,527 0,299 1,185 0,501 0,326 1,602 0,310 1,561 1,579 1,771 0,792 0,395 0,338 0,29 0,252 1,882 1,553 1,786 0,68 0,445 0,705 0,132 0,55 0,964 1,392 1,730 0,777 1,966 1,135 0,599 0,544 0,276 0,237 1,978 1,405 0,847 1,421 0,49 0,882 1,328 0,576 0,505 0,250 0,19 0,477 1,527 1,94 0,734 1,800 1,762 1,150 0,690 0,322 0,223 0,800 0,303 0,860 0,369 1,480 0,238 0,57 0,528 0,875 0,156 0,794 1,32 1,461 1,58 0,982 1,283 1,454 0,946 0,329 1,383 0,778 1,884 0,300 0,750 0,7 0,380 1,723 0,329 1,640 1,348 1,916 0,755 0,646 1,30 0,184 1,377 0,350 0,709 0,978 1,171 1,966 0,502 1,438 1,839 1,56 0,260 1,54 1,271 1,357 1,9 1,264 0,231 1,890 1,686 1,15 1,349 0,698 0,34 0,4 0,882 1,843 0,849 1,138 1,920 1,390 1,7 0,761 1,88 0,98 0,337 0,287 0,991 0,267 1,661 0,5 0,64 0,936 1,521 1,682 0,908 1,532 0,524 1,366 1,278 1,79 0,427 1,17 1,526 0,200 0,676 0,534 0,424 0,483 0,60 1,450 1,763 0,18 1,949 1,321 1,54 1,605 1,828 1,1 1,466 1,384 1,808 1,68 1,112 1,188 0,77 0,1 1,678 1,320 0,548 0,917 1,598 1,352 1,837 1,48 1,193 0,748 1,453 0,347 0,156 1,969 1,664 1,525 1,813 0,651 1,975 1,216 1,281 1,920 0,214 0,600 0,743 0,226 0,824 1,21 1,610 0,928 0,435 1,457 1,630 0,252 1,645 0,869 1,890 1,429 0,516 0,194 0,114 0,680 0,966 0,890 0,770 0,125 0,427 1,3 0,608 1,537 0,644 0,920 0,499 0,950 1,812 0,905 0,695 1,458 1,669 1,431 1,629 0,298 1,958 1,479 0,61 0,894 1,759 0,210 1,47 0,877 1,292 0,458 1,65 1,834 0,435 1,804 1,583 1,751 1,569 0,135 1,743 1,287 0,241 0,201 1,995 0,461 0,406 0,833 1,695 0,557 1,483 0,217 0,659 1,335 1,14 1,640 0,165 1,855 1,388 0,724 1,186 1,658 0,711 1,465 0,742 0,130 0,962 1,954 1,577 1,37 0,453 1,644 0,854 0,523 0,764 0,896 0,73 1,487 1,141 1,438 0,842 1,444 1,989 0,606 0,704 0,89 0,932 1,293 0,141 1,333 1,632 0,431 0,78 0,942 1,382 1,472 1,781 1,937 0,211 0,489 1,524 1,422 0,978 1,163 0,217 1,610 1,989 0,217 1,745 0,811 0,284 0,725 0,578 0,433 1,427 1,214 1,176 1,385 1,696 1,972 0,842 0,377 1,873 0,113 1,382 0,294 1,541 0,776 1,413 0,513 1,829 0,215 0,308 1,738 1,367 1,571 0,465 1,800 0,856 0,981 0,239 1,991 1,908 0,418 1,2 0,453 0,39 1,741 0,289 1,688 0,606 1,181 0,885 0,675 0,701 0,373 0,863 1,111 0,379 1,274 0,548 1,974 1,195 1,880 0,528 1,585 0,652 1,437 0,407 0,505 0,313 1,12 0,17 0,189 0,588 0,731 1,359 1,255 1,295 0,662 0,394 1,611 0,692 0,75 0,601 0,591 1,498 1,852 0,662 0,587 0,749 0,497 1,712 1,84 1,788 1,145 1,316 1,920 1,424 1,944 1,130 0,200 0,969 0,361 1,276 1,146 0,959 0,290 1,171 0,227 0,399 0,644 1,742 1,423 1,77 0,419 0,495 1,605 1,215 1,372 0,81 1,865 1,118 0,363 0,708 1,752 1,772 0,619 1,706 0,375 1,322 1,23 0,313 0,700 0,473 0,244 0,718 0,410 1,422 1,619 1,235 1,867 0,658 1,941 1,537 1,621 1,507 1,112 0,292 1,992 0,814 0,641 1,108 1,949 0,227 0,917 1,66 1,229 0,476 1,438 0,265 0,842 1,352 1,966 0,260 1,193 0,108 1,707 1,272 1,67 1,337 0,479 0,468 1,415 1,136 0,75 1,981 0,142 1,202 1,662 1,753 1,822 1,793 0,769 1,598 0,415 0,307 0,43 0,7 0,456 1,382 1,976 0,773 0,635 0,404 1,872 0,725 1,235 1,769 0,696 0,912 0,640 0,81 0,981 0,993 1,638 0,559 0,649 1,945 1,214 0,641 1,722 1,371 0,341 0,746 1,715 1,960 1,379 1,474 1,719 1,285 0,219 1,327 1,578 1,259 1,737 1,314 1,575 1,335 0,649 1,416 0,572 1,908 1,347 0,605 1,673 1,974 0,48 0,547 0,767 1,999 0,645 0,147 0,178 0,47 0,777 0,986 0,713 0,659 1,780 1,300 0,929 1,627 0,831 0,754 0,647 1,155 0,269 1,596 0,912 1,952 1,4 1,319 0,443 0,350 1,459 0,349 1,566 1,675 1,46 0,128 1,957 1,245 0,614 1,333 0,685 1,421 1,238 0,884 1,822 1,351 1,592 1,539 0,257 0,68 1,328 1,989 0,905 0,994 0,665 0,120 0,682 1,996 0,922 0,598 1,132 0,66 1,559 1,773 0,887 1,558 0,437 0,490 0,669 1,301 0,219 1,91 0,67 0,971 1,875 0,638 0,27 0,790 1,573 1,362 0,988 1,297 1,538 0,310 0,611 1,77 0,927 0,526 1,291 0,426 0,655 0,772 1,179 0,76 0,391 1,173 1,598 1,783 0,34 1,558 1,617 1,770 0,587 1,743 0,795 0,254 1,448 1,719 0,304 1,805 0,640 0,225 1,179 1,98 0,169 1,50 0,250 1,162 0,690 1,558 1,430 0,619 0,174 1,847 1,434 0,295 0,850 1,913 0,941 0,200 0,361 0,749 0,945 0,55 0,481 1,488 1,330 1,234 0,97 0,105 0,314 1,659 0,721 1,290 0,683 1,489 0,830 0,784 1,835 0,627 1,718 0,208 1,203 1,810 1,344 1,107 0,379 1,42 1,165 1,576 0,922 1,365 0,273 0,320 1,764 1,753 1,284 1,204 1,877 0,218 0,935 1,888 1,965 0,640 0,832 1,684 0,764 0,26 0,21 1,752 1,686 0,636 0,66 1,395 1,657 0,540 1,541 0,85 1,466 0,501 1,329 0,806 0,509 0,524 1,99 0,501 0,107 1,883 0,375 0,994 0,909 0,413 0,661 1,335 1,718 1,539 0,600 1,393 0,893 0,466 1,69 1,632 1,347 0,753 0,17 1,866 1,121 1,542 0,625 1,351 0,630 1,79 0,551 1,514 0,423 1,465 0,923 1,624 0,425 1,281 0,440 1,424 1,339 1,19 0,652 0,517 1,151 1,370 0,578 0,775 1,768 1,12 1,398 0,51 0,381 0,450 0,296 1,908 0,643 0,730 1,606 0,341 1,216 1,919 0,255 1,544 0,283 1,420 0,164 0,349 0,37 0,586 0,972 0,659 0,844 1,277 1,634 1,261 1,771 0,411 1,300 0,902 0,299 1,873 0,656 0,270 1,696 0,632 1,509 0,176 1,274 0,434 1,906 1,288 0,163 1,158 1,695 0,126 0,850 1,943 1,184 1,258 0,440 0,270 0,641 1,248 0,357 0,583 0,232 1,495 1,11 0,657 1,836 0,471 0,359 1,13 0,216 1,141 0,138 0,319 1,813 1,371 1,363 1,19 1,821 0,941 0,522 0,824 0,308 0,932 1,427 0,508 1,762 0,328 0,393 0,755 0,795 0,247 1,717 1,306 1,595 1,895 0,376 0,557 0,881 0,374 0,266 1,348 0,920 1,42 1,908 0,495 1,280 0,60 0,535 1,765 1,176 0,440 1,298 0,294 0,527 1,379 1,133 1,75 1,975 0,125 0,2 0,621 1,261 1,379 0,207 1,461 1,871 1,95 0,79 0,4 1,496 1,271 0,239 1,409 1,733 1,93 0,849 0,640 0,235 1,777 0,624 1,47 0,778 1,642 0,318 0,432 1,431 1,476 1,481 1,823 1,523 1,675 1,469 1,185 0,395 0,333 0,840 0,174 0,758 0,755 0,534 1,628 1,460 1,787 1,423 1,905 0,205 0,116 0,803 1,861 1,407 0,918 1,620 0,469 1,719 1,900 1,348 0,549 1,700 0,126 0,738 1,583 1,917 0,418 1,209 0,500 0,697 0,904 0,237 0,154 1,655 0,725 1,480 1,712 1,602 0,420 0,980 0,272 1,211 1,338 1,993 1,871 0,156 0,989 1,312 1,35 0,981 1,396 0,497 1,857 0,222 1,562 1,752 1,818 0,228 1,9 0,631 1,970 1,593 1,798 1,208 1,207 1,52 1,255 1,394 0,69 1,141 0,372 0,48 1,850 1,25 0,885 0,408 0,81 0,930 0,74 1,257 0,813 0,828 1,982 0,583 0,708 1,79 0,910 1,639 0,668 0,46 1,188 1,75 1,825 0,367 1,868 0,932 1,215 0,767 1,328 1,595 1,188 1,233 1,507 1,587 1,225 1,786 0,91 1,300 0,286 0,427 0,713 1,217 0,507 0,72 1,582 1,277 1,868 1,584 0,83 1,848 0,635 1,126 0,320 0,347 0,936 1,808 0,486 1,235 0,935 1,974 1,573 1,376 0,672 0,705 0,930 0,897 0,719 1,911 1,499 1,325 1,384 0,424 1,637 1,820 1,956 0,807 0,810 1,472 0,731 1,276 0,971 0,313 1,928 0,609 0,874 0,214 1,506 1,821 1,326 0,640 1,294 0,622 0,128 0,92 0,256 0,550 1,560 0,698 1,547 0,377 0,208 0,253 0,370 0,79 1,693 0,209 1,49 0,428 0,779 0,652 1,467 1,757 1,498 1,125 1,818 0,470 0,781 1,716 1,23 0,107 1,116 1,814 1,36 0,780 0,810 1,628 0,860 1,439 1,797 1,328 0,971 0,876 1,907 1,698 1,920 1,105 0,228 0,132 0,802 0,620 0,161 1,37 1,935 1,436 1,316 0,806 1,103 1,120 1,521 1,676 1,890 0,363 0,532 1,340 1,717 1,146 0,451 0,178 0,704 0,783 1,952 1,376 1,397 1,264 0,849 1,270 1,978 0,230 0,139 1,55 1,612 1,332 0,21 0,13 1,8 1,160 1,51 0,118 1,465 0,563 1,418 1,127 1,895 0,175 1,59 1,42 1,148 1,127 1,569 1,41 1,770 0,711 0,477 0,842 0,247 1,768 0,484 1,634 0,646 1,760 1,459 0,880 1,750 0,323 1,844 0,378 0,712 0,985 0,549 0,377 0,956 0,405 0,612 0,72 1,468 1,813 1,729 1,482 0,226 0,609 0,552 1,806 0,526 1,505 1,562 1,63 0,91 0,459 1,916 1,631 1,326 0,242 1,429 1,602 1,86 1,607 1,884 1,227 1,100 1,182 1,222 1,244 1,274 1,803 0,55 0,356 1,756 0,919 0,108 1,195 0,211 0,772 1,179 1,947 1,107 0,156 0,459 1,210 1,846 1,681 0,759 1,851 1,990 1,975 0,980 0,120 0,973 0,129 1,709 0,135 0,842 0,618 1,284 1,344 1,929 1,768 1,314 1,316 0,556 1,945 1,555 0,379 0,733 0,703 1,426 1,271 0,296 1,977 0,209 0,126 0,862 1,799 0,468 1,564 0,795 1,356 1,139 1,237 0,646 1,550 1,156 1,861 1,977 0,61 0,354 1,12 0,176 0,89 1,694 1,302 0,51 1,605 0,108 1,465 1,202 1,941 0,827 1,744 0,157 0,465 0,174 1,534 1,752 0,444 0,47 1,259 0,706 0,91 1,928 0,646 1,279 0,28 0,748 1,783 1,654 1,353 0,31 0,222 0,272 0,666 1,914 0,893 0,414 0,723 1,230 0,881 0,4 1,463 0,416 1,753 1,706 1,948 1,780 1,38 0,181 0,468 0,946 1,969 1,152 1,939 1,598 1,184 1,617 0,45 0,307 1,498 1,14 0,914 1,991 1,411 1,675 0,40 0,667 0,614 0,653 0,420 1,346 1,264 1,377 1,681 1,164 0,812 0,657 0,409 0,67 0,104 1,815 0,12 1,358 0,281 1,815 0,37 1,356 0,676 1,939 0,916 0,536 1,147 0,885 0,151 0,236 1,44 1,505 1,983 0,716 0,510 0,95 1,487 1,142 0,171 0,16 0,189 1,773 0,264 0,783 0,503 0,66 1,518 0,239 0,40 1,962 1,500 1,956 1,864 0,116 1,176 1,883 0,166 1,681 1,225 1,518 1,721 0,231 0,370 0,681 1,113 0,919 0,613 0,403 1,862 0,389 1,172 0,692 1,511 0,466 1,847 1,716 1,693 0,20 1,284 0,665 0,615 1,668 0,257 0,62 1,777 0,712 0,710 1,553 0,534 0,379 1,546 1,57 1,493 0,931 1,851 1,739 0,815 0,170 0,783 1,512 0,64 0,845 0,644 1,279 0,571 1,587 0,354 1,184 1,645 0,679 0,684 0,665 0,848 1,831 1,367 0,442 0,754 0,517 1,543 0,282 1,919 0,254 1,893 0,911 1,875 1,963 1,460 1,967 1,870 1,311 0,347 0,941 1,822 1,663 0,38 1,616 0,709 0,388 1,244 1,844 1,573 0,48 1,977 1,244 0,686 1,329 0,697 1,407 0,789 0,190 1,134 0,181 0,425 1,896 0,745 1,743 0,577 0,643 0,268 0,28 0,470 0,111 1,129 0,464 1,776 1,289 1,904 1,677 0,837 0,137 0,548 0,42 0,141 0,132 1,961 1,649 1,255 1,834 0,183 0,10 1,342 1,563 1,628 1,440 0,928 0,318 0,464 0,723 1,990 0,301 0,125 0,469 1,192 1,628 0,226 0,687 1,283 1,893 1,654 0,538 0,477 0,902 1,616 1,9 1,961 0,851 0,596 1,179 1,461 1,109 1,343 0,33 0,265 0,255 0,461 0,357 1,543 1,834 0,742 1,99 0,799 1,634 1,25 0,901 0,407 1,332 1,726 0,317 0,71 1,262 0,593 0,196 0,768 1,474 0,358 1,585 1,191 1,384 1,344 0,177 0,174 1,469 0,726 0,193 0,514 0,788 1,336 0,343 0,115 0,235 1,732 0,623 0,672 0,59 1,886 0,732 1,626 0,35 1,558 1,582 1,147 1,828 0,851 1,150 1,833 1,961 1,766 0,728 0,395 1,772 1,330 0,698 1,118 0,733 1,62 1,819 1,42 0,89 1,312 0,954 1,122 0,535 1,530 1,686 0,720 1,420 0,20 0,683 0,707 0,817 1,612 0,494 1,16 0,398 1,124 0,186 0,135 1,829 0,175 0,859 1,653 0,260 0,513 0,481 1,922 1,782 0,908 1,839 1,802 0,458 0,940 0,879 0,137 1,829 0,570 1,756 0,298 0,385 0,736 1,902 1,363 0,318 1,67 0,888 0,949 0,128 0,888 1,843 0,431 0,129 0,344 1,109 0,29 0,733 0,722 0,48 0,204 1,489 1,266 0,729 0,684 1,993 0,128 0,164 1,360 1,143 1,567 1,422 0,458 1,282 1,303 1,574 0,558 0,888 1,587 0,309 0,135 1,363 1,354 1,215 1,314 0,452 0,424 0,281 0,272 0,805 1,80 0,373 0,452 1,529 1,870 1,196 1,216 0,963 0,24 1,618 0,970 1,701 1,184 1,246 0,417 1,21 1,418 0,913 0,773 1,180 1,913 1,45 0,610 1,795 0,654 1,547 1,976 1,896 0,829 0,440 0,104 1,461 1,357 0,988 1,226 0,637 1,560 0,238 1,490 0,940 0,776 1,619 1,323 0,904 1,535 1,938 1,694 0,856 1,331 1,902 1,293 0,646 0,732 0,990 0,69 1,393 0,767 1,626 1,392 1,63 1,957 0,113 0,452 0,970 0,610 1,623 1,660 1,719 1,458 0,623 1,456 0,344 1,862 1,283 0,306 1,902 1,431 0,545 1,785 0,268 0,321 0,472 1,976 0,360 0,280 1,664 0,263 0,664 0,351 0,572 1,723 1,803 1,759 1,600 0,360 0,471 1,99 1,816 1,570 1,248 1,602 1,943 1,124 1,359 1,421 1,624 1,94 1,489 1,728 0,694 0,263 0,490 0,467 0,784 1,319 0,717 1,746 0,534 1,852 0,945 0,854 1,65 1,322 1,120 1,647 0,164 1,640 0,298 0,537 0,826 1,345 1,324 1,774 1,90 1,444 0,357 0,329 1,789 1,836 1,447 0,246 1,134 0,298 1,667 0,362 1,202 1,866 0,424 0,73 0,809 1,487 0,287 1,91 1,75 1,0 0,428 0,398 1,836 0,388 1,576 0,343 0,144 0,974 0,4 0,628 0,734 0,413 1,951 0,882 0,585 0,288 0,740 0,859 1,204 0,269 1,450 0,237 1,682 1,238 0,652 1,846 0,94 0,571 0,571 1,364 1,640 0,479 1,233 0,778 1,584 0,720 1,641 1,964 0,734 0,626 0,826 0,14 0,231 1,174 0,942 0,654 1,791 1,28 0,894 0,532 1,763 1,535 1,905 0,666 0,412 1,815 0,945 1,686 1,672 1,921 0,483 0,770 0,948 1,19 1,550 0,282 0,390 0,820 1,421 0,189 1,273 1,403 1,477 1,961 0,846 1,346 0,772 1,635 1,47 1,443 0,857 1,107 1,892 1,357 0,372 0,922 0,821 1,742 0,446 0,943 1,123 0,414 1,917 1,644 0,813 1,895 0,545 1,520 0,702 0,879 0,43 1,58 1,901 0,141 1,829 0,633 1,423 1,350 1,119 1,554 1,562 0,487 1,71 1,847 0,654 0,437 1,104 0,972 1,845 1,45 1,121 1,562 1,80 1,261 1,441 1,876 0,573 1,19 0,806 0,524 0,459 1,842 1,798 1,536 0,878 0,315 0,647 1,325 0,329 1,464 1,760 1,82 0,975 1,394 1,866 1,219 0,889 1,519 0,704 0,681 0,216 1,148 0,491 0,461 1,945 0,715 0,783 0,378 1,346 0,786 1,110 1,426 0,319 1,339 0,555 1,899 1,634 0,505 0,374 1,526 1,460 1,400 1,251 1,71 1,471 0,313 0,66 1,943 1,672 0,44 1,664 1,117 0,431 1,165 1,719 0,468 1,684 1,611 1,997 0,988 1,533 1,163 1,480 1,268 1,716 0,661 0,352 1,614 1,80 1,901 1,765 0,215 0,111 1,974 1,0 0,691 0,342 1,592 1,956 1,643 1,976 1,949 1,673 1,390 1,694 1,399 1,282 0,540 1,143 1,265 0,740 0,354 1,445 0,211 1,904 1,854 0,333 0,50 0,861 1,320 1,615 1,740 0,675 0,969 1,753 1,24 1,111 0,304 0,788 0,40 0,705 0,999 0,705 1,58 1,519 0,808 0,398 0,153 0,438 1,831 0,474 0,143 1,519 1,831 1,615 0,335 1,715 1,77 0,902 1,987 0,598 1,926 1,1 0,462 0,41 0,626 0,613 1,642 1,32 1,687 1,521 1,215 1,594 0,30 0,712 0,464 1,592 0,107 0,318 1,754 1,162 1,893 1,179 1,173 1,383 1,356 1,459 1,745 0,668 0,842 0,565 1,839 0,496 1,29 0,991 1,85 1,336 1,759 1,603 0,888 1,779 1,469 1,438 0,374 0,286 1,864 1,372 1,792 0,40 1,556 0,817 0,371 0,497 0,506 1,671 1,407 1,394 0,205 0,751 1,70 1,718 0,952 0,905 1,129 1,463 0,987 0,431 0,528 0,703 0,914 1,781 0,200 1,675 0,842 0,43 1,475 1,748 0,694 0,287 0,940 1,845 1,393 0,772 0,150 1,442 0,31 1,41 1,931 1,843 1,848 1,420 0,681 1,353 0,855 1,102 0,930 1,432 0,498 1,662 0,872 1,667 0,129 0,427 1,135 0,967 0,868 0,713 1,5 0,499 1,497 0,921 1,353 0,580 1,133 1,261 0,518 1,63 1,547 1,203 0,894 0,768 1,504 0,674 0,237 0,863 0,487 1,739 0,515 1,373 1,780 1,497 1,521 1,637 0,769 1,457 1,769 1,888 0,960 1,286 1,393 0,728 0,759 1,595 1,550 1,173 0,114 0,382 0,194 0,976 1,886 1,956 0,444 0,655 0,383 1,870 0,500 0,740 1,230 1,133 1,511 0,957 1,762 1,816 0,379 0,365 0,418 0,767 1,887 1,178 1,376 1,144 0,927 1,907 1,31 0,855 0,927 0,570 0,18 1,313 1,227 1,301 0,185 0,38 1,304 0,259 0,858 1,379 0,930 0,99 0,309 1,685 1,656 1,380 0,155 0,92 0,241 0,220 1,338 0,797 0,890 0,239 1,146 0,675 0,416 0,357 1,770 1,226 1,584 0,575 1,697 0,20 1,923 1,986 0,719 1,699 1,52 0,615 0,180 1,488 0,843 1,427 1,130 1,267 1,842 0,894 0,970 1,551 0,92 1,452 1,937 0,909 0,857 0,557 1,43 0,206 0,684 1,638 0,272 0,245 1,737 1,53 0,9 0,862 0,652 0,628 0,582 0,171 0,139 0,987 1,821 0,299 0,12 0,656 1,799 0,848 0,74 1,566 1,470 1,916 1,357 1,678 0,982 0,425 1,590 1,102 0,350 0,133 0,707 1,11 0,498 0,462 1,352 0,496 0,712 0,881 0,153 0,538 1,747 0,647 0,388 0,784 0,424 0,565 0,311 0,689 1,814 1,236 0,236 0,803 1,221 1,506 1,246 1,852 1,800 0,325 1,118 0,156 1,657 1,846 0,743 0,419 1,839 0,969 1,284 1,894 0,738 0,847 0,98 0,147 1,623 1,882 1,586 1,43 1,918 0,253 0,893 1,371 1,454 0,869 0,304 1,554 1,735 1,557 0,635 1,583 1,378 0,728 1,420 1,496 0,251 0,475 1,200 0,652 0,128 0,846 0,354 0,762 1,774 1,251 0,133 1,525 1,69 0,39 0,101 0,108 0,902 1,167 0,483 0,326 1,989 0,356 1,664 0,492 1,188 0,368 1,108 0,616 1,415 1,788 1,785 1,495 0,367 0,351 1,195 0,683 1,198 0,197 0,832 1,867 1,795 1,508 1,346 0,383 0,651 0,838 0,26 1,555 1,189 1,564 1,453 0,19 1,218 0,144 0,863 0,23 0,50 1,148 0,273 0,149 1,638 0,379 0,100 1,247 0,45 0,625 1,71 0,686 0,411 0,13 0,880 1,13 0,199 1,191 0,217 0,719 1,371 1,710 1,745 1,967 0,828 1,563 0,372 0,378 1,409 1,438 1,418 1,315 1,760 1,799 1,291 0,271 1,525 0,996 1,197 1,823 1,24 1,327 0,386 0,542 1,999 0,592 0,145 0,824 0,794 0,87 1,285 1,104 0,996 0,989 1,364 0,432 0,548 0,275 0,318 1,883 0,215 0,842 1,535 0,753 1,550 0,907 0,925 1,401 0,784 1,961 0,439 1,747 1,741 0,687 0,312 0,103 1,540 0,809 1,489 1,99 1,290 1,823 0,407 1,140 0,688 1,436 1,622 1,112 1,225 0,147 1,355 1,318 1,688 0,173 1,152 0,681 1,448 1,241 0,472 1,175 0,397 0,633 0,748 0,567 0,55 0,267 0,801 0,409 1,680 0,344 0,490 1,17 0,874 1,326 1,887 1,694 0,7 0,167 0,655 0,867 1,584 0,45 0,78 0,221 0,138 1,175 1,555 1,340 1,428 0,307 0,538 0,27 1,478 0,557 0,8 1,605 0,949 0,384 1,19 0,543 1,390 0,629 0,729 0,934 0,694 0,181 0,351 0,884 1,426 0,503 0,636 0,796 1,336 0,543 0,711 1,663 1,339 0,719 0,789 0,606 0,369 1,743 1,361 1,718 0,347 1,917 1,59 1,593 0,450 0,891 1,262 0,138 1,466 0,227 1,402 1,868 1,210 0,673 1,967 1,866 0,674 0,898 0,800 1,924 0,514 0,566 1,144 1,577 0,643 0,155 1,73 1,84 0,782 1,754 0,661 0,876 0,770 0,838 1,799 1,591 0,764 1,426 0,700 1,197 1,850 1,129 1,630 0,127 1,71 0,982 1,53 1,4 1,574 0,141 1,622 1,870 0,989 0,571 1,653 1,361 1,743 0,354 0,923 0,341 0,517 1,228 0,499 1,927 1,554 1,774 1,278 0,986 1,371 1,744 0,920 0,116 0,410 1,587 0,261 0,643 1,880 0,406 1,743 1,201 1,117 1,663 1,540 1,254 0,968 1,0 0,855 0,830 0,278 1,336 0,826 0,693 0,502 1,935 1,29 0,525 1,880 1,344 1,140 1,350 1,741 1,309 0,50 1,643 1,183 0,761 1,641 0,650 1,907 1,102 1,684 1,337 0,433 1,286 1,811 0,446 1,644 1,922 0,233 1,634 1,522 0,63 1,248 1,335 0,532 1,846 1,968 1,112 0,397 1,62 1,213 1,46 1,86 1,380 1,320 0,676 1,831 0,749 1,265 1,138 1,82 0,862 1,969 1,987 0,263 1,154 1,692 0,530 0,536 0,241 0,100 1,303 0,313 1,133 0,205 1,903 0,892 0,286 1,874 0,790 0,880 0,760 1,13 1,930 1,273 0,19 1,80 1,243 0,652 1,567 1,552 0,293 1,863 0,255 0,648 0,205 0,938 0,856 1,554 1,901 1,568 1,696 0,44 1,209 0,148 0,763 0,123 0,934 1,615 1,855 1,4 1,368 0,979 0,453 1,597 1,723 0,227 0,18 1,73 1,761 0,512 1,60 1,224 0,57 0,955 0,502 0,356 1,102 1,745 1,581 0,250 1,592 1,909 1,375 1,983 0,255 0,651 1,1 1,494 1,951 0,336 0,908 0,796 0,315 0,422 1,291 1,111 0,909 1,912 0,367 0,925 1,513 0,568 0,849 1,653 1,142 1,459 0,341 1,279 0,729 0,101 1,199 0,850 1,555 0,66 0,968 0,701 0,912 1,762 0,680 0,285 0,578 1,113 1,991 0,496 0,409 1,431 1,64 0,759 0,868 1,414 0,122 0,945 0,601 1,896 1,305 1,51 0,302 0,709 1,773 1,989 1,471 0,208 0,41 0,613 0,639 1,885 0,942 1,569 0,43 0,35 1,420 1,322 1,220 0,214 1,907 1,621 0,333 1,598 0,262 1,921 0,870 0,586 1,286 0,991 1,208 1,917 1,154 0,52 1,73 1,308 1,265 0,128 1,352 1,572 0,138 1,14 1,955 1,763 1,727 1,18 0,395 1,96 1,973 1,442 1,747 1,443 1,437 0,138 0,472 1,781 0,348 1,589 0,325 1,945 1,928 0,507 1,711 0,169 1,426 1,933 1,852 0,24 1,573 1,755 0,718 0,60 1,981 1,333 1,562 0,356 0,200 0,638 1,889 0,626 0,272 0,620 0,682 0,985 1,853 0,588 0,454 1,664 1,206 1,150 1,100 0,384 0,415 1,588 1,680 0,233 1,139 0,827 1,141 0,48 1,249 1,497 0,993 0,489 0,517 1,397 0,124 0,205 0,821 1,977 0,560 1,308 0,108 1,165 1,216 1,23 1,429 1,994 1,233 0,779 0,895 0,116 1,97 1,22 1,553 1,406 0,103 0,847 0,267 0,488 1,462 1,107 1,462 1,232 1,833 0,781 0,203 1,633 0,798 1,421 0,535 0,488 0,420 0,693 0,666 0,963 1,368 1,109 0,311 1,221 1,175 1,614 1,12 0,812 1,281 1,974 1,798 0,980 1,420 1,706 1,503 1,273 0,946 1,86 1,676 0,446 0,907 0,11 1,49 1,599 0,767 1,937 1,567 1,761 1,441 1,435 1,56 1,62 1,141 1,342 0,625 1,633 0,915 1,869 1,493 0,614 1,41 1,300 1,338 1,374 1,540 1,275 0,529 0,80 1,228 1,859 0,965 0,216 1,882 0,718 1,741 1,272 1,651 1,556 1,461 0,71 1,589 0,445 0,313 1,678 0,518 0,738 0,453 1,879 1,276 1,855 1,554 1,18 0,882 1,768 0,612 1,376 1,696 0,996 1,784 0,774 0,382 1,722 0,117 1,670 0,515 0,494 0,849 1,922 0,47 0,922 0,393 0,954 1,699 0,170 1,4 1,511 1,880 0,496 1,578 0,935 1,1 1,567 0,67 0,471 1,583 1,707 0,544 1,910 1,702 1,309 0,579 0,563 0,787 1,395 1,874 0,195 1,549 1,107 0,79 1,829 1,428 1,21 0,344 1,203 1,37 1,888 0,442 1,618 1,905 1,730 1,504 1,754 0,673 1,176 1,832 1,596 1,674 0,74 0,893 1,73 1,276 0,607 0,450 1,412 0,442 0,893 0,3 0,421 0,249 1,248 0,732 1,331 0,528 1,37 0,413 0,796 1,707 0,532 0,949 1,922 0,277 0,553 1,313 0,636 0,312 1,276 0,432 1,626 0,297 1,208 1,211 0,562 1,440 1,256 1,839 1,646 1,253 0,352 1,630 1,874 1,171 1,954 1,35 0,260 1,734 1,4 1,583 1,396 1,131 1,896 0,184 1,763 0,219 0,495 1,776 0,157 1,295 1,644 1,607 0,31 1,721 0,886 0,834 1,988 0,219 0,993 0,447 0,954 1,986 0,196 1,50 0,863 1,33 1,353 0,694 0,82 1,237 0,896 0,593 1,1 0,673 1,852 1,566 0,475 0,761 1,54 0,97 0,625 0,496 0,210 1,761 1,909 0,982 1,815 0,614 1,731 0,736 0,531 0,806 0,89 1,126 0,721 0,531 1,106 0,79 0,31 1,755 0,107 0,713 0,693 1,197 0,508 0,835 0,7 1,705 1,184 0,527 0,221 0,45 0,591 1,518 0,96 0,109 1,201 1,993 1,165 1,377 0,564 0,62 1,562 0,354 1,715 0,306 1,259 0,789 1,973 1,515 0,364 1,218 1,189 0,993 1,508 0,517 0,972 0,134 0,324 0,13 0,772 0,513 0,414 0,63 1,446 0,882 0,396 1,113 0,855 0,298 1,847 1,239 0,189 1,239 1,971 1,511 1,284 1,601 1,15 1,968 0,453 1,506 0,625 1,873 1,288 1,405 0,449 0,700 1,919 0,267 1,505 0,753 1,59 1,58 0,600 1,752 1,414 0,735 1,779 1,240 1,596 0,858 1,1 0,759 0,99 1,311 0,835 0,339 0,185 0,375 1,612 1,515 1,182 1,990 0,328 0,847 1,26 0,752 0,715 1,446 1,191 0,986 1,96 1,603 1,867 0,21 1,912 0,157 0,274 0,327 1,966 1,65 0,702 0,252 1,467 1,502 0,204 1,746 1,740 1,685 0,668 1,204 0,553 0,250 1,764 0,4 1,815 1,371 0,562 1,109 0,954 1,161 1,635 0,671 1,497 1,550 1,870 0,379 1,765 1,582 1,123 0,240 0,820 0,689 0,162 0,42 0,895 0,945 0,453 0,56 0,780 1,852 1,999 0,527 0,170 0,729 1,913 0,323 1,576 0,994 1,458 0,642 0,448 0,621 0,137 1,545 0,188 0,597 0,339 0,295 1,748 0,797 0,484 1,106 0,938 1,247 0,674 0,521 1,814 1,239 1,95 0,736 1,471 1,296 0,710 1,681 0,814 0,609 0,372 1,931 1,250 0,164 1,266 1,134 0,485 0,237 1,918 0,27 0,701 0,919 0,97 1,674 0,250 1,797 1,57 0,850 1,352 1,425 0,803 0,272 0,732 1,656 0,781 1,460 0,163 0,47 0,38 1,359 1,220 1,971 0,122 1,249 0,559 0,369 0,372 0,552 1,52 0,57 1,145 1,207 0,730 1,6 0,249 0,308 1,845 0,551 1,484 1,247 1,198 0,686 0,78 0,478 1,594 1,523 0,587 0,36 1,616 1,738 0,889 1,438 1,138 0,809 0,944 1,433 1,877 0,555 0,778 1,766 0,890 1,92 0,472 1,733 0,772 1,786 0,645 0,1 0,804 1,435 0,461 1,632 1,476 0,189 0,685 0,522 1,438 0,788 0,602 0,413 0,956 0,694 0,754 1,656 1,298 1,467 1,376 0,754 0,191 0,143 1,399 1,671 0,891 1,665 1,842 0,546 1,821 0,670 0,683 1,571 1,278 1,641 1,768 0,97 0,340 0,730 1,446 0,501 0,904 1,197 1,645 0,8 1,842 0,14 0,517 0,29 1,157 0,954 1,465 1,66 1,882 0,30 1,531 0,873 1,580 0,186 1,13 0,549 1,51 1,505 1,624 1,9 0,884 0,500 0,53 1,138 0,321 0,995 0,148 1,196 0,745 1,216 0,702 1,320 0,384 1,162 1,878 0,280 0,301 0,11 0,883 0,962 1,707 1,14 1,234 0,924 1,204 1,117 0,805 1,120 1,235 1,367 0,665 0,241 1,455 1,961 1,519 1,30 0,219 0,522 0,126 0,860 0,960 1,661 0,319 1,297 0,103 0,441 1,775 1,126 0,707 0,581 1,858 1,901 1,521 0,287 0,711 1,78 0,514 0,134 0,611 0,582 1,444 1,18 1,241 1,805 0,953 0,855 0,494 0,424 1,188 0,28 1,26 0,834 1,545 0,134 1,781 1,330 0,612 1,319 1,647 0,557 0,194 1,520 1,173 0,540 0,37 1,929 0,642 1,240 0,316 0,513 1,842 1,243 1,494 0,686 1,893 0,53 1,963 1,630 1,822 0,945 0,645 1,263 0,759 0,184 1,48 1,591 1,234 0,348 1,744 0,178 1,547 1,839 0,504 1,165 0,34 1,3 0,673 1,624 0,665 0,5 1,709 1,564 0,178 1,654 1,286 0,906 0,931 1,898 0,943 0,561 0,91 0,416 1,827 0,783 0,881 0,291 1,902 0,678 1,941 0,258 0,40 1,55 1,437 0,184 1,917 0,184 1,644 0,786 1,282 0,912 1,750 0,203 1,150 0,64 0,290 1,139 1,48 1,714 1,16 0,365 1,263 1,806 1,728 0,129 1,729 0,882 1,966 0,2 0,532 0,415 1,593 1,470 1,909 1,242 0,26 0,304 1,212 1,371 1,343 0,971 1,694 0,388 0,38 1,938 1,796 1,247 0,763 0,295 1,248 0,370 1,911 1,863 0,107 1,646 1,24 1,549 1,241 0,888 1,927 1,441 0,906 1,188 0,601 1,61 0,580 0,818 1,445 1,208 0,415 1,924 1,47 0,93 1,212 0,648 1,11 1,84 0,634 1,502 1,173 1,209 0,235 0,201 0,317 1,458 1,721 0,812 0,875 1,515 1,195 0,945 0,201 1,398 0,612 1,731 1,117 1,549 1,329 0,872 1,132 0,450 0,235 0,168 0,650 1,521 1,491 1,227 1,61 1,805 0,977 1,424 0,469 1,329 1,919 1,399 1,452 0,361 0,918 1,730 1,62 0,492 0,741 1,848 0,363 1,106 1,181 1,476 0,102 0,544 1,212 0,834 0,605 0,981 1,141 0,938 1,558 1,879 0,622 1,976 1,388 0,137 1,595 0,552 1,373 0,273 1,256 0,416 0,743 0,495 1,660 0,427 1,72 1,452 1,718 1,853 1,133 0,116 0,987 1,339 1,627 1,582 1,977 0,631 0,559 1,898 1,353 0,959 0,312 0,292 0,355 1,599 1,63 0,687 1,231 0,527 1,263 1,48 0,71 1,430 1,913 0,790 1,8 1,818 1,730 0,528 0,695 1,175 1,14 0,630 1,110 0,998 0,970 0,787 0,917 0,821 0,70 1,417 0,232 1,874 1,356 1,886 1,110 1,841 0,284 1,445 0,578 1,919 1,75 0,485 0,185 0,100 1,238 0,259 0,203 1,359 0,135 1,377 0,364 0,166 1,270 0,873 1,981 0,386 0,419 1,896 1,691 0,143 1,673 1,741 0,54 0,386 1,565 1,320 1,41 0,949 1,660 1,181 0,943 1,816 1,852 0,299 1,608 1,695 0,922 1,856 1,226 0,412 1,754 1,179 1,684 1,763 0,90 0,150 0,457 0,552 1,416 1,780 1,722 1,241 1,614 1,180 1,283 1,391 0,603 1,56 0,135 1,82 0,611 1,330 0,713 0,525 0,943 1,987 1,193 1,724 0,679 0,909 1,577 1,138 0,67 0,480 0,864 0,177 0,46 0,705 1,520 0,217 1,107 1,557 1,108 1,813 1,8 1,638 1,961 1,901 0,156 0,106 0,931 0,254 1,125 1,784 0,745 0,22 1,419 1,411 1,63 0,592 0,187 0,224 0,295 0,481 0,233 1,627 0,692 1,398 0,372 0,765 0,131 0,266 1,750 0,512 0,358 1,340 0,226 1,631 0,233 0,418 0,540 0,868 0,537 0,242 1,58 0,895 1,645 1,99 0,607 0,67 1,420 0,427 1,449 0,121 0,278 1,148 1,765 1,49 0,780 0,602 0,174 1,349 0,508 0,282 1,31 0,844 0,2 1,551 0,688 1,47 1,810 0,113 0,990 0,303 0,126 1,310 1,685 0,218 0,67 1,172 1,631 1,399 1,636 0,988 0,938 0,0 1,445 1,340 1,682 1,213 0,488 1,912 0,703 1,475 0,69 0,958 0,807 0,518 1,199 0,372 1,123 1,775 1,156 0,594 0,849 1,779 0,741 0,596 1,104 1,66 1,181 1,915 0,557 0,469 0,974 0,88 1,410 0,481 1,866 0,785 0,391 1,436 1,261 0,16 0,297 1,210 1,990 1,233 0,386 1,40 0,666 0,963 1,108 0,405 1,438 1,518 1,890 0,925 1,169 0,283 1,740 1,173 1,21 1,953 0,772 0,337 0,260 0,931 0,318 0,125 1,49 1,183 0,292 0,873 0,456 0,912 1,679 0,690 1,422 0,597 1,824 1,382 1,331 1,117 0,665 0,534 1,397 1,306 1,171 1,243 0,359 0,359 1,434 0,30 0,696 0,736 1,707 1,633 1,807 1,659 0,763 0,311 0,334 0,820 1,553 1,970 0,405 1,96 0,146 0,989 0,790 0,375 1,630 0,755 0,389 1,193 0,161 1,768 1,216 1,933 0,201 1,432 1,889 1,44 0,618 1,553 0,909 1,432 0,907 1,105 0,746 1,485 0,592 0,363 0,220 0,302 1,971 1,260 0,708 0,508 0,404 0,740 1,26 1,463 0,263 0,845 0,518 1,384 1,197 1,151 0,762 1,440 1,365 1,46 0,289 0,503 0,182 0,226 1,79 1,398 0,238 0,243 0,895 1,560 0,802 0,421 1,965 1,297 1,754 0,241 1,97 0,963 1,535 1,762 1,287 1,888 1,860 1,950 1,994 0,647 1,757 1,485 0,89 0,303 0,970 0,468 0,953 0,658 1,378 0,438 0,759 1,824 0,937 1,291 0,78 0,401 0,299 1,888 0,983 0,440 1,755 1,242 1,498 1,64 0,354 1,488 0,658 1,207 1,543 0,274 1,187 0,916 0,913 1,22 1,932 1,786 1,936 1,157 1,467 1,543 0,243 0,777 1,2 0,517 0,418 1,646 0,207 1,349 0,209 0,754 1,520 0,707 0,793 0,885 1,310 0,386 0,357 1,757 1,985 0,857 1,39 0,232 0,833 0,760 0,443 0,444 1,120 0,682 0,813 0,849 1,456 0,95 0,337 0,257 0,782 0,140 0,235 1,839 1,653 1,721 1,649 1,566 1,427 1,239 1,536 0,350 1,373 1,87 1,157 1,404 0,154 1,115 0,59 0,17 0,215 1,49 1,565 1,746 0,881 1,980 0,463 1,700 0,324 1,80 1,33 0,20 1,481 0,904 1,875 1,811 1,970 1,797 0,128 0,330 0,737 1,306 1,335 0,236 1,49 1,308 1,737 0,199 1,716 0,555 0,812 0,131 0,75 0,671 0,321 1,189 1,644 0,602 0,287 0,556 1,6 1,663 1,650 1,348 0,516 1,693 0,304 0,333 0,963 1,568 0,955 1,482 0,836 0,419 0,218 0,351 1,20 1,557 0,615 1,66 1,176 0,624 0,61 0,568 1,185 1,262 1,308 1,918 1,777 1,103 1,944 0,111 1,191 0,966 1,429 0,180 0,324 0,521 0,766 0,761 0,215 1,843 1,924 0,697 1,307 0,246 1,718 0,380 1,12 1,703 0,483 0,243 1,132 0,954 0,296 1,485 0,36 1,275 1,594 0,825 0,92 0,640 0,242 0,351 0,426 1,683 0,960 1,190 0,485 0,832 0,820 0,845 0,556 0,282 1,138 1,704 1,811 0,526 0,943 1,648 1,355 0,759 0,613 0,220 0,711 1,77 1,448 1,974 1,516 0,495 0,500 1,440 0,964 0,435 1,809 0,461 0,404 1,330 0,798 0,878 1,528 0,291 1,747 0,434 0,541 0,990 1,876 1,904 0,338 1,409 0,86 0,453 1,579 0,759 0,864 0,740 1,197 0,981 0,870 0,114 0,669 1,289 0,700 0,527 0,784 1,372 1,150 0,16 0,60 1,63 1,638 0,819 0,925 1,396 0,442 1,824 1,381 0,115 0,666 0,911 0,336 1,844 0,648 1,370 1,387 0,320 0,131 1,263 0,489 1,720 1,124 1,187 0,270 0,649 0,57 1,191 0,637 1,83 0,580 1,87 1,494 1,688 1,67 0,613 0,94 1,137 0,642 1,729 1,844 0,847 0,71 0,912 0,119 1,154 1,442 1,700 1,958 0,837 1,261 1,862 1,558 0,807 1,782 0,344 1,930 0,445 0,363 1,191 1,891 1,735 1,662 1,957 1,545 0,135 1,58 1,376 0,733 0,648 0,782 0,683 0,221 1,41 0,733 0,104 0,780 1,439 0,867 1,147 1,42 0,91 1,483 1,927 1,974 0,854 1,393 0,535 0,725 0,732 0,580 1,375 1,212 1,759 1,762 0,791 0,220 1,872 0,102 1,878 1,799 0,269 0,985 0,768 0,772 0,947 0,918 0,543 0,162 1,921 1,573 0,18 1,582 1,140 1,520 0,405 0,422 1,803 0,876 0,451 0,987 1,285 1,17 0,469 1,919 1,496 1,342 0,787 0,676 0,431 0,92 0,460 1,92 0,929 1,917 1,456 1,680 0,245 0,601 1,785 0,447 1,40 1,532 0,463 1,548 0,302 0,759 0,813 1,506 0,132 0,972 1,752 0,168 0,659 1,332 1,569 1,459 1,328 0,551 0,182 1,155 0,826 0,84 1,879 1,875 0,754 1,884 0,885 1,455 1,403 1,37 0,982 1,538 1,587 0,390 0,687 1,717 1,893 0,309 1,821 1,669 0,738 1,979 0,607 0,393 0,8 0,727 0,309 1,515 0,862 1,899 0,43 0,838 0,272 0,72 0,793 1,477 0,923 1,934 0,109 1,922 0,280 0,817 1,495 1,983 1,251 0,516 1,756 1,94 1,261 1,72 0,347 1,641 1,796 0,96 1,154 1,97 1,103 0,525 1,483 0,931 0,88 1,641 1,758 1,403 0,498 1,703 0,847 0,272 0,251 0,3 1,185 1,878 1,608 0,390 0,768 0,650 0,324 1,739 1,435 1,95 1,309 0,259 0,461 0,772 0,421 1,5 0,129 1,817 0,750 0,198 1,494 0,127 1,792 1,827 0,830 1,822 0,809 1,441 0,397 1,682 0,385 0,743 1,729 0,224 1,311 1,268 0,547 1,359 1,273 1,814 1,942 0,246 0,10 0,886 1,626 0,660 1,572 1,429 0,585 1,723 0,55 0,301 1,626 0,512 0,51 1,380 1,216 1,707 0,600 0,483 0,404 1,523 0,714 0,200 0,695 1,476 1,183 0,838 1,677 0,927 1,423 0,672 0,256 0,99 1,241 1,775 1,528 1,69 0,916 1,262 0,625 0,478 0,72 0,4 1,233 0,738 0,575 0,162 0,103 1,483 1,629 1,677 0,658 1,638 0,340 0,190 0,732 1,297 1,200 0,450 1,815 0,545 0,599 1,804 0,600 1,292 0,310 1,219 0,887 0,522 1,153 0,839 0,703 0,66 0,912 0,90 1,536 1,56 1,145 1,497 1,696 0,238 0,458 1,83 0,694 1,422 1,755 1,935 1,585 0,961 1,135 0,291 1,403 0,433 0,228 1,300 1,230 0,279 0,476 1,254 1,468 0,622 1,328 1,732 1,227 0,242 1,756 0,242 0,935 1,652 0,115 1,719 1,436 0,27 1,742 0,546 1,636 0,249 0,598 1,689 0,85 0,215 1,67 1,971 1,36 0,304 1,220 1,631 0,306 0,207 0,728 0,893 0,662 0,599 0,354 1,486 0,729 0,729 0,785 1,990 1,533 0,877 0,549 1,88 0,210 1,995 0,218 0,664 1,702 1,632 1,912 1,850 1,236 1,985 1,42 0,658 0,944 0,909 1,662 0,123 0,507 1,312 0,596 1,687 1,996 1,268 1,31 1,776 0,484 0,760 0,499 0,130 0,909 0,517 1,146 0,524 1,420 0,234 0,332 1,599 0,318 0,102 0,586 1,550 0,395 0,179 0,866 1,304 1,344 0,299 1,996 0,698 1,28 0,596 1,578 0,939 1,875 0,327 1,160 0,567 0,537 0,120 1,324 0,307 0,39 0,640 0,898 1,445 1,685 1,812 1,496 1,455 0,766 0,631 1,606 1,367 0,165 1,343 0,431 0,461 1,791 1,724 0,468 1,357 0,702 1,176 0,262 1,645 0,967 0,311 0,509 1,200 1,456 1,891 1,810 1,348 0,625 0,43 0,595 1,705 0,651 0,844 1,313 1,309 0,145 0,247 0,122 1,918 1,869 1,57 0,277 0,119 1,63 0,41 0,259 1,311 1,910 1,336 0,474 1,918 0,677 0,129 0,202 0,746 1,361 0,149 0,915 0,398 1,469 1,490 0,844 0,948 1,482 1,226 0,454 1,742 0,810 0,135 0,675 0,268 0,375 1,384 0,494 0,153 0,628 0,703 0,767 0,803 1,343 1,302 0,948 1,561 0,837 0,673 0,316 0,78 1,293 1,247 1,543 0,160 0,725 0,649 0,301 1,424 0,693 0,299 1,445 1,26 1,227 0,68 0,249 1,766 1,435 1,65 0,310 0,198 0,323 0,8 1,794 0,324 1,246 1,904 0,370 1,738 1,719 1,263 1,870 1,911 1,929 0,685 1,353 1,248 1,698 0,290 1,907 0,384 0,340 0,927 0,89 0,909 0,461 0,713 0,492 0,462 1,651 1,957 0,520 0,146 1,422 0,720 0,885 1,532 1,451 0,507 0,738 1,176 0,896 0,798 1,622 1,94 0,996 1,615 0,269 0,174 0,219 0,793 0,564 1,890 0,365 0,115 1,611 0,75 0,340 0,574 0,768 1,795 0,792 1,321 0,892 1,166 0,761 0,724 1,392 1,361 0,690 1,172 0,848 0,360 0,775 0,11 1,913 0,577 0,136 0,42 1,929 0,18 1,613 1,669 1,174 0,889 1,899 1,199 1,187 1,942 1,867 0,964 1,383 0,221 0,929 0,667 0,785 1,761 1,750 0,223 0,588 1,412 0,390 0,289 0,332 0,894 1,979 1,516 1,314 0,455 0,589 1,670 0,536 0,5 0,18 0,124 1,145 0,940 0,155 0,179 1,93 1,363 1,285 0,689 1,153 1,132 0,488 0,276 0,52 1,167 0,898 0,856 1,587 0,376 0,981 1,712 1,218 1,45 1,628 1,850 1,785 0,502 0,675 0,801 0,103 1,654 0,33 0,239 1,422 1,538 0,469 1,116 0,407 0,68 1,738 0,363 1,445 1,665 1,464 1,685 0,908 1,13 0,516 1,570 1,708 1,590 0,949 0,296 0,67 1,670 0,782 1,431 0,105 0,36 0,447 1,143 1,638 1,60 1,295 1,101 0,309 1,384 0,330 0,827 1,13 0,746 1,503 0,515 0,746 1,708 1,208 0,148 0,253 0,936 1,14 0,610 0,444 1,666 1,886 0,380 1,681 0,668 1,405 1,701 1,223 0,76 1,56 1,58 1,54 1,105 0,376 1,122 1,804 1,525 0,24 0,810 0,383 0,554 0,554 0,172 0,873 0,263 1,834 0,905 1,270 0,919 1,319 1,318 0,17 0,120 0,203 1,284 0,812 1,991 0,606 0,615 1,166 0,973 1,942 1,237 0,746 0,774 0,61 1,920 0,893 1,256 1,434 0,625 1,493 0,442 1,907 1,50 0,469 0,469 0,569 0,21 0,798 0,154 0,489 1,601 1,153 1,60 0,331 1,165 0,481 1,944 1,218 1,755 0,875 0,908 1,356 1,881 0,52 0,838 0,435 1,435 1,13 0,678 1,675 1,378 0,782 0,437 1,365 0,626 0,35 0,846 1,853 0,921 0,498 1,979 0,657 0,861 0,519 0,105 0,951 0,224 1,515 0,329 0,444 0,421 0,787 1,831 1,730 1,529 1,629 0,697 1,647 0,383 1,625 0,907 0,735 0,468 0,427 0,752 1,92 0,209 1,344 1,770 1,331 0,76 1,962 0,178 0,204 1,649 0,842 1,659 0,416 0,552 1,892 0,459 0,928 0,592 1,984 0,989 0,349 1,840 1,670 0,389 0,141 1,303 1,804 1,935 0,528 1,709 0,768 1,134 0,286 0,917 0,490 1,66 1,983 1,393 0,73 1,739 1,554 0,187 0,844 1,527 0,446 0,344 0,95 0,476 0,68 1,811 1,347 0,553 1,646 0,234 1,88 1,645 1,461 0,743 1,316 1,825 1,92 0,316 1,279 0,618 1,796 1,170 1,675 1,226 0,589 0,102 1,615 0,128 0,498 0,857 0,828 1,573 1,0 0,282 0,264 0,711 0,628 1,299 0,814 1,982 1,43 1,698 0,780 1,176 0,791 1,988 1,279 0,265 0,857 0,843 0,599 1,865 0,256 0,336 0,426 1,533 1,108 0,700 1,374 0,615 0,985 1,952 0,209 0,885 0,36 1,43 1,802 1,94 0,470 0,95 0,804 0,376 0,485 0,848 1,919 1,846 1,696 1,906 1,862 1,508 0,161 0,651 1,934 1,519 1,653 1,64 0,574 1,695 0,219 0,299 1,895 1,763 0,111 0,187 1,965 0,696 1,259 0,2 1,574 1,564 0,115 0,467 0,810 0,704 0,501 0,266 0,160 1,684 1,81 0,939 1,824 0,493 0,556 1,486 1,724 0,644 0,672 0,136 1,727 0,35 1,864 1,641 1,191 1,457 1,549 0,321 1,121 1,726 1,818 1,536 1,640 1,959 0,672 1,6 1,572 0,895 0,818 0,742 1,139 0,165 1,944 1,69 0,5 0,998 1,221 1,341 1,877 0,616 0,498 0,718 0,119 1,407 0,401 0,421 1,99 0,611 1,753 1,904 1,809 0,103 0,409 0,484 1,833 1,202 0,981 0,107 0,951 0,56 0,160 0,834 1,449 0,660 0,285 1,718 0,128 0,952 1,115 0,683 0,749 1,502 1,45 1,139 1,553 1,273 1,98 0,564 0,371 0,257 1,543 0,433 0,727 0,186 1,897 0,843 0,722 1,823 0,955 1,816 1,16 1,983 1,11 1,479 1,211 1,398 1,744 0,584 1,468 0,971 0,310 0,311 0,776 1,716 1,613 1,833 0,218 0,618 1,530 1,1 1,824 1,53 1,712 1,467 0,461 0,677 0,264 0,990 1,588 1,277 0,196 0,593 1,484 0,417 1,307 0,461 1,755 1,177 0,656 0,937 1,20 0,219 1,240 0,585 0,747 0,145 1,264 0,671 0,832 1,952 1,306 1,252 0,878 0,38 0,314 1,502 1,192 1,827 1,735 0,889 0,76 0,312 1,692 0,712 1,152 0,893 1,117 1,992 1,373 0,616 0,740 1,196 1,416 0,305 0,576 0,535 0,463 0,134 0,923 1,486 0,514 0,36 1,635 0,280 1,95 1,551 1,880 0,102 0,74 0,625 1,149 0,947 0,955 1,739 1,521 0,720 1,227 1,426 1,593 1,184 0,747 0,295 1,382 0,463 0,198 0,424 1,62 0,127 0,603 0,148 1,980 1,738 1,833 0,199 0,709 1,857 1,545 1,876 1,170 0,766 0,35 1,268 0,673 0,531 1,49 0,627 0,352 0,714 0,701 0,779 0,796 0,994 0,410 1,225 1,450 1,964 0,484 0,400 1,928 1,970 0,286 0,331 1,741 0,760 0,329 0,557 1,59 0,305 1,277 1,910 0,859 0,498 1,774 1,420 0,215 0,610 1,355 0,295 1,260 1,191 1,251 0,224 0,716 0,865 1,222 1,141 0,970 0,862 0,552 1,870 1,287 1,548 1,67 1,170 0,921 0,343 1,695 1,215 0,587 1,60 1,521 0,69 1,782 1,864 1,594 0,820 0,18 1,185 0,143 1,452 1,790 1,576 1,593 1,922 0,12 0,255 1,502 1,434 0,234 0,94 1,251 0,484 0,857 0,730 1,883 0,735 0,436 0,120 1,709 0,60 1,119 0,535 0,891 0,278 1,945 1,542 0,981 1,285 0,344 1,674 0,704 0,791 1,682 1,564 0,67 0,437 1,310 0,810 1,371 0,367 0,638 0,601 1,623 1,624 0,898 0,914 1,730 0,167 1,530 1,454 1,857 1,919 1,801 0,454 1,314 1,769 1,112 0,705 0,783 1,473 1,977 1,616 1,880 1,446 0,830 0,107 1,553 0,613 1,517 1,490 0,580 0,337 0,29 1,41 1,661 0,909 0,881 1,189 0,980 1,885 1,48 0,600 0,440 1,702 1,593 0,550 1,632 1,657 1,679 1,712 1,37 0,988 0,695 0,574 1,566 0,792 1,267 1,109 0,718 1,43 1,922 1,668 1,679 1,321 1,190 1,279 1,632 0,962 0,872 0,862 0,534 1,77 1,2 0,626 1,185 1,528 1,567 0,248 1,924 1,785 1,424 1,706 1,309 0,809 1,411 0,124 0,72 1,364 1,645 0,9 0,655 0,557 0,509 1,13 1,664 1,705 0,133 1,161 1,33 1,258 0,644 1,820 0,470 1,672 0,867 0,455 0,194 1,102 0,333 1,325 1,825 0,512 0,61 1,899 1,979 0,832 1,237 1,680 1,538 1,982 1,718 0,293 1,670 0,935 1,382 0,237 0,130 1,32 0,631 1,483 0,521 0,233 1,526 0,211 1,435 0,314 1,399 0,212 0,86 0,362 0,491 1,982 0,996 0,927 1,641 1,590 0,619 0,758 1,318 1,790 0,218 0,863 1,335 0,456 0,442 1,882 0,836 0,767 0,355 1,271 0,881 0,763 1,940 1,81 0,484 0,638 0,568 1,262 1,402 1,653 0,399 1,595 1,47 1,191 0,752 0,74 0,80 1,862 0,98 1,396 0,418 1,45 0,697 1,28 0,67 1,877 1,923 1,233 0,21 1,866 0,450 1,799 1,744 0,159 0,783 1,766 0,637 0,975 1,324 0,371 0,257 0,154 1,577 1,566 0,474 0,723 1,791 0,423 0,614 1,134 1,173 0,222 1,879 1,215 1,34 0,196 0,959 1,608 0,614 0,34 1,238 1,694 0,494 0,98 0,989 1,433 1,269 0,392 0,428 1,972 0,403 1,858 1,566 0,207 0,32 0,57 1,855 0,72 0,47 1,745 0,483 1,982 1,651 0,827 1,847 1,286 0,687 1,61 1,849 1,467 1,890 1,337 0,742 0,914 1,582 0,583 1,770 0,853 0,67 1,752 1,549 1,487 1,302 1,787 0,485 1,614 1,643 1,941 1,911 1,689 1,430 1,813 1,292 1,380 1,306 1,669 0,215 0,514 1,578 0,783 1,467 1,438 1,47 0,322 1,42 0,33 0,770 0,16 1,908 0,613 0,932 1,116 1,582 0,966 1,805 0,359 0,48 1,466 0,644 0,725 0,257 1,308 1,914 0,960 1,406 0,295 0,925 0,683 0,337 0,243 1,347 0,947 1,9 0,547 1,244 0,812 0,920 0,988 0,276 1,226 0,40 0,218 1,81 0,363 1,453 0,641 0,282 1,874 1,852 1,272 1,938 0,360 1,516 0,789 0,82 0,39 1,932 1,428 1,489 0,303 0,391 1,46 1,591 1,671 0,438 0,587 1,653 1,499 0,923 0,308 1,482 0,578 1,338 0,778 1,670 1,539 0,316 1,772 0,238 1,938 0,279 0,548 1,535 0,947 1,904 0,101 1,685 1,855 1,363 0,947 1,850 1,732 0,239 0,450 1,792 1,413 0,780 0,960 0,246 0,298 0,860 0,993 1,210 0,263 1,642 0,569 0,614 0,73 1,23 1,839 0,791 0,995 0,85 1,369 1,452 1,545 0,324 0,281 1,930 1,99 0,136 0,597 0,700 1,334 1,305 0,977 1,196 0,852 0,729 1,776 0,484 1,68 0,891 0,412 1,278 0,829 1,89 1,607 0,932 0,981 0,935 0,841 0,71 1,392 0,777 0,605 0,76 1,319 0,175 0,415 1,919 1,253 0,843 1,553 1,725 1,255 1,296 1,182 1,398 1,348 0,603 0,462 0,497 0,119 1,89 0,602 1,620 0,179 0,684 0,109 0,586 1,217 1,887 1,494 0,512 0,160 0,442 1,697 0,185 1,209 1,173 1,55 0,882 1,928 1,913 1,385 1,948 0,863 1,452 0,630 0,876 0,225 0,207 1,623 1,902 1,693 1,865 1,952 1,683 1,297 0,983 0,365 0,496 1,992 1,537 1,937 1,980 0,682 1,997 1,922 1,587 1,653 1,339 1,285 1,408 1,602 0,104 0,594 1,195 1,733 1,474 0,518 0,310 1,399 0,305 1,531 0,602 1,458 0,149 0,695 1,719 1,5 1,419 1,221 0,106 1,341 0,283 1,984 0,171 1,351 1,374 1,944 1,347 0,274 1,818 1,973 1,969 1,227 0,76 0,267 1,586 1,102 0,733 1,635 0,167 1,324 0,948 1,59 0,258 1,580 0,800 1,127 1,475 0,745 0,762 1,459 1,991 1,503 0,588 0,285 1,687 1,228 0,602 1,666 0,965 1,5 0,562 1,276 0,884 0,870 1,373 0,442 1,127 1,173 0,418 1,905 1,32 0,716 0,902 0,960 1,135 0,827 1,737 0,886 1,965 0,130 0,558 0,81 1,658 1,860 0,750 0,537 1,676 1,990 1,54 1,251 1,551 0,584 1,337 0,746 1,55 1,301 1,792 0,639 1,740 1,190 1,62 0,748 0,495 1,211 0,904 0,798 1,295 0,116 1,492 0,868 1,646 1,597 1,798 1,566 1,204 0,931 1,665 1,856 1,720 1,975 1,638 1,724 0,56 1,952 1,494 0,33 1,824 1,707 0,770 0,933 1,450 0,466 1,255 1,281 1,176 1,666 1,238 0,855 1,432 1,289 0,770 0,789 0,792 1,162 0,115 1,332 1,67 0,217 0,222 0,18 1,975 1,688 0,242 0,546 0,714 0,638 1,960 1,627 1,72 1,635 0,599 0,601 0,139 1,434 0,476 1,682 1,973 0,959 0,435 0,726 0,303 1,513 1,423 1,653 0,762 0,542 0,864 1,183 0,681 1,392 0,260 0,336 1,854 0,395 1,798 1,584 0,664 0,57 0,717 0,93 0,924 0,528 1,938 1,389 0,902 1,723 1,943 1,188 0,75 1,235 1,295 1,492 1,2 0,795 0,487 1,871 1,77 0,330 0,737 0,458 1,451 0,956 0,251 0,849 1,67 0,596 1,739 0,263 0,883 0,874 0,227 0,295 0,976 1,601 1,718 1,246 0,389 1,172 1,686 1,196 0,18 1,320 0,624 1,44 0,849 0,152 0,964 1,589 1,753 0,936 1,507 0,719 0,259 1,629 1,942 0,856 1,126 0,902 1,250 1,238 0,189 1,114 1,704 1,53 0,926 0,571 0,259 1,753 0,317 1,793 1,977 0,436 0,706 1,474 1,572 1,228 0,319 0,501 0,384 1,681 0,825 0,742 0,422 1,941 0,257 0,105 1,109 0,974 0,297 0,143 0,269 1,265 1,458 1,609 1,137 0,348 0,656 1,495 1,96 1,45 1,628 0,228 0,586 1,301 0,681 0,747 1,962 1,206 0,870 1,152 0,773 0,519 1,600 1,294 0,297 0,307 1,808 1,823 0,591 1,965 1,729 1,839 0,557 1,124 0,32 1,197 1,783 0,548 0,135 0,916 0,496 0,913 0,527 0,172 1,791 1,962 0,903 0,526 1,474 1,59 1,632 0,702 1,450 1,87 0,661 0,216 1,530 0,382 0,444 0,351 1,499 0,88 0,329 1,503 0,548 1,186 1,122 1,660 0,862 0,796 1,82 0,507 1,923 1,818 1,582 0,346 0,634 1,614 1,507 0,888 1,693 1,512 0,158 1,961 0,73 0,956 1,825 0,963 1,413 1,422 1,249 0,984 0,733 1,547 0,78 0,839 0,865 0,7 1,894 1,70 0,103 1,991 1,183 0,986 0,890 1,734 0,935 0,10 0,329 1,153 1,584 0,252 0,135 1,905 1,570 1,885 0,112 1,605 1,779 0,758 0,910 1,449 1,771 0,804 0,484 1,875 1,109 1,432 1,312 1,5 1,182 0,769 1,206 1,297 1,208 1,2 0,226 0,641 0,526 0,145 1,249 0,178 0,685 0,932 0,259 1,569 1,704 0,972 0,406 1,815 1,519 0,181 1,201 1,229 1,490 1,604 0,459 1,33 1,78 1,383 0,792 1,916 1,829 1,992 1,604 1,894 0,665 0,198 1,933 0,769 0,44 0,694 0,441 1,595 1,799 1,8 0,525 0,246 0,353 0,975 1,637 0,520 0,14 0,670 1,760 1,201 0,977 1,767 0,837 1,480 0,770 1,809 0,287 1,717 0,316 0,463 0,716 0,147 0,765 1,794 1,878 1,763 0,708 1,204 0,711 1,298 0,918 1,42 1,786 1,257 1,249 0,750 1,748 1,9 1,285 0,427 1,78 1,369 0,603 1,776 1,487 1,383 1,312 0,900 0,436 1,666 1,196 1,225 0,974 1,409 0,811 0,675 0,43 1,481 0,325 1,572 0,137 1,968 1,344 0,468 0,499 1,602 1,326 1,154 1,731 1,57 1,586 1,700 0,640 1,400 0,542 1,125 0,14 0,893 0,643 1,143 0,16 0,280 0,733 0,580 0,745 1,505 1,277 1,444 0,458 0,449 1,140 0,491 0,624 0,949 0,968 0,449 0,907 0,252 1,146 0,115 1,246 0,527 1,672 1,644 0,881 1,131 0,375 1,419 1,767 0,518 1,815 1,242 1,689 0,365 0,21 0,681 1,423 1,923 0,152 1,200 1,860 0,661 1,387 0,191 1,346 1,751 1,128 0,967 1,398 1,830 1,305 0,390 1,299 0,207 0,796 1,551 1,118 1,85 1,687 1,694 0,735 0,378 0,493 1,524 0,999 1,615 0,695 0,545 0,644 0,928 1,988 1,38 0,386 0,179 1,427 1,298 1,67 0,468 1,716 1,719 1,467 1,869 0,360 0,760 1,228 0,607 1,567 0,995 0,677 0,902 1,841 0,685 0,469 1,992 1,520 0,643 1,414 0,172 0,943 1,775 1,175 1,124 0,685 1,75 1,172 1,688 1,881 1,926 0,298 0,995 1,730 0,391 0,429 1,392 0,194 1,312 0,701 1,335 1,224 1,491 1,278 1,760 0,355 1,976 0,102 0,312 1,685 1,445 1,708 0,297 0,42 0,829 1,887 0,741 0,569 0,145 0,801 1,477 0,25 0,198 0,342 1,400 1,451 0,236 1,112 0,887 0,56 1,234 1,245 0,588 0,680 1,923 1,936 1,64 0,233 0,561 0,443 0,735 1,374 0,587 1,408 0,22 0,140 1,684 1,24 1,38 1,233 1,612 0,317 0,103 0,762 1,908 1,826 1,670 1,599 1,821 1,731 0,323 0,252 1,532 1,783 0,200 1,439 0,103 1,656 1,670 0,969 1,985 1,529 0,562 1,236 1,781 0,278 1,672 0,807 1,8 0,534 0,416 0,647 0,983 1,879 0,328 0,761 0,242 0,299 0,756 1,701 1,653 1,315 0,38 1,975 1,437 0,140 0,145 1,826 0,912 1,594 0,899 0,750 0,382 0,493 0,784 1,960 1,697 1,933 0,681 0,656 1,908 0,313 1,449 0,804 1,290 0,329 1,40 0,773 1,188 0,361 1,987 0,18 1,237 0,688 0,561 0,421 1,673 1,229 0,616 1,963 0,385 0,307 1,450 0,471 1,610 0,634 1,640 1,880 1,861 0,603 0,153 0,176 0,440 0,868 0,590 0,657 0,654 0,180 0,271 1,143 0,762 1,813 0,706 0,482 0,216 1,814 0,216 1,927 0,894 1,623 1,621 1,48 1,397 1,985 0,276 1,548 0,533 0,604 0,675 1,265 1,682 0,844 0,963 1,349 0,394 1,773 1,881 0,1 0,247 0,865 0,433 0,797 0,537 1,192 0,701 1,748 0,308 0,811 1,492 1,764 1,297 0,591 0,280 1,431 0,229 1,545 0,835 0,712 1,573 0,164 0,420 1,422 0,735 0,9 1,223 0,701 1,652 0,645 0,935 1,855 0,274 1,527 0,926 0,992 1,960 1,647 0,424 1,244 0,779 0,860 1,481 1,99 0,993 0,498 1,241 0,180 0,880 0,917 1,828 0,943 1,225 0,482 0,506 1,294 1,734 1,205 0,816 1,624 0,237 1,345 1,428 1,406 1,681 1,92 1,855 0,542 0,83 1,612 0,895 0,472 0,176 1,75 1,687 0,632 1,603 1,673 1,89 1,16 1,657 0,661 0,415 0,848 0,752 0,30 0,27 0,718 1,228 1,977 1,341 0,711 0,738 1,636 0,136 0,440 0,509 0,331 0,377 0,784 1,352 1,788 0,377 1,762 1,14 0,366 1,432 1,559 0,46 1,244 1,18 1,288 0,901 1,457 0,931 0,924 0,679 1,939 1,194 1,878 0,55 1,861 0,902 1,902 0,415 0,9 1,617 0,840 1,892 0,92 0,74 1,768 1,881 1,344 1,770 0,502 1,172 0,436 1,70 0,420 0,818 0,373 0,341 1,639 1,285 0,420 0,479 0,907 1,361 1,651 1,971 0,416 1,706 0,97 1,876 1,27 1,42 1,124 1,569 1,124 1,579 1,708 0,801 0,325 1,234 1,984 1,168 1,512 0,12 1,801 1,816 1,365 1,463 1,853 1,53 1,156 1,395 0,866 1,733 1,878 0,312 0,317 0,960 1,846 1,767 1,84 1,526 0,287 0,268 0,133 0,204 1,756 1,987 0,703 0,133 0,360 0,131 1,512 1,946 1,14 1,673 1,207 1,514 1,379 0,154 1,545 1,942 1,672 1,758 0,222 1,403 1,741 0,676 1,216 1,1 1,402 0,240 1,927 1,877 1,112 0,974 1,759 1,535 1,599 1,688 0,442 1,148 0,216 1,787 0,367 0,353 1,783 0,102 1,519 0,466 1,515 0,536 1,251 1,571 1,654 0,409 0,684 1,855 0,278 1,883 1,296 1,184 1,591 0,624 0,279 0,859 1,795 1,201 0,343 0,85 1,228 0,767 1,724 0,831 1,222 0,237 1,938 0,193 0,871 1,387 0,552 1,319 1,777 1,402 1,313 1,36 1,679 0,323 1,738 1,416 0,294 0,296 1,46 1,341 0,93 0,179 0,283 1,665 1,472 0,489 1,447 1,907 0,563 1,651 1,288 0,350 0,224 1,672 1,385 0,38 0,954 0,119 0,913 1,817 0,197 1,909 0,948 1,760 0,440 0,23 1,195 1,466 1,37 0,728 0,255 1,70 0,386 1,485 0,46 1,153 0,291 1,366 1,208 0,468 1,885 0,984 0,496 0,409 1,176 1,984 1,445 0,204 0,185 0,463 0,3 0,44 0,575 0,842 1,824 1,495 1,897 1,202 1,73 0,869 1,595 1,30 0,374 1,964 1,785 0,831 0,653 0,456 1,947 0,245 0,831 1,338 1,45 1,184 1,303 0,478 1,262 0,422 1,137 1,801 0,998 1,646 0,599 0,787 1,584 0,479 0,285 0,706 0,960 1,305 0,166 1,380 1,736 0,687 1,61 1,236 0,214 0,46 0,993 0,495 1,229 0,285 0,401 0,911 0,94 0,151 0,810 1,359 1,425 0,394 1,311 0,241 0,192 0,469 1,245 0,474 0,958 1,55 1,995 0,210 1,78 1,944 1,707 0,591 1,465 0,822 0,962 1,398 0,81 0,224 1,59 0,274 1,485 1,472 1,569 1,175 1,86 1,598 0,955 1,87 0,317 0,347 0,308 0,977 1,165 0,697 1,422 1,198 0,341 1,148 0,587 1,405 0,591 1,822 0,380 0,342 0,827 0,920 1,94 1,432 0,321 1,420 1,40 0,415 0,592 0,327 0,942 1,178 0,399 0,928 0,846 0,229 0,814 0,519 0,599 1,34 0,763 0,418 0,449 1,516 1,187 1,680 1,186 0,711 0,323 1,867 1,775 0,734 0,97 1,812 1,214 0,461 0,596 1,676 0,102 1,905 1,536 0,677 1,543 0,331 1,44 0,253 0,934 1,300 1,567 0,610 1,694 0,299 0,886 0,599 1,103 0,541 1,649 1,51 0,492 0,562 0,579 1,428 1,123 0,101 1,383 0,222 1,392 1,165 0,652 0,128 1,664 1,391 1,911 1,568 1,882 0,394 1,851 0,590 1,591 1,877 0,66 1,490 1,985 0,529 1,222 1,222 0,986 0,790 0,994 0,145 1,111 0,927 0,139 1,631 1,326 1,510 1,339 1,545 1,646 1,172 0,123 0,176 0,731 0,964 1,148 1,499 0,484 0,577 0,202 0,94 1,800 0,81 0,427 0,452 1,124 0,455 0,859 1,937 1,6 0,272 1,891 1,413 0,337 0,799 1,49 0,333 1,529 1,916 1,469 0,41 0,36 0,205 1,932 1,48 1,809 1,591 0,499 0,532 0,540 0,499 0,418 0,670 0,518 1,481 0,421 0,487 1,885 1,125 0,919 1,476 1,895 0,989 0,566 1,342 0,644 0,552 1,754 1,987 1,808 1,853 0,350 1,218 0,262 0,201 0,190 1,189 1,843 0,307 0,793 0,169 0,45 1,866 1,473 1,138 0,296 0,503 1,138 0,66 0,791 1,582 0,938 0,90 1,57 1,590 1,863 1,515 0,513 0,536 1,612 1,828 1,893 1,421 1,979 1,964 0,166 0,320 0,754 1,686 0,627 1,271 0,864 0,197 0,142 0,385 0,714 1,188 0,351 0,892 0,803 0,397 1,555 0,544 1,713 1,294 0,325 0,604 1,675 0,431 1,311 0,935 0,948 1,196 1,539 0,213 1,162 0,209 1,770 0,28 0,878 0,128 0,874 1,519 1,498 1,229 0,923 0,813 0,163 1,79 1,192 0,183 0,58 0,503 1,925 0,930 1,235 1,326 0,337 0,754 0,139 0,276 0,859 1,658 1,367 0,997 0,629 1,522 0,784 0,455 0,765 0,902 0,13 1,29 1,192 1,851 0,86 1,804 0,513 1,118 0,10 1,50 1,657 1,803 1,954 1,124 1,277 1,280 0,862 1,37 0,33 1,136 0,756 1,781 1,143 1,617 0,619 1,919 0,130 1,806 1,206 0,84 0,960 1,644 1,215 1,632 1,602 1,404 1,380 0,245 0,774 1,807 1,217 1,359 0,122 1,883 1,715 0,612 1,184 1,237 1,866 1,431 1,490 0,159 1,151 1,71 1,234 1,676 1,94 1,140 1,529 1,852 1,948 0,304 0,350 1,399 1,481 1,306 0,172 1,302 1,641 1,95 1,863 1,80 0,866 1,567 1,941 0,241 1,838 0,757 0,742 1,600 0,324 0,564 0,92 1,769 0,45 0,178 0,371 0,671 0,64 1,734 0,335 1,735 1,437 0,227 0,465 1,564 0,91 0,380 0,708 0,934 0,584 1,29 0,329 1,822 0,264 0,839 1,19 1,613 1,516 0,162 1,766 0,425 0,119 0,647 1,729 1,928 1,943 1,421 1,270 1,333 1,467 0,870 0,837 0,237 1,568 1,566 0,608 1,701 1,588 0,793 1,140 0,189 0,803 0,439 0,922 1,123 1,578 1,783 1,324 1,399 1,972 0,300 1,90 1,945 0,168 1,46 0,281 1,213 0,762 0,747 1,733 1,343 1,422 1,608 1,997 0,50 0,686 0,367 0,515 0,180 0,275 0,99 0,598 1,219 0,531 1,957 1,749 0,178 0,1 1,225 0,367 1,132 0,125 1,288 0,642 0,945 0,476 1,92 1,270 1,9 0,69 1,948 1,98 1,294 0,536 1,756 1,341 0,775 1,336 1,543 1,996 1,594 1,264 1,782 0,153 1,694 1,60 0,53 0,973 0,144 1,67 1,795 1,782 0,150 1,172 0,430 1,80 1,544 1,143 1,755 0,758 0,998 1,79 0,789 1,740 1,857 1,953 1,615 0,563 1,904 1,45 1,469 1,554 1,957 0,823 0,618 0,667 0,921 0,457 0,456 0,640 0,148 1,398 0,347 0,165 1,502 1,848 1,61 1,102 0,433 0,355 1,442 1,475 0,4 1,834 0,470 1,565 0,503 0,673 1,198 1,629 1,921 1,389 1,922 0,288 0,851 1,817 1,422 0,670 0,392 0,837 1,303 0,642 0,668 0,641 1,820 0,504 1,825 0,989 1,917 0,479 1,620 1,531 1,932 0,469 0,503 1,715 1,167 0,156 0,522 1,24 0,235 0,598 1,26 1,844 1,720 1,33 1,350 1,84 0,46 1,486 1,129 1,629 1,414 0,294 0,500 0,191 1,8 0,644 1,806 1,336 0,47 0,704 1,105 0,917 0,480 1,222 1,746 1,751 0,514 0,881 0,616 1,674 1,373 0,627 0,827 0,704 0,447 0,444 0,615 1,356 1,620 0,447 0,290 1,531 0,400 0,18 0,924 1,816 0,3 1,723 1,531 1,685 1,645 0,274 0,324 0,604 1,231 1,826 1,573 0,855 1,722 0,25 0,210 1,340 1,977 1,299 1,735 1,749 0,976 1,632 1,501 0,671 0,347 0,925 1,395 0,77 0,472 0,291 0,638 0,181 0,443 0,783 1,683 1,37 0,419 1,639 1,301 0,912 0,550 0,684 0,442 1,277 1,404 1,73 0,998 1,550 0,116 1,181 1,363 0,153 0,830 1,261 0,838 0,235 0,302 1,608 1,402 1,916 1,633 1,215 1,755 1,718 1,341 0,526 0,318 1,550 0,829 1,841 1,70 1,902 1,438 0,117 0,567 0,269 0,67 1,895 1,263 0,364 1,663 1,985 0,451 1,321 1,879 0,664 0,370 1,105 1,344 0,173 0,912 0,602 0,707 0,344 1,445 1,757 1,723 1,512 1,629 1,818 1,281 1,967 1,407 1,842 1,961 1,501 0,341 1,989 0,905 0,415 1,397 0,190 1,738 1,473 1,114 0,555 1,733 0,839 1,354 1,847 0,661 1,724 1,82 1,122 0,155 0,433 0,214 1,114 0,115 1,960 1,42 1,92 0,377 1,284 1,443 0,474 0,814 0,668 1,362 0,225 0,271 0,774 1,665 0,262 1,917 0,269 0,413 1,655 0,51 0,536 0,332 1,379 1,377 0,295 1,24 1,313 0,845 0,8 0,338 1,966 0,38 0,430 0,8 0,400 0,426 1,767 1,529 1,975 0,704 1,724 0,290 0,511 0,415 1,414 0,193 1,451 1,171 1,250 0,825 1,356 1,301 1,342 1,509 0,59 0,36 1,984 0,753 1,554 1,395 0,174 1,591 0,995 0,802 1,591 1,11 0,782 0,889 0,950 0,626 0,422 1,47 1,388 1,708 1,134 1,156 0,771 0,736 1,31 0,296 0,361 1,703 0,664 1,565 1,250 0,582 0,539 1,838 1,594 0,911 0,85 0,887 0,142 0,778 0,653 0,22 0,938 1,610 1,15 0,947 1,157 1,766 0,930 0,504 1,30 0,988 0,753 0,426 0,271 1,350 0,227 0,22 1,206 1,890 0,622 1,338 0,977 1,533 0,743 0,63 0,354 0,222 1,976 1,979 0,861 1,288 0,386 0,310 0,472 0,168 1,207 0,477 1,843 0,719 1,842 0,262 1,73 1,38 1,644 1,477 1,793 0,99 0,177 1,997 0,75 0,2 0,753 1,717 1,749 0,646 0,412 0,951 0,521 0,529 1,630 0,946 1,125 1,278 1,567 1,535 1,26 0,978 0,352 0,629 0,804 1,435 0,314 0,922 1,518 0,935 0,202 0,307 0,435 1,450 0,791 1,229 0,613 1,379 0,241 1,104 1,364 0,848 1,772 0,457 1,872 0,716 1,385 0,616 0,725 0,83 0,349 1,535 0,517 0,598 1,136 1,916 1,900 1,547 1,880 0,660 0,293 0,625 0,917 1,442 0,122 1,576 1,246 0,189 1,880 1,668 0,242 0,519 0,510 1,972 1,173 0,122 0,128 0,115 0,63 1,608 0,408 0,332 1,893 0,542 0,856 1,536 1,878 0,5 0,962 0,700 1,986 0,947 0,77 0,414 1,221 1,522 1,659 0,463 1,517 0,995 0,151 0,438 0,530 0,243 1,835 0,747 0,774 1,894 1,255 0,794 0,701 0,155 1,390 1,624 1,652 1,425 1,994 0,347 0,411 1,361 0,176 1,355 0,754 0,399 0,490 0,425 1,545 1,889 1,908 0,953 1,606 1,100 1,677 0,491 1,571 0,882 1,371 1,226 0,972 1,336 0,996 1,84 0,826 1,299 1,507 0,269 0,636 1,900 0,465 0,438 0,272 1,478 1,112 0,643 1,79 0,760 1,550 0,712 0,884 1,293 1,430 1,717 0,659 0,907 1,335 0,401 1,791 0,420 1,996 0,596 1,966 0,934 0,723 1,22 0,970 0,255 1,495 1,768 0,683 0,633 1,955 0,876 0,178 0,795 1,648 0,296 0,635 1,637 0,350 1,230 0,905 1,957 1,306 1,416 1,396 1,30 0,132 0,71 1,61 1,77 0,65 0,196 0,476 0,167 1,309 1,168 0,188 1,300 1,284 1,498 0,61 1,619 1,518 0,419 1,392 0,906 1,312 1,95 1,791 1,532 1,257 0,401 1,8 1,839 1,971 0,124 0,216 1,526 0,200 0,140 1,913 0,189 0,40 1,546 1,174 1,281 1,830 0,476 0,250 1,53 0,962 0,163 0,503 0,973 0,111 1,894 0,843 0,563 1,266 1,859 1,56 0,898 0,743 0,92 1,688 0,651 1,869 0,980 1,402 0,408 1,932 1,38 0,647 1,262 0,369 1,255 0,361 0,311 1,384 1,168 0,683 1,554 0,785 1,306 1,957 0,308 0,882 1,452 1,899 1,396 1,225 1,779 1,662 1,64 0,105 0,170 0,160 0,958 1,851 0,563 1,912 0,545 0,714 0,387 1,999 1,753 1,199 0,22 0,767 1,384 0,325 0,936 0,887 0,307 0,80 0,217 1,112 0,417 1,724 0,674 1,581 0,840 1,816 1,964 1,853 0,534 0,447 1,155 0,598 1,74 0,10 1,769 1,919 1,84 1,305 0,974 0,25 1,976 0,253 0,285 1,803 0,405 1,163 0,842 0,530 0,492 0,254 0,351 0,884 1,379 0,225 1,148 1,689 1,51 0,24 0,557 1,458 1,694 0,550 1,988 0,254 0,43 1,560 1,120 0,780 0,618 1,951 1,817 1,326 1,653 0,559 1,522 1,39 0,869 1,229 1,529 0,805 0,642 0,413 1,445 0,103 0,396 0,807 0,958 0,831 1,835 1,791 0,925 1,712 1,755 0,61 1,292 0,231 0,609 1,358 1,458 0,172 1,215 1,330 1,972 0,53 0,151 0,484 0,320 0,669 0,111 0,30 0,980 0,90 1,361 1,662 1,513 0,156 1,493 0,831 0,908 1,827 1,969 0,200 0,326 0,822 0,486 0,21 0,399 1,101 1,454 0,518 1,642 1,124 0,452 1,60 1,585 0,621 1,953 0,275 0,498 1,463 1,226 0,969 1,842 1,538 1,535 0,778 1,331 0,829 0,401 1,386 0,583 0,676 1,417 1,471 1,390 1,616 0,166 1,130 1,70 1,787 1,877 0,855 1,33 0,685 0,411 0,495 0,274 0,574 1,615 0,105 0,756 1,148 0,135 0,476 1,781 0,146 0,951 0,485 0,997 1,865 1,906 1,462 1,96 1,910 1,664 0,95 0,145 0,947 0,308 0,108 1,998 1,274 1,877 0,45 1,243 0,666 0,950 0,482 0,311 0,945 1,513 0,746 0,562 0,524 0,509 1,647 1,120 1,223 1,906 1,331 1,88 0,277 0,319 0,473 1,203 0,105 0,566 0,317 0,874 1,95 1,981 0,686 1,31 1,79 1,54 1,971 0,63 1,386 0,351 1,409 0,402 0,675 0,108 1,241 1,342 1,34 0,830 0,421 1,427 1,947 1,577 1,873 1,618 0,107 1,593 1,279 1,896 0,74 0,994 1,910 0,70 1,338 0,92 1,675 1,451 0,738 0,293 0,666 1,662 1,503 0,460 1,228 0,838 1,124 0,165 1,111 0,365 1,274 1,812 1,670 1,590 0,937 1,377 1,440 1,920 1,446 0,1 0,489 1,580 0,483 1,287 1,689 1,37 1,91 0,351 1,78 1,764 1,857 1,627 1,753 0,464 1,536 1,343 0,301 0,220 0,722 1,158 0,93 1,802 1,355 1,361 1,40 1,4 0,33 0,769 0,643 0,35 0,943 1,616 0,705 0,993 1,572 1,807 0,260 1,234 0,570 1,396 1,467 0,963 1,606 0,221 1,288 1,975 1,634 0,884 1,884 0,375 0,863 1,302 0,391 1,326 1,227 1,951 0,839 1,285 0,995 1,322 0,323 1,424 0,279 0,138 0,649 1,467 1,610 1,398 1,406 1,762 0,941 1,323 1,422 0,631 0,663 1,99 0,929 0,303 0,979 0,404 1,451 0,385 1,793 1,147 1,252 0,913 1,562 1,528 0,860 1,341 0,396 0,473 0,669 1,250 1,927 1,457 0,680 1,973 1,962 0,985 1,698 1,319 0,117 1,367 1,489 0,581 1,602 1,297 0,722 0,705 0,755 0,656 0,822 1,729 0,279 0,148 1,219 0,757 1,807 0,251 0,528 0,227 0,48 1,340 1,24 0,835 1,3 1,834 0,531 0,827 0,672 0,887 0,745 1,573 1,604 0,710 1,209 0,972 1,12 0,426 0,90 1,158 0,402 1,955 1,311 0,958 1,546 1,326 1,439 1,211 1,903 0,194 0,77 0,440 1,729 0,322 1,304 0,418 1,487 0,512 0,322 0,951 0,307 1,18 0,25 0,986 1,332 0,81 1,989 1,854 0,22 1,561 1,656 0,488 0,985 0,14 1,752 1,258 1,456 0,249 1,404 1,207 0,58 0,130 0,546 0,339 0,813 0,167 1,132 0,665 0,555 1,201 0,988 0,770 1,339 0,910 1,882 1,68 0,218 1,947 0,888 0,832 1,167 1,310 1,833 1,266 0,955 1,128 1,752 0,654 0,37 0,438 0,22 1,690 1,632 1,632 0,158 0,657 0,18 1,29 0,224 0,473 1,318 0,84 0,329 0,268 1,706 1,467 1,228 1,117 0,245 1,974 1,667 0,314 0,389 0,912 1,816 1,18 1,784 0,331 0,828 1,103 0,935 0,778 0,723 0,629 0,798 1,144 0,876 1,485 1,765 0,46 1,79 0,283 1,411 1,884 0,58 1,908 0,560 0,7 0,681 0,171 1,593 0,665 0,577 0,327 0,241 0,916 1,175 0,1 1,137 1,574 1,554 0,716 0,7 0,597 1,863 1,946 0,688 1,908 0,194 1,496 1,176 0,111 1,895 0,261 0,788 0,972 0,633 0,295 0,690 0,601 0,759 1,787 1,6 1,708 1,279 1,604 0,899 1,162 1,652 1,747 1,723 1,32 1,945 0,390 1,685 0,716 1,970 1,990 0,557 0,109 1,690 1,671 0,575 0,931 0,175 0,731 1,574 1,553 1,301 0,628 1,697 0,195 0,145 1,472 1,818 0,508 0,464 1,900 1,96 0,781 1,96 1,452 0,266 0,265 1,183 1,916 1,513 1,64 1,94 0,872 1,230 0,431 1,888 1,205 0,112 1,425 1,212 0,685 1,319 1,672 1,999 1,734 0,831 0,69 1,906 1,649 0,132 1,615 0,0 0,434 1,659 0,138 0,87 1,769 1,87 0,955 1,79 1,649 1,748 1,379 0,638 1,32 1,984 1,976 1,860 0,780 1,635 1,693 1,339 1,707 1,626 0,649 0,528 1,927 0,941 1,765 1,352 1,508 1,576 1,62 0,761 0,588 1,677 0,880 0,944 1,194 1,227 1,672 1,847 1,64 1,727 1,91 1,860 1,727 1,11 0,372 0,150 1,159 0,468 1,766 0,46 0,688 0,582 0,747 1,968 0,397 0,975 1,630 0,937 0,829 1,677 1,590 1,646 0,987 0,650 0,473 0,708 0,207 1,534 1,793 1,529 0,613 0,329 1,251 0,277 0,886 1,695 1,68 0,393 0,945 0,533 0,964 1,510 1,591 0,517 1,223 1,783 1,813 1,296 1,616 0,434 0,860 1,118 1,118 1,836 1,791 0,942 0,1 1,700 1,566 1,182 1,42 0,220 0,971 1,494 1,153 0,48 1,120 0,534 1,420 1,905 1,766 0,216 1,913 0,515 0,132 1,809 0,487 1,998 1,707 0,989 0,304 1,758 1,295 1,830 0,235 1,824 1,400 0,613 1,830 1,198 0,619 0,44 0,419 1,390 1,178 1,578 1,808 0,574 0,225 1,165 0,702 0,758 0,101 1,191 0,374 1,426 0,560 1,158 0,597 0,623 1,354 1,447 0,934 1,134 0,630 1,249 1,419 1,398 0,978 1,653 0,457 0,499 1,924 0,62 1,302 1,268 0,39 0,567 0,606 1,612 1,410 0,240 1,509 1,498 0,99 0,861 1,600 0,754 0,796 1,612 0,400 1,109 1,821 1,839 0,623 0,268 1,720 0,144 1,39 0,288 1,734 1,815 0,796 1,316 1,148 1,580 1,51 1,785 1,819 1,204 1,613 1,345 1,816 1,329 0,97 1,902 1,233 1,878 0,605 0,625 0,612 1,253 1,127 0,52 0,668 1,112 1,802 1,672 1,977 1,591 1,495 0,888 0,51 1,762 1,246 1,825 0,805 0,704 1,895 0,899 0,658 0,956 0,806 1,169 0,608 1,611 0,866 1,799 0,657 1,596 1,279 0,455 1,572 0,822 0,622 0,529 1,78 1,952 1,30 0,766 1,219 0,898 0,42 1,405 1,616 1,98 0,717 1,754 0,515 0,848 1,585 1,689 1,401 0,873 0,964 1,841 0,946 1,825 0,928 1,300 0,278 1,234 1,475 0,290 0,520 1,174 0,39 0,77 0,21 0,144 0,981 1,633 0,93 0,43 0,708 0,444 1,479 0,428 0,153 0,418 1,707 1,209 1,128 0,693 1,410 0,265 1,96 0,597 1,878 1,799 1,289 1,62 0,997 0,27 1,286 0,114 1,163 1,528 0,529 0,827 1,792 0,593 1,310 0,192 0,190 0,971 0,447 1,13 0,372 1,834 0,658 1,869 1,637 1,393 1,676 0,590 1,436 1,428 1,25 1,55 0,419 1,884 0,306 1,402 1,212 0,286 1,38 1,790 1,683 1,205 1,898 1,626 0,850 1,787 0,565 1,625 0,102 1,584 1,38 1,757 0,781 1,610 1,155 0,99 0,511 0,328 1,860 0,343 0,761 0,666 0,794 0,516 0,469 1,39 1,297 0,668 0,180 0,216 0,522 0,707 1,631 1,766 1,834 1,780 1,453 1,715 0,565 0,88 0,499 1,982 0,513 0,988 1,401 0,268 0,92 1,717 1,250 0,555 1,692 1,531 0,591 0,888 0,995 1,218 1,830 0,343 1,367 0,631 1,163 1,118 1,660 1,890 1,982 0,121 0,87 1,4 0,202 1,742 0,869 1,691 0,163 0,895 0,390 0,627 1,317 0,393 0,680 1,108 0,507 0,91 1,126 1,986 1,843 1,828 1,54 0,918 0,267 1,752 1,844 0,758 0,34 1,161 1,277 0,640 0,58 0,95 0,985 1,778 0,180 0,620 1,429 0,554 0,720 0,532 1,744 0,205 0,964 1,610 0,158 0,847 0,126 1,893 0,316 1,480 1,59 0,883 0,952 1,345 1,667 0,310 0,953 0,458 0,161 0,350 0,275 1,326 0,2 0,738 1,738 1,195 1,372 1,345 1,547 1,675 0,684 1,949 0,693 0,460 0,772 0,952 1,934 1,696 0,863 0,669 1,531 1,883 1,659 0,88 0,73 0,876 0,551 0,991 1,316 1,622 1,801 1,401 1,454 1,757 0,599 0,397 1,648 0,449 0,138 1,318 0,289 1,98 0,882 0,47 0,481 0,605 1,621 1,937 0,626 1,187 0,70 1,241 1,865 1,830 1,132 1,95 0,96 1,702 0,693 0,514 1,892 1,458 0,208 0,376 1,578 1,904 0,967 0,429 1,423 0,258 1,19 0,240 0,692 0,505 1,332 0,370 1,189 1,325 1,199 1,430 1,898 1,671 0,607 1,325 0,665 0,943 1,999 1,523 0,646 1,706 1,160 1,173 1,412 1,81 0,490 0,94 1,565 1,106 1,288 1,921 1,334 0,406 1,544 1,536 1,422 0,317 1,654 1,826 0,454 1,321 1,220 0,287 1,92 1,239 0,888 0,463 0,469 0,315 0,247 1,943 1,142 1,386 0,598 0,291 1,759 0,605 0,834 0,692 1,267 0,669 0,801 0,512 1,935 1,208 0,730 0,165 0,954 0,378 1,741 0,512 1,194 0,630 1,492 0,188 0,291 0,736 0,50 0,823 0,7 0,895 0,278 0,657 1,330 0,458 0,554 1,302 1,599 0,742 0,741 0,857 0,901 1,751 1,871 0,153 0,288 0,365 1,457 1,862 0,965 1,640 1,741 0,45 0,609 0,808 0,689 1,822 0,91 0,111 1,271 0,666 0,162 1,55 1,974 0,358 0,138 0,895 1,28 0,82 1,738 1,129 1,498 1,97 1,270 0,477 0,959 0,533 1,571 1,571 1,608 1,746 1,319 1,827 1,211 1,181 1,485 0,505 0,949 0,255 1,143 0,911 1,995 1,351 1,735 0,653 1,352 1,647 0,281 1,20 1,884 1,579 0,238 1,99 1,670 1,186 0,669 1,509 0,63 1,879 0,311 1,101 1,219 1,9 1,753 1,128 0,123 0,787 0,463 0,719 1,207 0,838 0,143 1,230 1,67 1,751 0,525 0,614 0,182 0,506 1,971 0,482 0,480 1,841 0,572 0,369 0,816 1,871 0,792 0,910 0,843 0,405 1,13 1,968 0,782 1,574 1,653 1,437 0,590 1,673 1,744 1,445 0,596 0,578 1,93 1,89 0,479 1,372 1,58 1,393 0,121 0,587 1,197 1,706 1,871 0,796 0,119 0,153 0,587 1,39 0,704 0,230 0,588 0,755 1,893 0,912 1,428 1,614 1,244 0,146 1,104 0,460 1,742 0,178 0,853 1,820 0,655 0,841 1,11 0,24 1,631 1,196 1,497 0,22 0,712 0,633 1,57 1,506 1,46 0,538 0,872 1,500 0,173 1,379 1,355 1,639 0,131 1,258 1,288 0,681 1,379 1,720 0,575 0,742 0,876 0,17 0,500 0,691 1,634 1,464 1,775 1,803 0,162 1,284 1,824 1,418 1,983 1,628 0,802 0,315 0,293 0,336 0,167 1,939 1,892 0,391 0,628 1,402 1,756 0,15 0,563 1,690 0,733 0,762 0,150 0,823 1,151 1,525 1,27 0,514 1,262 0,512 0,823 1,575 0,907 0,959 0,394 1,290 1,731 0,165 1,245 0,115 1,801 1,824 1,323 1,687 1,835 0,400 1,92 1,653 1,478 0,506 1,254 0,418 1,423 1,930 0,355 1,823 0,652 0,52 1,743 0,265 1,261 0,322 1,404 1,595 0,243 0,557 1,853 0,369 1,265 0,886 1,672 0,213 1,487 0,734 1,929 1,560 1,689 0,271 1,56 1,210 0,742 0,791 1,673 1,184 1,607 1,470 1,459 1,106 1,84 0,392 1,994 1,14 1,532 1,299 1,0 0,325 0,458 1,319 1,9 1,606 0,362 0,849 1,149 1,211 1,287 0,350 1,933 1,19 1,736 0,309 1,712 1,52 0,584 0,226 0,546 0,543 1,419 1,74 1,946 1,519 0,510 1,582 0,987 1,37 0,466 1,364 0,677 1,619 0,108 0,957 0,306 1,492 0,63 1,606 1,616 0,17 0,585 1,301 1,773 0,976 1,977 1,375 0,235 0,340 1,292 0,852 0,35 1,495 0,230 0,462 0,97 1,109 1,124 0,384 0,327 0,720 1,182 1,939 1,577 0,657 0,359 0,341 0,888 0,120 1,34 0,31 1,455 0,632 1,6 0,160 1,974 1,563 0,283 1,138 0,366 1,347 0,615 0,334 0,81 0,314 1,96 1,870 0,735 0,996 0,865 0,213 0,989 0,308 0,233 0,808 0,138 1,587 1,465 0,767 0,838 0,670 1,626 1,48 1,61 0,504 1,678 1,776 0,4 0,33 1,19 0,710 0,871 1,572 1,342 1,257 1,279 0,271 1,815 1,543 0,730 0,893 1,418 1,771 1,394 1,508 1,136 0,689 0,520 0,353 1,965 0,651 0,466 0,572 1,770 1,202 1,205 0,974 1,442 0,897 1,801 1,167 0,426 0,174 0,772 0,724 0,896 1,128 0,153 1,7 0,510 1,609 1,969 0,801 1,78 0,507 0,409 0,346 1,713 0,920 0,86 0,333 0,670 1,379 0,977 1,307 0,829 1,254 1,830 0,188 0,481 0,473 1,634 0,501 0,652 1,622 0,390 0,308 0,921 1,504 0,8 1,721 0,628 0,594 0,473 0,532 1,803 1,528 0,942 1,817 1,913 1,899 1,821 0,677 1,467 0,379 0,805 1,832 0,812 0,592 0,24 1,886 0,293 1,447 0,200 1,327 1,263 1,222 1,388 0,539 0,348 1,112 1,881 1,547 0,379 1,142 0,203 0,782 1,247 1,81 1,170 1,948 0,790 0,650 0,114 1,469 0,312 0,514 1,294 1,778 0,164 0,487 0,628 1,710 1,256 1,767 0,220 1,44 1,677 1,837 0,351 1,106 1,781 0,405 0,563 0,223 0,161 0,763 0,745 0,69 0,942 0,267 0,882 1,193 1,230 0,317 0,909 0,109 0,299 1,833 1,651 1,415 1,829 1,588 0,36 0,738 0,565 1,864 0,755 1,411 0,779 1,399 1,752 0,34 0,275 1,245 1,400 1,941 0,463 1,547 0,986 0,53 0,355 0,999 0,586 0,553 0,357 0,390 0,868 1,831 1,958 1,843 1,698 0,119 0,42 0,183 1,246 0,922 1,614 1,874 0,483 1,187 1,899 0,264 1,86 1,238 1,369 0,547 0,182 1,605 0,886 1,840 0,845 1,212 0,966 0,822 0,836 1,460 1,841 1,646 1,17 1,608 0,682 0,352 1,607 0,70 0,5 1,895 0,179 0,327 0,467 1,735 0,169 0,943 0,583 1,199 1,595 1,702 0,128 1,203 1,558 1,940 1,734 1,409 1,980 0,375 0,117 0,616 0,687 1,254 1,345 1,164 1,465 0,863 0,694 1,966 0,378 0,451 0,698 0,260 1,561 0,630 1,342 1,746 0,789 1,345 1,605 1,422 0,347 1,708 0,461 0,830 0,921 1,891 1,727 0,918 1,580 1,698 1,528 0,251 0,524 1,433 0,111 0,697 1,905 1,541 0,533 1,46 0,254 1,289 1,18 0,204 0,660 0,276 0,654 1,102 0,170 0,605 0,216 1,765 1,983 0,831 0,464 1,290 0,850 1,751 0,927 0,891 0,503 0,859 1,237 1,115 1,795 1,684 0,633 1,267 1,974 1,77 1,665 0,547 0,494 0,301 1,572 1,238 1,268 0,963 1,7 0,181 0,430 0,924 0,824 1,850 0,300 1,663 1,821 1,590 0,999 0,11 0,161 0,369 1,929 0,928 1,255 0,67 0,774 1,293 1,668 1,534 1,998 0,927 0,981 1,97 0,663 0,947 1,252 1,471 1,520 1,697 1,558 1,549 1,661 1,437 0,591 0,353 0,605 0,105 0,963 0,59 0,672 1,828 0,616 1,998 1,111 1,709 1,985 0,414 0,232 1,194 0,648 0,192 1,832 0,758 1,906 0,449 0,385 0,250 1,488 0,244 1,471 1,283 1,609 0,176 1,708 0,687 1,140 0,422 0,835 1,817 0,328 0,73 1,555 0,115 1,568 0,970 0,736 0,819 0,799 1,793 1,940 1,448 0,930 0,516 1,904 1,482 1,108 1,983 0,477 1,729 0,770 0,944 0,515 1,438 0,988 0,483 1,721 0,755 1,778 0,951 0,190 0,362 1,558 0,995 1,854 1,2 0,348 1,300 1,393 0,715 0,548 1,832 1,760 0,710 0,881 1,53 0,97 1,372 0,55 1,574 1,248 1,550 0,496 0,833 1,109 1,637 0,265 0,896 0,598 0,669 0,937 0,241 0,857 1,486 0,527 0,459 1,403 1,489 0,675 1,128 1,752 0,309 1,257 1,12 0,424 1,594 1,51 0,413 0,270 1,982 0,32 0,370 1,872 1,486 0,697 0,611 1,304 1,134 1,90 0,154 1,746 1,893 1,457 1,821 0,795 1,66 1,261 0,426 1,708 1,260 0,320 0,9 0,878 0,281 1,62 1,925 1,407 0,20 0,604 1,445 0,537 0,134 0,335 1,789 0,432 1,276 0,798 1,832 1,104 0,237 1,569 1,765 0,356 0,866 0,525 0,247 0,163 1,466 0,402 1,650 1,747 1,137 1,56 0,149 0,774 1,779 1,499 1,89 1,98 0,625 0,38 1,171 1,442 0,285 0,605 1,14 1,76 0,344 0,728 1,141 1,65 1,244 0,49 0,879 1,694 0,948 1,624 0,335 0,149 1,466 0,239 1,435 1,235 1,85 0,36 0,415 0,895 1,428 1,990 1,714 0,764 1,614 0,874 1,120 0,31 1,765 1,38 0,592 1,852 1,325 0,137 0,411 0,930 0,427 0,639 1,376 0,862 1,230 0,850 0,230 0,810 1,80 1,2 0,632 0,979 1,978 0,984 0,294 0,758 1,55 1,480 0,14 0,145 0,198 1,96 0,196 0,215 1,243 0,266 1,740 0,771 0,154 0,972 0,373 1,82 1,381 0,608 0,806 0,605 0,379 1,634 1,340 1,784 0,151 1,247 0,611 0,893 0,938 1,262 0,468 0,454 0,601 1,89 0,992 1,158 1,37 0,564 1,183 1,255 0,119 1,337 1,882 0,304 1,818 0,183 0,703 0,849 1,383 1,697 1,234 1,298 1,221 1,845 0,807 1,608 0,856 0,173 0,409 1,482 0,521 1,247 0,101 0,785 0,471 0,211 1,75 0,88 1,267 0,971 0,722 1,819 0,162 0,836 1,480 0,424 1,327 1,240 1,501 1,701 0,326 1,254 1,194 1,762 1,825 0,891 0,842 0,138 0,928 0,574 1,701 0,80 1,320 1,15 0,572 0,759 0,751 1,59 1,865 0,410 0,890 1,756 0,472 1,456 0,623 0,780 1,561 0,111 0,60 1,159 0,217 0,586 1,456 0,438 1,113 0,706 0,509 0,741 1,460 1,754 0,547 1,894 0,143 0,522 0,550 0,620 1,798 0,778 0,791 0,138 1,881 1,57 1,671 0,709 1,813 0,736 1,426 0,13 1,731 1,931 0,131 1,761 0,186 0,807 0,375 1,709 1,411 0,757 1,956 1,992 1,537 1,879 1,826 0,215 0,531 1,889 1,106 0,868 0,330 1,981 0,107 1,83 0,772 0,689 1,932 1,707 1,196 1,171 1,725 1,618 1,980 1,54 1,92 0,202 1,114 1,700 1,475 1,141 1,351 1,687 1,562 0,94 0,152 1,42 0,374 1,946 0,575 0,983 1,186 1,866 1,557 0,587 0,667 1,551 1,54 0,646 1,313 1,466 1,688 1,471 0,219 0,779 0,903 0,797 1,666 0,207 0,714 0,987 0,951 0,284 1,455 1,974 1,8 0,826 1,474 0,986 1,935 0,508 1,968 0,437 1,703 1,834 0,958 0,722 1,254 0,935 0,489 0,463 0,741 1,923 1,483 1,590 1,455 1,389 0,198 1,275 0,967 0,318 1,116 1,767 1,701 0,172 1,659 1,824 0,283 1,654 0,113 0,351 1,503 1,272 1,886 0,764 0,834 1,299 1,855 0,298 0,364 1,612 1,645 0,939 1,667 1,287 1,909 1,477 0,733 1,728 1,51 0,878 0,893 1,498 1,800 0,9 0,531 1,271 0,964 0,772 0,757 1,670 1,397 0,43 0,459 0,873 0,556 1,503 1,272 1,540 1,11 1,971 0,708 1,448 0,633 0,793 1,521 0,774 0,207 0,453 1,945 1,82 0,647 0,801 1,955 1,369 0,109 0,811 0,787 0,111 1,531 1,108 0,353 0,505 1,158 1,222 1,43 0,925 0,564 0,483 1,125 0,517 0,865 1,806 1,404 0,268 1,586 0,14 1,257 1,236 0,395 0,198 0,894 1,289 1,32 0,205 0,193 0,27 0,863 1,359 1,629 1,879 1,847 1,122 1,138 0,463 0,994 0,846 1,908 1,581 0,38 1,307 1,627 1,610 1,412 1,207 1,929 1,121 1,959 0,408 1,336 1,307 1,907 0,683 1,298 0,869 1,121 1,66 0,769 1,193 0,735 0,299 1,954 1,809 1,726 1,150 1,981 1,61 0,13 1,479 1,681 1,682 0,335 0,291 0,681 0,137 1,966 0,868 0,847 0,432 0,52 1,461 1,395 0,158 1,561 1,54 1,204 1,448 0,390 1,135 1,612 0,44 1,466 0,84 1,24 0,798 1,70 1,391 0,72 1,870 0,728 0,477 1,273 1,833 0,702 1,81 1,433 0,553 0,793 1,421 0,193 1,950 1,186 1,267 1,691 1,451 1,422 1,231 0,545 0,439 1,798 1,338 0,120 1,455 1,196 1,741 0,834 0,298 0,422 0,191 1,127 0,205 0,838 0,780 0,193 0,738 1,854 1,626 1,151 0,584 1,341 1,260 1,438 1,545 0,382 1,308 1,933 0,912 0,291 0,42 0,564 0,516 1,161 1,40 1,944 1,641 1,8 0,468 0,508 0,764 0,77 1,215 0,164 1,432 0,259 0,789 0,906 1,858 1,222 0,709 0,252 0,275 1,29 1,888 0,484 0,342 1,473 1,362 1,367 0,492 1,854 0,507 1,841 0,405 1,201 1,404 1,282 0,358 1,809 0,13 1,230 1,811 1,502 1,699 1,316 1,762 1,274 1,824 1,145 1,657 0,601 0,340 0,483 0,624 1,345 0,740 1,858 1,3 1,892 0,881 1,437 0,562 1,545 0,354 1,44 1,802 1,797 1,392 1,565 1,154 0,440 1,424 1,429 0,631 1,76 1,376 1,456 1,2 1,298 0,598 0,86 1,854 1,911 0,714 1,419 0,907 0,570 0,196 1,755 1,533 0,196 1,164 0,932 0,599 1,252 1,559 1,265 0,54 0,178 1,17 0,603 1,248 0,231 1,819 0,468 0,728 1,134 1,331 1,871 1,801 1,577 0,933 0,979 0,10 0,785 1,199 0,532 0,141 0,946 0,714 1,7 0,233 1,692 0,714 1,596 0,700 0,613 1,594 0,364 0,760 0,303 0,757 1,885 0,225 1,225 0,755 0,450 0,564 1,250 0,595 1,430 1,576 0,411 1,789 0,119 0,46 1,518 1,740 0,760 1,607 0,883 0,613 0,995 0,881 0,336 1,859 1,402 1,443 1,907 0,342 1,768 1,932 0,760 1,623 0,756 0,895 0,350 1,582 0,268 1,629 1,581 0,658 1,768 0,760 0,911 0,585 1,971 0,68 0,223 1,56 0,660 0,522 1,729 1,985 0,828 0,208 1,126 0,712 0,399 0,283 0,98 0,460 1,424 0,132 1,600 1,456 1,194 1,389 0,286 1,26 1,788 0,450 0,372 1,853 1,67 0,927 1,386 1,567 0,441 0,837 1,261 0,542 0,570 1,264 1,500 0,470 1,559 0,583 1,989 1,241 1,2 0,783 0,274 1,129 1,421 0,802 0,280 1,32 1,241 1,243 0,186 0,175 0,35 0,284 1,190 0,893 1,532 1,132 1,355 1,653 0,240 0,824 1,163 0,348 0,744 0,970 1,737 0,607 1,619 0,508 0,515 0,774 0,437 0,597 0,967 1,185 1,326 0,0 0,131 1,812 0,4 0,414 0,669 1,589 0,322 0,299 0,717 1,251 1,946 0,179 0,393 1,485 1,57 0,660 1,657 0,650 1,759 0,997 1,537 1,761 1,850 0,258 1,468 0,704 0,186 1,772 0,92 1,12 0,81 1,565 1,558 1,274 1,881 1,153 0,103 1,202 1,389 0,450 1,228 0,101 1,310 1,995 0,454 1,646 1,237 0,613 0,819 1,42 1,491 0,789 1,482 0,666 1,238 1,638 1,566 0,549 0,849 0,272 1,12 0,422 1,702 1,588 1,463 1,290 0,171 1,524 1,666 0,866 0,51 0,77 0,471 1,773 0,936 0,515 0,133 0,39 0,512 0,567 0,57 0,800 1,284 0,442 1,260 1,827 1,628 1,574 1,225 1,226 1,85 0,883 0,649 1,117 0,638 0,961 0,457 0,230 1,236 1,608 1,273 1,184 0,195 0,631 1,531 0,138 1,58 1,385 1,870 0,628 0,114 0,803 1,949 1,509 0,347 1,581 0,445 0,651 1,516 0,206 1,29 0,499 1,183 0,147 1,676 0,554 0,427 0,479 1,778 0,240 1,14 1,389 1,70 1,306 1,488 1,37 0,815 1,490 1,236 1,521 0,639 1,312 0,254 1,207 0,250 1,378 1,792 1,699 0,74 1,757 1,459 1,975 0,453 0,298 0,826 1,955 1,800 1,980 1,538 0,472 1,122 1,850 1,846 0,324 1,999 0,30 0,623 0,140 0,66 1,51 1,185 0,25 1,521 1,943 0,93 1,768 1,905 1,2 0,31 0,296 0,801 0,847 0,215 0,638 1,449 0,239 0,674 1,962 1,286 1,795 0,35 1,850 1,377 1,313 1,458 1,338 0,556 0,150 1,261 1,782 1,466 1,602 1,425 0,812 1,917 0,270 1,706 1,24 1,267 0,803 1,413 1,396 0,257 1,59 1,329 1,537 0,573 0,142 1,6 1,884 0,97 1,659 0,21 0,449 0,751 1,909 0,259 1,662 0,876 0,131 0,993 1,778 1,613 0,809 1,24 1,597 0,673 1,368 1,132 1,169 0,246 1,375 0,573 1,129 0,598 1,824 1,90 1,304 1,287 0,665 0,35 0,452 0,696 0,206 0,734 0,329 0,395 0,742 0,806 0,836 1,867 1,241 0,295 0,759 1,833 0,925 0,729 0,617 0,921 0,393 1,436 1,432 0,684 0,845 1,871 1,873 1,491 1,340 0,256 0,836 1,822 1,500 1,968 0,4 0,400 1,589 0,221 0,609 1,417 1,636 0,790 1,181 1,941 0,485 1,871 1,293 0,572 0,995 0,992 1,903 0,495 0,497 1,212 1,603 1,384 0,421 1,725 1,428 1,154 0,838 1,584 1,448 1,29 1,138 0,379 1,888 0,381 0,511 1,338 0,869 1,804 0,185 1,62 1,193 0,355 0,599 0,908 0,106 0,924 1,879 0,627 1,607 0,876 1,784 1,855 1,23 1,592 1,686 0,736 0,750 0,655 1,142 1,302 0,354 1,258 0,4 0,551 0,369 1,963 1,566 1,796 0,636 0,474 1,671 1,683 0,870 0,735 1,290 1,885 0,194 1,940 0,854 1,330 0,741 0,187 0,878 0,337 1,983 1,627 1,838 0,8 0,537 0,44 0,30 0,79 1,637 1,547 0,515 1,371 0,692 0,819 1,117 0,315 1,921 0,303 1,937 1,355 1,363 0,719 1,162 0,49 0,45 1,133 0,135 0,107 0,691 1,677 1,115 0,386 0,899 1,151 0,97 1,221 0,631 1,32 1,540 0,504 1,338 0,184 0,373 1,887 0,325 0,248 1,739 0,891 1,972 1,333 1,305 1,950 0,201 0,539 1,162 0,222 0,537 0,383 0,959 0,789 0,419 1,29 0,454 0,426 1,819 1,218 0,146 0,986 1,314 1,159 0,478 0,569 0,820 1,919 1,327 1,363 0,617 0,276 0,317 0,994 0,576 0,924 0,364 0,496 0,974 0,903 1,592 1,394 0,671 0,988 0,975 0,912 1,950 1,676 1,141 1,859 1,689 1,689 1,22 1,532 0,302 1,941 0,589 1,250 0,438 0,966 1,316 1,419 0,697 0,966 0,38 0,161 0,966 0,942 0,59 1,2 1,332 0,61 1,704 0,803 0,158 1,474 0,791 1,53 1,998 0,342 0,901 1,534 1,522 1,27 0,962 0,569 1,393 0,653 1,420 0,122 0,858 1,917 0,300 1,298 1,684 0,976 0,528 0,108 0,399 1,439 1,902 1,847 1,306 1,682 0,297 1,830 0,158 1,782 1,713 0,699 1,536 1,558 0,674 0,883 1,458 0,188 1,861 1,788 1,7 1,472 0,283 1,431 1,48 0,91 0,398 1,737 1,748 1,328 1,419 0,620 0,652 1,304 0,506 0,351 0,632 1,346 0,889 1,723 1,586 1,939 0,260 1,826 0,870 0,815 1,848 0,812 0,248 1,914 1,691 0,621 1,642 1,357 1,315 0,275 1,524 0,795 0,407 0,513 0,750 1,526 0,882 0,630 1,273 0,323 0,958 1,841 0,306 1,587 1,519 1,335 1,542 0,546 1,671 0,559 1,485 1,41 1,489 0,959 1,157 0,828 1,145 1,513 1,62 0,753 1,287 1,888 0,722 1,829 1,415 0,521 1,948 1,496 0,264 0,31 1,620 0,672 0,50 1,822 1,810 0,86 0,864 1,334 0,259 0,121 1,913 1,951 0,890 0,161 1,420 0,10 1,433 0,186 0,714 0,369 1,850 1,556 1,880 0,323 1,406 0,356 0,458 0,896 1,155 1,867 1,921 0,320 1,165 0,895 1,67 1,546 1,442 1,574 1,678 0,597 0,709 0,978 1,817 1,41 0,618 1,557 1,527 1,93 0,913 1,462 0,783 0,740 1,957 1,576 0,628 1,549 1,867 1,449 1,155 0,280 1,796 0,218 0,347 1,42 0,759 1,120 1,644 0,22 0,787 0,198 0,173 0,586 0,667 1,309 0,928 0,386 1,983 1,561 1,113 0,839 1,262 0,51 1,4 1,523 0,115 1,126 0,284 1,205 0,107 1,908 0,306 0,160 0,815 1,624 0,503 1,710 0,107 0,717 1,361 0,566 0,199 0,45 0,845 0,334 0,318 0,31 0,20 0,807 0,333 0,580 1,423 0,985 1,968 1,434 0,625 1,586 0,303 0,131 0,951 1,242 1,346 1,357 1,797 0,408 1,303 0,741 0,426 1,69 0,928 1,777 1,47 0,452 0,761 0,600 0,777 1,757 0,976 1,49 0,398 0,992 0,619 0,375 1,600 0,767 0,13 0,108 0,363 1,869 1,853 0,909 1,248 0,60 0,348 0,416 1,376 0,223 1,592 0,393 0,134 1,896 1,222 0,573 1,984 1,246 0,488 0,528 0,845 0,624 0,939 0,446 1,826 0,152 0,25 0,937 0,707 1,252 0,75 0,975 0,183 0,5 1,91 0,433 1,735 1,231 0,552 0,700 0,152 0,746 1,78 1,505 0,230 1,647 1,353 0,709 0,60 0,565 1,179 1,884 1,842 1,445 1,568 0,906 0,897 1,932 0,728 0,810 0,447 1,955 0,867 1,903 1,101 1,997 1,345 1,682 1,614 1,409 0,492 1,40 0,174 1,559 1,3 1,320 0,867 0,396 0,7 1,777 1,797 0,568 1,232 1,832 1,933 1,571 0,39 1,988 1,366 0,800 0,154 1,887 0,795 0,412 0,258 0,74 0,346 1,566 1,90 1,682 0,861 1,958 0,919 0,930 1,897 1,376 1,363 1,474 1,453 0,615 1,783 0,371 0,905 0,615 0,220 0,67 0,662 1,603 0,360 1,141 1,555 1,710 1,423 0,13 0,241 1,539 0,413 0,8 0,199 1,3 0,253 1,408 0,329 1,394 0,364 1,962 1,522 1,614 0,356 1,639 1,919 1,170 1,474 1,399 0,279 0,72 0,211 0,392 1,63 1,948 0,979 1,485 1,450 0,779 1,348 0,239 0,315 1,378 0,932 0,873 1,604 1,37 0,800 0,916 0,588 1,802 0,264 0,85 0,181 0,711 1,144 0,957 1,7 0,356 1,918 0,400 0,460 1,83 0,149 1,297 0,993 0,804 1,222 0,504 0,706 0,254 1,295 1,239 1,267 0,318 1,408 1,631 1,162 0,697 0,45 1,235 0,908 0,448 1,700 0,903 1,368 0,344 0,315 0,267 0,8 0,762 0,335 1,851 1,513 1,977 0,683 0,226 0,711 0,598 1,119 1,43 0,665 0,624 0,713 1,342 1,880 0,702 0,434 1,832 1,226 0,724 0,458 0,238 1,315 0,76 0,890 1,794 1,120 1,118 0,243 0,847 1,674 0,144 0,814 1,138 0,366 0,994 0,398 1,450 0,876 1,326 1,224 1,662 1,199 0,435 0,722 0,485 1,462 1,876 0,622 0,968 0,814 1,409 1,689 1,58 0,220 1,80 0,544 0,700 0,558 1,758 0,908 0,886 1,656 1,243 0,889 0,763 1,104 1,388 0,83 1,803 1,815 1,57 0,814 1,669 0,552 1,175 0,731 0,907 0,450 0,731 0,263 1,83 1,95 0,46 0,655 0,311 1,330 0,785 1,136 1,965 1,263 0,145 1,753 1,868 0,365 1,655 1,49 0,386 1,2 0,764 0,41 1,357 1,668 0,132 1,88 1,604 0,744 0,193 0,827 1,685 0,830 1,919 0,853 0,793 1,511 1,858 0,170 1,208 1,330 0,624 0,744 0,379 1,163 0,40 0,135 1,620 1,328 1,960 0,669 1,588 1,893 1,572 1,0 0,516 0,894 0,312 1,710 1,562 0,12 1,358 1,872 0,763 0,463 1,192 1,509 1,573 1,9 1,104 0,166 0,442 0,352 1,933 0,358 0,932 0,280 1,446 1,705 1,430 1,399 1,72 0,539 1,131 1,131 1,131 1,568 0,567 1,443 1,402 1,969 1,87 0,990 0,832 1,869 1,642 1,279 1,379 0,723 0,165 0,771 1,586 0,422 0,103 1,858 1,131 1,261 0,897 1,953 0,140 0,141 1,286 0,28 1,265 0,480 0,686 1,103 1,151 1,720 1,304 0,682 1,887 1,975 0,197 0,391 1,919 1,742 1,818 1,411 1,356 0,129 1,584 1,319 0,90 1,741 1,712 0,872 0,430 0,952 0,942 1,798 0,457 0,180 0,783 0,129 0,806 0,7 0,693 0,215 0,598 1,865 1,421 0,452 1,369 0,78 1,504 1,76 1,757 0,515 0,157 1,765 0,990 1,586 1,954 1,370 1,323 0,375 1,928 1,808 1,860 1,134 0,490 0,699 0,500 1,79 1,320 1,18 0,488 1,574 1,703 1,162 1,629 1,539 1,489 0,40 1,378 0,424 1,916 0,574 0,621 0,392 0,601 0,453 0,906 1,758 1,801 1,877 0,560 1,691 0,279 1,362 0,116 1,365 0,350 0,763 1,656 1,203 1,755 0,51 1,937 1,438 1,17 0,526 1,885 0,461 1,758 1,434 0,288 1,142 1,218 0,742 0,437 0,70 1,488 0,522 1,406 0,297 1,30 1,931 1,498 1,282 0,20 1,85 1,888 1,495 0,286 1,891 0,835 0,471 0,86 0,100 0,715 0,427 0,596 1,427 0,907 0,268 1,694 0,589 1,714 1,258 1,974 0,485 1,27 0,557 1,739 1,152 1,891 0,486 1,107 0,785 1,325 0,593 0,20 1,620 0,858 1,236 0,215 1,345 1,950 0,592 0,722 0,943 0,469 1,875 1,259 0,465 0,419 1,291 1,231 0,831 1,728 0,642 0,524 0,228 1,307 1,233 0,345 0,217 1,473 1,171 0,951 1,371 1,887 1,608 0,976 0,790 1,707 1,791 1,388 0,922 0,897 0,61 0,390 1,586 1,7 1,8 0,224 0,499 1,183 0,508 0,130 0,299 0,415 1,890 1,240 1,284 0,858 0,720 0,568 1,268 0,650 0,949 1,908 0,820 0,872 1,122 0,867 0,944 0,660 0,919 0,943 0,60 0,477 1,754 0,954 0,998 0,998 1,563 1,208 1,937 0,710 0,926 1,546 0,307 0,633 1,548 0,151 1,409 1,756 1,180 1,940 1,557 0,724 1,800 1,882 0,226 0,967 0,195 0,894 0,169 1,225 1,125 1,529 1,212 0,23 1,760 1,256 0,416 0,532 0,681 0,221 1,414 1,904 0,250 0,652 0,661 1,887 1,258 1,529 0,986 0,484 1,220 1,417 0,577 1,401 0,301 0,875 0,627 0,389 1,673 1,733 0,414 1,817 1,909 0,130 1,194 0,439 1,389 1,861 1,672 1,776 1,287 0,604 1,902 1,775 0,719 0,998 0,207 1,368 1,664 0,960 0,75 1,476 1,428 0,446 0,989 1,900 0,673 1,107 1,377 1,527 1,450 0,204 1,749 0,562 1,784 1,207 0,90 0,568 1,454 1,280 1,755 1,368 1,342 1,535 1,26 1,293 1,205 0,204 0,137 1,336 0,880 0,857 0,744 0,568 0,177 1,646 0,872 0,2 1,121 0,408 1,937 1,707 0,895 0,662 1,614 0,69 1,143 1,425 1,887 0,459 0,696 1,731 1,812 0,545 1,538 1,597 0,68 1,179 0,607 1,830 0,593 0,885 1,660 0,755 1,856 0,182 1,940 1,639 1,498 0,586 1,650 1,153 0,734 1,464 0,424 0,642 1,274 0,323 1,417 0,501 0,69 1,735 0,452 1,462 1,689 1,771 1,284 1,235 1,843 0,502 0,893 0,154 1,456 1,693 0,469 1,53 1,911 1,98 0,111 1,329 0,461 1,727 0,415 0,454 0,558 0,71 1,610 0,742 1,942 0,787 0,49 0,242 1,286 1,63 1,134 1,53 0,740 1,997 0,35 0,527 1,65 1,837 1,643 0,293 0,233 1,851 0,51 0,585 0,87 0,935 0,840 0,384 0,196 1,345 0,529 0,952 0,243 0,556 1,941 1,532 1,717 0,957 1,188 1,487 0,201 0,773 0,299 0,869 0,207 1,511 1,914 1,638 0,784 1,312 0,173 1,826 0,478 1,433 0,981 0,511 0,167 1,178 0,217 1,301 1,363 0,422 1,585 1,762 1,935 0,456 0,956 0,999 0,821 1,160 1,835 1,188 0,781 1,742 1,532 0,573 1,421 0,602 0,923 1,415 1,802 1,476 1,59 0,604 1,512 0,443 0,855 0,732 0,860 0,790 1,486 1,766 1,654 0,38 1,704 0,640 0,357 1,947 0,883 1,249 1,369 0,125 0,98 1,923 0,975 1,125 1,408 1,877 0,30 1,68 1,664 1,339 0,147 0,862 1,814 0,123 1,285 0,774 0,49 0,177 1,830 0,512 1,795 0,854 1,605 1,206 0,410 1,596 0,312 1,250 1,576 0,795 0,733 1,984 1,848 0,809 0,328 1,245 1,378 1,121 0,598 0,528 1,980 0,984 0,636 0,208 0,398 0,600 0,147 1,643 0,326 0,687 1,56 0,417 0,119 1,598 0,576 1,663 0,45 1,870 0,843 0,93 0,513 1,949 0,132 1,349 0,508 1,841 1,967 1,299 0,148 0,218 0,49 1,662 1,253 1,810 1,561 1,704 0,562 1,76 0,615 0,502 0,327 0,989 0,963 1,962 0,101 1,479 0,251 1,899 1,359 0,897 0,164 0,54 0,286 1,212 1,279 0,393 0,688 0,704 1,281 0,77 1,142 0,21 1,632 0,513 1,679 0,977 0,367 0,977 1,832 0,268 1,336 0,889 0,314 0,141 1,792 1,757 0,550 1,411 1,373 0,357 0,416 0,690 1,237 0,957 0,367 0,611 0,787 1,265 0,548 0,611 0,563 1,403 0,37 0,683 0,397 0,41 0,765 1,346 1,25 1,21 1,328 0,70 1,901 0,6 0,79 0,663 1,957 0,682 0,489 1,657 0,46 0,949 0,916 1,894 1,383 1,525 1,292 1,314 1,68 1,941 1,663 1,289 1,781 0,114 0,272 0,576 0,784 1,654 1,879 1,585 0,959 1,315 0,625 1,885 0,940 0,519 1,8 1,741 1,19 1,318 0,111 1,728 0,650 0,652 1,753 1,324 1,216 0,506 1,990 0,870 1,947 0,368 1,267 0,962 1,424 0,805 0,896 0,550 1,349 1,761 0,585 0,819 1,163 0,24 0,459 1,562 0,680 0,368 0,483 1,736 0,532 1,403 1,312 0,345 1,810 1,843 1,64 1,810 1,878 0,297 1,816 0,334 0,411 1,879 0,295 0,82 0,505 0,189 1,408 0,314 0,136 0,880 1,51 0,991 1,524 1,509 0,361 1,438 1,397 0,586 1,537 0,239 0,675 1,745 0,88 1,797 0,317 1,737 0,278 0,475 0,625 1,590 1,758 1,591 0,325 0,836 1,48 1,623 1,440 0,744 0,276 0,894 0,214 0,907 1,410 1,237 0,252 1,490 1,885 0,713 1,75 1,722 0,595 0,495 0,810 0,976 0,262 0,302 1,284 1,44 1,11 0,240 0,355 1,378 1,934 1,771 1,808 0,519 0,943 0,520 0,563 1,954 1,468 0,395 0,400 1,89 1,104 1,124 1,844 1,888 0,613 0,507 1,560 1,148 1,878 1,144 0,490 1,330 1,719 0,932 0,673 0,196 0,622 0,508 0,348 0,580 0,685 0,620 0,640 1,961 0,859 1,912 0,852 0,519 1,215 0,131 0,862 0,879 0,162 0,441 0,107 0,968 1,567 1,831 0,858 1,487 1,35 1,883 1,419 1,993 1,462 0,895 1,882 0,480 0,348 1,315 0,54 1,587 1,571 1,191 0,486 1,439 1,806 0,298 0,847 0,102 1,209 1,808 1,830 1,339 0,576 0,649 1,680 0,638 0,741 1,68 0,492 1,581 0,687 0,931 1,27 0,966 0,80 1,489 0,562 0,201 1,821 1,633 1,474 1,292 1,365 0,690 1,205 0,619 1,201 0,137 0,853 1,159 1,318 1,875 1,497 1,527 0,277 1,694 0,152 1,18 0,742 0,881 1,59 0,1 1,101 1,500 0,662 1,609 0,114 0,730 1,283 1,95 1,171 1,872 0,788 1,666 0,642 1,930 0,180 1,890 1,763 0,352 0,542 0,716 1,80 0,805 1,401 0,980 1,898 1,111 0,612 1,521 0,406 0,666 0,664 1,378 0,559 1,391 1,277 0,853 0,880 1,997 0,21 1,508 1,612 1,16 1,947 0,106 1,498 0,346 1,368 1,924 1,627 0,927 1,846 0,145 1,30 0,522 1,196 1,442 1,702 1,177 1,868 1,200 1,631 0,644 0,672 0,373 0,648 0,21 1,120 1,216 1,950 1,338 1,955 0,968 1,315 0,291 0,644 0,783 1,776 1,329 0,435 0,505 1,534 1,873 1,448 0,106 1,662 0,203 1,410 1,84 1,435 0,283 1,128 0,664 1,395 0,174 1,533 0,560 1,23 0,4 1,220 0,859 0,897 1,120 1,10 0,954 0,55 0,996 1,701 1,735 1,253 1,795 0,381 1,476 0,687 1,796 0,426 1,922 1,627 0,334 0,730 0,62 0,272 0,27 0,943 1,545 0,437 0,653 1,298 1,879 1,844 1,634 1,240 1,95 1,53 1,897 0,798 1,884 0,940 1,415 1,356 0,700 0,659 1,997 1,287 0,995 1,919 1,104 0,356 1,768 0,214 0,63 1,837 1,330 1,663 1,782 1,490 0,747 0,658 0,589 0,303 0,209 1,215 1,490 0,878 1,399 0,694 0,407 1,96 1,869 0,653 1,966 0,411 1,312 0,666 0,154 1,165 0,478 1,220 1,705 1,290 0,770 1,618 0,634 0,688 1,313 1,122 1,105 1,251 0,123 1,514 0,864 1,861 0,97 1,230 0,860 1,173 0,740 1,222 1,203 1,11 0,153 1,549 0,804 0,952 1,571 0,468 1,793 0,229 0,265 1,170 0,885 0,927 1,810 1,473 0,968 0,196 1,290 0,541 0,329 1,570 0,857 1,286 1,666 1,323 1,546 0,477 1,341 0,948 0,359 0,641 0,261 0,262 0,329 1,823 0,813 1,451 0,220 1,930 0,928 1,103 1,568 1,913 0,6 1,856 1,158 1,358 1,508 0,378 1,983 1,504 1,634 0,611 0,30 0,870 0,555 0,305 0,569 0,789 0,230 1,664 1,920 0,447 0,915 0,714 1,654 0,693 0,637 1,854 1,748 0,488 0,559 1,855 1,32 1,296 1,404 0,201 1,534 0,606 1,745 0,243 1,61 0,333 1,212 0,279 0,545 0,271 0,557 1,419 1,686 1,141 0,381 0,564 1,175 0,416 0,867 0,59 1,138 0,678 1,661 1,400 0,740 0,286 0,473 0,842 0,870 0,960 1,19 1,326 1,752 1,997 0,643 0,14 1,849 1,29 1,322 0,670 0,437 1,956 0,143 0,804 1,124 1,792 0,604 1,99 1,505 0,778 0,651 0,849 1,3 1,370 0,684 0,614 1,848 1,454 1,913 1,62 1,456 0,897 1,812 1,819 1,639 0,479 1,65 1,852 1,722 0,777 0,981 1,998 0,987 1,646 1,246 0,385 1,611 1,767 1,212 0,956 1,802 0,486 1,6 1,136 1,953 0,698 0,995 1,217 1,163 1,762 0,430 1,896 1,658 1,880 1,39 0,685 0,134 0,922 1,696 1,843 0,865 0,314 1,781 1,501 0,18 1,249 1,337 1,81 0,703 1,618 0,42 1,223 1,65 1,146 1,364 0,664 0,420 0,55 1,790 1,581 0,491 1,186 1,706 1,387 1,596 1,346 1,271 1,505 1,766 0,480 1,243 0,700 1,898 1,430 0,319 0,198 1,101 1,776 1,320 0,195 0,525 0,78 1,423 1,807 0,957 0,197 1,405 0,669 1,846 0,895 1,168 0,140 1,330 1,71 0,173 1,815 1,358 1,198 1,813 0,689 1,675 1,903 0,946 1,110 0,502 0,768 1,883 0,505 0,47 0,332 1,228 1,660 0,598 1,412 1,993 1,731 1,62 1,205 0,148 0,436 0,452 1,372 1,803 0,484 1,146 1,580 1,154 1,857 1,927 0,869 1,599 0,830 0,845 1,799 1,122 0,142 1,234 1,213 1,937 0,201 0,896 0,102 0,890 1,263 1,545 0,690 0,69 1,461 0,460 0,462 1,802 1,102 0,408 1,489 0,104 1,105 0,146 1,394 0,492 1,931 0,502 1,175 0,748 1,736 0,522 0,370 0,683 1,399 0,817 0,102 0,416 0,647 1,851 0,331 1,520 1,340 1,623 1,696 0,691 0,979 0,848 0,348 1,394 1,631 0,365 1,912 0,656 0,463 1,861 1,681 0,973 1,42 0,334 1,704 1,265 0,712 1,560 1,194 1,362 0,213 0,703 0,846 0,160 1,750 1,140 1,727 0,292 1,419 0,151 1,377 1,412 0,505 1,486 1,289 0,80 1,340 1,249 0,455 1,980 1,5 0,400 1,343 0,777 0,163 1,406 1,233 0,397 1,74 0,568 1,915 1,258 0,155 1,747 1,181 0,332 1,781 0,683 0,619 1,277 0,3 0,2 1,386 0,522 1,403 0,159 1,913 1,908 0,772 0,531 0,378 1,415 0,32 0,925 1,556 1,641 1,142 0,370 0,940 1,915 1,744 1,913 0,49 0,158 0,490 0,898 0,832 0,208 1,459 0,861 0,471 0,679 1,460 1,606 0,319 1,196 1,597 1,263 1,407 0,424 1,196 0,684 0,36 1,680 0,579 0,534 0,506 1,447 0,866 0,479 1,911 1,575 0,135 0,988 1,341 1,321 0,765 1,933 1,229 1,383 0,216 0,809 0,979 0,537 0,183 0,467 0,501 0,117 0,149 1,61 1,419 0,882 0,15 0,692 0,277 1,737 1,799 1,973 1,664 0,906 1,7 0,994 0,67 0,829 0,680 0,673 1,513 0,28 0,805 1,811 1,256 0,958 1,239 1,932 1,771 1,733 1,307 0,46 1,970 0,798 1,743 0,599 1,318 1,292 0,771 0,856 0,73 0,792 1,873 0,644 0,851 0,932 0,287 1,48 0,464 0,148 1,457 0,235 1,605 0,71 0,453 1,596 0,412 0,542 0,865 1,728 0,751 1,436 1,868 0,851 0,453 1,46 1,732 0,84 0,478 1,385 0,315 1,581 0,796 0,982 0,22 0,531 1,55 0,908 1,544 0,174 0,61 0,968 0,531 1,801 0,291 1,404 1,105 0,81 0,116 1,782 1,181 0,329 1,337 1,318 1,858 0,684 0,623 1,785 0,199 1,67 1,668 0,896 1,747 1,528 1,922 0,962 0,994 0,69 0,241 0,30 1,619 1,937 1,59 1,396 0,531 0,902 0,789 0,938 1,444 1,718 1,480 0,187 1,403 0,478 1,997 0,704 0,106 0,858 1,439 0,422 1,81 0,128 1,189 1,646 0,382 1,21 1,281 1,490 1,72 0,945 0,317 0,331 0,56 1,4 0,235 1,301 1,162 1,925 1,128 0,881 0,478 1,679 1,60 1,292 0,44 1,287 1,402 1,260 1,48 0,883 0,944 0,574 0,794 0,614 1,993 0,418 1,661 0,432 0,619 0,230 0,454 1,838 0,779 0,701 0,949 1,24 1,722 0,571 0,578 0,369 1,657 1,512 1,238 0,640 1,169 0,463 0,988 0,774 1,64 0,748 1,357 0,833 1,211 0,114 0,920 0,50 0,554 1,941 0,714 1,876 1,958 0,528 0,266 1,949 1,924 0,562 1,950 1,768 1,573 0,586 0,77 0,668 1,275 0,637 0,283 0,384 0,675 1,541 1,413 1,169 1,227 0,376 1,902 1,976 1,96 1,294 0,468 1,204 1,324 0,816 1,147 0,956 0,932 1,17 0,688 1,4 1,580 0,924 1,284 1,466 1,251 0,287 1,798 1,650 1,378 1,460 1,863 1,727 0,427 1,6 0,393 1,680 1,856 1,926 0,6 1,265 1,957 0,434 1,775 1,549 0,721 1,767 1,879 0,651 0,37 1,532 1,488 0,546 0,234 1,770 1,46 1,963 0,266 0,55 1,624 0,291 1,263 0,567 0,962 1,488 1,747 0,439 1,450 0,28 0,745 0,424 1,291 0,321 0,928 0,80 1,896 1,456 1,465 1,52 1,598 0,238 0,198 0,71 1,279 0,91 1,939 1,552 0,45 0,190 1,414 1,326 1,85 0,278 0,30 0,915 0,46 1,795 1,583 1,158 1,91 0,217 1,939 0,25 1,158 0,488 1,789 0,732 1,230 0,721 1,309 0,41 1,460 1,525 1,706 1,161 0,482 1,453 1,899 1,724 1,226 0,154 0,935 1,812 0,33 0,288 1,586 0,259 0,442 1,637 0,470 0,474 0,365 0,300 0,799 0,2 0,292 0,14 0,481 0,301 0,748 1,183 1,853 0,215 1,855 1,326 1,691 1,844 1,774 0,856 1,219 0,593 0,930 0,405 1,671 0,496 0,819 0,751 0,607 0,628 0,856 0,886 0,973 0,165 1,222 0,144 0,921 0,253 1,157 0,113 1,470 1,50 1,770 1,931 1,125 0,742 1,773 0,785 1,768 1,452 1,710 1,654 0,104 0,481 1,747 1,453 0,392 0,249 1,73 1,59 1,370 0,363 0,774 0,716 0,542 0,237 1,84 1,636 1,693 1,883 1,675 0,190 0,774 0,32 1,580 1,141 0,790 1,812 1,419 0,162 1,305 0,48 1,61 1,954 1,829 0,606 0,718 0,489 1,293 0,246 0,205 1,213 1,47 0,212 1,723 0,161 0,725 0,56 1,778 1,33 0,920 0,512 1,630 1,728 1,649 0,362 1,983 1,44 0,851 1,628 1,991 0,67 1,583 0,932 0,674 0,81 1,645 1,403 1,566 1,508 0,797 0,216 0,468 0,508 0,628 0,476 0,726 0,612 1,585 1,907 1,219 0,500 1,458 0,552 1,81 0,712 1,311 0,491 0,534 0,715 0,118 0,124 0,281 1,997 1,321 0,437 0,20 1,270 1,354 1,763 0,321 0,504 1,419 0,246 0,938 0,822 1,421 1,466 0,255 1,47 1,728 0,479 1,327 0,936 1,605 0,124 1,59 1,178 0,822 0,75 1,874 1,503 0,546 0,886 0,503 0,372 1,535 1,721 1,821 0,340 0,744 0,532 1,895 1,107 0,599 0,300 0,69 0,702 0,834 0,218 0,584 0,781 1,581 1,741 0,966 1,299 0,865 0,86 1,620 0,842 1,683 0,946 1,906 0,29 1,34 1,153 1,690 0,920 0,375 0,925 1,780 0,164 1,249 1,468 1,389 0,21 1,549 1,104 0,153 0,256 0,313 0,69 1,866 1,851 1,29 1,206 0,798 0,359 1,854 0,45 0,276 0,406 0,302 0,344 0,439 1,88 0,42 0,890 0,416 0,796 0,651 1,822 1,370 1,153 1,274 0,273 0,165 0,781 0,537 0,862 1,161 1,851 1,198 1,595 0,775 0,808 0,502 1,775 0,912 1,42 1,978 1,624 1,866 0,944 0,511 0,897 0,136 0,835 1,785 1,709 0,555 0,768 0,523 0,117 1,308 1,472 1,226 1,887 0,177 0,993 0,181 1,439 0,304 0,810 0,43 1,858 0,94 1,481 1,411 1,160 0,321 1,350 1,402 1,762 0,772 0,6 0,177 0,824 1,333 0,388 0,596 0,208 1,386 1,172 0,179 0,724 0,317 1,763 1,981 1,187 1,220 1,95 0,613 0,714 1,816 0,716 1,809 1,35 1,497 1,490 0,226 1,574 0,820 0,573 1,795 0,956 0,979 1,466 0,68 0,864 1,710 1,263 0,959 1,535 1,866 1,854 1,554 0,196 1,366 1,793 0,653 1,862 1,489 1,896 1,365 1,22 0,621 0,584 0,962 0,101 1,611 0,770 1,563 0,410 0,281 0,201 0,68 0,574 1,401 1,644 1,701 1,908 1,993 0,319 0,281 0,40 1,537 0,925 1,9 1,533 0,726 0,306 1,294 0,27 0,79 1,18 0,325 1,412 0,870 1,182 0,177 0,850 0,282 0,818 0,817 0,500 0,556 0,288 1,422 1,560 0,811 1,185 1,31 0,725 0,608 1,544 1,385 0,820 1,940 1,389 1,635 1,663 1,79 1,446 0,245 0,697 1,2 1,964 0,237 0,143 1,304 1,449 0,180 1,351 1,825 1,672 0,221 1,534 0,242 0,217 0,149 1,837 0,272 0,373 1,195 1,241 1,177 1,838 1,744 1,883 0,55 1,888 1,147 0,511 0,441 0,740 1,515 0,129 1,386 1,761 1,395 0,349 0,879 1,837 1,671 1,494 1,464 1,53 0,150 1,478 1,344 0,307 0,147 0,884 1,779 0,814 0,957 0,263 0,603 1,41 1,434 0,88 0,307 0,57 0,357 0,194 0,682 0,393 1,428 1,425 1,273 0,319 1,80 1,443 0,764 1,472 1,213 0,175 0,557 0,592 1,107 0,240 1,988 1,222 0,496 0,990 0,343 0,761 1,555 1,626 0,432 1,842 1,984 0,127 1,873 0,57 1,5 1,217 1,61 1,851 0,377 0,218 1,764 1,664 0,836 1,575 0,867 1,443 1,287 0,25 0,321 0,929 1,407 0,109 1,15 0,861 0,13 1,257 1,80 1,151 0,881 1,114 1,238 0,145 0,982 0,642 1,36 0,144 0,631 1,834 1,733 0,276 1,963 1,505 0,386 1,160 0,153 0,951 1,801 0,170 1,948 0,125 0,161 0,463 1,211 1,51 0,816 0,846 0,953 1,775 1,544 0,946 0,944 1,647 0,949 0,637 0,858 1,153 1,933 1,364 1,500 0,828 1,893 1,981 1,216 0,105 0,757 1,115 1,855 1,781 1,246 1,717 0,573 0,99 0,870 0,100 0,953 0,606 1,362 1,393 0,377 1,444 1,943 1,356 0,174 0,423 0,181 1,18 1,818 1,501 0,909 1,565 0,195 1,553 0,256 0,69 1,492 1,306 1,326 1,361 0,86 1,117 0,512 1,225 1,789 1,3 0,996 1,44 0,973 0,422 1,950 0,229 1,593 1,423 0,235 0,68 0,971 1,260 1,552 1,507 0,640 0,585 0,570 0,221 1,868 0,290 1,110 0,257 0,816 1,787 0,500 1,987 0,656 1,356 1,593 0,552 0,30 0,663 0,872 1,547 0,160 0,181 1,492 1,409 1,980 0,111 1,521 1,743 1,819 0,650 1,257 1,28 1,777 0,591 1,766 0,796 1,722 0,440 1,517 0,877 1,101 1,14 1,99 0,609 0,493 0,221 0,734 0,402 0,970 0,954 0,144 1,353 1,935 1,113 0,532 0,302 1,767 0,406 0,426 0,425 0,299 0,814 1,377 1,106 0,163 1,849 1,549 1,396 1,500 1,711 1,164 0,480 0,277 0,411 0,230 0,886 0,976 1,303 0,982 0,364 0,63 1,67 1,834 1,373 1,670 0,424 1,169 1,89 1,176 1,267 1,671 0,277 1,171 1,317 0,714 1,28 1,919 0,438 1,620 0,90 1,52 0,408 0,520 1,315 0,502 1,134 0,284 0,892 0,206 0,803 0,118 1,637 1,379 0,389 0,950 0,609 1,670 0,930 1,362 1,270 0,842 0,278 1,615 1,929 1,521 0,414 0,147 1,761 0,262 1,222 1,965 0,773 1,961 0,261 1,496 0,127 0,309 1,241 1,194 0,800 1,811 1,346 1,795 1,353 0,900 0,858 1,485 1,886 0,731 1,117 1,630 0,898 0,598 1,563 0,480 0,296 1,200 0,807 1,867 0,705 1,341 0,868 0,377 0,352 1,438 0,625 1,44 0,631 1,21 0,463 1,644 1,276 0,930 1,124 1,731 0,828 0,852 0,68 0,578 0,258 1,642 0,415 0,825 1,589 1,278 0,370 1,422 0,271 1,181 1,128 1,489 1,801 0,265 1,694 1,557 0,926 0,743 1,817 1,308 1,783 0,373 0,395 0,712 0,83 0,389 0,154 0,720 0,192 0,257 0,901 1,683 1,333 0,337 0,101 1,108 1,190 0,502 0,837 0,448 0,895 1,83 0,325 1,381 0,534 1,452 1,89 1,955 1,450 0,748 0,315 0,720 1,592 1,379 0,949 1,228 0,530 1,12 1,108 0,464 0,871 1,265 0,972 0,207 1,510 1,83 0,294 1,6 1,616 0,886 0,183 1,66 0,317 0,258 1,598 0,893 0,483 1,669 1,955 1,248 0,394 0,304 1,36 1,358 0,852 1,460 1,110 0,48 1,227 1,865 1,584 0,112 1,563 1,364 0,855 0,330 1,963 1,352 0,68 0,199 0,651 0,593 1,470 1,464 1,143 1,878 1,379 0,462 0,680 1,322 1,619 1,382 1,690 0,136 1,718 0,825 0,121 0,24 0,904 0,673 0,25 1,377 0,821 0,696 0,864 0,409 1,312 1,825 0,515 0,186 0,500 1,232 1,998 0,156 0,687 0,932 0,987 0,189 1,342 1,959 1,100 1,747 0,272 0,690 0,379 0,574 0,157 0,994 1,777 1,92 0,308 1,648 1,3 1,262 0,492 0,484 1,766 0,587 1,345 1,485 1,120 0,571 1,362 0,94 0,227 1,371 1,668 0,479 1,925 0,58 1,695 0,793 0,169 0,37 0,611 0,117 0,867 1,215 0,356 0,685 1,157 1,365 1,858 1,962 0,276 1,464 1,848 1,134 1,937 1,488 0,456 1,698 1,34 0,832 1,959 1,948 0,571 1,296 1,677 1,538 0,45 0,323 1,174 0,487 1,818 1,243 1,234 1,998 1,169 0,687 0,16 0,222 0,747 0,818 1,98 1,336 1,757 1,684 0,389 1,645 0,466 1,219 1,329 0,486 0,251 0,359 0,755 1,545 0,683 0,657 1,936 0,785 0,224 1,136 0,151 1,50 1,382 1,575 1,273 0,751 1,61 0,924 0,112 1,587 0,301 0,606 1,694 1,21 1,317 0,296 1,981 1,898 1,997 0,385 1,4 0,449 0,721 1,125 0,8 1,390 0,558 1,501 0,324 0,770 1,749 0,628 1,466 0,90 0,385 1,253 0,791 1,478 0,262 0,470 0,742 1,699 1,414 0,427 1,231 1,434 0,653 0,388 0,659 0,641 1,522 0,290 1,397 1,34 1,856 1,510 0,34 1,907 0,214 0,32 0,605 0,41 1,213 0,508 1,55 0,32 0,164 0,522 0,864 1,861 0,260 1,241 0,546 1,773 0,993 0,700 1,880 0,791 1,991 0,694 1,529 1,537 0,625 1,446 1,290 1,223 0,213 0,863 1,96 1,988 1,299 1,880 1,510 0,552 0,255 1,997 0,25 0,287 1,759 0,422 1,477 0,282 1,964 1,352 0,776 0,239 1,755 0,600 1,112 1,364 1,318 1,314 1,259 0,812 0,371 0,961 0,71 0,115 0,365 0,492 1,757 0,665 1,71 1,170 1,586 1,649 0,525 1,684 0,984 1,616 1,305 1,779 1,919 1,790 1,320 0,907 0,853 1,286 0,43 0,868 1,834 1,838 1,890 0,618 0,689 0,525 1,280 0,167 1,454 0,303 1,897 0,691 1,384 0,462 0,78 1,796 0,117 1,896 1,746 1,76 0,785 0,832 0,826 1,336 1,505 0,618 1,325 0,747 1,487 1,864 0,785 0,810 1,552 1,725 1,143 1,919 1,289 0,181 1,897 1,3 0,222 1,982 1,960 0,907 1,492 1,473 1,997 1,587 0,122 1,188 1,676 1,492 1,418 1,793 1,92 0,747 0,436 0,208 0,258 0,975 1,635 1,787 1,271 1,425 0,712 0,165 0,356 1,623 0,835 1,250 1,354 1,94 1,1 0,60 0,525 0,410 1,20 0,96 1,620 0,152 0,979 1,420 1,338 0,766 1,708 0,847 1,830 0,981 0,397 1,30 0,941 0,957 0,10 0,982 1,435 1,804 1,550 1,654 1,402 0,998 0,965 0,145 1,542 0,481 0,63 0,485 1,76 1,802 0,575 1,434 0,762 1,763 0,776 1,435 1,240 0,421 1,396 1,289 1,696 0,777 0,20 1,534 1,399 1,339 1,446 1,516 1,919 1,331 0,536 0,941 1,485 1,48 0,411 1,584 1,49 0,278 0,128 1,779 0,409 1,354 1,643 1,340 1,83 1,963 0,893 0,819 1,685 0,596 1,437 1,726 0,225 0,60 1,579 0,895 1,309 0,213 1,168 0,252 0,724 1,774 0,849 1,229 1,607 1,258 0,236 1,206 0,477 0,313 1,496 1,329 0,787 1,301 0,295 1,993 0,606 1,42 0,282 0,276 0,41 1,693 1,28 0,777 1,852 1,850 1,416 0,870 1,103 0,576 0,1 1,259 0,297 1,979 0,203 1,760 1,587 0,285 1,657 1,577 1,634 0,687 0,87 0,318 0,919 1,108 1,261 1,67 1,276 1,756 1,431 0,909 0,454 0,300 0,228 1,575 1,137 0,384 0,144 0,292 1,21 0,716 0,597 1,49 0,895 0,641 0,568 1,461 0,426 1,874 0,806 1,746 1,300 1,275 0,338 1,47 0,831 1,201 1,569 1,305 1,562 1,551 1,950 0,323 1,280 1,581 1,220 1,783 0,745 0,149 0,587 0,274 1,115 1,464 1,850 1,513 0,970 0,796 0,147 0,771 0,163 0,48 1,676 0,74 0,720 1,151 0,169 0,33 1,275 0,420 1,365 1,75 0,129 0,367 0,138 0,431 1,261 0,294 0,385 1,372 0,177 1,712 1,673 0,633 1,919 1,285 0,544 1,970 0,477 0,171 1,34 0,972 1,763 0,278 0,755 0,100 0,522 1,464 1,120 1,642 1,503 1,948 0,759 1,908 1,570 1,823 0,775 0,725 0,458 0,294 1,390 1,300 1,509 1,769 0,992 0,363 0,274 0,295 1,581 1,492 0,280 0,583 1,700 0,249 0,849 1,865 1,325 0,115 1,979 1,654 0,79 1,997 0,411 0,288 1,459 1,171 0,151 0,952 1,472 1,516 0,921 0,28 0,273 1,267 0,754 0,180 1,939 1,617 1,504 0,77 1,260 1,777 0,870 0,104 0,973 1,130 1,215 1,884 1,235 0,739 1,390 1,559 1,721 0,348 1,348 0,810 1,4 0,427 1,820 1,871 0,816 1,964 0,716 0,284 0,754 0,317 1,584 0,920 1,284 0,729 1,747 1,974 0,559 0,132 1,130 1,315 0,993 0,579 1,829 0,811 0,450 0,492 1,531 0,506 0,988 1,948 1,968 0,109 1,189 0,954 1,463 0,432 1,117 0,730 0,632 0,238 1,747 0,412 1,484 1,214 1,972 0,76 0,983 1,450 1,263 1,129 0,801 0,569 0,68 0,104 1,719 1,222 0,302 1,762 1,710 1,374 0,636 1,615 0,483 0,342 1,483 1,676 0,159 1,312 1,855 1,179 0,544 1,301 1,509 0,332 0,888 1,799 0,334 0,342 1,501 1,393 1,606 0,798 1,77 1,744 1,991 0,703 0,108 1,166 0,811 1,771 0,859 1,125 0,924 0,811 1,426 0,714 1,396 1,406 0,569 1,961 0,831 0,368 0,252 1,937 0,15 0,472 0,427 1,880 0,247 0,951 0,234 1,575 1,979 1,629 1,820 1,682 1,927 1,66 1,183 1,955 0,536 0,524 1,12 0,20 1,78 1,595 1,476 1,718 1,297 1,94 0,439 1,67 1,989 1,437 1,309 1,195 1,939 0,471 1,246 0,293 1,883 1,283 1,935 1,172 1,275 0,189 1,145 1,982 1,693 1,76 1,819 0,817 0,616 1,287 1,665 1,799 0,846 0,637 0,813 0,35 1,492 0,464 1,910 0,242 0,337 1,774 1,514 1,485 1,454 0,158 1,171 1,193 1,169 0,90 0,784 0,171 0,476 0,353 0,212 0,677 0,989 0,805 0,279 1,99 0,39 1,232 1,694 0,994 0,840 1,814 1,329 0,563 1,772 1,126 0,596 0,895 0,116 0,63 0,121 0,171 0,163 0,252 0,713 1,661 0,172 1,671 0,670 0,80 1,362 0,36 0,960 0,239 0,149 0,4 0,915 0,132 0,97 0,794 0,781 1,508 1,23 1,702 1,578 0,281 0,891 0,458 1,819 0,341 0,94 0,722 1,609 1,965 1,874 0,136 0,696 1,151 0,496 0,210 0,824 0,837 1,751 1,268 1,345 1,966 0,134 1,726 0,62 0,918 1,848 1,541 0,350 0,646 1,105 0,94 0,96 0,331 0,302 1,247 1,277 1,40 1,538 0,812 0,764 0,505 0,71 0,581 0,725 0,910 0,289 0,272 0,974 1,111 1,341 0,297 1,210 0,100 0,124 0,301 1,426 1,927 0,340 1,139 0,577 1,672 0,117 1,119 1,250 1,194 0,256 1,412 0,698 0,374 1,145 0,960 1,818 0,34 1,151 1,993 0,735 0,602 1,555 1,520 0,777 0,57 0,214 0,658 0,684 1,432 0,978 0,272 1,368 0,222 0,684 1,768 1,983 1,294 1,879 0,201 1,104 0,871 1,987 1,803 1,125 0,431 0,517 1,308 1,454 1,61 0,476 0,863 1,388 0,404 1,52 1,473 0,251 0,295 0,381 0,772 1,921 0,69 0,306 1,677 0,728 1,702 0,881 1,553 1,129 1,391 0,914 1,274 0,724 0,722 0,950 1,464 0,917 0,630 0,230 1,850 1,669 1,429 0,896 1,449 0,856 0,223 1,785 0,621 1,381 1,134 0,445 0,221 1,782 0,387 1,848 1,306 0,128 1,911 1,393 0,585 0,892 1,908 1,914 1,813 1,927 1,818 1,752 1,367 0,963 1,254 1,191 1,339 1,915 0,915 1,196 1,804 0,499 1,109 0,669 0,591 1,211 1,80 1,973 0,605 0,531 1,950 0,159 0,942 0,574 0,826 0,673 1,759 1,793 1,646 0,624 0,56 0,132 1,639 0,713 0,120 0,93 0,493 1,842 1,322 0,835 1,708 0,469 1,22 0,231 0,996 1,617 0,562 1,996 1,935 1,118 0,971 1,279 0,585 1,149 0,324 0,108 1,792 0,424 0,647 0,191 0,3 1,201 0,370 1,336 0,966 0,100 0,90 1,279 0,836 1,211 0,606 1,954 0,809 0,146 1,144 0,582 0,822 0,331 1,724 0,542 1,254 1,514 0,356 0,182 1,529 0,685 0,440 1,579 1,850 0,0 1,77 1,807 0,621 0,295 1,817 1,138 1,421 0,585 0,454 1,262 0,691 1,212 0,286 0,609 1,37 1,147 0,454 1,899 1,859 0,272 0,699 0,310 0,227 1,103 0,738 1,379 0,420 1,738 0,841 0,442 0,673 0,359 1,651 0,663 1,491 0,248 0,481 1,423 1,144 0,931 0,602 1,765 0,435 1,771 1,786 1,728 1,586 1,856 1,623 0,760 1,864 1,262 1,785 1,949 1,325 1,277 0,27 1,489 0,267 0,213 1,981 1,0 0,5 0,102 1,759 1,240 1,121 0,920 1,882 0,724 0,961 0,753 1,857 1,328 0,99 1,25 0,838 1,768 1,23 0,890 0,165 1,551 0,639 1,890 0,228 0,133 1,446 0,464 0,938 0,371 1,439 0,485 1,888 0,131 1,285 1,96 0,672 1,879 1,653 1,929 1,897 1,779 1,980 1,459 1,661 0,103 1,448 1,67 1,171 1,523 1,986 1,393 0,170 0,943 1,592 0,3 0,593 0,623 1,0 1,233 0,785 1,591 0,50 0,607 0,712 0,524 0,816 0,489 0,498 1,948 0,299 0,779 1,5 1,312 0,579 0,984 0,222 1,987 1,180 0,373 1,908 0,234 1,177 1,28 1,172 1,898 0,472 1,803 0,625 0,160 1,834 1,284 0,249 1,873 0,604 1,594 0,40 0,674 1,598 1,170 1,384 1,500 1,340 1,756 1,584 1,676 1,265 1,120 1,765 1,433 0,926 0,167 0,825 0,238 0,389 0,401 0,655 0,72 0,696 0,987 1,611 0,800 0,160 0,495 1,495 0,851 0,400 1,757 1,371 0,422 1,133 1,812 0,527 0,873 0,170 1,266 0,949 0,504 1,977 0,209 1,757 1,662 0,42 1,474 0,828 1,244 0,759 1,366 0,801 1,603 0,765 0,963 1,282 1,631 0,576 1,487 0,225 1,918 1,729 1,760 0,491 1,33 0,662 1,788 0,526 1,407 1,940 1,588 0,725 0,509 1,107 1,190 1,906 0,736 0,636 0,885 0,298 0,915 0,49 1,299 1,798 1,301 1,343 1,555 0,391 0,502 0,521 0,333 1,200 0,280 0,903 0,490 1,633 0,118 1,936 1,198 0,334 0,914 1,465 1,61 1,81 1,136 0,260 1,5 1,645 0,246 0,376 0,133 0,387 1,542 0,337 1,455 0,563 1,92 0,907 0,215 1,127 0,79 0,212 0,980 1,618 1,756 1,896 1,231 0,787 0,664 1,322 1,652 1,771 0,856 0,960 0,588 0,406 0,699 0,629 0,347 0,445 0,111 0,525 1,356 0,199 0,182 0,840 1,298 1,427 1,391 1,970 1,251 0,740 0,462 0,360 0,261 1,602 1,890 0,241 0,126 1,304 1,685 1,563 1,423 1,184 0,804 0,213 1,491 1,646 0,38 1,284 1,947 1,476 0,340 0,755 0,254 0,633 0,267 0,490 0,852 1,258 0,689 1,892 1,841 0,556 0,572 1,263 0,489 1,588 0,881 0,998 0,285 0,272 0,529 1,206 0,674 1,759 0,953 1,787 0,781 1,851 1,692 0,855 1,353 1,596 1,630 1,37 0,853 1,981 0,276 0,870 1,721 1,707 1,591 0,629 0,886 0,343 1,237 1,537 1,298 0,676 1,919 1,235 1,775 0,299 0,355 1,2 1,658 1,689 1,708 0,739 0,75 0,448 1,947 0,693 0,889 1,260 1,640 1,826 1,801 0,847 1,915 1,819 0,339 0,510 1,672 1,761 1,574 1,107 1,37 1,941 1,77 0,842 0,408 1,821 0,237 0,244 1,859 0,211 0,518 0,139 0,107 1,42 1,14 0,619 0,785 1,387 1,243 0,976 1,845 1,665 0,580 1,213 0,158 0,335 0,324 1,970 1,351 0,171 1,582 1,118 0,857 1,295 0,924 1,311 0,517 0,8 0,493 0,875 0,29 1,118 0,327 1,645 1,473 1,39 1,26 0,16 0,55 0,566 0,610 1,881 0,77 1,143 1,309 0,667 0,757 0,56 0,908 0,383 0,41 0,472 0,472 1,905 1,833 0,585 0,819 0,786 0,504 1,8 1,569 0,518 1,82 1,768 1,879 1,53 1,427 0,445 1,286 1,964 0,812 1,458 1,105 0,4 0,729 0,230 1,810 1,352 0,524 0,283 0,18 0,51 0,431 0,728 0,693 1,284 0,565 1,317 0,885 1,843 1,479 0,180 1,487 0,483 1,11 0,936 0,846 0,905 1,68 0,119 1,560 1,216 0,99 1,653 0,21 0,980 1,261 0,455 0,728 1,213 1,816 0,911 1,373 0,575 1,890 1,167 0,732 0,50 1,129 0,384 0,410 1,586 1,59 0,64 1,881 0,855 1,477 1,880 1,368 0,276 0,120 0,318 1,155 1,691 1,166 1,470 1,176 0,119 0,702 1,488 0,929 0,605 1,94 1,839 1,516 0,807 0,655 0,926 0,89 1,714 1,743 1,803 0,931 0,328 0,632 1,915 0,113 1,827 1,672 0,117 0,160 1,638 0,756 0,306 1,791 1,757 0,526 0,270 0,859 0,941 0,941 0,398 1,808 0,37 1,138 1,205 1,629 1,561 0,755 0,697 0,478 0,466 1,601 0,101 0,333 1,251 0,711 1,58 0,412 1,707 1,540 1,798 0,512 1,544 0,99 1,540 0,396 1,982 0,159 1,287 1,225 0,63 0,778 0,543 1,611 0,822 0,262 0,349 0,3 0,916 0,377 0,83 0,706 0,187 0,628 1,767 1,924 0,491 0,116 1,60 0,468 0,899 1,16 1,293 0,942 1,603 0,227 1,277 1,411 1,670 1,681 1,391 0,225 0,371 1,374 0,573 1,653 1,772 0,852 1,364 0,953 0,574 1,390 0,135 0,398 0,361 0,192 0,253 1,649 1,783 1,694 0,87 0,688 1,956 0,576 1,936 0,312 1,97 0,458 1,568 0,119 0,599 0,604 1,242 1,749 1,707 1,897 1,473 0,828 0,141 0,977 0,145 1,748 1,526 0,44 0,509 1,602 0,767 1,654 1,474 1,74 0,988 1,948 1,613 0,28 1,579 1,474 0,479 0,722 0,199 0,467 0,737 1,40 1,841 0,888 0,992 0,571 1,506 1,2 0,103 0,962 0,976 0,76 0,552 0,953 1,564 1,103 1,973 1,447 1,929 0,51 1,637 1,361 1,449 0,645 0,310 0,385 0,999 1,235 1,857 1,602 1,99 0,608 0,576 0,893 1,239 1,553 0,202 1,109 0,13 1,454 0,524 0,420 1,636 0,324 0,832 1,519 0,336 1,11 1,418 1,676 0,759 1,874 1,576 1,259 1,701 1,922 1,454 0,958 0,129 1,379 1,615 0,916 1,823 1,73 0,403 0,174 0,339 0,84 0,18 1,141 0,539 0,500 1,430 1,40 1,894 1,368 1,644 1,923 1,447 1,422 1,404 1,6 1,610 1,788 0,711 0,850 1,95 1,991 1,946 1,390 0,558 0,789 1,45 1,498 1,791 1,710 1,288 0,176 0,552 0,283 1,432 1,289 1,190 0,165 0,496 0,720 1,516 1,441 1,623 0,984 1,28 0,231 0,258 0,856 0,88 0,330 0,363 1,365 0,472 0,971 0,295 1,131 0,212 1,219 0,972 1,849 0,147 0,732 1,248 1,606 0,647 0,356 1,277 0,92 1,42 1,661 0,745 0,714 0,948 1,303 1,559 0,775 1,147 0,152 0,660 0,526 1,488 0,350 1,339 0,563 1,476 1,764 1,219 1,765 1,869 1,248 0,462 1,379 0,848 0,69 0,850 0,644 0,745 0,748 0,981 1,100 0,871 0,34 1,891 0,962 0,544 0,760 0,290 1,915 0,933 0,837 1,683 0,7 1,930 0,607 0,729 0,266 1,625 1,611 1,440 0,88 1,13 1,959 0,752 0,45 1,975 1,87 0,605 1,461 1,93 1,683 1,793 0,382 1,154 0,452 1,472 0,908 0,518 0,110 0,191 1,712 1,611 1,288 0,495 1,509 1,614 0,548 0,534 1,754 0,575 0,171 1,743 1,802 1,972 0,313 1,896 0,464 0,470 0,950 0,47 0,753 1,493 1,47 0,59 1,118 1,445 1,106 0,109 1,810 1,876 1,930 1,768 0,668 1,392 1,947 0,12 1,803 1,658 0,591 1,319 1,226 0,147 1,547 1,774 1,923 0,790 0,250 0,662 0,300 0,14 1,189 0,89 1,170 0,427 0,311 0,183 0,520 1,559 0,511 0,840 0,59 1,108 1,764 0,317 1,711 1,840 0,4 0,538 1,195 0,341 1,38 1,652 0,405 1,395 0,333 0,119 0,416 1,404 1,15 1,28 1,594 1,958 0,414 0,155 0,281 0,662 1,515 1,972 1,281 1,320 1,937 1,652 1,41 1,82 0,361 0,885 0,584 1,283 1,478 1,365 1,615 1,289 0,879 0,569 0,366 1,148 1,52 0,658 0,56 1,146 1,956 1,87 0,395 0,214 0,613 1,526 0,5 0,592 1,774 0,529 1,812 0,579 1,581 0,602 1,977 0,755 1,372 1,611 1,604 0,474 0,472 1,123 0,699 0,799 1,420 1,310 1,708 0,450 0,99 0,427 1,450 1,740 1,684 0,624 1,444 0,227 0,395 0,887 1,348 1,411 0,406 0,153 0,304 0,816 1,994 1,819 0,379 1,227 1,576 1,585 1,629 1,874 1,848 1,7 0,836 0,512 0,875 0,43 1,159 1,706 0,653 0,641 0,893 0,849 1,703 1,104 1,480 0,211 1,223 1,17 0,233 0,565 0,603 1,868 0,626 1,948 1,188 1,403 1,796 1,82 0,232 1,744 0,675 1,552 1,969 1,304 0,261 0,790 0,820 0,465 1,449 0,765 1,490 0,638 1,468 0,630 0,446 1,690 1,316 0,963 1,413 0,364 1,532 1,611 0,152 0,15 0,168 1,229 1,725 1,864 1,342 1,172 1,442 0,274 0,539 1,598 0,969 0,381 1,309 1,635 0,80 0,470 0,718 0,262 0,575 1,742 1,179 0,474 0,554 0,464 0,414 0,399 1,607 0,902 0,4 0,830 0,906 0,240 0,460 1,802 1,24 1,840 0,645 1,745 1,956 1,749 1,986 0,302 1,545 0,502 0,443 0,258 0,423 0,34 1,677 1,566 0,638 1,424 0,791 1,413 1,446 1,28 1,402 1,200 1,828 1,480 0,797 0,660 1,238 1,776 0,866 1,368 1,348 0,168 1,189 0,459 0,631 0,448 0,329 0,975 0,717 1,690 0,784 1,318 0,706 1,316 1,847 0,532 1,2 1,325 0,870 0,735 1,547 0,832 0,298 1,199 0,285 0,557 1,780 0,104 1,36 0,809 1,934 1,252 1,886 0,671 1,314 1,759 1,772 0,380 1,335 1,868 0,784 0,492 1,761 1,268 0,125 1,382 0,375 0,219 0,392 1,909 1,147 1,885 0,674 0,275 1,852 0,672 1,800 1,112 0,3 1,614 1,968 0,863 1,697 1,189 0,136 0,856 0,308 1,150 1,781 0,47 1,39 0,634 0,387 0,107 0,483 0,250 0,354 1,814 0,159 1,623 1,225 1,826 1,526 0,264 1,731 0,931 0,519 0,479 0,174 0,441 1,98 1,821 1,194 1,99 0,531 1,911 0,279 1,201 1,204 0,714 0,344 1,736 0,92 0,744 1,605 0,7 0,880 0,751 1,916 0,127 1,628 0,161 0,684 0,663 0,173 1,295 1,84 1,762 0,362 1,519 0,631 1,647 1,241 0,380 0,663 1,629 1,998 0,928 0,932 0,782 1,770 1,387 1,211 1,800 0,425 1,501 1,128 0,429 0,698 1,779 1,967 1,624 0,396 0,605 1,7 1,295 1,797 1,506 0,156 0,117 0,449 1,898 0,61 0,40 1,417 1,778 1,288 1,71 0,538 1,837 0,159 0,768 1,648 0,800 0,130 1,644 1,301 0,346 0,677 1,90 1,733 1,756 1,122 1,8 0,851 0,194 0,655 0,243 1,744 0,163 1,497 0,971 0,508 1,991 0,167 0,67 1,970 0,450 1,373 1,850 1,341 0,86 1,443 0,259 0,492 0,859 1,809 1,713 1,944 0,32 0,166 1,672 1,822 0,606 1,527 1,383 0,711 1,244 1,258 1,998 1,763 0,691 0,897 1,11 1,106 1,721 1,909 1,530 0,757 1,515 1,948 0,357 1,437 1,563 0,119 1,373 1,706 0,8 0,879 1,196 0,8 1,140 1,676 0,406 1,528 0,494 0,928 1,34 0,605 0,699 0,630 1,171 0,968 1,542 1,101 0,455 1,812 1,711 0,482 1,161 1,937 0,316 1,609 1,639 0,949 1,257 0,620 0,435 1,308 0,377 0,83 0,64 1,262 1,203 0,317 0,375 0,434 0,144 0,265 1,679 0,44 0,764 0,166 1,871 1,754 1,975 0,175 0,774 1,625 1,910 1,693 1,270 0,742 1,396 0,748 0,381 1,451 0,181 1,720 1,564 1,973 0,580 1,922 1,786 1,886 0,740 1,434 0,499 1,740 0,572 0,554 1,903 1,674 1,57 0,189 0,453 1,733 0,95 0,676 0,475 1,669 1,761 0,166 0,941 1,48 1,251 0,215 1,30 0,148 0,281 0,94 1,21 1,452 1,635 0,883 1,177 0,558 1,905 0,178 0,945 0,866 0,602 1,908 0,636 0,454 1,644 0,83 0,325 0,999 1,105 0,957 1,335 1,475 1,238 1,60 0,4 0,545 0,400 0,478 0,665 1,314 0,722 0,376 1,997 0,578 0,389 1,26 1,330 1,927 1,351 0,177 1,959 0,740 0,950 0,738 0,154 0,879 0,709 0,865 0,284 1,761 0,734 1,385 0,27 1,143 0,445 1,636 1,856 1,825 0,242 1,875 1,689 0,470 0,688 0,292 1,858 0,96 0,560 1,125 1,442 1,390 1,596 0,208 1,368 1,52 0,933 1,493 0,350 1,204 1,481 1,967 0,858 1,546 0,798 1,582 1,990 1,382 1,797 0,58 0,443 1,88 1,57 1,368 0,452 1,269 0,643 0,701 1,758 1,616 0,26 1,591 1,865 0,460 0,413 1,893 0,525 0,615 1,474 0,592 0,64 0,376 0,343 1,856 0,530 1,213 0,629 1,67 0,431 1,296 0,3 0,921 1,725 1,765 1,130 0,901 0,535 0,246 1,88 0,935 1,14 0,567 0,457 1,540 0,789 0,491 1,45 0,824 0,574 0,959 0,917 1,559 1,450 1,249 0,840 1,789 0,64 1,265 0,736 0,908 0,223 1,514 0,550 1,395 1,210 0,416 1,451 0,7 0,296 0,224 0,794 0,690 0,325 1,452 0,878 1,879 0,644 1,693 0,98 0,218 0,916 1,690 1,107 0,258 0,363 0,471 0,787 0,158 1,590 1,933 1,342 1,401 0,789 0,154 1,963 0,430 1,326 1,941 1,136 1,629 1,354 0,413 0,416 1,582 1,993 0,925 0,405 1,700 1,108 1,678 0,733 0,170 0,101 0,5 1,991 0,596 1,626 0,797 0,104 1,949 0,675 0,262 0,102 0,809 0,950 0,794 1,850 0,831 0,956 0,99 1,719 1,553 0,962 0,797 0,611 0,544 1,877 0,157 0,754 0,785 1,673 0,334 1,621 0,12 1,35 1,604 1,118 0,740 0,695 1,73 1,65 1,892 1,899 1,875 0,992 1,76 1,595 0,533 0,55 0,212 1,354 0,574 0,127 1,939 1,966 1,261 1,702 0,859 0,750 1,947 0,601 1,483 0,264 0,275 0,778 0,982 1,200 1,145 0,836 0,654 0,479 1,848 1,289 1,179 1,669 0,268 0,455 1,593 0,429 0,806 0,765 0,57 0,216 1,254 1,409 0,344 0,415 0,959 0,495 1,519 1,157 1,955 0,847 1,502 0,655 1,717 1,286 1,240 1,36 1,516 0,283 1,414 1,972 1,566 0,4 0,87 1,382 1,377 0,379 1,320 0,377 0,565 0,442 1,851 1,829 0,751 1,495 0,434 0,804 0,647 0,534 0,66 1,580 1,745 1,801 0,521 0,488 0,670 1,121 0,509 1,99 0,5 0,684 0,771 0,978 1,825 0,362 0,121 1,853 0,610 0,152 1,788 0,396 0,341 0,23 1,651 0,15 0,98 0,389 1,906 1,288 0,908 0,621 1,395 0,793 0,417 1,21 0,56 0,42 0,540 0,522 0,517 0,588 0,983 1,231 0,777 1,790 0,53 1,974 1,139 1,903 1,249 0,332 0,881 1,94 0,596 1,853 1,799 0,567 1,667 1,268 0,444 0,604 0,852 1,768 0,883 0,515 1,169 1,551 1,601 1,871 1,616 1,783 1,696 1,896 0,688 0,983 0,960 1,783 0,651 1,103 0,195 0,1 1,499 0,149 1,422 0,18 1,211 1,839 0,837 1,548 0,606 0,489 0,743 1,130 0,503 1,367 1,843 0,909 0,637 0,324 0,997 1,845 1,427 1,462 0,62 1,352 1,809 1,279 1,780 0,707 0,240 0,458 0,775 0,722 1,280 0,816 1,778 0,742 0,544 1,20 1,30 0,29 1,951 0,601 0,299 0,856 0,22 1,145 1,263 0,697 0,827 0,972 0,802 1,328 1,45 1,824 0,572 0,518 1,785 1,331 1,412 0,855 0,205 1,897 0,836 0,689 0,951 1,575 0,38 0,892 1,81 0,643 1,554 0,487 1,61 0,509 1,894 1,302 1,624 1,647 1,719 1,63 0,15 1,28 1,646 0,413 1,445 1,733 0,309 1,280 0,789 0,447 0,873 1,670 1,846 1,2 0,587 0,372 0,570 0,243 0,869 0,622 0,41 1,678 0,440 1,839 1,94 1,376 0,431 0,767 1,745 0,88 1,560 0,38 1,716 0,974 1,880 1,137 1,497 0,230 1,101 1,456 1,646 0,453 0,677 1,230 0,784 0,839 1,694 0,67 1,173 1,866 1,498 0,200 0,571 1,397 1,48 0,787 1,102 1,160 1,362 1,687 1,361 1,998 1,215 1,243 1,333 0,3 1,409 1,336 0,739 0,162 0,557 0,609 0,140 1,483 0,575 0,603 1,773 1,837 1,264 1,428 1,663 0,391 0,129 0,550 0,763 1,292 0,780 1,715 0,919 0,533 1,449 1,797 0,490 0,728 0,923 0,562 1,586 1,134 0,284 1,459 0,321 0,383 1,871 0,73 1,636 0,483 0,568 0,260 0,595 0,289 0,131 0,924 0,861 0,324 1,291 0,136 1,402 0,130 0,443 0,560 1,608 0,32 0,530 0,562 0,998 1,469 1,524 0,111 0,221 0,414 0,904 1,777 0,300 1,798 0,284 0,402 1,188 1,43 0,533 0,292 1,298 0,918 0,512 0,399 0,485 1,0 1,319 0,946 1,811 0,489 1,396 1,753 1,192 1,625 0,2 0,661 0,221 1,249 0,312 1,273 0,8 1,611 0,559 1,767 1,743 0,982 0,415 1,890 0,746 0,963 1,744 1,577 1,256 1,122 1,772 0,909 0,801 1,345 1,37 1,21 1,967 0,532 0,80 1,980 0,509 1,425 1,948 0,131 0,846 0,890 0,339 1,714 1,122 1,214 0,777 0,908 0,464 1,903 0,695 0,392 0,937 1,437 1,344 0,843 1,736 1,932 0,2 1,803 1,209 0,178 0,898 0,313 0,518 1,947 0,976 0,4 1,7 1,85 1,698 1,930 0,253 0,852 1,532 1,497 1,392 1,600 1,343 0,901 0,555 1,368 0,258 1,689 0,744 1,159 0,204 1,239 0,164 1,465 1,496 1,262 1,539 1,65 0,444 0,282 1,603 1,12 0,293 1,5 0,560 0,733 0,224 0,675 1,731 0,66 1,745 0,828 0,532 1,762 0,898 0,827 1,421 1,619 1,745 0,982 1,159 1,82 1,600 0,602 0,329 0,650 0,767 0,347 0,7 1,267 0,472 0,122 0,611 0,262 0,780 1,887 0,575 0,752 1,491 0,130 1,510 1,132 0,698 1,943 0,317 0,875 1,135 0,872 0,725 0,336 0,542 0,189 0,873 1,362 1,668 0,399 0,942 0,949 1,685 1,875 0,929 0,601 1,577 0,668 1,269 1,523 0,699 0,981 1,132 0,22 0,976 1,231 0,432 1,115 0,923 1,150 1,143 0,41 1,882 1,461 0,745 0,691 1,129 0,565 0,983 0,716 1,903 1,52 0,337 1,370 1,624 0,964 1,33 1,383 0,195 1,117 1,244 0,811 1,237 1,40 0,667 0,149 1,0 0,819 1,152 1,588 0,812 1,918 1,264 1,120 1,338 1,736 0,827 1,334 0,70 0,91 1,537 1,311 1,777 1,263 1,990 0,117 0,655 1,951 0,119 1,993 1,921 1,136 0,872 1,621 1,129 1,969 1,828 1,929 1,444 1,874 1,521 0,730 1,518 1,486 0,497 1,903 1,501 1,37 0,748 1,652 1,233 0,386 0,13 0,600 0,772 0,566 1,293 0,572 0,552 0,556 0,822 1,370 1,146 0,775 1,292 1,354 0,246 1,989 0,818 0,830 1,768 0,502 1,582 0,819 0,276 1,505 0,817 0,2 0,834 1,99 1,995 0,199 0,199 0,995 0,291 1,463 0,26 0,704 0,532 0,947 1,445 0,17 1,479 1,700 1,481 0,82 0,163 1,593 0,954 0,998 0,121 1,589 1,412 0,572 0,355 1,493 0,674 1,431 0,994 1,921 1,284 0,105 0,540 0,833 1,511 1,887 1,510 0,471 1,495 1,372 0,928 1,676 1,329 1,981 1,760 0,663 1,177 0,6 1,422 0,645 0,390 1,575 1,568 0,792 1,543 0,231 1,812 1,592 1,280 1,38 0,470 1,228 0,279 0,603 1,108 0,418 1,277 0,798 1,286 0,869 1,576 1,857 0,400 0,891 1,204 1,413 1,739 1,947 0,123 0,667 0,831 1,57 1,719 0,115 0,976 1,899 1,288 1,83 1,190 1,77 0,449 1,236 0,447 0,459 1,440 1,619 0,668 0,679 1,790 0,542 1,372 0,655 1,481 1,977 1,657 1,863 1,332 0,732 1,521 0,726 1,32 0,409 0,703 1,421 0,69 1,181 0,348 0,875 1,487 0,374 0,316 0,644 0,8 1,904 1,297 0,322 0,954 0,772 1,127 1,957 0,933 1,792 1,241 1,356 0,747 1,708 0,98 0,897 0,446 0,803 0,681 0,512 0,30 1,157 0,594 1,398 1,768 1,717 0,586 1,704 0,27 1,598 1,499 0,508 1,272 0,670 0,577 1,171 1,701 1,560 1,441 0,734 0,419 0,283 0,987 1,898 1,219 1,778 1,103 1,249 1,301 1,86 0,816 0,783 0,290 0,990 1,547 1,151 1,508 1,90 0,795 1,804 0,489 0,766 1,538 0,725 1,970 0,125 1,189 1,958 1,990 0,500 1,715 0,855 0,987 1,551 0,794 0,583 0,598 0,594 0,109 1,509 0,826 0,696 1,884 0,690 1,993 1,993 1,459 1,401 1,204 1,297 1,64 1,100 1,985 1,340 1,985 0,60 1,251 0,510 0,152 1,579 0,523 1,458 1,840 1,894 1,435 1,736 0,545 0,214 1,660 1,559 0,102 1,462 1,103 1,404 1,719 1,518 1,853 1,638 0,646 1,205 0,561 0,34 1,684 0,929 1,974 0,290 1,381 0,598 0,35 1,615 1,400 1,256 1,469 0,69 1,323 1,133 0,660 1,616 1,904 0,877 0,674 0,810 1,250 0,224 1,538 0,654 1,721 0,470 0,353 0,300 1,558 0,261 1,109 0,128 1,298 1,619 1,114 1,226 0,333 0,750 0,706 0,700 0,526 1,281 1,713 1,359 1,476 1,689 1,906 0,990 1,200 1,281 0,145 1,604 0,788 0,68 1,331 0,15 1,610 0,143 1,744 1,652 1,607 1,953 0,494 0,711 1,905 0,496 1,656 1,942 1,347 1,830 0,873 1,282 1,845 1,390 0,130 1,629 0,395 0,663 0,771 1,34 1,830 0,9 0,944 1,531 1,41 0,76 0,743 1,784 0,655 0,538 0,831 1,905 1,310 0,438 0,723 1,476 0,414 1,724 1,999 1,720 1,950 0,799 1,777 0,47 1,696 1,435 0,874 0,971 1,29 1,692 0,901 0,99 0,849 0,191 1,571 1,131 1,750 0,288 1,815 1,35 0,621 1,135 1,834 0,751 1,161 1,278 0,988 1,840 1,554 0,914 0,815 1,504 0,874 0,369 1,71 1,989 1,725 0,625 1,189 0,728 1,996 0,338 1,829 1,910 1,562 1,383 0,914 0,849 1,108 1,265 0,60 1,508 1,444 0,758 1,228 0,573 0,465 1,917 1,349 1,265 0,575 1,98 1,641 1,780 1,763 1,639 1,565 1,667 0,783 0,576 1,311 1,424 0,464 0,700 0,895 0,977 1,227 0,884 1,589 1,799 1,1 1,56 1,616 1,144 1,77 1,386 1,695 1,13 0,442 1,77 0,292 1,694 0,708 1,569 0,946 1,854 1,188 1,599 1,828 1,529 1,136 0,698 0,719 0,177 0,731 1,540 1,376 1,758 1,131 0,148 1,722 1,687 0,744 1,198 0,687 0,31 0,340 0,544 0,257 0,451 1,530 1,482 1,796 1,481 1,997 0,185 0,988 0,670 1,512 0,825 0,180 1,269 0,296 1,962 0,258 0,519 0,890 1,707 1,602 1,744 1,180 1,763 0,207 0,997 0,192 1,315 1,421 0,683 1,952 1,18 1,688 1,608 0,664 0,428 0,140 0,348 0,403 1,682 1,708 0,652 0,242 0,567 1,520 0,546 0,19 0,43 0,483 0,666 1,834 0,264 0,883 0,724 1,581 0,835 0,581 0,999 0,675 0,472 1,705 1,4 0,654 0,98 0,654 1,232 1,964 1,716 1,776 0,505 0,364 1,176 0,281 0,418 0,87 1,124 0,428 1,945 1,871 1,723 1,521 0,839 1,652 0,707 0,722 1,463 0,835 0,232 1,3 1,626 1,102 0,534 0,841 1,893 1,94 1,903 0,97 0,390 1,317 1,248 0,674 0,601 1,218 1,994 1,416 1,267 1,294 1,572 0,774 1,117 0,105 1,177 0,379 0,263 0,853 1,340 0,288 1,819 0,378 1,55 1,476 0,616 0,55 1,48 0,275 1,835 1,530 0,106 1,276 1,324 0,981 1,658 0,199 1,283 0,723 1,646 1,618 0,703 1,586 0,138 0,476 1,832 0,113 1,343 0,781 1,736 0,664 1,360 1,741 1,891 1,276 1,711 1,713 0,569 0,467 1,50 1,537 0,436 0,42 1,139 0,200 0,922 0,347 1,139 1,336 1,626 0,420 1,530 0,97 1,279 1,407 0,488 0,751 0,968 1,603 1,129 0,789 0,267 1,155 1,43 1,818 0,594 1,80 1,134 1,329 0,81 0,803 1,8 1,112 0,426 0,837 0,456 0,306 1,349 0,49 0,859 1,768 1,417 1,711 1,618 1,13 0,349 1,826 1,530 0,879 0,706 1,179 0,857 0,682 1,160 0,901 0,20 1,626 1,796 1,925 0,892 0,14 1,22 0,669 0,730 0,527 1,725 0,337 0,118 0,897 0,75 1,596 0,84 0,591 1,806 0,381 1,731 0,259 0,622 1,524 1,70 0,965 1,0 1,765 0,625 1,707 1,687 0,249 0,386 0,754 0,678 1,145 1,483 1,958 0,908 0,185 1,99 0,254 0,959 0,908 1,110 1,689 0,228 1,968 0,653 1,486 1,478 0,39 1,886 1,5 0,460 1,989 1,482 0,620 1,317 1,459 0,209 0,311 1,299 1,337 1,952 1,290 1,124 1,411 0,736 0,736 1,96 0,487 0,921 1,321 0,989 1,945 1,671 0,785 1,89 1,964 1,672 0,876 0,457 0,845 1,44 1,327 0,964 1,648 1,144 0,881 0,419 0,221 0,376 0,523 0,192 1,129 1,940 0,551 1,48 0,261 0,52 1,124 0,975 1,523 0,125 1,453 0,993 1,510 0,462 0,737 1,528 0,422 1,645 0,261 1,478 1,273 1,645 1,702 0,767 1,135 1,383 0,752 0,94 0,13 1,379 0,93 0,984 1,941 1,233 1,954 1,347 0,684 0,938 1,143 1,893 1,250 1,471 0,149 1,346 0,96 1,398 0,698 1,341 0,343 1,948 1,499 0,469 0,366 0,775 1,656 1,150 0,680 0,382 0,55 0,518 1,951 1,88 0,896 1,339 1,854 1,284 0,808 1,589 1,408 0,747 0,67 0,17 1,660 1,585 1,380 0,729 1,490 1,507 0,803 0,939 0,995 1,398 1,858 1,13 0,19 1,859 0,673 0,58 0,848 1,844 1,159 0,334 1,250 1,121 1,706 0,199 1,812 1,106 1,211 1,744 0,669 0,171 1,551 0,951 0,164 0,632 1,83 1,939 1,720 1,309 1,241 0,752 1,429 0,677 0,850 0,154 1,135 1,140 0,729 0,106 1,134 0,958 0,504 1,933 0,685 1,757 1,646 1,756 0,804 0,1 1,208 1,304 1,292 0,358 0,3 0,139 0,760 0,408 1,152 1,677 1,459 1,71 1,462 0,442 0,597 0,652 0,819 1,264 0,36 0,365 0,297 0,67 0,694 0,796 0,847 0,152 1,882 0,199 0,851 1,49 0,101 1,804 0,88 0,318 0,683 0,396 0,230 0,327 0,106 0,131 1,299 1,934 0,51 0,728 1,532 0,650 0,789 0,112 0,241 0,134 0,263 0,716 0,932 0,1 1,842 0,327 0,248 0,376 1,27 0,234 1,604 0,277 0,470 1,146 1,595 1,801 1,11 0,501 0,524 0,179 0,192 0,829 1,363 1,598 1,569 0,656 1,292 0,59 0,494 0,191 1,436 1,552 0,688 1,111 0,382 0,217 0,287 1,375 0,533 1,258 0,958 1,915 1,198 0,291 0,475 0,219 0,599 0,189 1,403 1,114 1,633 0,247 0,360 0,601 1,44 1,619 0,865 0,381 1,318 1,663 0,158 1,185 1,119 1,452 1,785 1,113 0,628 1,379 0,147 1,86 1,518 1,95 1,323 0,72 1,752 1,835 1,253 0,660 1,257 0,478 1,403 1,999 1,749 1,681 1,688 1,285 1,391 0,231 0,606 1,914 1,682 0,723 1,794 1,51 0,940 1,924 1,196 1,599 0,525 0,841 0,256 0,777 0,841 1,724 0,949 0,5 1,419 1,959 0,708 0,899 0,183 1,618 1,64 1,626 1,438 0,644 1,364 0,806 0,792 1,401 1,182 1,332 1,377 1,886 1,634 1,418 1,570 1,889 0,91 1,284 1,891 0,190 0,733 1,222 0,731 1,988 1,172 1,975 0,281 1,105 1,8 1,667 1,621 0,590 0,261 0,190 1,738 0,252 1,917 0,524 0,955 0,195 1,311 1,933 1,428 1,963 1,477 0,759 0,95 0,625 1,335 1,183 1,468 1,335 1,740 0,573 1,736 0,345 0,328 0,111 0,555 1,35 0,894 1,691 1,882 1,890 1,227 1,541 1,6 1,203 1,779 0,75 1,384 1,131 0,899 1,72 0,577 1,268 1,670 1,785 0,894 0,928 1,508 0,553 1,358 1,367 0,278 1,70 1,439 0,311 1,10 0,560 1,770 1,414 0,535 0,350 0,1 1,308 1,556 0,360 1,201 1,991 1,579 1,284 1,530 0,260 0,620 1,258 1,511 1,236 1,687 0,149 0,195 0,767 0,570 0,830 1,952 0,248 0,387 1,144 0,810 1,859 1,30 0,662 0,512 0,860 1,463 1,80 1,467 1,528 0,915 0,560 0,257 0,236 0,749 0,371 1,88 1,645 1,301 0,601 0,882 0,466 1,707 1,941 1,908 1,355 1,111 1,758 0,849 1,554 1,77 0,168 1,67 1,509 0,506 0,641 1,418 0,500 0,762 0,133 0,615 1,209 1,571 0,254 0,465 1,161 1,896 1,374 0,639 1,599 1,62 1,870 1,915 1,190 1,393 0,630 0,980 0,260 1,359 1,572 1,320 1,253 0,903 0,115 1,707 0,364 1,801 1,587 0,914 1,938 0,308 1,608 1,437 0,561 1,582 0,519 1,295 0,572 0,969 0,647 0,857 1,15 0,504 0,167 0,282 0,901 0,215 0,361 1,101 0,699 1,414 1,945 0,960 0,195 0,838 1,513 1,277 1,755 0,646 1,745 1,29 1,606 1,356 1,659 1,94 1,854 1,678 0,815 0,447 1,880 1,452 1,824 0,528 0,586 1,957 1,291 1,846 1,170 1,320 1,885 1,489 1,11 1,414 0,610 0,386 1,377 1,80 1,570 0,411 0,702 0,899 0,361 1,56 0,187 1,144 0,518 1,247 0,669 0,201 0,478 1,341 1,568 0,152 1,217 1,789 0,411 0,106 0,975 1,43 0,953 1,235 0,816 0,442 0,794 1,227 0,289 0,731 1,644 1,89 0,691 1,245 0,566 1,898 0,915 1,990 1,346 1,65 0,93 1,201 0,689 0,543 1,674 1,658 1,968 1,658 1,64 1,851 0,910 0,789 1,393 1,363 1,85 1,870 1,182 0,876 0,475 0,104 1,692 1,562 0,188 1,951 1,464 1,824 1,518 1,995 0,331 1,939 1,565 1,821 1,291 1,407 1,579 1,837 0,269 1,928 1,293 0,743 1,771 0,754 0,220 0,687 0,851 0,209 1,980 1,69 0,163 1,18 1,909 0,942 0,104 0,222 0,402 0,232 0,528 1,417 0,353 0,887 0,349 1,809 0,229 0,590 0,636 0,563 0,832 0,357 1,668 0,383 0,79 1,827 0,162 0,993 0,696 0,790 1,637 0,370 1,252 1,936 1,138 1,943 1,941 1,353 0,395 0,612 1,168 0,678 1,778 1,705 1,212 1,859 0,449 1,569 1,861 1,947 0,51 1,828 0,194 1,948 0,753 0,837 0,292 1,503 1,842 0,815 0,910 1,289 0,117 0,914 1,618 1,906 1,846 0,835 1,381 1,387 1,734 1,476 1,480 0,290 1,286 1,572 1,646 1,279 1,710 0,651 0,409 1,5 0,544 1,173 0,744 1,91 1,843 1,428 1,166 0,480 0,615 1,340 0,341 0,917 0,946 1,797 1,530 1,394 1,519 1,323 0,200 1,952 0,16 1,903 1,871 1,888 1,903 0,379 0,913 0,542 1,314 1,855 1,361 0,272 1,918 1,225 1,779 0,689 1,732 1,170 1,558 0,634 0,245 0,637 1,297 1,674 0,537 1,517 1,597 1,410 1,573 1,687 1,507 1,976 1,534 0,853 0,552 0,91 0,207 1,395 0,272 0,441 1,792 1,397 1,592 1,244 1,57 1,295 0,232 1,422 1,512 1,833 1,346 0,633 1,10 0,990 0,283 1,506 1,250 1,685 0,193 0,552 0,801 0,618 1,754 1,579 1,185 1,927 1,65 1,701 0,787 0,966 0,41 1,627 0,996 1,934 1,75 0,580 0,770 1,455 0,469 1,665 1,786 0,296 1,467 1,899 0,367 0,662 1,190 0,728 1,1 0,489 1,637 1,287 0,426 1,654 0,262 1,154 0,409 0,939 1,456 0,367 0,91 1,534 1,69 1,439 1,83 0,23 1,855 0,35 0,107 1,418 0,71 0,502 0,729 1,560 0,270 0,973 0,432 0,405 0,996 1,793 1,230 0,836 1,646 1,362 1,177 1,870 1,408 0,920 1,553 1,431 1,879 1,333 0,43 0,446 0,584 0,166 0,15 1,448 1,792 1,301 1,221 1,837 1,275 0,781 1,408 1,843 0,240 1,945 0,930 0,865 1,845 0,360 0,899 1,106 1,546 0,123 1,803 0,406 0,327 1,543 1,998 1,946 0,242 0,947 0,205 0,79 1,473 1,73 0,319 0,93 1,240 0,376 1,354 1,765 0,338 1,365 1,474 0,931 0,320 1,698 1,990 1,595 1,826 1,110 1,761 1,580 1,6 0,806 1,95 1,293 1,87 0,775 1,480 0,392 0,346 1,271 1,480 1,83 1,993 1,189 0,813 1,686 1,229 1,561 1,557 0,796 1,587 0,843 1,131 1,268 0,684 0,828 1,709 1,528 1,119 0,679 0,235 0,686 1,142 1,2 1,773 1,582 1,378 0,374 1,259 0,851 1,495 1,636 0,558 0,434 0,205 0,379 0,826 1,751 1,274 1,270 0,789 1,498 1,587 0,837 1,329 1,334 0,301 1,899 0,446 0,101 0,200 0,899 1,430 1,752 1,428 1,60 0,745 0,648 1,440 1,448 0,525 1,28 1,785 1,230 1,310 1,773 0,393 1,9 1,996 0,395 1,173 1,538 0,534 0,758 0,702 1,478 0,810 0,799 0,742 1,978 1,404 0,400 1,941 1,978 0,984 1,718 1,184 1,41 1,555 0,528 0,632 0,759 1,176 1,314 1,955 1,186 0,334 0,210 0,750 0,103 1,888 1,66 1,622 1,700 0,358 0,156 0,127 0,999 1,725 1,849 1,478 1,368 1,544 1,957 0,351 1,787 1,765 0,87 1,539 1,141 0,83 1,765 0,148 1,685 0,639 0,649 1,41 0,862 1,987 1,390 1,359 0,951 1,814 1,663 1,738 0,167 0,415 0,519 0,983 0,87 1,99 1,766 1,488 0,931 1,724 0,480 1,551 0,502 0,455 0,984 0,131 0,875 1,970 0,569 0,227 1,520 1,388 1,715 1,9 1,467 0,231 0,563 0,832 0,549 0,332 0,410 1,661 1,606 1,755 1,679 0,981 1,65 1,429 0,75 1,117 1,33 0,854 0,346 0,273 0,647 0,569 1,561 0,280 1,573 0,46 1,272 1,313 0,440 1,713 0,768 0,177 1,30 0,704 0,900 1,143 0,342 1,33 1,215 0,483 0,663 0,846 0,664 0,370 0,154 0,953 0,615 0,472 1,220 0,907 1,4 1,167 0,354 1,185 1,195 0,433 1,260 1,715 0,83 1,896 1,149 0,589 0,440 0,799 1,174 1,446 1,685 1,254 0,94 0,808 0,647 0,288 0,378 1,953 0,858 1,643 0,992 1,481 0,217 0,865 0,953 1,127 0,615 1,939 0,410 1,853 0,80 0,424 0,538 1,977 1,259 1,875 0,64 1,429 0,534 1,96 0,262 0,133 0,711 1,53 0,823 0,621 1,801 1,115 0,72 0,554 0,411 0,488 0,815 1,861 1,188 0,949 1,583 0,972 0,470 1,239 0,44 0,325 0,958 0,718 0,593 0,204 1,527 1,151 1,218 1,343 0,819 0,756 0,990 1,767 0,170 0,691 1,999 1,346 1,468 0,553 0,404 0,486 1,38 0,338 0,364 1,859 0,275 1,334 0,75 1,993 0,720 0,334 0,568 0,312 0,717 0,952 0,897 0,568 1,336 1,581 1,650 0,53 0,600 1,422 0,342 1,562 0,415 0,632 1,585 1,338 0,604 0,981 1,566 0,63 1,607 0,91 1,566 0,538 0,288 0,484 1,227 0,780 0,19 0,440 1,44 0,550 1,480 1,301 0,751 0,369 0,897 1,601 0,34 0,257 1,729 1,188 1,944 0,954 1,509 1,754 0,98 0,711 0,670 0,558 1,885 1,437 1,912 1,175 0,677 0,762 1,493 0,165 0,875 1,357 1,985 1,669 0,707 0,125 1,722 1,436 0,136 1,326 1,984 1,417 1,547 0,212 0,806 0,726 1,720 0,88 0,569 0,493 0,977 1,959 1,565 0,925 1,326 0,881 0,559 1,900 0,416 1,264 1,723 0,575 1,255 0,162 1,494 0,315 0,744 0,801 1,943 0,543 1,503 0,605 1,775 0,123 1,497 0,629 1,509 1,191 0,496 1,372 0,872 0,404 0,300 0,363 0,384 0,602 0,799 1,785 0,293 0,263 1,883 0,917 1,460 1,715 0,924 0,648 1,172 1,116 1,274 1,379 1,66 1,218 0,128 0,607 0,780 1,346 1,598 1,898 0,411 0,976 0,799 1,895 1,424 0,666 1,457 1,307 0,50 1,878 1,994 1,992 0,39 0,286 0,536 1,745 1,785 0,38 1,248 0,273 0,857 0,867 0,846 0,899 1,281 1,462 1,556 0,455 0,173 1,359 1,18 0,790 1,522 1,569 1,504 0,118 0,651 0,158 1,191 0,299 0,281 1,82 0,707 0,311 1,92 1,502 0,186 0,10 1,618 1,899 1,344 0,679 0,638 0,294 0,208 0,987 0,503 1,209 1,879 0,5 0,763 1,152 1,422 1,276 1,433 0,636 0,738 1,909 1,65 1,164 0,978 0,406 0,659 0,952 0,545 1,469 0,202 0,348 1,266 0,946 1,488 0,931 1,315 0,483 1,891 1,474 0,507 0,12 0,600 1,74 1,17 0,963 0,89 0,185 1,249 1,728 0,578 0,766 0,97 1,98 0,575 0,892 0,958 1,648 0,522 0,922 0,380 1,574 0,748 0,715 1,837 0,944 0,462 1,195 0,674 0,374 0,496 0,883 1,961 1,88 1,543 0,378 1,83 0,139 1,948 1,652 0,294 1,940 1,429 1,50 1,931 1,688 0,210 0,111 1,828 0,211 0,141 0,423 1,522 0,463 0,572 1,509 1,765 0,844 0,47 0,266 1,866 1,584 0,232 1,331 0,138 1,646 1,127 1,134 1,364 1,380 1,981 1,401 0,22 1,975 0,410 0,234 1,882 0,525 0,836 1,523 1,736 0,646 0,281 1,980 0,926 1,961 1,2 0,344 1,379 0,442 1,768 0,470 1,101 1,917 1,971 0,816 1,222 0,565 0,94 0,83 0,98 1,581 1,321 1,926 0,472 1,947 0,745 0,96 1,755 0,354 1,948 1,161 0,860 0,367 1,334 1,641 1,19 0,154 0,274 1,508 1,808 1,956 1,609 1,925 0,396 1,956 0,808 0,614 0,927 1,965 0,324 1,928 1,19 0,571 1,44 1,572 1,318 0,765 0,48 0,882 1,643 1,288 0,624 0,142 1,648 1,807 0,762 0,564 1,336 1,869 1,242 0,483 1,107 0,355 1,533 1,393 0,376 1,555 1,536 1,954 0,871 0,230 0,321 0,971 1,386 0,946 1,544 0,863 1,438 0,793 0,827 0,582 1,545 1,596 0,574 1,605 0,340 1,767 0,730 0,17 0,96 0,583 1,28 1,963 1,217 0,803 1,998 1,568 0,411 0,519 0,4 1,217 0,221 0,935 1,837 1,408 1,646 0,998 1,185 1,460 1,628 0,230 1,427 0,108 0,188 0,964 0,413 0,541 1,763 1,228 1,892 1,175 0,83 0,767 0,231 1,929 0,946 0,465 0,715 1,711 0,792 1,827 0,47 1,668 1,266 0,986 0,335 1,705 1,794 1,105 0,73 1,581 1,764 1,929 1,229 0,753 0,321 1,737 1,79 1,722 0,463 0,49 1,577 1,135 1,123 1,824 1,378 1,413 1,727 0,949 0,58 0,71 0,697 0,824 0,33 1,620 1,155 1,758 0,496 1,665 0,239 0,78 1,153 0,855 0,259 0,161 1,520 0,989 1,111 1,795 0,156 1,937 1,213 0,304 1,187 0,947 1,23 1,453 0,8 1,285 1,907 1,820 1,997 0,375 1,3 1,458 0,587 1,116 0,876 1,282 1,399 1,939 1,430 1,345 0,961 0,589 1,963 1,252 0,227 0,488 0,646 0,827 0,803 0,446 1,228 1,194 1,175 1,72 1,194 0,618 1,124 0,922 1,974 1,466 1,45 1,821 0,186 0,461 0,530 1,572 0,665 0,521 0,360 0,63 0,97 0,230 1,436 0,798 1,244 0,27 0,62 0,570 1,349 1,852 0,928 1,935 0,424 1,127 0,36 0,981 1,537 1,424 0,566 1,734 0,162 0,321 1,409 1,783 0,168 0,920 1,30 1,630 1,750 1,194 0,540 0,531 0,489 0,947 0,665 0,906 1,954 1,516 0,651 1,868 0,578 1,608 0,39 0,247 1,588 1,533 1,42 1,280 0,419 1,738 0,339 0,125 0,830 0,603 0,197 1,448 1,907 1,739 1,35 0,587 1,213 1,159 1,69 1,75 1,571 1,297 1,742 1,594 1,648 1,711 1,646 1,820 1,254 0,139 1,477 0,101 0,676 1,905 1,888 0,395 1,32 1,338 0,292 0,904 0,880 1,934 1,961 1,399 0,774 0,378 0,959 1,325 1,537 0,34 1,329 0,138 1,763 1,108 1,658 1,310 0,402 0,0 1,215 0,734 0,790 1,176 1,419 0,331 0,550 0,138 0,118 0,70 0,743 0,617 0,257 0,873 1,345 0,661 1,863 0,555 1,679 0,809 1,796 1,5 0,934 1,520 0,64 1,457 0,242 1,630 0,502 1,416 1,797 1,26 0,279 1,562 1,122 1,834 1,565 1,764 1,556 0,28 0,787 1,554 0,695 1,659 0,946 1,73 1,352 0,881 0,893 0,439 1,141 0,666 0,528 1,814 1,932 1,292 0,982 0,498 1,995 1,588 1,663 0,657 0,568 0,231 1,291 0,763 0,832 0,944 0,81 1,70 0,562 0,692 0,739 0,18 1,928 1,960 1,186 0,675 0,128 1,519 1,604 1,774 0,482 0,606 0,521 0,155 0,498 1,184 1,441 0,526 0,859 1,307 0,901 0,376 1,487 0,783 0,192 1,171 0,522 1,749 0,848 1,179 1,389 1,442 1,878 1,432 1,212 0,816 1,11 0,675 0,398 0,528 0,740 0,936 0,163 1,837 1,361 0,207 1,838 1,742 0,609 1,640 1,969 0,610 0,62 1,700 1,148 1,869 1,412 0,136 0,237 0,668 1,519 0,499 1,736 0,422 1,935 1,661 1,749 0,436 0,519 1,80 1,158 0,216 1,152 0,429 0,911 0,780 0,915 0,509 1,423 1,616 1,205 1,820 1,68 1,886 1,372 1,536 1,847 1,727 0,979 1,617 0,459 1,45 1,463 1,180 0,545 1,740 0,243 1,45 1,989 1,116 0,652 0,964 0,839 0,897 0,953 1,23 0,530 0,485 1,118 1,866 1,177 0,667 1,912 1,570 0,388 1,819 1,111 0,378 1,145 0,979 1,254 0,893 0,426 0,261 0,926 0,620 0,67 1,584 1,465 0,113 0,632 1,980 1,25 0,939 1,646 0,640 0,387 1,848 0,241 0,775 0,919 1,626 0,946 1,297 0,119 0,694 1,499 0,649 0,217 1,69 1,296 0,69 1,439 0,831 0,304 0,5 0,18 1,426 0,395 0,989 1,717 1,719 0,476 0,503 0,605 0,197 1,972 0,373 0,353 1,276 1,994 0,50 0,970 0,130 0,278 0,367 0,201 0,760 1,770 0,840 0,897 0,463 1,158 0,93 1,676 1,466 0,172 1,964 1,10 1,874 0,778 1,422 1,484 1,101 0,199 1,119 1,870 0,630 1,63 0,391 1,169 0,0 0,833 1,622 0,652 1,57 0,120 1,320 0,263 0,899 1,228 1,358 0,208 0,308 0,600 1,51 0,484 0,570 0,13 1,239 0,690 0,579 0,243 0,263 0,860 1,539 1,970 0,139 0,368 0,247 0,476 0,323 1,806 1,259 1,732 1,676 1,76 0,23 1,9 0,737 0,116 0,487 1,122 0,99 0,543 0,159 1,0 0,493 1,468 1,385 1,468 1,355 0,762 0,373 1,274 1,979 1,299 1,18 0,795 0,512 0,836 0,619 0,399 0,495 0,49 0,498 1,866 0,831 1,476 0,902 1,494 1,642 1,140 1,618 1,367 0,528 0,32 0,846 0,22 0,914 0,878 1,817 0,446 0,257 1,48 0,683 1,951 1,577 1,372 1,455 0,797 0,433 0,36 0,380 1,385 0,228 0,488 0,18 0,21 1,126 0,298 0,235 0,728 1,261 0,858 0,967 0,292 1,318 0,60 1,487 0,139 0,597 1,73 0,841 0,92 1,406 1,761 1,79 0,800 0,938 1,16 0,942 0,928 1,383 1,314 1,886 0,649 0,886 1,424 0,707 0,812 0,583 1,526 1,327 0,487 0,199 0,773 0,372 0,332 0,102 1,325 0,839 0,66 0,549 0,747 1,968 0,583 1,19 1,143 0,188 1,24 1,573 0,379 1,128 1,544 1,555 1,571 1,455 0,266 1,679 1,160 1,10 0,673 0,61 0,335 0,239 0,94 1,100 1,540 0,655 0,408 0,199 1,514 0,927 1,931 1,989 0,828 1,910 1,9 0,627 0,289 1,431 0,565 0,40 1,46 0,750 0,86 1,719 1,49 1,85 1,421 0,887 0,187 0,875 0,541 0,329 0,87 0,987 1,553 0,224 1,139 0,726 1,406 0,966 1,52 1,870 1,656 0,987 0,407 0,198 1,418 1,67 1,468 1,998 0,877 0,238 0,508 0,545 1,948 0,309 0,980 1,173 0,751 0,278 1,911 0,325 1,987 1,714 1,608 1,702 0,788 0,570 1,987 1,693 1,967 1,506 1,464 1,695 0,54 1,771 0,126 1,444 0,984 0,196 1,61 1,98 1,218 1,58 1,910 0,288 0,501 1,253 1,321 0,75 1,134 0,288 1,111 1,601 0,459 1,521 0,397 0,218 1,45 0,941 1,339 1,669 0,798 1,363 1,722 0,887 0,279 0,952 0,722 1,317 0,385 1,210 0,892 1,648 0,92 0,923 1,81 1,259 1,316 0,946 0,558 0,509 0,941 0,986 0,422 1,378 0,600 1,621 0,654 0,362 1,739 1,756 1,867 0,762 1,664 1,105 0,124 1,720 1,136 0,321 1,367 1,440 0,249 1,435 1,169 1,265 0,245 1,682 0,142 0,710 0,0 1,31 1,834 0,796 0,644 1,898 0,304 0,586 0,605 0,553 1,129 1,77 1,807 1,58 0,132 0,1 0,398 1,544 0,750 1,950 0,415 1,447 1,69 0,634 0,5 1,869 1,467 1,661 1,71 1,112 1,972 1,635 0,650 1,300 1,991 0,815 0,567 1,926 0,998 0,401 0,755 1,234 1,837 1,93 0,198 1,678 1,247 1,106 0,994 0,879 0,702 0,131 0,894 0,890 1,426 0,189 0,822 0,251 1,293 1,437 0,261 1,852 0,645 0,602 0,461 1,495 0,581 0,18 0,613 0,979 0,628 0,308 0,506 1,270 0,8 0,184 0,317 1,625 0,666 1,378 0,491 0,305 0,430 1,791 1,260 1,919 1,140 1,399 0,957 0,251 0,569 1,188 1,669 0,677 1,628 1,253 1,26 1,323 1,422 1,418 0,972 0,758 1,727 0,102 0,468 1,842 0,674 1,921 0,393 0,5 1,156 0,524 0,835 0,374 1,657 1,808 1,499 0,304 0,171 1,590 0,158 0,113 0,334 0,690 1,513 1,461 0,223 1,754 1,491 0,665 0,884 0,380 0,48 0,312 1,508 1,495 0,908 0,791 1,308 1,449 0,431 0,981 1,2 0,508 1,740 1,331 1,505 0,61 0,794 0,713 1,450 0,855 1,188 0,339 0,270 0,23 1,716 1,840 0,203 0,759 1,979 0,631 0,551 1,961 0,809 1,635 1,1 1,998 0,927 1,884 0,369 1,915 0,967 1,423 0,698 1,700 0,73 0,734 0,74 1,278 0,450 1,393 1,198 1,759 0,313 0,476 1,919 1,745 1,594 0,728 0,418 0,973 0,542 1,634 0,777 0,460 1,934 1,646 0,780 1,246 1,369 0,513 0,831 1,284 1,515 1,311 0,89 0,285 0,352 1,128 1,81 1,184 0,661 1,246 1,631 0,153 1,514 1,252 0,567 1,578 0,277 1,671 1,31 1,65 1,396 0,428 0,604 0,191 1,688 1,892 0,870 0,250 0,734 0,292 1,311 0,517 0,225 1,111 0,469 1,615 0,445 0,698 1,539 1,365 1,532 0,609 1,555 1,963 1,591 1,739 1,380 1,527 0,650 1,286 1,676 0,111 0,242 0,789 0,708 0,456 1,772 0,592 1,495 0,810 1,508 0,128 0,920 0,831 0,118 0,897 0,289 0,202 0,422 1,288 0,133 0,426 1,291 0,172 0,475 1,788 1,344 0,79 0,923 1,170 1,33 0,2 0,833 1,352 1,821 0,745 0,549 0,997 0,236 1,784 1,530 0,565 1,426 0,408 0,956 0,795 0,196 1,369 1,373 1,374 1,68 1,817 1,892 1,160 1,350 1,483 1,293 1,865 0,518 1,588 1,617 0,605 0,552 1,186 0,295 1,321 0,329 0,726 1,654 1,505 1,915 0,853 1,429 0,963 0,31 0,184 0,83 0,99 0,838 1,705 0,860 1,516 0,243 1,844 1,296 1,930 0,971 1,261 0,86 1,244 1,264 0,591 1,308 0,981 0,19 1,877 0,819 0,464 1,217 1,108 1,847 1,939 0,569 0,154 0,745 0,394 1,518 1,799 0,787 1,455 1,645 1,705 0,901 1,236 1,456 0,155 0,294 1,345 0,121 1,671 0,748 0,101 0,715 0,376 1,168 1,479 0,822 0,47 0,388 1,860 0,875 1,915 0,901 0,183 1,183 0,223 0,57 0,883 1,665 1,536 0,498 1,190 0,862 0,629 1,577 1,854 0,194 0,677 0,994 1,672 1,219 1,321 1,21 1,472 0,887 0,563 1,691 1,128 1,623 1,126 1,769 1,391 0,446 1,649 1,232 0,879 1,132 0,83 1,860 0,139 1,471 0,630 1,620 0,902 1,200 1,836 0,651 0,769 1,713 1,879 0,161 0,501 0,704 1,273 0,240 0,710 0,19 0,486 1,94 0,690 0,482 0,847 0,215 0,121 0,819 0,104 1,447 1,848 1,276 1,432 0,21 1,227 0,970 1,442 1,273 0,267 0,28 1,30 1,874 0,799 0,535 0,148 1,201 0,383 1,649 0,340 0,854 0,764 0,877 0,611 1,280 1,27 1,811 0,118 0,411 1,683 1,867 0,574 0,593 1,393 1,820 0,260 1,321 1,386 0,175 0,713 1,997 0,736 1,294 1,550 1,603 1,521 0,28 1,7 1,943 0,486 1,55 1,154 1,734 1,551 0,592 1,546 1,806 1,929 0,686 1,45 1,970 0,739 0,642 0,671 0,240 1,11 1,334 1,696 0,973 1,2 0,12 0,516 1,7 1,104 0,322 0,829 0,260 0,795 1,237 0,914 0,623 1,844 0,919 0,958 0,645 0,692 1,763 1,217 0,422 1,328 0,539 0,250 1,992 1,347 0,744 0,780 1,416 1,814 1,853 1,118 1,791 1,334 1,982 0,194 0,316 1,643 1,463 0,37 1,718 1,164 1,737 0,474 0,654 1,209 0,681 0,585 0,901 1,127 0,815 1,705 0,177 0,513 1,169 0,607 0,350 1,680 0,138 0,562 0,943 0,437 1,363 1,736 1,923 0,616 0,320 1,541 0,313 0,185 0,302 0,550 1,554 1,726 0,508 1,189 0,692 1,190 1,315 1,315 0,405 0,550 0,708 1,814 0,904 1,954 0,105 1,956 1,122 0,858 0,755 0,847 0,730 1,475 0,467 1,474 0,950 1,797 0,296 0,861 0,170 0,377 0,644 1,642 0,627 0,89 0,340 0,977 1,897 1,458 0,575 0,95 1,329 0,50 0,701 0,959 1,719 0,836 1,140 0,614 0,605 1,99 0,628 0,178 1,634 0,135 0,5 0,96 0,604 0,428 1,744 1,866 0,985 1,440 0,536 1,915 0,416 0,393 0,768 1,788 1,886 1,656 1,333 1,305 0,179 0,361 0,983 1,873 0,533 0,495 1,772 0,997 1,29 0,539 0,819 0,714 1,22 0,642 0,807 1,127 1,274 0,111 1,338 1,645 0,178 1,796 1,172 0,214 0,671 1,539 0,585 1,708 0,906 1,251 0,451 0,197 1,539 0,745 0,227 1,619 0,939 0,555 1,180 1,365 1,785 1,353 0,462 1,744 1,843 0,500 0,553 0,580 1,954 0,465 0,101 0,975 0,27 0,692 1,982 1,688 0,27 1,981 1,537 0,692 1,465 0,639 1,276 0,508 0,394 1,588 0,335 0,795 0,584 0,135 1,904 1,525 0,549 1,553 1,21 0,588 1,145 1,194 1,441 0,601 1,582 0,214 1,45 1,820 0,550 0,737 0,62 1,485 0,918 1,588 0,757 1,902 1,576 1,302 0,564 1,348 0,763 0,856 0,441 1,251 1,45 0,429 0,407 0,909 1,520 1,860 1,567 0,948 1,840 1,8 0,503 1,819 0,225 1,47 0,275 0,649 1,758 0,338 0,647 1,612 1,623 0,884 0,706 1,603 1,271 0,5 1,147 0,991 0,447 0,920 0,618 1,853 1,660 1,481 0,322 0,754 0,645 0,295 0,171 1,671 0,655 0,524 0,484 1,814 1,710 0,841 0,822 1,257 1,147 1,332 0,587 0,324 1,737 0,252 0,612 0,579 1,5 0,93 0,59 1,931 1,751 1,378 0,43 1,85 0,279 0,664 0,681 1,293 0,569 1,586 1,980 0,118 0,662 1,427 1,530 0,515 1,343 0,697 0,162 0,185 0,211 1,452 1,767 1,634 0,266 0,444 0,858 1,157 1,16 1,438 0,999 0,227 1,591 0,650 1,602 1,56 0,236 0,73 0,439 0,850 1,734 0,427 0,459 1,828 1,591 0,325 1,535 0,835 0,131 1,453 0,809 1,740 1,24 0,729 1,966 1,293 0,344 1,668 1,890 1,861 1,167 1,402 0,317 1,694 1,46 1,889 0,219 0,133 1,631 0,605 1,988 0,47 0,389 1,254 0,776 1,127 0,874 0,371 0,355 1,871 0,466 0,707 1,880 0,307 1,862 1,387 1,209 1,899 1,400 1,905 0,23 1,448 1,604 0,744 0,945 0,239 1,489 0,843 0,456 1,864 0,9 1,677 0,620 0,57 0,231 1,575 1,471 1,201 0,992 0,275 0,803 1,216 0,826 1,253 1,955 0,435 0,563 0,49 0,663 0,645 1,687 1,727 1,272 1,913 1,609 0,204 1,980 1,688 1,16 0,722 1,691 0,343 0,459 0,573 1,46 1,89 1,977 0,598 0,377 1,168 0,663 1,388 0,107 0,560 0,817 1,706 1,725 1,197 0,377 0,625 1,452 1,350 1,598 1,330 0,387 0,896 0,638 1,325 1,954 0,154 0,19 1,70 1,331 1,182 1,639 0,831 0,151 1,311 0,187 1,925 1,565 0,180 0,860 0,287 1,141 1,761 1,181 0,93 1,304 0,782 0,997 1,922 1,832 1,5 1,238 1,703 0,213 1,779 1,320 0,743 0,206 0,30 0,126 1,887 1,759 0,918 1,539 0,401 1,294 0,657 0,76 0,951 0,935 1,424 0,759 1,783 0,343 1,877 1,300 0,688 0,206 1,145 0,594 0,979 0,896 1,745 0,490 1,390 1,510 1,62 0,963 0,159 0,196 1,712 1,477 1,665 0,44 1,121 0,152 0,544 0,825 0,164 0,338 0,36 0,285 0,581 1,497 0,7 1,784 0,454 0,176 1,598 1,396 0,198 1,619 1,956 1,367 0,91 0,565 1,985 0,552 1,347 1,197 1,330 1,498 0,919 1,493 0,138 0,413 1,812 1,257 0,932 1,165 1,543 1,217 0,518 0,669 1,937 0,647 1,59 0,778 1,915 0,854 1,713 0,332 0,320 1,595 0,590 1,882 0,247 0,173 0,673 1,561 0,21 0,24 1,61 0,183 0,963 0,477 1,719 1,938 0,749 1,511 1,886 1,168 0,344 0,275 1,835 0,146 1,389 0,878 0,647 0,183 0,753 1,924 1,739 0,40 0,730 1,677 1,766 0,862 0,447 1,429 1,167 1,502 0,377 1,904 0,326 1,996 1,617 0,803 0,954 0,301 1,424 0,459 1,267 0,438 0,471 0,254 1,372 1,651 0,610 0,435 0,462 0,190 1,542 1,436 0,695 1,158 1,920 1,411 1,726 1,196 1,76 0,352 0,657 1,752 1,77 1,210 1,531 1,119 0,12 1,802 1,740 1,808 0,586 0,463 1,476 1,582 1,763 0,449 0,320 0,262 0,311 1,338 1,795 1,957 0,543 1,46 0,690 1,899 1,925 1,555 1,269 0,60 1,575 0,310 1,156 0,0 1,371 1,187 1,166 0,462 1,731 1,511 0,961 0,906 1,461 0,774 0,388 1,716 1,127 1,307 0,537 0,478 0,5 1,257 0,106 1,326 1,860 1,731 1,784 1,348 1,422 1,616 1,820 1,510 1,359 1,426 1,510 0,516 1,333 1,299 1,905 1,832 0,651 0,254 1,166 0,64 0,138 0,696 0,918 0,209 1,38 1,574 1,613 1,506 1,240 1,29 1,661 1,949 1,207 0,114 1,621 1,160 1,532 0,986 1,714 1,569 0,134 0,560 0,416 1,152 1,974 0,747 1,523 0,659 1,259 0,614 1,337 0,279 1,427 1,308 1,328 1,685 0,596 0,583 0,157 1,94 1,607 1,548 1,88 1,757 0,453 1,378 0,707 1,249 1,956 1,450 0,941 0,966 0,771 1,436 1,314 0,568 0,306 1,501 0,62 1,212 1,639 0,796 1,87 1,282 0,507 0,237 1,777 1,769 0,58 1,715 1,916 0,104 0,538 1,314 0,328 0,24 0,441 1,570 0,916 1,126 0,499 1,853 0,645 1,572 1,594 1,943 1,654 0,519 0,721 1,837 1,985 1,197 0,624 1,175 0,373 1,1 0,939 0,787 1,267 0,546 0,364 1,929 1,2 1,687 0,664 1,323 0,117 0,702 1,231 1,638 0,701 0,709 1,861 0,112 0,160 0,12 0,268 1,463 0,14 0,37 1,327 0,963 1,270 0,0 1,8 0,939 0,150 1,611 1,484 0,734 0,611 0,809 0,539 0,817 1,304 0,236 0,976 0,860 0,979 0,262 0,52 1,737 0,560 0,265 1,523 1,660 1,567 0,164 1,520 1,612 0,129 0,175 0,808 0,18 1,523 0,82 1,161 0,980 1,443 1,52 1,390 0,242 0,480 0,440 0,981 1,433 0,717 1,992 1,256 1,8 1,639 0,398 1,110 0,7 0,640 1,689 0,945 1,34 0,51 1,499 1,541 0,115 0,711 1,319 0,373 0,686 0,860 1,401 1,917 0,647 0,682 1,263 0,502 1,658 1,18 1,302 0,992 1,26 0,222 0,736 1,684 1,851 1,938 1,63 0,824 0,539 1,75 1,354 1,283 1,384 1,69 0,875 0,261 0,813 0,82 0,348 0,467 0,181 0,492 0,963 0,920 0,698 0,255 1,617 0,337 0,870 1,914 0,862 0,606 0,955 0,202 1,102 0,235 0,885 0,34 0,851 0,438 0,868 0,453 0,74 0,759 0,470 1,763 1,778 0,838 0,104 1,585 0,192 0,241 1,66 1,367 0,804 0,406 0,779 0,80 0,841 1,847 0,23 1,676 0,787 1,161 1,320 1,103 1,783 0,141 0,296 0,273 0,864 0,186 0,489 1,457 0,930 0,974 1,625 0,918 0,166 0,503 1,282 1,578 0,889 1,441 1,203 1,191 1,936 1,230 0,838 1,960 1,176 0,101 0,857 0,76 0,680 0,336 0,770 1,162 1,987 1,786 0,498 1,84 1,208 1,684 1,564 1,620 1,952 1,238 0,878 1,798 1,693 0,4 0,984 0,645 0,299 1,908 0,834 1,5 0,466 1,813 1,429 0,742 0,46 0,191 1,637 0,791 1,398 1,801 1,570 0,998 0,916 0,44 0,104 0,198 0,700 1,980 1,909 0,560 0,346 1,766 1,293 0,695 1,472 0,602 0,349 0,66 0,766 1,296 0,134 1,155 0,63 1,691 0,725 1,350 1,753 0,690 0,552 0,634 1,690 1,196 1,96 0,899 1,856 1,925 0,182 0,431 1,843 0,533 1,478 1,497 0,364 0,505 0,987 1,34 1,863 0,514 1,376 1,976 0,103 0,885 1,39 0,482 0,664 0,708 0,720 1,390 1,497 0,223 0,644 0,349 0,85 1,232 1,664 0,132 1,942 1,0 0,925 1,902 0,324 0,803 0,22 0,162 0,649 1,948 0,192 0,667 1,282 0,114 0,304 1,251 0,883 1,475 1,389 0,537 1,477 1,700 0,36 0,561 0,342 0,781 1,935 0,403 1,88 0,577 0,76 0,819 1,111 1,870 1,752 1,146 1,352 0,682 1,964 1,750 1,27 0,42 1,650 1,772 0,589 0,308 0,826 0,318 1,193 0,172 0,331 1,696 1,33 1,672 0,749 0,647 1,170 1,131 0,288 1,874 0,549 1,484 0,288 1,730 0,674 1,384 1,757 1,245 1,618 1,876 0,103 0,517 0,659 1,864 1,941 1,292 1,289 1,698 0,599 0,319 1,155 0,967 1,66 1,396 1,976 0,87 0,62 1,144 0,719 1,785 1,970 1,82 1,631 0,761 0,855 0,78 0,673 1,843 1,16 1,247 0,178 1,783 1,886 0,72 0,826 1,963 1,162 1,562 1,492 0,923 0,719 0,689 0,496 1,725 1,740 1,789 1,894 1,124 1,527 1,354 0,143 1,962 1,359 0,621 1,989 0,620 1,703 0,293 1,152 0,627 1,115 1,900 0,758 1,134 0,340 1,952 0,743 1,399 1,599 0,895 0,149 1,93 0,225 0,172 1,887 1,225 0,951 0,654 1,82 1,746 0,138 1,654 1,770 0,779 1,449 1,146 1,439 1,781 1,805 1,653 1,13 1,653 0,812 1,482 0,815 0,870 0,552 0,118 1,313 0,738 0,354 0,514 0,5 1,360 0,75 0,425 1,756 0,496 0,478 1,183 0,435 0,151 0,634 1,783 1,259 0,31 1,51 0,905 0,803 0,457 1,369 0,286 0,886 1,308 1,688 1,351 0,548 0,944 1,527 0,388 1,868 1,983 1,399 0,547 1,371 1,500 0,977 1,677 0,788 0,702 1,786 1,250 1,83 1,269 1,14 0,408 1,37 1,907 0,703 0,838 1,233 0,267 1,583 0,488 1,389 1,38 1,378 0,890 1,66 1,612 1,247 0,12 1,874 0,839 0,482 1,546 1,923 0,436 0,611 0,964 1,104 0,314 0,470 0,583 1,998 1,147 0,628 0,203 1,398 1,746 1,241 1,252 1,758 0,448 1,454 1,974 1,996 1,571 1,94 1,638 0,391 0,918 1,746 0,838 1,747 1,234 1,879 0,948 0,399 1,715 0,647 1,94 0,378 0,822 0,860 1,672 1,619 0,334 1,506 1,61 1,931 1,62 1,202 0,734 0,781 0,579 0,471 0,371 0,641 1,420 0,448 1,293 0,887 1,428 0,530 0,685 0,446 1,382 1,912 1,595 1,312 1,547 1,143 1,108 1,928 0,994 1,608 0,654 0,51 0,906 0,48 0,228 0,608 1,801 0,606 1,95 0,865 1,244 1,641 1,780 1,900 0,521 1,892 0,872 1,963 0,31 1,644 0,969 1,343 1,516 0,51 1,387 1,14 1,178 0,880 1,950 1,762 1,897 1,425 0,911 1,966 0,583 1,920 0,391 0,680 1,671 0,824 1,623 0,953 1,180 1,835 0,781 0,200 1,196 1,55 1,629 1,835 0,722 1,967 0,323 0,971 1,548 0,725 1,851 0,536 1,575 0,102 0,220 0,684 0,426 0,994 1,385 1,557 0,466 0,72 1,287 0,0 1,510 1,5 0,999 0,297 1,239 0,459 0,834 0,990 1,39 1,656 0,232 0,844 1,811 0,815 0,701 1,536 1,122 1,635 1,522 0,641 1,839 0,485 1,76 1,879 0,924 1,297 1,138 0,901 0,151 1,360 1,106 0,872 1,956 1,840 0,468 0,928 0,946 0,272 1,957 1,938 0,102 1,491 1,436 1,558 0,354 0,574 0,797 0,849 1,277 0,279 1,345 1,89 0,373 0,8 0,991 0,818 1,346 1,787 0,826 0,925 0,330 1,806 1,139 1,692 0,241 0,208 0,373 1,122 0,919 1,959 1,207 0,664 0,596 0,267 1,17 1,892 0,883 1,268 0,914 1,662 0,384 0,574 0,904 1,755 1,14 0,115 1,684 1,564 0,868 1,899 0,4 0,637 1,833 1,177 0,360 0,161 0,985 1,867 1,832 0,117 1,409 0,237 1,966 0,576 1,874 1,628 0,196 0,321 0,893 0,83 0,638 1,826 0,7 1,489 1,613 1,760 0,566 1,698 0,471 0,270 1,560 0,158 0,416 1,924 0,578 1,501 1,885 1,166 0,204 1,783 1,5 1,473 1,798 0,509 1,338 1,774 1,36 0,984 0,319 0,550 0,312 1,670 0,220 1,104 1,563 0,382 0,79 0,209 1,268 0,923 1,746 1,673 1,752 1,574 1,726 1,419 1,696 0,76 0,655 1,80 0,423 0,762 0,125 0,811 0,888 0,837 1,60 1,143 0,761 0,698 1,390 1,341 1,727 1,686 1,231 1,820 1,27 0,591 1,604 0,478 0,83 0,403 1,299 0,954 0,700 0,647 0,106 0,963 0,205 0,539 0,575 0,33 1,957 0,951 1,459 1,661 0,380 1,831 1,255 0,224 1,966 1,329 0,595 0,189 0,68 1,854 0,110 0,862 1,400 0,91 0,131 0,478 1,87 1,202 0,110 0,911 0,851 0,606 0,201 0,163 1,951 0,736 1,151 0,924 0,998 0,843 1,453 0,602 1,772 0,121 1,763 1,282 0,868 0,860 0,482 0,429 0,61 1,248 0,124 1,909 0,570 0,647 0,178 1,959 1,264 1,52 0,285 0,479 0,5 1,835 1,49 1,642 1,137 0,374 1,587 0,230 0,523 0,32 1,868 1,228 1,501 0,802 1,694 1,180 1,755 1,76 0,793 0,163 0,874 1,524 1,806 0,259 0,408 1,207 1,152 1,178 1,366 0,290 0,174 0,433 0,136 1,493 1,717 0,77 1,481 0,977 1,962 1,316 0,253 0,107 1,669 1,844 1,505 1,768 1,536 0,265 1,667 1,134 0,361 0,214 1,582 1,244 0,653 1,885 1,793 1,893 1,648 1,868 0,526 1,523 0,538 0,425 1,851 0,686 0,324 0,744 1,221 1,462 1,947 0,575 1,666 1,902 1,892 1,593 1,486 1,650 0,414 0,33 1,361 0,354 1,566 0,408 1,241 0,49 0,526 1,563 0,205 1,580 1,748 0,239 0,214 1,621 0,930 0,147 1,883 1,141 1,631 0,760 0,281 1,534 1,155 1,159 0,215 0,253 1,503 1,652 0,316 0,73 1,991 0,556 1,148 1,649 1,554 0,466 0,129 1,423 1,489 0,321 0,102 0,450 0,29 0,381 0,156 1,31 1,934 1,761 1,483 0,742 0,253 0,377 1,301 0,888 0,471 1,570 1,671 1,642 0,377 0,27 0,980 1,859 1,354 0,515 0,468 1,146 0,100 0,20 0,539 1,161 1,67 0,500 0,667 1,921 1,315 1,651 0,66 1,769 0,753 1,900 1,947 0,221 0,17 0,730 0,546 1,767 0,197 1,356 1,548 0,933 1,797 0,910 1,79 1,547 1,161 0,997 1,163 0,907 1,528 0,448 1,450 0,642 0,90 1,8 0,37 0,311 1,609 0,92 1,721 0,62 0,552 0,764 0,6 0,79 1,484 1,310 1,186 1,759 0,744 1,471 1,306 1,882 0,643 1,912 1,35 0,554 1,900 0,115 0,930 0,565 0,462 0,704 0,946 0,541 1,271 1,525 0,364 1,450 1,856 1,51 1,927 1,121 1,853 0,314 1,959 1,582 1,969 1,951 1,712 1,873 1,224 1,438 1,13 0,338 0,74 1,48 0,696 1,563 1,145 1,732 0,913 0,753 1,39 1,355 1,361 1,863 0,818 1,728 1,498 1,225 0,895 0,19 0,262 0,185 1,483 0,676 0,682 1,308 0,931 1,764 1,773 1,746 1,298 1,220 1,304 0,192 0,519 1,246 1,550 1,525 0,874 0,734 0,47 1,151 0,884 0,988 1,553 0,758 1,894 1,155 0,583 1,623 1,705 1,747 1,742 0,317 1,804 0,364 0,464 0,849 1,145 1,287 1,382 1,929 1,417 0,761 1,966 1,760 0,169 0,39 0,311 0,369 0,171 1,54 1,45 0,44 0,388 1,622 0,854 0,551 1,967 1,371 1,489 0,662 1,658 1,402 1,234 0,162 1,653 1,119 0,451 1,497 1,464 0,594 0,84 0,697 1,657 1,579 0,135 0,737 0,486 1,282 0,681 1,354 0,517 1,296 0,961 1,244 0,384 0,169 1,953 0,96 1,279 1,547 1,510 1,18 1,596 1,307 1,442 1,564 0,646 1,706 1,577 0,976 0,514 0,6 0,610 0,908 1,171 1,380 1,796 0,953 1,362 0,670 0,506 1,125 1,779 0,315 1,915 0,3 1,195 0,751 1,447 1,142 0,373 1,890 0,252 1,384 0,162 1,326 1,900 1,175 0,455 1,983 1,119 0,541 1,821 0,381 0,291 1,603 0,511 0,895 1,447 1,933 1,922 0,60 1,495 1,879 0,894 1,466 1,109 0,101 0,916 0,268 1,478 0,854 0,958 0,571 0,256 1,581 1,191 0,781 1,643 0,796 0,333 0,902 1,4 0,980 1,330 1,339 0,77 0,977 0,734 1,930 1,148 1,466 1,359 0,93 0,86 1,827 0,387 1,520 0,13 0,964 0,175 1,524 0,231 0,916 1,56 0,893 1,645 1,828 1,139 0,448 0,5 1,217 1,343 1,842 0,1 1,232 0,337 1,523 1,980 1,768 1,193 0,142 1,443 1,374 1,368 1,49 1,430 1,440 0,252 1,648 0,561 1,322 1,706 1,87 0,830 1,109 1,640 0,970 0,291 1,610 0,430 1,716 1,276 1,165 0,454 0,94 1,485 0,913 0,533 0,82 0,568 0,721 1,485 0,359 1,996 0,342 1,350 0,83 0,430 1,810 0,908 1,365 1,237 1,65 1,134 1,371 0,27 1,332 1,407 1,343 1,522 0,170 0,549 1,183 0,766 0,721 1,835 1,753 1,990 1,148 1,958 1,627 0,108 0,669 0,126 1,659 1,565 1,683 1,520 0,305 1,44 1,773 1,86 1,75 0,784 0,586 0,498 0,489 0,584 0,952 0,131 0,325 0,282 1,544 1,990 1,829 0,916 0,622 0,494 1,599 0,516 1,365 0,826 1,97 0,216 1,627 1,128 0,667 0,403 1,693 0,745 0,370 0,189 0,550 0,520 0,117 0,603 1,665 1,802 0,680 1,159 1,258 1,14 1,397 0,171 0,287 1,450 1,364 1,900 0,199 1,223 1,868 1,355 1,236 0,666 0,747 1,23 0,77 0,889 1,702 1,503 0,939 0,169 1,825 0,306 1,673 0,284 0,578 1,667 1,480 1,262 0,181 1,681 0,451 0,817 0,732 0,474 0,461 0,436 0,915 1,779 0,205 1,933 0,588 1,284 1,230 1,186 0,696 0,407 0,340 1,1 1,502 1,611 1,249 1,655 1,346 0,362 1,126 1,278 1,475 1,209 1,697 0,414 1,277 0,679 0,895 1,976 1,812 0,953 1,459 0,452 1,53 1,37 0,904 1,399 1,563 1,124 1,750 1,719 1,205 1,151 1,224 0,612 0,210 0,211 0,756 0,506 0,77 1,988 0,224 1,688 1,526 1,550 1,437 0,467 1,211 0,537 0,893 1,900 1,285 0,411 1,613 1,445 1,56 0,712 0,132 1,458 1,974 0,885 1,111 0,346 1,939 1,476 1,904 0,426 1,441 1,4 1,359 1,958 1,740 0,210 0,138 1,466 1,503 0,605 1,373 0,236 0,315 0,266 0,678 1,211 1,644 1,876 0,866 1,272 0,808 1,966 0,861 0,923 1,415 0,974 1,734 0,468 0,652 1,863 1,141 1,178 1,445 1,594 0,804 0,953 1,137 1,980 1,867 0,266 0,802 0,361 0,505 0,461 1,962 0,136 1,456 1,753 0,729 1,226 1,876 0,750 1,452 0,197 0,577 0,93 1,963 0,27 0,41 0,743 0,143 1,951 1,393 0,375 0,772 1,55 1,108 1,119 0,7 1,988 1,438 0,673 0,369 1,923 0,262 1,603 0,229 0,64 0,312 1,233 1,744 1,240 1,362 0,626 1,959 0,373 1,634 0,377 0,366 0,209 0,82 1,825 0,598 0,854 1,989 0,427 0,604 1,904 1,866 1,555 0,672 0,990 1,473 0,629 0,445 0,386 0,907 0,493 1,80 1,858 1,31 0,26 1,577 1,477 0,183 0,508 1,960 0,294 0,116 1,233 1,206 0,451 1,527 0,326 1,729 0,966 0,931 1,686 1,772 0,39 0,284 1,200 1,879 0,984 1,455 1,837 1,911 0,373 0,100 0,746 0,778 1,144 1,191 0,64 0,515 1,412 1,694 1,739 0,492 1,780 0,602 1,239 1,974 1,947 0,143 1,517 0,338 0,895 0,494 1,189 0,313 0,306 1,800 0,873 0,35 1,237 0,107 1,552 1,984 0,839 1,909 1,328 1,646 1,602 1,787 1,176 1,544 0,288 1,64 1,972 0,112 0,869 1,96 1,170 1,179 0,536 0,883 1,669 0,927 1,805 0,337 0,150 0,866 0,304 1,626 1,762 1,739 1,256 0,487 1,406 1,119 0,383 0,530 0,849 1,340 0,818 1,89 1,184 1,973 1,479 0,134 0,24 0,149 1,328 1,407 1,229 1,381 1,565 0,159 1,426 1,895 0,420 1,682 1,218 0,591 0,48 1,472 1,615 1,643 1,140 0,428 1,943 0,738 1,542 0,439 0,586 1,710 0,14 1,507 1,208 1,642 0,196 0,400 1,930 1,257 1,728 0,436 0,19 1,562 0,720 1,403 1,86 1,71 1,826 1,167 1,288 1,118 1,202 1,85 0,517 1,222 1,522 0,598 1,947 0,811 1,968 1,668 1,84 1,19 1,273 0,198 1,474 1,148 0,793 0,601 1,289 1,7 0,226 1,898 1,97 1,715 1,315 0,543 0,125 0,162 1,15 1,985 0,936 0,863 1,870 1,115 0,238 0,462 0,806 1,354 0,144 1,841 0,335 1,612 1,170 1,279 0,981 1,834 0,725 0,981 0,215 0,191 1,137 1,401 1,98 1,690 1,810 0,424 0,855 0,921 1,82 1,198 0,297 0,100 1,97 0,788 1,221 1,184 1,245 1,958 1,2 0,961 1,425 0,349 1,111 1,871 0,562 1,655 1,150 1,12 1,117 0,721 0,64 0,73 1,926 0,962 0,202 1,180 1,471 0,82 1,710 1,485 0,941 1,35 1,523 1,497 1,515 1,891 0,897 1,265 0,333 1,132 0,852 0,368 1,493 0,856 1,982 1,12 1,183 0,240 1,72 0,675 0,453 0,953 1,354 0,855 1,391 1,657 1,57 0,720 0,671 0,987 1,605 0,575 1,446 1,869 0,278 1,656 1,84 1,773 0,425 1,593 1,61 0,845 1,463 0,393 1,70 1,771 0,787 1,781 1,576 1,320 1,266 0,119 0,405 1,809 1,984 0,322 1,143 0,592 0,404 0,238 1,126 1,448 1,951 0,477 0,548 1,821 0,475 0,783 1,278 0,818 1,310 0,639 1,418 1,296 0,81 0,475 0,120 0,751 0,567 0,661 1,110 0,362 1,447 0,567 1,106 0,12 0,959 0,782 0,677 0,223 1,62 0,937 1,417 0,778 1,196 0,869 0,805 1,622 0,736 1,5 1,83 0,454 1,495 0,260 1,195 0,999 1,770 1,18 1,299 1,299 0,573 0,472 0,270 0,574 1,868 1,12 0,214 0,616 1,713 0,467 1,744 0,337 0,793 0,385 1,795 1,814 0,25 0,171 1,886 1,692 1,279 1,384 1,641 0,249 1,742 1,940 0,132 0,843 1,324 0,466 1,384 1,643 1,204 0,824 0,44 0,195 1,901 0,294 1,618 1,20 0,934 0,581 0,27 1,154 0,449 0,855 0,400 1,196 1,790 1,129 1,130 1,328 1,117 1,131 0,556 0,451 0,132 1,891 0,136 1,751 0,412 1,342 0,185 1,879 0,875 1,60 0,541 0,390 0,974 1,264 0,7 1,847 0,56 0,508 1,4 0,492 0,842 1,139 1,746 0,793 0,201 1,239 1,179 0,188 0,46 0,222 0,311 0,680 0,523 1,616 1,637 0,165 0,203 0,569 0,426 1,516 1,721 1,96 0,541 1,76 1,702 1,577 0,319 1,654 1,929 1,434 1,877 0,87 1,3 1,774 0,996 1,813 1,374 1,975 1,922 0,785 0,823 0,856 1,423 0,111 1,190 0,627 0,762 1,33 1,620 1,872 1,51 1,744 1,688 0,417 1,315 1,307 0,4 1,401 1,216 0,161 0,649 1,657 1,628 0,987 1,881 1,449 0,666 1,641 0,777 1,411 0,262 0,814 0,90 0,496 1,560 1,145 0,462 1,194 1,822 0,430 0,658 0,595 1,742 0,222 0,385 1,76 0,97 1,136 0,806 1,632 1,186 1,455 0,183 0,288 0,116 1,210 1,956 0,804 1,789 1,904 0,903 1,407 1,541 0,878 0,598 0,578 0,934 1,287 1,877 1,720 1,396 1,756 1,512 1,206 1,595 1,980 1,803 1,39 1,940 1,881 1,687 1,198 0,549 0,777 0,132 1,847 1,604 0,753 1,969 1,625 1,498 0,633 0,261 0,828 1,212 1,519 1,346 0,851 1,131 0,641 0,707 1,335 0,854 0,906 0,59 0,92 1,376 0,38 1,103 1,59 0,271 1,832 1,648 1,285 1,270 0,996 0,326 1,447 1,476 1,638 1,583 1,499 1,123 1,271 0,590 1,564 0,295 1,114 0,749 1,775 0,770 0,632 0,168 1,202 1,975 0,410 0,595 0,440 0,35 1,805 0,624 1,596 0,232 1,32 1,107 1,912 1,833 0,993 0,620 0,794 1,822 0,166 1,499 1,30 1,801 1,828 0,272 0,834 1,31 1,217 1,823 0,968 0,425 1,640 0,924 0,490 0,40 1,639 0,107 1,4 0,451 0,837 0,698 0,507 0,928 1,539 0,783 0,782 1,873 0,256 0,358 1,676 1,300 1,14 1,39 0,765 1,339 0,261 1,933 0,443 0,7 0,208 0,694 0,717 0,810 0,327 1,396 1,607 1,446 0,279 0,267 0,380 0,129 1,385 1,587 1,617 1,332 0,362 0,873 0,359 0,675 0,714 0,289 0,427 1,591 1,77 0,599 1,929 1,258 0,856 0,909 0,779 0,357 1,963 0,786 0,566 1,821 0,729 1,143 1,195 1,966 0,445 1,830 0,952 1,167 1,389 0,737 0,115 1,66 1,664 1,449 1,23 1,890 0,116 0,377 0,52 1,10 1,410 0,481 1,450 1,823 1,242 0,880 1,687 1,818 1,358 1,389 0,60 0,661 1,264 1,54 1,812 1,583 1,658 1,754 1,538 0,676 1,862 0,926 1,310 0,678 0,473 0,102 1,212 1,455 0,516 1,470 1,427 0,686 1,190 1,126 1,910 1,345 0,114 0,472 0,758 1,676 1,735 0,991 1,947 1,112 0,273 0,733 1,194 0,804 1,876 1,832 1,667 0,826 1,13 1,580 0,131 0,489 1,333 1,489 1,438 1,184 0,262 1,844 0,195 1,166 0,646 1,505 0,823 1,830 0,670 0,461 1,382 0,981 0,816 1,367 0,483 1,445 1,9 0,70 0,367 1,995 1,127 0,653 0,477 1,426 0,63 0,943 0,361 1,528 0,549 1,4 1,799 0,855 1,131 0,234 1,414 1,599 0,583 1,63 0,796 1,618 0,300 0,738 1,470 1,207 0,276 1,632 1,69 0,108 1,760 1,723 0,379 0,247 0,997 0,869 0,335 0,915 0,651 0,77 0,338 1,242 1,374 0,783 0,526 0,77 0,940 0,296 1,596 1,910 1,418 0,377 1,850 1,992 0,694 0,788 0,883 0,367 1,650 1,858 0,553 1,899 1,278 0,365 0,133 0,367 0,463 1,309 0,310 0,75 0,505 1,748 0,142 0,166 0,57 0,179 0,483 0,574 0,415 1,243 1,94 1,923 0,466 0,575 1,62 1,401 1,354 0,781 1,656 0,94 0,545 0,799 1,386 0,891 1,645 1,381 0,578 1,426 1,143 1,316 0,77 1,793 0,250 0,821 0,70 1,262 1,178 0,697 0,129 1,474 1,742 1,416 1,371 0,631 1,367 0,229 1,650 1,299 0,328 1,694 1,854 1,557 0,176 0,267 0,512 0,408 1,376 0,976 1,962 1,957 1,350 1,851 1,666 1,653 0,582 0,925 1,62 1,63 1,782 1,242 0,405 0,63 1,405 0,368 1,249 1,58 1,137 0,396 0,381 0,630 1,970 0,884 0,133 0,588 1,633 1,389 1,851 1,650 0,968 1,98 1,988 1,326 1,339 1,412 1,514 0,307 0,94 1,696 0,461 1,290 0,998 1,240 0,312 0,818 1,344 1,708 0,406 0,319 1,221 0,84 0,135 0,987 0,472 0,371 1,768 0,951 1,915 1,777 1,560 0,167 1,944 1,368 0,292 0,128 0,450 1,955 1,616 1,896 1,287 0,683 1,973 0,443 1,579 1,295 1,610 1,429 0,214 1,73 1,442 1,742 0,423 0,558 1,210 0,701 1,750 0,757 0,431 1,52 1,312 1,427 1,548 0,121 0,16 1,670 0,77 0,74 0,115 1,533 1,587 0,682 1,565 1,180 1,965 1,454 1,281 0,871 0,701 0,394 1,416 1,667 0,219 1,981 0,580 0,867 0,482 0,704 1,997 0,292 1,869 1,974 1,708 0,449 0,32 1,893 0,672 1,480 1,28 0,529 1,137 0,135 1,109 0,359 0,241 1,711 1,295 1,809 1,720 1,96 1,451 1,93 0,384 0,693 1,969 1,223 1,787 0,901 0,642 1,846 0,434 1,593 1,550 0,931 1,982 0,324 0,788 0,164 1,72 0,156 1,363 1,656 0,2 1,727 0,383 0,991 0,523 1,787 0,112 0,732 1,241 1,108 0,435 1,261 0,984 0,636 1,238 1,382 0,899 0,540 1,322 0,950 0,403 1,208 0,257 0,397 1,890 0,757 1,456 0,284 1,14 1,13 1,56 0,43 1,646 0,464 1,190 1,776 0,511 1,700 1,446 1,906 0,32 0,833 0,608 1,229 1,147 0,91 1,393 1,763 0,603 0,896 1,935 0,209 0,141 0,903 0,384 0,307 1,712 1,649 1,370 1,809 0,933 0,962 1,222 1,688 1,819 1,275 0,922 1,175 1,791 0,851 0,178 1,910 0,953 1,60 1,953 0,163 1,860 1,254 1,65 1,322 0,816 0,547 1,638 0,385 0,287 1,892 0,362 0,107 1,276 1,890 0,962 0,476 1,258 1,634 1,245 0,338 1,989 0,17 0,383 0,951 0,140 1,681 0,172 0,59 1,289 1,418 0,710 0,551 0,315 1,825 0,322 0,307 1,716 1,887 0,574 0,868 0,494 0,318 1,960 1,138 1,269 1,927 1,51 1,747 0,962 1,472 1,310 0,638 1,24 0,908 0,12 0,261 1,132 1,960 0,211 0,850 1,693 1,261 0,869 1,857 0,963 0,389 1,390 1,822 1,810 0,161 1,886 0,585 1,302 1,774 1,167 1,374 0,722 0,330 0,324 1,158 0,574 0,777 1,528 0,806 1,878 1,587 1,837 1,36 0,301 1,801 1,480 1,211 1,606 1,528 1,595 1,758 1,480 1,154 1,611 0,73 0,71 1,183 1,785 1,447 1,703 1,529 0,802 0,29 1,484 0,386 1,129 0,121 1,831 1,680 0,901 0,558 0,111 0,723 0,94 0,101 1,227 0,663 0,634 0,318 1,115 1,779 1,508 0,600 0,798 0,724 1,380 1,594 0,974 1,465 0,602 1,946 0,853 1,76 1,508 0,369 1,223 0,170 1,17 0,932 0,901 0,461 0,919 0,702 0,279 1,891 0,704 0,723 0,272 0,952 1,900 0,435 0,60 1,235 0,817 0,953 1,683 0,639 0,803 0,978 0,454 0,890 1,84 0,837 1,530 0,396 1,39 1,211 1,485 0,766 1,616 0,6 0,1 0,544 0,768 1,855 1,333 1,777 1,189 1,158 1,609 1,578 0,367 1,639 0,769 0,6 1,784 0,256 1,645 1,240 0,551 0,723 0,839 0,150 0,450 0,18 1,922 0,939 1,741 0,467 1,480 1,947 0,907 0,719 0,511 0,381 1,919 0,944 0,131 0,756 1,233 0,166 0,693 1,624 0,547 1,60 1,866 0,826 1,762 0,684 0,773 1,331 0,151 1,232 1,520 0,599 1,349 0,865 1,879 1,794 0,363 0,268 0,10 0,397 0,659 1,935 1,192 0,963 1,713 1,455 1,118 1,266 0,652 0,340 0,897 1,976 0,597 0,574 1,107 1,32 1,479 0,181 0,341 0,442 0,451 0,413 1,286 1,874 1,366 0,911 1,859 1,0 1,994 1,63 0,146 1,286 1,401 1,858 0,813 1,311 1,800 1,301 0,702 0,95 1,35 0,500 0,596 1,476 0,371 0,574 0,441 0,273 0,739 1,10 0,976 0,74 1,111 0,121 1,44 1,399 1,459 0,320 0,476 0,364 0,908 1,323 0,852 0,965 1,196 1,58 0,400 0,161 1,829 1,399 1,884 0,826 1,365 0,220 1,154 1,324 0,628 0,332 0,831 1,543 1,170 1,227 0,339 1,346 0,839 1,430 1,982 1,339 1,727 1,858 0,91 0,102 1,463 1,167 1,981 1,54 1,722 0,585 0,426 1,316 0,111 1,153 0,932 0,785 0,209 1,676 0,529 0,463 1,679 0,143 0,703 1,7 1,442 1,759 0,147 0,484 0,862 0,759 0,705 0,880 0,815 1,200 1,227 0,468 0,789 1,674 1,187 0,926 0,407 1,659 0,485 0,17 1,392 0,131 0,110 1,659 0,411 1,595 0,693 0,391 1,772 0,54 1,423 0,27 0,798 0,140 0,649 0,849 0,254 1,537 0,88 0,712 1,322 1,985 1,367 0,92 0,69 0,60 1,352 0,599 1,866 0,381 1,73 0,540 1,648 1,238 1,128 1,859 1,340 1,859 1,290 0,201 0,857 0,737 0,520 1,681 1,563 0,484 0,980 1,467 1,902 1,107 1,563 0,64 1,324 1,147 1,17 0,317 1,53 0,896 1,204 1,522 1,303 1,168 0,256 1,382 1,830 0,425 1,194 1,191 0,749 0,402 0,946 1,68 0,674 0,615 1,844 0,894 1,825 0,306 0,358 1,580 0,715 0,111 1,671 1,676 0,108 1,244 0,137 1,13 0,826 1,708 1,398 0,58 0,894 0,407 1,559 1,789 1,363 0,842 0,892 0,966 0,605 0,824 0,534 0,243 0,743 0,342 1,828 0,957 0,490 1,764 0,80 1,72 0,635 1,129 1,55 0,172 1,915 0,726 0,439 0,212 1,924 1,237 1,559 0,419 1,449 1,661 1,134 0,75 0,987 0,694 1,457 0,504 1,49 0,10 1,207 1,305 0,385 1,729 0,690 0,135 0,616 1,216 0,938 0,766 0,226 1,954 1,153 1,773 1,761 0,106 0,82 1,354 1,964 0,897 0,358 0,421 1,224 1,975 0,941 0,659 0,763 1,228 0,706 0,430 0,760 0,110 1,11 1,985 0,660 0,734 1,20 1,652 1,822 1,632 0,253 1,462 1,3 0,463 1,867 0,150 0,10 1,33 1,724 0,567 1,28 1,731 0,258 1,48 1,98 0,661 0,830 1,712 0,739 0,99 1,520 1,497 1,544 1,875 0,617 0,76 1,385 0,184 1,809 0,743 1,104 0,236 1,913 1,650 0,645 0,874 1,95 0,864 1,422 1,428 0,631 1,813 1,823 1,449 1,892 0,689 0,114 1,606 1,438 1,170 1,891 1,882 0,178 0,431 1,853 0,129 0,780 1,253 1,322 1,727 0,489 1,420 0,89 0,962 1,957 0,636 0,421 0,702 1,727 1,455 1,404 1,213 0,923 1,684 0,813 0,371 1,53 1,924 1,566 0,721 1,668 1,254 1,909 0,483 0,20 0,102 1,752 0,820 0,876 1,937 0,687 1,229 1,122 1,551 1,83 1,816 1,33 0,39 1,864 0,747 1,333 0,932 1,46 1,49 1,641 1,768 1,720 1,284 1,948 1,339 0,242 1,797 0,704 1,912 1,287 1,751 1,882 1,457 1,203 1,528 0,844 1,297 0,598 1,930 1,66 0,124 0,859 0,265 0,882 0,404 0,90 1,698 0,772 0,810 0,117 1,217 0,74 1,737 1,707 1,842 0,656 0,729 0,848 1,818 0,408 1,813 0,172 0,633 1,612 1,817 0,410 1,989 0,631 1,733 0,430 1,486 0,702 1,900 0,718 0,310 0,395 0,572 0,797 0,734 1,253 1,255 1,146 0,521 1,262 1,75 0,402 0,114 1,758 1,869 1,438 0,361 0,531 0,565 0,585 0,679 0,421 0,664 0,313 0,60 0,213 1,272 0,260 1,536 1,928 1,410 1,633 0,685 1,437 1,373 0,903 0,336 1,364 1,778 1,657 1,280 1,54 1,70 0,623 1,470 1,985 1,851 1,678 0,538 0,885 0,1 0,382 0,283 1,388 1,199 0,941 1,995 1,413 0,736 0,31 1,198 0,142 1,406 1,532 0,554 1,836 1,846 1,981 1,130 1,899 0,2 1,491 0,455 0,611 1,232 1,92 1,234 0,712 0,878 1,913 0,323 0,523 1,986 1,684 0,199 0,489 1,414 0,521 0,945 0,333 1,771 0,423 0,956 0,667 0,256 1,661 0,623 1,727 1,486 0,313 0,302 0,106 1,362 0,968 0,353 0,21 0,491 0,589 1,108 1,607 0,599 0,548 1,228 0,105 0,660 1,304 1,947 0,418 1,156 1,617 1,689 1,12 0,675 1,841 0,322 0,86 0,150 0,730 0,194 0,783 0,520 1,436 1,436 0,108 0,420 1,449 0,539 1,520 0,415 0,992 1,577 0,839 0,189 0,711 1,368 1,484 0,734 1,173 0,590 0,886 0,815 0,683 1,992 0,717 1,867 0,897 0,849 1,960 0,204 0,872 1,103 1,436 0,524 0,43 0,674 1,870 0,294 0,302 1,50 1,345 0,244 1,34 0,24 0,105 1,978 1,481 0,411 1,518 1,245 1,638 0,706 0,191 1,903 0,310 0,965 1,760 0,159 0,41 1,181 0,988 0,202 0,503 1,736 1,70 0,159 1,28 1,73 0,736 1,301 0,915 1,396 1,7 1,66 0,988 0,576 1,979 0,603 0,525 0,133 1,750 0,701 0,630 0,138 1,232 0,318 1,839 1,728 1,239 0,414 1,75 0,515 0,291 0,168 1,397 0,89 1,726 0,673 0,543 0,97 1,913 0,154 1,567 1,617 1,604 0,458 1,792 1,319 0,900 1,502 0,514 0,11 1,348 0,957 1,33 1,185 1,569 1,605 1,71 1,783 1,909 1,990 0,333 1,671 0,213 1,998 0,68 0,562 0,820 0,36 0,205 1,377 1,935 0,624 0,819 1,645 1,44 0,443 0,638 0,427 1,177 1,322 0,433 0,424 1,702 1,462 0,80 1,80 0,786 1,323 0,200 1,810 1,125 1,311 0,840 0,105 0,228 1,561 0,735 1,210 0,363 0,978 0,160 1,487 0,613 1,193 0,846 1,60 0,519 0,413 1,516 1,164 0,177 1,494 1,233 1,162 1,165 0,943 0,88 0,753 0,815 1,894 0,847 1,125 1,660 0,566 0,450 1,597 1,48 1,629 1,885 0,867 0,620 1,118 0,107 0,196 1,484 0,444 0,280 0,876 0,189 1,512 1,767 1,705 0,781 1,661 0,282 1,102 0,202 1,878 1,621 1,334 0,652 0,193 0,288 1,716 1,790 1,262 1,43 0,45 1,935 1,153 0,697 1,51 0,103 1,608 0,428 1,99 0,686 1,446 1,496 0,101 0,296 1,21 0,353 1,492 1,760 0,471 0,874 1,212 0,868 1,966 1,55 0,17 1,865 0,167 0,706 1,487 1,16 0,547 1,266 1,265 1,435 0,314 1,455 1,432 0,583 0,980 1,935 0,985 1,722 1,12 1,70 1,3 1,368 1,112 1,839 0,960 1,859 0,803 1,219 0,626 0,959 0,365 0,114 0,107 1,426 1,759 0,855 1,538 1,313 1,67 0,381 0,784 1,610 1,779 1,344 0,437 1,636 1,537 0,456 0,68 1,28 1,810 1,323 1,107 0,867 0,660 0,815 1,227 0,849 0,171 1,705 0,496 1,81 1,371 1,318 1,426 0,509 1,784 1,841 1,287 0,882 1,206 1,629 0,226 0,889 0,143 0,608 0,483 1,750 1,729 0,514 0,439 1,78 1,293 0,869 1,463 0,652 0,919 0,764 1,10 0,425 0,767 0,547 1,808 0,932 1,60 1,779 1,975 0,484 0,102 1,148 0,960 0,405 1,492 0,761 1,471 0,751 1,994 1,858 1,703 0,245 0,240 1,175 0,18 0,973 0,709 0,455 1,898 0,16 0,418 0,332 1,887 0,334 0,509 0,361 0,178 0,790 1,442 0,322 1,822 1,165 1,932 1,403 0,161 0,895 0,324 0,852 1,541 1,595 0,698 0,505 1,87 1,576 0,939 1,196 1,581 1,121 0,435 0,998 0,994 0,867 1,752 0,731 0,488 0,927 0,960 1,761 1,979 0,188 0,976 0,717 0,492 1,167 0,554 1,471 1,998 1,389 1,56 1,4 0,717 1,543 1,42 0,427 0,878 1,562 1,253 0,93 1,981 0,579 0,383 1,821 1,298 1,140 0,870 0,262 1,388 1,281 0,487 1,945 0,876 1,499 1,705 1,469 0,392 1,822 1,470 1,886 0,432 0,924 0,862 1,568 1,808 1,505 0,582 0,657 1,899 1,959 0,791 0,331 0,368 1,593 0,15 1,887 0,363 1,183 0,189 1,812 1,639 0,451 1,232 1,518 1,981 0,467 0,232 0,899 0,771 0,285 0,7 0,882 1,821 1,834 0,372 1,788 0,170 0,699 0,441 1,577 0,482 1,422 0,545 1,32 0,627 1,153 0,108 1,383 1,725 1,885 1,685 1,358 0,919 0,298 0,884 0,489 1,215 0,137 1,63 1,75 1,565 0,445 0,460 1,866 0,239 1,220 0,227 1,967 0,133 1,728 0,700 0,44 0,809 1,953 1,607 1,631 0,276 0,107 1,80 0,201 1,731 1,410 1,395 0,630 1,528 0,933 0,102 1,883 0,854 1,913 1,498 0,119 1,707 1,120 0,352 1,506 1,14 1,545 1,451 1,101 0,880 0,545 1,404 0,845 0,995 0,387 1,561 0,432 1,358 1,614 0,979 0,947 0,842 1,820 0,672 0,720 1,168 1,154 1,440 0,950 0,786 1,299 1,653 1,106 1,992 0,909 1,897 1,245 1,485 1,575 0,715 1,379 1,237 0,356 1,463 1,490 1,426 1,775 0,633 1,950 1,983 1,199 1,475 1,709 0,767 1,886 0,452 1,826 0,102 0,751 1,436 1,124 0,37 0,98 1,735 0,62 0,495 1,757 0,522 0,159 1,302 1,569 1,154 0,619 0,882 0,740 1,893 0,359 0,451 1,719 1,547 1,234 1,568 0,454 0,922 1,812 0,697 0,410 1,636 1,599 1,458 0,995 1,956 0,762 1,297 0,463 0,255 1,84 0,960 1,282 1,619 1,176 0,260 0,710 1,855 0,186 1,389 0,765 0,799 0,876 0,902 0,785 0,210 1,3 1,956 0,32 0,142 1,899 0,307 0,866 0,38 1,294 1,979 0,177 1,169 1,390 1,512 0,266 1,444 1,436 1,830 0,707 1,91 0,500 0,214 1,416 0,830 1,142 1,623 1,768 1,28 0,394 0,740 0,435 0,611 0,461 1,791 1,18 1,83 0,51 0,256 1,152 1,14 0,447 0,370 0,403 1,754 0,292 1,504 0,317 1,992 0,277 1,209 0,875 1,400 0,727 1,279 0,582 0,881 1,608 1,876 0,353 0,407 1,421 1,32 0,639 1,972 0,834 0,654 1,970 0,348 0,230 0,449 0,513 1,152 0,764 0,387 0,220 1,891 0,656 0,436 0,836 0,831 0,444 1,174 1,899 0,929 0,191 0,728 0,330 1,295 1,478 0,514 0,45 0,493 0,794 0,609 1,457 1,697 1,592 0,642 0,798 0,505 0,767 1,975 0,780 1,567 1,789 1,170 0,864 1,758 0,605 1,205 0,413 0,916 1,589 1,977 0,433 0,345 0,191 0,532 1,242 1,536 1,523 1,170 1,341 1,973 1,184 0,130 1,850 1,841 1,137 0,62 1,961 1,179 1,616 0,706 1,518 0,562 1,655 0,800 0,597 0,869 1,864 1,482 0,915 1,27 0,235 0,94 1,378 1,295 0,274 0,621 1,491 1,695 1,47 0,715 0,160 1,942 0,585 0,356 0,710 1,913 1,712 1,435 0,172 1,770 0,8 1,756 0,459 0,550 1,154 1,231 1,223 1,43 1,667 0,73 1,369 0,360 0,305 0,520 1,448 0,652 0,58 1,265 1,961 1,94 0,462 0,522 0,269 0,778 1,841 1,98 0,833 0,233 1,920 1,122 0,646 0,679 0,579 0,852 1,235 1,584 0,699 0,829 0,749 1,42 1,970 1,707 0,875 0,648 0,11 1,603 1,589 1,990 1,168 1,187 0,145 1,364 0,257 1,928 0,927 1,677 0,216 0,359 0,3 0,126 1,597 1,913 1,422 1,154 0,770 0,633 0,118 0,173 1,405 0,617 0,330 1,604 1,645 1,534 0,723 0,911 0,494 0,630 0,691 1,826 0,481 0,277 0,387 1,79 0,788 1,650 0,129 0,74 0,873 0,182 0,554 0,3 0,300 0,391 1,25 0,423 0,819 1,711 0,911 1,343 1,234 1,283 0,837 1,838 0,116 0,953 1,999 1,201 0,214 1,206 1,196 1,461 0,935 1,802 1,712 1,938 0,34 0,791 0,486 1,58 0,771 1,296 0,363 1,855 0,306 0,706 0,72 0,49 0,884 1,936 1,113 0,364 0,343 0,50 1,281 1,589 1,273 1,157 1,945 0,725 1,227 0,565 0,53 1,273 1,484 1,779 1,7 1,482 1,453 0,456 0,283 1,776 0,213 0,352 1,919 0,988 1,623 1,12 1,490 0,888 1,769 0,804 1,581 0,615 1,712 1,357 0,329 0,476 1,950 1,944 0,917 1,217 0,702 0,111 1,381 0,888 1,903 1,468 0,251 1,544 0,910 1,781 1,315 1,36 0,226 1,270 0,368 1,378 1,909 0,608 0,622 1,130 0,881 1,984 0,432 1,73 1,997 1,743 0,646 0,306 1,963 0,658 1,439 0,661 1,653 0,588 0,612 1,900 1,836 0,361 1,286 1,404 1,890 1,299 0,611 1,593 1,408 1,55 1,288 1,763 1,254 0,76 0,567 0,832 1,948 1,111 1,952 1,327 1,400 1,147 1,172 0,37 1,520 0,993 0,328 0,981 0,825 0,724 1,717 0,987 1,682 1,501 0,711 0,884 1,840 0,649 1,644 1,804 1,746 0,569 1,799 0,871 0,297 1,99 0,805 0,532 1,240 0,404 1,90 0,519 1,856 0,772 1,865 1,622 0,628 1,277 0,715 0,673 1,763 1,482 0,326 0,708 0,698 1,352 1,403 1,21 0,728 1,822 0,443 1,436 1,381 0,409 1,703 0,182 1,929 1,121 1,40 1,170 0,511 0,319 0,834 0,562 0,888 1,774 1,525 0,997 0,347 0,760 1,51 0,921 1,435 0,963 1,176 0,273 1,410 0,984 0,654 1,779 1,345 0,714 1,919 1,311 1,658 1,140 1,324 0,240 1,524 1,563 0,492 1,300 1,657 1,940 0,921 0,464 0,799 1,313 1,134 0,314 1,338 0,392 1,726 1,590 0,51 0,821 1,28 1,96 1,363 0,83 0,716 0,221 0,74 0,964 1,19 0,225 1,61 1,635 1,178 0,460 1,854 0,883 0,402 1,775 0,334 1,526 0,622 0,827 1,841 1,643 1,241 0,995 1,596 0,981 1,849 0,251 0,657 1,591 0,732 0,257 1,201 0,317 1,693 1,37 0,118 0,578 1,695 1,42 1,151 1,193 0,852 0,789 0,764 0,106 1,114 0,775 1,792 1,937 1,433 0,75 0,923 0,92 0,447 1,6 1,830 0,287 1,221 0,498 0,750 1,404 1,616 0,882 1,425 0,419 0,495 0,120 1,521 0,263 1,867 0,350 0,330 1,109 1,444 0,304 1,956 1,986 1,737 1,958 0,698 1,212 1,598 0,365 0,764 1,341 0,875 0,341 0,326 1,271 1,295 0,99 0,748 0,42 1,305 0,254 1,24 1,898 0,434 1,947 1,134 0,701 1,556 1,452 0,748 0,952 1,426 0,373 0,538 0,902 0,404 0,72 0,586 1,917 0,597 0,619 1,485 0,834 1,847 0,714 1,369 0,375 1,991 0,346 0,486 1,13 0,294 1,31 0,380 1,199 1,834 0,745 1,889 0,67 1,768 1,103 0,557 0,456 1,741 0,774 1,203 0,682 0,873 0,452 1,802 0,107 1,479 1,734 0,853 1,501 0,584 1,587 0,787 0,804 0,41 0,645 0,192 1,340 1,35 0,0 0,601 0,603 1,704 1,166 0,750 0,636 1,104 1,254 0,314 1,779 1,906 0,861 0,150 1,86 1,86 1,509 0,432 1,969 1,451 0,354 1,612 1,25 0,629 0,306 0,555 1,489 1,232 1,106 1,974 0,850 1,991 0,849 1,710 1,288 1,160 1,280 1,339 1,37 0,668 1,210 1,574 0,860 0,148 1,347 1,488 0,480 1,366 1,121 1,915 1,442 1,270 0,595 0,324 0,710 0,327 0,676 1,217 0,751 0,454 0,875 0,931 1,298 1,792 1,700 1,950 1,402 1,583 0,421 1,345 0,346 0,596 1,512 0,600 1,125 1,533 1,916 0,374 0,851 1,409 1,675 0,229 1,947 1,437 0,113 0,737 0,777 0,692 1,681 1,115 0,484 1,535 1,228 1,945 1,519 1,130 0,22 1,700 1,206 0,178 1,2 0,132 1,336 1,588 1,202 0,915 0,714 1,468 1,22 1,155 1,994 1,956 1,343 0,105 1,65 1,173 0,361 0,209 1,577 1,123 1,519 0,458 1,719 0,203 1,924 0,154 1,570 1,221 0,238 1,845 0,540 0,709 0,545 1,168 0,31 0,369 0,696 1,208 0,102 1,431 1,270 1,424 0,142 1,914 0,277 0,389 0,105 0,485 0,67 1,524 1,100 0,550 1,171 0,126 1,102 1,118 1,728 0,526 1,656 0,376 0,175 0,835 1,103 1,581 0,923 1,217 1,356 1,255 0,385 1,264 1,408 1,22 0,177 1,917 0,171 0,607 0,813 0,261 1,234 0,601 1,919 0,92 1,653 1,655 1,841 0,602 1,407 0,148 1,411 1,319 1,4 1,805 0,531 0,432 0,929 1,594 1,834 0,963 1,444 1,730 1,868 0,356 0,394 0,159 0,690 1,555 0,670 0,901 1,73 0,927 0,842 1,24 1,719 0,757 0,288 1,543 0,442 0,505 1,805 0,960 0,387 1,741 1,732 1,955 0,917 0,989 1,470 1,967 0,699 1,623 1,919 0,325 1,17 1,737 1,725 1,252 1,422 0,532 0,812 0,818 0,525 0,718 1,367 1,18 0,373 0,372 0,450 0,360 0,291 0,786 1,471 0,196 1,231 1,383 0,685 0,498 0,333 0,252 1,796 1,295 1,116 0,591 1,968 1,965 0,102 0,607 1,939 1,340 0,594 0,960 0,310 0,850 1,786 0,538 0,320 0,24 1,302 1,330 0,427 1,523 1,598 0,630 1,455 1,134 0,403 0,177 0,813 1,718 0,144 1,452 1,658 1,222 0,634 1,196 1,750 1,220 0,110 1,597 0,328 1,816 1,148 1,749 1,235 0,168 0,215 1,729 1,809 1,413 0,716 0,337 1,519 0,513 1,498 1,624 1,352 1,612 0,580 0,877 0,610 0,475 0,992 1,489 1,940 1,903 0,91 0,127 0,206 1,238 1,639 0,20 0,83 1,87 1,210 1,799 1,386 0,81 0,107 1,823 0,181 1,877 1,37 0,421 0,679 1,967 1,586 1,61 1,120 0,803 1,302 1,36 1,972 1,278 0,145 1,189 0,864 1,424 0,591 0,380 0,58 1,409 1,708 1,352 1,43 0,258 0,267 1,561 1,786 0,506 1,757 1,784 0,37 0,372 1,584 0,623 1,786 0,921 1,77 1,880 0,778 0,961 1,479 0,125 1,236 1,520 0,558 1,897 1,764 1,644 1,353 0,260 0,162 0,528 0,354 0,103 1,701 1,800 0,77 0,722 0,568 1,140 0,513 1,650 1,783 0,85 0,994 0,997 1,425 0,617 0,836 0,569 1,363 1,44 0,63 0,365 0,87 0,656 0,235 0,728 0,13 1,72 0,380 1,539 1,160 1,511 0,817 1,621 0,722 0,5 1,463 1,350 0,77 0,829 1,562 0,43 1,70 1,652 0,376 1,622 0,384 0,742 0,947 1,333 0,549 1,760 1,758 1,204 0,571 0,828 1,535 0,581 1,890 0,973 1,750 0,890 1,637 1,756 1,86 0,45 0,422 0,123 1,710 1,575 0,477 0,742 0,752 0,872 1,737 1,926 0,648 0,427 0,961 0,127 1,729 1,975 0,486 1,23 1,728 1,312 1,418 0,617 1,875 0,243 1,46 1,880 0,541 0,626 0,765 1,768 1,641 1,171 1,594 0,839 0,619 0,495 0,981 0,162 1,452 0,993 1,279 0,805 1,641 0,7 0,722 1,864 0,908 0,558 1,116 0,94 1,197 0,870 1,545 1,592 0,973 0,497 0,818 0,257 0,725 0,739 1,646 1,972 0,394 0,916 0,829 0,356 0,83 1,396 0,27 1,732 0,912 0,128 0,525 0,936 1,222 0,349 1,209 0,274 1,41 0,478 1,19 1,747 0,444 1,605 1,291 1,93 1,631 0,867 0,754 0,909 1,907 1,667 1,579 0,792 0,662 0,113 0,892 1,528 1,747 1,430 1,89 0,190 0,345 0,331 1,841 1,390 0,909 0,482 1,888 1,908 0,419 1,881 0,669 0,797 0,98 1,203 0,106 1,710 1,940 1,888 1,472 1,123 1,195 1,986 0,447 0,156 1,330 0,789 1,281 1,451 0,302 1,663 1,303 0,868 1,973 0,408 0,878 1,263 1,213 0,349 0,729 1,898 1,81 1,467 0,269 0,244 1,478 0,337 0,548 1,250 0,806 1,942 1,346 0,744 0,687 1,861 0,221 0,852 0,964 1,242 0,789 0,184 0,145 0,277 1,134 1,495 0,97 1,22 1,808 1,866 0,212 1,959 1,722 0,975 1,379 0,767 0,147 0,691 0,254 1,100 0,146 0,449 1,306 0,462 1,438 1,339 0,445 0,436 1,336 1,151 1,346 1,144 0,65 1,878 0,240 0,825 1,773 1,796 0,24 0,716 0,834 0,984 1,744 0,251 1,403 0,101 1,933 0,437 0,850 1,863 1,483 1,325 0,706 1,624 0,477 0,742 0,36 0,709 0,628 1,758 0,922 0,279 1,620 0,750 0,540 1,669 0,517 0,11 1,379 0,837 1,525 0,460 0,461 1,871 0,624 0,276 0,955 1,191 0,645 0,159 0,101 1,246 1,536 1,221 1,770 0,736 1,87 1,335 0,804 1,456 0,677 0,986 1,535 0,991 0,935 1,90 0,146 0,323 0,874 0,766 1,368 1,664 0,483 1,566 0,666 1,225 0,694 0,35 1,345 0,946 1,856 0,63 0,270 1,144 0,899 0,729 1,298 0,508 0,865 0,839 1,942 1,621 1,96 0,530 0,971 0,345 0,470 0,996 1,730 0,936 1,234 1,249 0,970 1,427 0,60 1,933 0,786 0,690 0,269 1,748 1,102 0,240 1,583 0,75 1,522 0,481 1,900 0,235 0,636 1,690 1,628 0,300 0,93 1,81 0,148 0,949 1,388 0,975 1,46 1,965 0,647 0,753 0,439 0,564 0,540 0,998 1,379 1,331 0,340 0,48 0,306 1,804 1,42 1,749 0,569 1,533 1,144 1,443 0,561 0,552 1,671 0,81 1,286 0,250 0,162 0,580 1,138 0,494 0,5 0,442 0,498 1,6 1,416 1,455 1,824 1,417 0,432 0,559 1,528 0,587 0,153 1,747 1,977 0,150 1,35 0,414 1,120 1,319 0,307 1,921 1,681 0,352 1,62 0,416 1,240 0,374 0,405 1,493 0,519 0,977 0,649 1,890 0,376 0,445 1,404 0,615 1,672 1,961 1,577 1,571 1,101 1,797 0,986 0,286 0,974 1,432 0,226 1,755 1,118 1,259 1,12 1,657 1,948 0,67 0,186 1,11 1,107 0,12 0,500 0,709 0,485 0,235 1,822 0,843 0,982 1,811 0,651 0,872 1,662 0,89 0,661 1,523 0,35 1,145 0,120 0,874 1,944 0,876 1,878 1,828 0,906 0,583 1,214 0,491 0,566 1,91 1,975 0,496 1,472 1,551 1,442 0,42 0,539 0,466 0,486 0,22 1,781 1,156 1,763 0,500 0,442 0,48 0,191 1,320 0,991 0,418 1,849 1,202 0,486 0,294 1,139 1,126 0,879 0,185 0,849 0,111 1,543 0,808 1,536 1,6 0,273 0,11 0,161 1,604 1,56 1,645 1,158 1,719 1,730 1,355 1,787 0,25 1,650 0,237 1,556 1,246 0,616 0,478 0,557 0,615 1,877 0,401 0,169 1,397 1,640 0,737 0,893 1,710 0,157 0,319 1,287 0,174 0,168 0,574 0,896 0,469 0,771 1,476 0,91 0,833 1,720 0,946 1,172 0,945 0,604 1,374 0,498 1,429 1,604 1,434 1,99 0,236 0,673 0,379 0,842 0,152 0,136 0,153 0,912 1,149 1,967 1,797 1,798 1,865 1,592 0,179 1,349 1,782 1,333 0,421 1,446 1,431 0,92 1,924 1,249 1,832 1,390 0,137 0,998 0,332 0,702 0,116 0,615 0,884 0,872 0,281 1,65 1,228 1,147 0,963 0,337 0,393 0,748 0,349 1,504 0,223 0,817 1,5 0,568 1,70 1,834 1,460 1,218 1,8 1,807 1,474 0,540 1,865 1,314 0,29 1,227 1,995 1,248 0,336 1,195 0,311 1,365 1,912 1,492 1,190 1,274 1,536 1,473 1,247 0,684 0,84 1,187 0,485 0,508 0,459 0,234 0,514 1,581 1,204 1,534 0,471 0,693 0,390 0,626 0,292 1,148 0,662 1,740 0,78 1,417 0,262 1,823 0,547 1,221 0,300 1,619 0,338 0,382 1,909 0,258 0,177 1,363 0,467 1,412 1,963 1,846 1,863 1,303 0,155 0,207 0,449 0,169 0,974 1,689 0,500 1,827 1,249 1,610 0,518 1,675 1,70 1,627 0,746 1,234 1,478 1,819 0,597 0,607 1,509 0,107 0,682 1,316 1,373 1,95 0,87 0,103 0,461 0,311 1,124 0,488 1,51 0,412 1,347 1,867 0,647 1,723 1,572 1,242 0,191 0,654 0,879 0,416 0,832 0,783 1,656 0,502 0,568 0,484 0,489 1,755 0,337 1,849 1,378 0,168 1,361 1,537 1,278 1,606 0,628 0,614 1,323 1,53 1,379 1,641 0,760 1,758 0,222 1,965 0,285 1,150 1,765 0,14 0,429 1,676 1,565 0,245 0,495 1,187 1,345 0,380 0,886 1,28 1,979 0,987 1,481 0,543 0,160 1,249 0,961 1,53 0,527 0,854 0,808 0,355 0,972 1,713 1,137 1,96 1,325 0,740 0,27 1,562 0,885 0,666 1,47 1,586 0,341 0,285 0,5 1,672 0,392 0,459 0,961 1,816 0,972 0,499 0,952 1,139 1,781 0,12 0,802 0,969 1,18 1,337 0,948 0,870 1,751 1,60 0,227 0,39 0,482 0,751 0,795 1,305 0,487 0,305 0,253 0,251 1,235 0,688 1,396 1,82 0,699 1,256 1,599 1,498 0,987 0,524 1,720 0,315 1,941 0,322 0,444 0,539 1,42 0,241 0,407 1,960 1,160 1,26 0,549 0,748 1,11 1,353 0,80 0,42 0,0 1,633 0,744 1,17 0,707 1,526 1,65 1,131 0,288 0,397 0,147 1,0 0,916 1,350 1,749 0,951 0,71 0,549 1,404 0,764 1,960 1,181 0,332 0,986 1,179 1,340 1,70 1,67 1,829 0,886 1,988 1,957 0,779 0,57 1,344 1,639 0,541 0,975 1,501 0,605 0,211 0,594 0,982 0,728 0,238 1,87 0,285 1,427 1,847 0,409 1,260 0,410 1,706 1,649 1,27 1,800 0,322 0,974 1,432 0,930 0,828 0,408 1,768 1,101 0,448 0,606 1,158 0,858 0,32 0,570 0,46 0,465 1,615 1,385 0,814 1,101 0,87 1,184 0,418 0,124 0,314 1,101 0,84 1,200 0,2 1,832 0,26 1,466 0,272 0,315 1,994 1,733 1,786 0,669 0,654 0,599 1,602 0,455 0,276 0,173 0,429 1,6 0,709 0,957 0,147 1,694 1,631 0,114 1,469 1,300 0,587 1,359 1,641 1,949 0,537 0,525 1,446 0,182 0,784 1,403 1,137 0,433 0,386 1,390 1,202 0,412 1,416 0,902 1,691 0,45 0,142 1,128 1,630 0,425 1,477 1,664 0,271 1,875 1,798 1,23 0,137 1,517 0,720 1,865 0,95 0,638 0,982 0,849 0,722 1,875 1,190 1,581 1,883 0,975 0,295 1,633 0,465 0,904 1,487 0,737 1,170 0,830 1,81 1,227 1,792 0,192 1,778 0,247 0,680 1,816 1,272 0,475 0,313 0,78 0,984 0,905 0,397 1,489 1,272 0,443 1,780 1,181 1,204 0,192 1,371 0,456 1,9 1,477 1,422 1,180 0,975 0,32 0,985 1,866 1,742 0,15 1,316 0,324 0,470 1,769 0,468 0,605 1,638 0,254 0,365 1,570 1,467 0,878 0,3 0,974 1,830 0,551 0,415 0,321 1,792 1,148 0,240 0,609 1,340 0,964 0,143 0,632 1,528 1,919 1,128 0,305 1,202 0,16 0,441 1,416 1,530 1,122 0,309 1,349 1,459 1,782 1,972 0,787 0,236 0,460 0,252 1,95 1,689 0,170 1,19 1,526 0,264 1,396 0,559 1,814 1,772 0,807 0,789 0,331 0,516 1,219 0,557 0,785 1,171 0,707 1,911 0,928 0,613 0,479 0,504 1,535 0,101 1,204 1,905 0,243 0,634 0,150 0,601 1,928 1,421 0,441 1,944 0,450 0,743 1,678 0,38 1,154 0,945 0,920 0,574 0,879 0,983 0,947 0,909 1,299 1,5 1,569 1,753 1,358 0,455 1,284 0,314 1,123 1,35 0,26 1,983 1,540 0,85 0,731 1,921 0,980 1,744 1,958 0,453 0,266 0,721 1,808 0,111 0,51 0,797 1,353 1,466 0,103 0,572 1,940 0,871 0,247 0,239 0,552 0,689 1,191 0,858 0,305 1,901 1,481 1,79 0,502 0,28 0,266 0,319 1,376 0,179 1,233 1,630 0,244 0,598 1,142 0,842 1,129 1,681 1,418 0,748 0,951 1,28 0,207 1,129 1,977 0,910 0,564 1,576 1,825 0,861 0,93 0,762 1,350 0,660 1,405 1,8 1,682 0,639 0,162 1,974 1,110 1,645 1,378 1,939 0,45 0,345 1,620 1,335 1,257 0,562 0,996 1,63 0,165 0,957 1,572 0,204 0,501 0,811 0,941 0,804 0,870 0,71 0,486 1,986 1,78 0,34 0,779 1,883 0,867 0,54 0,414 1,487 1,404 1,442 0,247 0,391 1,762 1,954 0,297 0,954 0,955 0,926 1,438 0,269 1,774 1,892 1,609 1,641 1,882 1,764 0,974 0,283 1,410 1,944 1,737 1,139 0,73 1,389 0,884 1,835 1,417 0,804 1,562 1,683 0,641 1,127 0,43 0,692 0,893 1,449 0,419 0,8 0,723 0,807 1,101 0,827 0,634 0,931 1,922 0,389 0,848 1,445 1,415 1,162 0,399 1,255 0,284 0,266 1,936 0,507 1,123 0,576 1,48 0,858 0,171 1,513 0,674 0,838 1,104 1,814 0,43 0,689 0,918 0,845 0,492 1,796 1,635 1,865 0,268 0,765 1,242 0,169 1,573 0,658 0,775 1,707 1,119 1,515 1,598 1,100 0,566 0,680 0,901 1,237 1,499 1,578 0,172 1,560 1,844 0,805 1,60 0,480 1,45 1,990 1,934 0,359 1,816 1,366 0,635 1,128 1,454 1,812 0,750 0,30 1,931 0,410 1,510 0,752 0,161 0,943 0,939 1,254 1,211 0,108 1,0 0,362 0,504 1,800 0,287 1,631 0,610 1,366 0,853 0,532 1,868 1,487 1,761 1,593 0,138 1,749 1,905 1,537 0,830 1,507 0,299 1,576 1,787 0,816 1,93 0,778 0,695 1,722 0,551 1,649 1,207 0,124 0,603 0,750 0,674 0,17 0,948 0,290 1,275 0,587 1,930 1,393 0,157 0,664 1,230 0,414 1,364 1,78 1,689 0,152 0,127 1,392 0,332 0,504 0,607 0,931 1,279 0,829 0,731 0,626 1,237 0,141 0,503 0,607 1,692 1,218 1,186 0,674 0,938 1,856 1,732 0,734 0,619 0,81 1,253 0,77 1,611 1,182 1,199 1,378 0,110 0,745 1,250 0,495 1,845 0,217 1,166 0,75 0,260 0,499 1,640 0,750 1,173 1,946 1,645 0,25 0,159 1,668 0,550 0,918 1,140 1,270 0,938 0,973 1,931 0,699 0,491 1,907 1,259 1,383 0,359 1,13 1,168 1,662 0,924 0,906 0,332 0,764 1,996 1,456 1,551 1,999 1,341 1,457 0,692 1,217 0,7 0,562 1,198 0,385 1,756 1,435 0,589 1,936 0,349 0,744 1,643 0,386 0,143 0,510 1,68 0,344 1,852 0,662 0,429 1,115 0,448 1,512 1,360 0,281 1,886 1,332 1,355 1,729 1,692 1,278 0,928 0,586 1,195 0,902 1,635 1,622 1,50 0,53 1,419 0,614 1,452 1,102 0,181 1,70 1,252 0,526 0,338 1,237 1,555 1,855 0,659 1,664 1,388 1,261 1,668 1,93 0,949 0,458 0,827 0,475 1,234 0,711 1,997 1,350 0,809 1,324 0,647 1,762 1,884 0,175 0,419 1,121 0,91 0,579 1,643 1,185 1,803 1,788 1,501 0,358 0,298 0,541 1,844 0,876 0,622 1,671 1,696 0,337 1,124 0,53 1,933 1,8 0,850 0,138 1,303 1,458 0,931 1,564 0,907 1,500 1,271 0,45 1,721 1,902 0,585 0,490 1,800 0,923 0,487 1,929 0,617 0,381 1,217 1,638 0,822 1,718 0,809 1,840 0,152 0,331 0,47 1,829 0,202 0,150 1,980 1,647 0,653 0,525 0,144 0,694 0,457 0,723 1,284 1,82 1,785 1,44 1,391 0,487 1,224 0,449 1,139 1,399 0,617 1,95 1,932 1,193 0,598 1,630 1,140 1,113 1,667 1,197 1,670 0,501 1,272 0,589 1,918 0,175 1,986 0,832 1,634 0,737 0,848 0,390 0,51 0,443 1,920 0,552 1,446 0,944 1,992 1,123 1,875 1,283 1,364 0,109 1,245 1,792 1,701 0,232 1,987 1,154 1,519 0,546 1,928 0,1 0,348 1,65 0,275 1,386 1,376 1,680 0,235 0,654 0,386 1,958 1,650 1,528 0,574 1,702 0,724 1,907 0,851 1,693 0,453 0,98 1,461 0,269 1,818 0,551 0,32 0,770 1,533 0,760 0,18 0,142 1,913 0,649 1,675 1,52 1,179 0,494 1,167 0,161 0,321 1,694 0,269 0,404 0,789 1,744 1,321 0,364 0,617 1,60 1,51 0,708 0,416 0,306 1,691 1,335 0,385 0,42 1,155 1,758 0,746 0,291 1,822 0,620 1,757 1,603 1,929 1,357 1,254 0,270 1,58 1,922 0,751 1,864 0,963 1,176 0,84 0,899 0,532 0,459 1,374 0,272 1,824 1,592 0,341 0,194 1,367 1,543 0,180 0,80 0,614 0,670 1,443 1,242 0,261 0,434 1,294 0,660 0,490 0,23 1,131 1,194 0,418 0,848 0,286 1,649 1,566 0,244 1,587 0,399 1,54 0,808 0,444 1,204 1,938 1,561 0,589 0,163 0,326 0,823 1,834 0,787 0,23 1,795 1,973 1,199 0,415 1,906 1,651 0,586 1,32 1,436 1,645 1,3 1,216 0,951 0,173 1,695 1,570 0,758 1,391 0,876 1,111 1,669 0,719 0,881 0,561 0,653 1,832 1,574 0,349 1,759 0,192 1,142 0,279 1,73 1,814 1,606 0,188 1,923 0,700 1,635 0,294 1,367 1,957 1,676 0,24 1,514 1,647 0,604 0,637 1,203 0,596 0,717 0,619 1,281 0,861 0,881 1,526 1,353 0,922 1,284 0,759 1,972 1,952 0,586 0,449 0,395 0,560 0,838 1,867 1,591 1,378 1,478 0,511 0,352 1,594 1,278 1,298 1,756 1,236 0,923 1,768 1,754 1,84 0,230 1,450 1,511 1,913 0,550 0,448 0,290 1,100 0,391 0,698 1,163 1,17 0,100 0,395 0,795 1,641 1,411 0,523 0,230 0,133 1,562 1,321 0,313 0,157 0,260 0,299 0,800 0,831 0,706 0,336 1,751 0,175 1,478 1,849 0,813 1,458 0,506 0,356 1,640 0,831 0,0 1,38 1,711 0,324 1,535 0,57 1,115 0,543 1,560 0,350 1,281 0,578 0,401 0,400 1,234 0,855 0,401 1,914 0,156 1,192 1,340 1,21 0,677 1,448 1,533 0,204 1,405 0,761 1,666 1,45 1,329 0,656 0,587 1,819 1,107 1,171 1,240 0,846 0,652 0,508 0,14 0,119 0,383 0,856 0,392 1,282 1,257 1,441 1,852 0,192 1,957 0,635 0,728 0,333 0,382 0,780 0,225 0,420 0,807 0,620 1,477 1,852 0,223 0,20 0,168 0,480 0,846 0,864 0,496 1,590 1,50 0,732 0,216 1,20 0,513 0,503 1,65 1,838 0,976 1,903 1,672 1,497 1,846 0,995 1,499 0,784 1,551 0,734 0,271 0,289 0,166 1,509 1,122 0,37 0,542 0,618 1,179 0,78 1,450 1,602 1,250 0,236 0,460 0,622 1,694 0,363 1,761 1,606 0,486 0,268 1,909 0,465 1,375 1,483 0,380 0,673 0,206 1,604 1,0 1,376 0,592 0,38 1,756 0,955 0,894 0,90 1,236 1,775 0,774 0,34 0,337 0,25 1,409 0,504 0,118 0,53 0,79 1,275 1,623 0,228 1,446 0,323 1,730 1,232 0,631 0,384 1,765 0,687 0,321 0,257 1,176 0,991 0,616 0,57 1,896 1,449 1,673 1,539 0,840 0,680 1,835 0,73 1,674 0,9 1,48 1,254 0,377 1,500 1,74 1,966 1,476 0,747 1,98 1,577 0,389 0,565 1,765 1,984 0,166 0,270 1,631 1,216 1,339 0,316 0,322 1,271 1,461 1,511 1,632 0,128 0,253 0,852 1,687 1,764 0,673 1,386 1,784 1,121 0,919 1,618 1,82 1,104 1,219 1,789 0,878 0,736 1,136 1,989 0,258 1,724 1,64 0,676 0,754 1,911 1,238 0,708 1,177 0,79 0,563 1,683 1,366 1,965 0,8 1,649 1,157 0,904 1,138 0,790 0,546 0,289 1,613 1,79 0,445 1,364 1,426 1,965 0,481 1,565 1,842 0,933 1,643 0,88 0,589 1,76 1,490 1,670 0,400 1,805 0,25 0,620 1,637 0,581 0,566 0,409 1,993 0,946 0,298 1,616 1,895 0,209 1,234 0,793 1,25 0,283 0,414 1,284 0,523 1,72 0,473 1,146 0,912 0,299 0,285 1,228 1,888 0,377 1,777 0,685 1,865 0,549 0,358 0,902 0,726 1,837 1,595 0,906 0,286 0,755 0,15 1,305 0,353 1,88 0,40 1,744 0,581 1,779 1,342 1,407 0,4 1,808 0,615 1,303 1,866 1,504 1,461 0,925 1,636 0,396 1,742 0,545 1,606 1,618 1,323 1,416 0,860 0,892 0,655 1,240 1,363 0,659 0,809 1,778 0,994 1,158 0,463 1,127 1,403 1,491 0,805 0,559 1,346 0,8 1,987 0,263 1,287 0,533 0,112 1,575 0,804 0,23 0,476 1,67 1,300 0,512 0,153 0,523 0,42 0,195 0,485 0,452 0,213 0,240 1,480 1,510 1,941 0,744 0,24 1,672 0,858 1,325 0,959 0,922 0,598 1,815 1,419 1,814 1,72 1,826 1,134 0,452 0,580 1,531 0,104 0,263 0,650 0,185 0,822 1,620 1,737 0,925 1,983 1,430 0,513 0,128 1,308 0,435 1,837 1,392 1,14 1,703 1,899 1,5 0,303 0,36 1,169 1,230 1,181 0,835 0,835 1,251 1,757 0,189 0,83 1,210 1,748 1,61 0,226 0,120 1,508 0,841 1,220 1,423 1,833 0,649 0,683 1,671 0,452 1,717 0,231 0,779 0,318 0,425 1,333 1,486 0,38 1,539 1,768 1,514 0,496 0,55 1,648 1,814 0,400 1,809 0,154 0,808 1,690 0,642 1,323 1,643 1,738 0,961 0,115 1,750 1,347 0,157 1,206 1,23 1,758 0,65 0,784 1,106 1,41 1,159 1,834 1,997 0,733 1,291 0,935 1,211 0,388 0,50 1,966 0,696 1,690 0,1 1,213 0,198 1,990 0,472 1,72 1,984 0,903 0,585 1,468 0,118 0,913 0,838 0,687 1,668 1,697 1,24 1,678 0,564 0,836 0,817 1,455 0,376 1,181 0,524 1,350 1,124 0,877 0,173 1,81 1,924 0,20 0,655 1,45 1,130 0,18 0,361 0,480 0,883 0,878 1,75 1,128 0,390 0,577 0,667 0,851 1,233 0,419 1,147 1,308 1,91 1,190 0,592 1,141 0,518 1,41 1,943 0,366 1,321 0,227 0,855 0,174 0,491 0,570 1,743 1,949 1,871 1,91 1,865 1,180 0,683 1,967 1,556 1,750 1,961 1,453 0,3 0,341 0,497 0,151 0,40 0,55 1,393 1,151 1,63 0,63 0,339 1,222 1,269 1,745 1,256 1,466 0,861 0,558 0,670 1,943 1,552 1,406 1,892 1,701 0,680 0,654 0,364 0,131 1,627 0,995 1,310 1,427 0,266 0,344 0,716 1,713 1,176 0,245 0,194 1,389 0,537 1,351 1,188 1,108 0,675 1,785 0,149 0,559 1,835 0,103 0,311 0,544 0,77 0,754 1,304 1,546 1,645 0,335 0,595 1,364 0,469 0,70 0,225 0,166 1,841 1,554 1,345 1,424 0,460 1,204 1,539 0,630 0,803 1,741 1,695 0,96 0,108 0,785 0,337 0,562 0,857 1,338 1,600 0,857 1,237 1,329 1,9 1,30 0,662 0,627 0,801 0,609 1,245 0,734 0,94 0,783 1,344 0,662 0,601 1,514 0,507 0,413 0,752 0,839 0,933 0,749 1,996 0,878 1,8 1,606 1,185 0,419 1,767 0,53 1,135 0,379 0,128 1,549 0,638 1,161 1,32 0,854 0,127 0,92 1,623 0,638 1,578 1,880 0,336 1,786 0,866 0,623 1,36 0,259 1,216 1,467 0,931 1,831 0,14 0,779 0,550 0,874 0,494 0,947 0,854 0,410 0,670 1,219 0,892 1,44 1,246 1,401 0,393 0,498 1,851 1,725 0,790 1,544 0,308 0,725 0,330 0,428 0,100 1,777 1,124 1,810 0,902 1,695 1,795 1,792 0,968 1,634 0,309 1,49 0,708 0,278 1,978 1,58 1,384 0,575 1,294 0,544 1,370 1,254 1,559 1,109 0,290 0,418 0,452 0,658 0,62 0,85 1,466 0,989 1,5 0,120 1,842 1,740 1,944 0,286 0,66 1,611 0,865 0,534 0,81 1,438 0,412 1,278 0,438 0,989 0,906 1,404 1,648 0,784 0,488 0,485 1,846 0,241 0,391 1,758 0,338 1,314 0,45 0,941 1,676 0,661 1,252 0,665 1,164 1,63 1,815 1,64 0,274 0,492 0,326 1,778 1,858 1,136 1,978 0,977 0,100 0,538 1,993 1,228 0,985 0,854 1,34 1,982 1,786 1,40 0,1 0,807 0,227 1,690 1,839 0,201 1,176 0,129 1,217 1,879 0,441 1,384 1,69 1,183 0,345 1,332 0,267 1,113 1,442 1,858 1,691 1,258 1,820 1,997 0,27 0,220 1,458 1,895 1,11 1,623 0,603 1,503 0,202 1,387 1,657 1,333 1,599 0,955 0,883 0,13 1,726 0,533 1,507 1,33 0,765 0,897 1,690 0,783 0,603 1,946 1,288 0,112 0,250 1,726 1,745 0,358 0,580 0,526 1,336 0,750 0,286 0,884 1,145 0,62 0,72 1,417 0,236 1,423 0,615 0,900 1,108 1,802 0,79 0,358 1,223 0,175 1,721 1,442 0,386 0,578 1,82 1,33 0,506 0,202 1,865 1,753 1,714 1,140 0,183 0,755 0,588 0,420 1,834 1,271 1,515 0,118 1,850 1,113 1,953 0,867 1,299 0,678 1,167 0,634 1,701 1,724 0,145 1,549 1,495 1,883 1,65 1,79 0,66 0,942 0,321 1,537 0,99 0,731 0,947 1,698 0,796 1,686 0,673 0,545 0,744 0,477 1,827 0,277 1,113 1,160 0,139 1,313 1,95 0,802 0,123 1,338 0,969 0,628 0,423 0,341 0,964 0,873 1,912 1,38 1,213 0,78 0,102 0,75 0,239 1,878 1,595 1,536 0,17 1,551 0,635 0,21 1,572 1,437 1,808 1,498 1,154 1,82 0,821 0,67 0,246 0,681 1,50 1,81 0,631 1,616 0,753 0,74 1,62 1,77 0,675 1,773 0,144 0,267 1,516 1,209 1,59 0,362 0,233 1,517 1,986 1,839 1,174 0,508 1,190 0,424 1,56 1,367 1,611 0,893 1,157 1,696 1,819 1,146 0,622 0,291 0,398 0,265 0,7 1,881 0,424 0,359 0,228 1,680 1,105 1,566 0,45 0,846 0,681 1,652 1,634 1,378 1,178 0,458 1,163 0,748 1,815 0,564 0,102 1,227 0,257 1,485 0,163 0,346 1,14 0,897 0,741 0,165 0,522 1,896 0,337 1,703 1,105 1,90 1,842 1,596 0,608 1,76 0,386 1,65 1,419 1,249 0,234 1,864 0,605 1,142 1,109 0,467 1,557 1,143 0,775 1,731 1,627 0,137 1,166 1,369 1,267 0,748 1,143 0,959 0,687 1,954 0,581 1,368 1,922 1,21 0,885 1,813 0,678 0,6 1,569 0,801 0,451 0,304 1,844 1,831 1,128 1,142 1,287 1,813 1,563 0,813 1,648 0,710 1,439 1,233 1,948 1,739 1,630 1,561 0,516 0,42 1,39 0,808 0,618 1,939 1,806 1,372 1,966 0,791 0,659 0,838 0,996 1,583 1,839 0,96 0,806 1,868 0,991 1,270 1,179 0,618 0,560 1,240 1,108 0,499 1,746 1,982 1,500 1,902 1,474 0,206 0,144 0,345 0,511 1,991 1,288 0,881 0,635 0,125 1,438 1,837 1,796 1,160 1,274 0,351 1,849 1,625 0,919 1,300 0,427 1,626 1,592 0,537 0,588 1,742 0,636 1,983 0,98 1,545 1,384 0,440 1,460 1,964 0,389 1,150 0,581 1,197 0,133 0,81 0,45 0,158 0,349 1,677 0,461 1,974 1,152 1,650 0,426 1,373 0,707 0,409 0,863 0,794 1,738 0,410 1,727 0,699 0,175 1,113 1,573 0,287 0,327 0,818 0,908 0,995 0,913 1,591 1,845 0,392 0,445 0,745 1,815 1,894 0,58 0,697 1,256 1,948 0,946 0,561 1,906 0,606 1,302 1,270 0,722 0,269 1,412 1,709 0,682 1,846 0,912 1,533 0,612 0,365 1,831 0,805 0,225 0,266 1,335 0,278 0,966 1,386 0,824 1,151 0,960 0,682 0,84 0,932 0,637 1,71 1,192 0,571 1,753 0,422 1,297 1,435 1,941 0,835 1,105 1,848 0,136 1,480 0,762 1,440 0,169 0,500 0,589 0,378 0,188 1,475 1,771 0,500 0,271 1,720 0,841 1,997 0,435 0,696 0,870 1,400 0,652 0,763 0,472 1,836 0,843 0,537 1,462 1,468 0,427 0,654 1,185 0,664 0,242 0,424 0,683 0,85 1,929 1,149 0,275 1,41 0,962 1,517 0,524 1,418 0,914 1,461 0,999 0,309 0,69 1,551 1,239 1,538 1,872 0,213 0,738 0,863 0,570 1,989 0,951 0,239 1,235 1,602 0,119 1,136 0,686 1,356 1,158 0,1 1,833 1,467 1,419 0,398 0,586 0,339 1,155 0,196 0,159 1,500 0,317 0,769 1,245 1,563 1,421 0,728 0,437 1,151 0,716 0,771 1,20 0,846 1,543 0,117 1,587 1,914 0,110 0,38 0,573 0,542 1,171 1,796 1,690 0,634 1,659 0,435 0,229 0,389 0,777 1,712 1,437 0,559 1,878 0,10 0,2 1,797 0,24 0,614 1,188 0,244 0,370 1,803 1,210 0,376 0,557 0,683 1,377 0,712 0,575 0,438 1,969 1,609 1,212 1,91 1,841 1,960 1,703 0,428 1,806 0,966 1,637 1,192 1,313 1,17 1,242 0,850 0,219 1,945 0,628 0,211 1,16 0,411 1,741 1,521 1,376 0,155 0,65 1,839 1,234 0,14 0,192 1,513 1,61 1,488 1,618 0,954 0,259 1,960 0,61 1,613 0,382 1,885 0,809 0,35 1,821 0,108 0,746 0,784 1,192 1,363 1,894 1,291 1,422 1,571 1,105 0,444 1,783 1,185 1,427 0,504 0,297 0,882 1,758 0,242 1,398 1,996 1,258 0,486 1,213 1,706 0,675 0,867 1,510 1,732 1,547 0,498 1,859 1,559 1,622 0,253 1,23 1,75 1,103 0,889 0,122 1,482 1,430 1,427 0,527 1,135 1,911 0,653 0,336 0,858 1,823 1,129 1,10 0,106 1,143 0,166 1,511 0,990 0,752 0,672 0,728 1,195 1,797 0,927 1,198 1,97 0,218 1,969 0,694 1,272 1,597 0,76 1,129 0,107 1,139 1,982 0,377 1,283 0,662 1,686 1,640 1,535 0,195 0,748 0,662 1,677 0,440 0,558 1,477 0,852 1,453 0,228 0,798 0,316 1,179 1,330 1,868 0,803 0,876 1,554 1,338 0,810 0,305 1,196 0,991 0,127 1,316 1,666 0,293 0,114 0,174 0,989 0,768 1,114 1,514 1,769 1,439 1,89 0,358 1,421 0,343 1,760 0,19 0,799 0,251 0,414 1,512 1,367 1,768 0,231 1,44 1,669 1,569 1,404 1,780 0,986 1,851 1,277 0,287 1,105 0,745 1,95 1,973 1,244 0,518 0,867 1,483 0,729 1,400 1,346 0,43 1,437 0,348 1,29 0,913 1,804 1,167 0,223 1,298 0,608 0,151 1,660 0,576 1,236 1,264 1,964 1,320 1,256 0,829 0,306 0,212 1,905 1,642 0,658 1,481 0,813 1,584 1,777 0,481 0,92 0,819 0,582 0,62 1,38 0,137 1,340 0,467 1,401 1,712 0,959 0,188 0,295 1,658 0,248 1,804 1,539 1,306 0,901 1,989 1,800 1,793 1,29 1,661 0,513 0,895 0,615 1,471 1,535 0,373 0,879 0,316 1,34 1,743 0,447 1,555 1,168 1,143 0,842 1,539 1,579 1,682 0,93 0,362 1,178 1,444 1,964 0,13 1,506 1,692 0,377 0,163 1,714 0,611 0,766 0,554 1,246 0,747 0,84 0,359 1,662 1,263 1,154 0,499 0,762 1,501 0,880 0,403 1,812 0,987 1,11 1,133 0,665 0,246 0,886 0,988 1,673 0,421 1,310 1,767 1,609 0,393 0,495 0,248 0,519 0,546 0,851 0,323 0,561 1,190 1,892 0,945 1,523 1,869 1,164 1,452 1,124 0,325 0,143 1,618 1,38 1,770 0,515 1,698 0,434 1,228 1,691 1,969 0,971 1,321 0,795 0,118 0,238 1,4 1,792 1,65 0,854 1,531 0,574 0,661 0,196 1,153 1,517 0,320 1,62 1,981 0,41 1,729 0,321 1,474 0,870 1,974 0,743 1,52 0,544 1,88 0,876 1,828 1,125 0,311 0,203 1,743 1,755 1,920 0,538 0,747 1,131 1,198 1,122 1,149 1,694 1,580 1,226 0,671 0,359 0,516 0,581 1,572 0,76 1,178 0,307 1,237 1,361 0,270 0,440 1,646 0,219 1,886 1,491 1,789 1,245 0,629 0,701 0,110 1,429 1,401 0,784 0,300 1,56 0,792 1,488 0,411 1,162 1,427 1,11 0,452 0,942 0,125 1,339 0,858 1,227 0,235 0,826 1,417 1,645 1,831 0,7 0,62 0,791 0,759 1,949 1,370 0,152 1,458 1,952 1,112 0,759 0,953 1,288 1,437 1,602 0,605 0,685 0,477 1,67 1,824 1,153 0,48 0,124 0,461 0,769 1,671 1,453 0,700 0,639 0,479 0,766 0,214 0,130 1,330 0,70 1,919 1,918 0,162 0,674 0,630 1,716 1,368 1,222 0,855 0,541 0,517 1,500 0,112 1,711 0,329 1,273 0,986 1,45 0,53 1,793 0,599 1,851 1,666 0,529 0,984 0,940 1,835 0,501 0,377 0,913 0,454 1,988 0,882 0,320 0,469 1,493 0,300 1,38 0,81 0,921 0,41 1,510 0,354 1,61 1,189 0,426 1,748 1,726 0,46 0,499 1,88 0,521 0,943 1,293 1,433 0,610 1,615 1,133 1,689 1,806 0,785 0,621 0,20 1,187 0,371 0,834 0,781 1,922 0,944 1,164 1,240 1,42 1,754 1,296 0,991 1,605 1,511 1,86 0,406 0,302 1,56 1,72 0,294 1,199 0,322 0,282 1,156 0,897 1,958 1,44 0,671 1,945 1,326 1,852 0,929 1,282 0,305 1,80 1,679 1,799 1,827 0,377 0,127 1,499 1,203 0,61 1,784 1,489 0,934 0,917 0,598 0,774 1,173 1,355 0,836 1,847 0,236 1,44 1,894 1,940 0,310 1,500 0,453 1,254 0,2 0,599 0,242 1,163 0,95 0,366 1,980 0,293 0,919 1,127 1,419 1,707 1,512 0,492 1,509 1,283 0,811 1,489 1,524 1,722 1,701 0,153 1,42 1,667 0,796 0,893 0,722 0,121 0,975 0,390 0,702 1,397 1,613 0,993 0,55 0,49 0,443 1,158 0,653 0,952 1,623 0,310 0,135 1,131 1,170 1,862 0,757 0,293 1,29 1,832 1,544 1,881 0,88 1,627 0,536 0,290 1,404 0,772 0,618 0,347 0,447 1,294 0,65 0,868 1,35 0,378 0,737 0,236 1,813 1,140 1,345 1,113 0,388 1,906 0,701 1,284 0,988 0,886 1,660 0,448 1,319 0,22 0,533 1,10 1,331 0,206 1,315 1,381 1,578 0,3 0,562 1,901 0,311 0,956 0,557 0,477 1,623 1,653 0,865 0,936 1,991 1,73 0,708 1,506 0,124 1,937 1,837 1,891 1,155 0,990 0,44 1,103 0,829 0,23 1,940 0,307 1,554 1,355 0,276 0,879 1,620 1,835 1,801 0,323 1,319 1,827 1,111 0,471 0,846 1,473 0,269 1,863 0,766 0,269 1,438 1,304 1,83 0,792 1,146 1,478 1,532 0,239 0,111 0,950 0,914 1,374 0,468 0,131 0,403 1,526 1,556 0,938 0,872 0,600 1,577 0,586 0,941 1,847 1,62 0,82 0,387 1,641 1,110 1,459 1,739 1,708 0,853 1,576 1,972 0,858 1,100 0,302 0,83 1,798 0,835 1,630 1,589 0,768 1,835 0,453 0,265 0,425 1,439 0,635 1,775 0,498 0,46 0,980 0,440 1,54 0,759 0,152 1,478 1,475 0,181 0,62 1,113 0,479 0,275 0,537 1,372 1,477 1,939 1,979 0,954 1,929 0,549 0,857 0,136 1,114 0,566 1,94 1,807 1,759 1,879 0,496 1,468 1,302 0,841 0,869 0,652 1,963 1,4 1,639 1,370 1,970 1,955 0,182 0,651 1,652 0,494 1,208 1,556 0,70 1,946 1,474 1,999 1,380 1,55 0,63 1,587 1,670 1,656 0,646 0,897 1,915 0,566 1,235 0,858 1,645 0,799 1,752 1,716 1,358 1,222 0,42 1,139 1,530 1,182 0,629 0,331 0,158 0,267 0,489 0,433 1,225 1,468 0,728 1,39 0,640 1,659 0,31 1,657 1,178 1,697 0,710 1,501 1,995 1,169 1,447 0,490 1,11 1,835 1,373 1,612 0,211 0,33 1,426 1,72 1,564 1,260 0,935 0,913 0,663 1,826 0,961 0,56 1,315 1,517 1,222 0,980 0,680 0,568 0,819 1,739 0,467 0,158 0,869 0,48 1,822 1,906 0,249 1,748 1,499 1,478 0,128 1,85 0,212 0,421 0,214 0,422 1,153 1,932 1,474 1,608 0,417 0,899 1,229 0,893 0,600 0,799 1,284 0,634 0,635 0,249 1,604 0,585 0,637 1,619 1,120 0,766 1,588 0,408 0,646 1,743 1,787 0,332 1,835 1,174 0,660 0,320 0,970 0,440 1,870 0,54 0,779 0,880 0,932 1,311 0,464 1,84 0,105 1,647 0,484 1,937 1,668 1,277 1,18 1,275 1,93 0,95 0,99 1,363 0,468 1,322 1,696 1,491 1,700 1,723 0,997 1,306 1,650 1,424 1,569 1,88 1,914 0,106 0,466 0,395 1,680 1,817 1,504 0,810 0,241 1,754 1,678 1,974 1,564 0,900 0,60 0,591 0,913 0,184 0,348 0,358 1,230 1,324 1,198 0,891 0,712 1,431 0,890 1,395 0,635 0,312 1,685 1,398 0,667 0,200 0,705 0,734 0,667 0,693 0,869 0,213 0,521 0,931 1,231 1,422 1,304 1,928 1,903 1,396 1,239 0,945 0,123 0,92 0,545 0,910 1,488 0,625 0,733 1,921 0,16 1,672 0,938 0,109 1,253 0,829 0,259 0,453 0,712 1,483 1,621 0,300 1,817 0,915 1,219 1,12 0,149 1,374 0,889 1,445 1,699 0,133 0,799 0,340 0,952 0,520 0,646 0,713 0,236 0,885 1,735 0,393 1,739 1,858 1,455 0,57 0,21 0,588 0,440 1,160 1,747 0,57 0,47 1,652 1,151 1,624 0,853 1,317 1,163 0,940 0,776 1,240 0,401 0,510 0,301 1,185 0,340 0,826 0,646 1,928 1,830 0,835 1,684 1,75 0,755 0,393 0,468 0,924 0,12 1,105 1,206 0,413 0,460 0,297 0,490 1,82 1,468 0,616 1,986 0,337 0,566 1,926 0,837 0,755 0,879 0,977 0,246 0,85 0,736 1,456 1,36 1,147 1,869 1,733 0,173 1,918 0,956 1,549 1,228 0,919 0,809 1,605 0,256 0,96 1,554 1,559 1,265 0,63 0,674 0,623 1,835 0,287 0,204 0,483 1,667 1,722 1,360 0,810 1,149 1,822 1,315 1,0 0,568 1,789 1,796 0,288 1,215 1,102 0,906 1,503 1,543 1,855 1,209 1,189 1,310 0,42 0,419 1,71 1,559 0,197 0,987 0,160 1,624 1,288 1,650 1,700 0,274 0,166 0,585 1,228 0,278 1,787 0,983 1,63 0,492 1,742 1,4 1,47 0,422 0,466 0,29 1,616 0,505 1,301 1,757 0,204 1,896 0,354 0,671 0,709 1,48 0,905 1,588 0,657 1,403 1,869 0,807 1,210 1,886 0,526 1,106 0,363 0,666 1,39 1,420 0,518 1,383 0,118 1,765 1,18 1,462 0,294 1,101 0,806 1,303 1,321 0,318 0,460 0,638 1,22 0,848 0,223 0,578 0,872 1,14 0,984 1,560 0,315 0,435 0,826 1,286 1,405 0,799 0,43 1,941 0,830 1,286 1,103 0,210 0,331 1,499 1,73 1,426 1,554 0,270 1,627 0,48 0,775 1,57 1,904 1,471 0,394 1,946 1,839 1,817 1,808 0,192 1,631 0,286 1,13 0,417 1,275 1,238 0,921 1,807 1,426 0,926 0,628 0,37 0,50 0,471 1,197 0,707 0,502 1,540 0,139 1,206 0,554 1,1 1,668 0,234 1,422 0,64 0,112 1,98 1,980 0,138 0,74 1,802 0,444 0,171 1,723 0,553 1,362 1,764 1,734 1,538 1,314 1,145 0,590 0,107 1,645 0,534 1,623 1,477 0,150 0,847 0,766 1,555 1,157 0,517 0,717 1,567 0,341 0,385 1,400 1,606 0,535 0,23 1,792 1,788 0,564 0,966 1,234 0,368 1,428 1,319 1,923 0,175 1,245 1,408 1,881 1,524 0,796 1,390 1,180 1,306 1,950 0,861 0,656 1,911 1,581 1,188 0,362 0,86 0,533 1,526 1,359 0,198 0,669 1,6 1,74 0,812 1,564 0,210 0,577 1,455 1,791 0,406 0,914 0,159 0,620 1,868 0,676 1,701 0,234 0,761 1,822 1,502 1,199 0,757 1,780 0,41 0,734 1,902 1,965 1,514 0,227 0,74 1,660 0,873 0,794 1,772 0,481 1,908 0,250 1,860 0,46 0,775 1,963 1,387 1,84 0,802 1,611 1,276 1,150 1,251 0,631 0,443 1,315 1,375 0,858 0,646 0,270 1,577 1,663 1,200 0,293 0,156 1,7 0,951 0,193 1,535 1,621 1,977 1,930 1,819 1,401 0,413 0,59 0,150 1,356 1,724 0,608 0,191 1,366 0,327 0,523 1,424 0,830 1,613 0,123 0,779 1,485 0,646 1,129 0,768 0,182 1,314 1,54 1,395 1,246 0,805 0,475 0,934 0,690 1,291 1,541 1,333 0,374 1,346 0,745 0,105 1,559 1,423 0,709 0,638 0,185 0,721 1,154 0,591 1,715 1,756 0,418 0,606 0,962 1,716 0,36 1,906 1,337 0,190 0,618 1,937 1,502 0,117 1,979 1,604 0,186 1,475 1,588 0,278 1,699 1,486 0,421 0,531 0,878 0,373 1,902 1,238 1,557 0,794 0,647 1,254 0,444 0,744 1,296 1,16 1,42 1,696 1,361 1,248 1,114 1,695 1,511 1,484 1,905 0,386 0,403 0,470 1,935 1,260 0,920 0,380 0,825 0,178 0,137 0,221 0,262 1,521 1,213 1,725 0,365 0,245 0,799 1,376 0,673 0,536 0,313 1,280 1,543 1,727 0,652 1,108 0,375 0,633 0,77 0,650 0,264 0,509 1,85 1,994 0,525 0,388 0,149 1,392 1,769 1,698 0,548 1,605 1,140 0,609 0,18 1,34 1,147 0,569 0,704 1,91 0,369 1,113 0,674 0,889 0,418 0,384 0,714 0,842 1,674 1,373 0,813 1,228 0,51 1,444 1,539 0,98 0,106 1,502 1,188 0,505 1,691 0,556 1,523 0,77 1,825 1,546 1,861 1,900 0,58 1,958 0,643 0,923 0,196 1,948 0,932 1,806 0,936 1,968 0,645 1,174 0,498 1,563 0,103 0,352 1,516 0,46 0,43 1,972 0,297 0,390 1,943 0,267 1,637 1,791 0,20 0,749 1,47 1,198 0,281 1,767 0,772 1,27 1,582 0,214 1,33 1,942 0,448 0,565 0,308 0,630 0,639 1,296 0,812 1,349 0,821 0,976 1,439 0,585 1,671 1,477 1,95 1,12 1,983 1,302 0,503 1,897 0,338 0,348 1,671 0,650 0,543 0,981 0,702 1,845 0,967 1,88 0,837 1,466 0,355 0,355 1,269 1,272 0,348 0,306 0,29 0,882 0,858 0,701 0,212 1,891 1,360 1,430 0,304 0,914 1,758 0,594 1,18 1,411 1,175 0,136 1,912 0,29 1,265 0,507 0,308 1,748 1,753 1,264 0,141 0,999 0,61 0,232 1,17 1,123 0,310 0,49 1,1 1,267 0,57 1,98 0,12 0,56 0,814 1,359 0,169 1,94 1,275 0,59 0,323 1,380 0,174 1,834 0,58 1,583 0,292 1,800 1,349 0,765 0,877 1,697 0,136 1,544 1,849 0,447 0,57 1,126 0,231 0,195 0,295 0,108 0,87 1,623 1,486 1,329 1,426 0,820 1,638 0,660 1,603 1,470 1,423 0,894 0,453 1,86 0,921 1,833 0,149 0,735 0,740 1,723 1,318 1,795 1,191 0,67 1,57 1,363 0,178 1,277 0,194 1,138 0,147 1,780 1,224 1,372 1,29 0,237 0,35 1,159 0,374 0,983 0,930 0,512 1,483 1,851 1,165 0,110 0,467 0,413 1,993 1,56 1,15 1,768 0,386 0,641 1,829 0,760 0,989 0,844 1,222 0,695 1,630 1,854 1,64 0,929 1,131 0,395 0,101 1,196 1,776 0,418 0,8 0,95 0,10 0,994 0,628 0,853 0,591 1,617 1,168 0,776 1,737 0,502 0,67 0,170 0,490 0,390 0,902 0,153 1,788 0,548 0,662 1,564 0,473 0,806 1,849 1,358 0,242 1,999 1,877 0,594 0,215 1,183 1,301 1,721 0,297 1,616 1,23 1,991 1,823 0,395 1,97 0,670 0,507 1,310 1,590 0,878 1,624 1,149 0,340 0,840 1,293 0,279 1,298 0,501 1,918 0,926 1,721 0,259 1,169 0,108 0,527 1,632 0,80 0,136 0,806 1,805 1,709 0,298 1,530 1,604 0,136 1,825 0,131 0,350 1,323 0,793 1,601 1,141 1,46 0,334 1,512 0,475 0,559 0,588 0,652 1,92 1,301 1,149 0,187 1,650 0,867 0,76 0,933 0,282 0,320 0,125 0,176 1,308 0,219 0,873 1,164 1,531 1,492 1,889 0,196 0,115 1,725 1,53 0,751 0,814 1,469 0,187 0,391 0,803 1,79 1,361 1,858 0,857 1,411 1,408 0,942 1,690 1,98 1,794 1,470 1,75 1,648 1,567 1,868 1,81 1,116 0,100 0,141 1,444 0,991 0,989 1,217 0,794 1,3 0,488 1,547 1,190 1,490 0,6 1,402 1,875 0,946 0,380 0,971 1,799 0,129 1,754 1,548 1,147 0,660 1,264 1,387 0,377 1,925 1,865 1,500 0,611 1,590 0,428 0,877 0,789 0,529 0,855 1,508 0,692 1,757 1,590 1,108 1,847 1,62 0,969 0,576 1,610 0,457 0,479 0,49 0,474 0,296 0,602 0,844 1,856 0,749 1,9 1,116 1,407 1,87 0,234 1,102 0,836 1,842 1,873 0,306 1,299 0,367 1,82 0,25 1,960 0,612 1,828 0,440 0,109 0,361 0,448 1,315 0,53 1,365 0,270 0,923 0,934 0,255 1,18 1,853 1,925 1,71 1,872 0,421 0,751 1,658 0,446 0,775 0,918 1,961 1,965 1,598 0,210 1,67 0,750 1,959 0,356 1,540 0,907 0,925 0,841 1,657 0,271 1,474 0,977 1,322 1,473 1,895 1,410 1,410 1,645 0,372 1,655 1,399 1,127 1,840 1,425 1,449 0,278 1,378 0,119 0,360 0,604 0,440 1,78 0,668 1,972 0,697 1,714 1,817 1,152 0,790 0,868 1,419 1,789 1,856 1,479 1,50 1,630 0,941 1,539 0,807 1,235 0,703 1,164 1,385 0,549 1,979 0,841 1,803 0,78 1,559 1,495 0,870 1,154 0,792 1,480 0,382 1,881 0,206 1,742 0,11 0,511 0,684 0,774 1,897 0,976 0,43 1,715 0,806 1,447 1,441 0,871 0,313 0,151 1,52 0,788 1,375 1,954 1,85 0,169 1,404 0,184 1,938 1,671 0,808 0,189 1,426 0,637 0,581 1,413 0,731 0,285 1,460 0,702 1,92 0,278 1,981 1,557 0,380 1,358 1,552 1,504 0,754 0,295 1,45 1,935 1,149 0,750 1,255 1,410 1,301 0,503 1,837 1,297 0,895 1,181 0,156 0,281 0,338 1,732 1,451 1,188 1,384 1,648 1,88 0,613 0,817 0,595 0,540 1,433 0,534 0,419 1,723 0,588 1,935 1,890 0,579 1,863 0,891 1,681 1,903 1,421 0,49 0,783 0,524 1,453 1,885 1,599 0,663 0,594 0,750 1,181 0,923 0,179 0,689 1,291 1,58 0,588 0,165 1,372 1,92 1,825 0,514 1,65 0,688 0,502 0,479 1,614 0,393 0,513 0,654 0,640 0,712 0,248 1,206 1,646 0,846 0,713 0,684 1,876 1,482 0,48 1,554 0,234 0,672 0,810 1,963 0,54 0,598 1,474 1,439 0,712 0,206 1,19 1,641 1,807 0,791 0,394 0,966 0,262 1,342 1,883 0,927 1,225 0,393 1,537 0,785 0,839 0,507 1,97 1,22 0,817 0,92 0,317 0,864 0,81 0,509 1,452 1,762 0,398 1,765 1,253 1,532 0,564 1,794 1,889 0,105 0,526 1,678 1,463 0,254 0,547 1,741 1,820 1,272 1,678 1,960 1,762 1,119 0,136 0,540 1,161 1,908 0,766 0,15 1,356 0,215 0,208 0,671 0,819 1,431 1,855 0,75 1,290 0,921 0,882 0,908 0,653 0,240 1,60 0,545 1,901 0,848 0,876 0,772 0,45 1,334 0,331 0,928 0,853 1,24 1,300 0,826 1,182 1,628 0,154 1,35 0,584 0,760 0,352 0,41 0,843 0,957 0,318 1,574 1,630 1,550 1,254 1,429 0,588 1,550 0,233 0,6 1,274 0,950 0,940 1,619 0,918 1,785 0,104 1,129 0,403 1,163 1,279 1,376 0,187 0,842 0,691 1,752 1,285 1,379 0,23 1,915 0,455 1,204 1,516 0,411 0,199 0,693 1,716 1,514 1,164 1,383 0,350 1,760 0,342 1,76 0,194 1,267 0,16 0,103 0,297 0,699 1,595 1,382 0,960 1,620 1,602 1,748 0,266 0,938 1,972 0,97 0,340 1,315 1,497 1,73 1,501 0,89 1,152 0,18 1,70 1,420 1,94 0,746 0,926 0,188 0,911 1,12 0,516 0,342 1,729 0,696 1,788 1,214 1,476 1,358 0,799 0,108 0,757 1,21 0,480 1,571 1,714 0,927 1,41 1,742 1,856 0,216 0,812 0,520 1,634 0,32 1,445 1,191 0,650 0,491 1,395 0,581 1,837 1,431 1,331 0,418 0,474 0,102 1,4 1,36 1,417 1,524 1,984 1,866 0,711 0,558 0,104 1,965 1,757 1,165 0,331 1,65 1,924 0,517 1,857 0,426 0,739 1,146 0,895 0,796 1,605 0,56 0,381 1,374 1,58 1,994 0,227 0,128 0,889 0,694 0,400 1,27 0,220 1,768 1,18 1,347 1,751 1,254 1,640 0,148 1,385 1,957 1,3 0,811 1,67 1,517 0,637 1,431 1,612 0,179 1,777 1,507 0,353 1,637 1,906 0,969 1,94 0,525 0,410 1,634 1,121 0,922 0,773 0,204 0,92 1,342 1,621 0,527 1,31 0,777 0,642 1,19 0,964 1,859 1,547 0,341 0,118 1,711 1,963 1,84 0,405 0,234 1,685 1,346 1,721 0,364 0,783 0,301 1,401 0,837 1,14 1,801 1,609 0,614 1,556 1,638 1,897 1,618 1,141 0,896 0,777 0,88 0,705 1,2 1,545 0,628 0,498 0,36 1,617 0,53 1,59 1,133 0,106 0,188 1,705 0,120 0,949 1,494 1,135 0,251 0,896 1,679 0,35 0,476 1,294 0,548 1,138 1,737 0,194 0,577 1,122 1,263 1,588 1,782 0,105 0,467 1,531 1,876 1,287 1,750 0,748 1,851 1,863 0,378 1,399 0,562 1,382 1,64 1,624 0,796 0,939 1,849 1,258 1,366 1,769 1,687 1,833 1,842 0,69 1,993 0,605 0,419 1,806 1,96 1,612 0,898 1,41 1,338 0,892 0,912 0,860 1,808 1,216 1,300 1,589 1,85 0,450 0,737 1,10 1,237 1,122 1,728 0,627 0,826 1,870 1,670 0,906 1,442 0,379 0,504 0,178 1,716 1,725 1,156 0,418 1,713 1,352 0,814 1,927 0,136 0,886 1,236 1,593 1,128 0,231 0,105 0,478 1,285 1,630 1,452 0,325 1,19 1,475 0,593 1,704 1,516 1,329 1,499 0,505 0,31 0,819 1,873 1,579 0,299 1,730 0,390 1,937 0,399 0,124 0,845 0,569 0,431 0,634 0,574 0,811 1,604 1,134 1,430 1,833 1,579 0,498 0,629 1,654 0,274 1,731 0,443 0,227 1,897 1,290 0,528 1,119 0,996 0,346 1,418 0,939 0,898 1,486 0,261 1,703 0,542 1,655 1,726 1,862 1,433 1,232 1,86 1,294 1,505 0,756 1,778 0,518 1,576 1,999 0,403 1,868 1,796 0,902 1,972 1,75 0,176 1,283 1,571 1,750 1,267 1,793 0,233 1,482 1,195 0,577 1,693 1,672 0,127 1,77 0,844 0,576 0,74 0,437 0,540 0,45 0,457 1,221 1,712 0,980 1,545 1,922 0,548 0,206 0,825 1,523 1,833 0,821 1,354 0,313 0,82 0,307 1,943 1,677 0,426 1,787 0,823 0,957 0,341 0,427 0,510 1,440 1,801 1,783 1,40 0,273 0,280 1,543 1,513 1,548 1,871 0,181 1,400 0,274 0,292 1,468 1,646 0,899 1,374 0,63 1,841 1,114 1,885 1,701 0,867 0,312 0,541 0,55 0,593 1,407 1,653 0,510 0,321 0,495 0,433 0,61 0,255 0,155 0,673 0,896 1,574 1,820 0,667 1,77 1,17 0,840 1,127 1,450 0,855 0,147 0,753 1,75 0,960 1,377 0,981 0,943 0,605 0,600 1,107 0,651 1,536 1,340 0,299 1,716 1,753 1,800 1,973 1,685 1,714 1,814 1,520 1,197 1,743 1,333 0,842 1,704 0,46 1,56 1,636 0,519 0,883 1,419 0,141 0,206 1,633 0,738 1,863 1,169 0,386 0,897 1,721 1,557 0,246 1,297 0,446 0,856 1,22 0,801 1,929 0,866 1,649 1,40 0,758 0,118 0,603 1,454 1,917 0,104 1,485 1,904 0,384 0,580 1,884 0,502 0,48 1,937 0,929 1,131 0,840 1,974 1,116 1,855 0,364 1,173 0,634 0,422 0,917 0,644 0,654 0,346 0,980 0,163 1,189 0,225 1,724 1,189 1,669 1,788 1,347 1,695 0,273 0,107 0,301 1,871 1,33 0,789 0,154 0,864 1,235 0,777 1,3 1,750 0,38 1,21 0,775 1,19 0,981 0,81 1,548 1,637 0,129 0,133 1,369 1,606 1,855 0,206 0,746 1,448 0,714 1,47 1,116 0,613 0,204 0,392 0,741 1,696 0,498 1,545 0,906 0,751 0,956 1,271 0,612 1,789 1,784 0,157 1,147 0,196 0,276 0,23 1,200 1,156 0,581 0,57 1,551 0,826 0,385 1,793 1,487 0,166 1,619 0,347 1,944 1,99 0,446 1,97 0,581 1,155 0,471 1,848 0,586 1,952 0,833 1,9 1,367 1,342 0,808 1,868 0,934 0,594 1,823 1,106 1,216 1,724 1,895 1,132 0,627 1,733 1,17 1,487 0,1 1,649 0,841 0,591 1,988 1,297 0,489 0,358 1,710 1,889 1,71 1,814 1,453 1,366 1,19 0,828 1,460 0,480 0,357 0,683 1,927 1,489 0,391 1,319 0,530 0,230 0,817 0,47 1,58 0,484 0,998 0,433 0,831 0,646 1,293 1,464 1,788 1,529 0,825 1,725 0,487 0,423 0,805 1,20 1,225 1,521 1,311 1,35 1,499 0,16 0,563 1,194 0,349 1,694 1,979 0,61 1,436 1,583 0,148 0,638 0,957 1,222 0,694 1,260 0,272 0,675 1,100 1,334 0,978 0,575 0,151 1,481 0,700 0,613 1,704 0,136 1,237 0,869 0,609 1,52 0,400 0,991 1,270 1,326 0,905 1,253 1,274 1,262 0,683 1,366 0,163 0,628 1,351 0,860 1,383 0,881 0,285 1,795 0,400 0,777 0,127 0,653 0,649 1,727 0,468 1,168 0,132 1,733 1,806 0,158 1,731 0,53 1,709 1,733 1,469 0,266 1,149 0,889 1,364 0,11 1,530 0,786 1,464 1,282 0,627 1,179 0,681 0,193 0,896 0,217 1,540 1,265 0,450 0,612 1,110 1,414 1,744 1,734 1,274 1,783 1,773 1,463 1,415 0,164 0,928 1,319 1,516 1,881 1,816 1,632 1,221 0,625 1,924 1,221 1,722 1,837 0,118 1,793 0,340 1,261 0,139 0,965 0,888 1,326 1,563 1,0 1,985 1,390 1,623 0,243 0,831 1,791 0,651 0,197 1,449 1,728 1,703 0,248 1,442 1,725 1,672 1,941 0,391 0,77 1,662 1,359 0,79 0,259 0,671 1,583 0,190 1,13 1,865 0,871 0,502 1,640 1,904 0,501 1,480 1,523 1,685 0,580 1,853 0,985 1,630 0,497 1,864 1,841 1,363 1,641 1,655 1,142 0,720 0,381 1,652 0,874 1,463 1,276 0,336 0,845 1,33 1,311 1,148 0,122 1,69 0,710 0,299 1,589 1,847 1,63 1,681 1,594 0,156 1,840 0,718 0,287 1,119 0,840 1,236 0,575 1,710 0,302 1,542 0,467 0,746 1,840 1,687 1,619 0,863 1,792 0,9 0,983 1,165 0,582 1,576 0,751 0,210 1,750 1,988 1,561 1,207 0,74 1,497 0,30 0,747 1,826 1,114 1,392 0,405 1,799 0,839 0,901 1,943 1,465 0,680 0,19 1,175 0,362 1,808 1,860 1,687 0,855 1,853 1,935 0,773 0,349 1,446 0,59 0,265 0,808 1,733 0,979 0,502 1,271 1,958 1,497 0,51 0,848 0,438 1,407 1,70 0,422 0,620 0,826 1,149 1,244 0,389 1,666 1,616 1,771 0,20 1,519 0,649 1,971 0,639 1,144 0,69 0,867 0,780 0,912 0,802 0,517 1,893 0,51 0,514 1,153 1,905 0,997 1,962 1,885 1,422 0,183 1,795 0,185 0,127 1,464 1,550 1,883 0,481 0,730 1,778 0,500 1,237 0,0 1,305 0,558 0,382 1,830 1,760 0,406 1,854 0,716 1,404 0,304 1,279 0,914 0,969 0,235 1,351 0,82 0,179 0,10 0,318 0,392 0,353 0,97 0,841 0,685 0,991 0,208 1,436 1,446 0,7 1,516 0,674 1,460 0,0 1,227 0,742 0,382 1,102 1,425 1,192 0,500 1,78 0,615 0,631 1,783 0,613 1,404 1,428 0,904 0,409 1,755 0,311 1,153 1,991 1,213 0,550 1,965 1,296 1,429 1,45 0,899 0,127 0,49 1,495 0,406 1,898 0,752 0,174 1,403 1,85 1,580 0,864 0,285 1,927 0,166 0,139 1,584 1,837 0,90 0,140 0,720 1,163 1,816 0,689 1,565 1,485 0,620 1,818 0,888 0,342 1,898 0,412 0,613 0,584 0,270 1,80 0,963 1,461 0,380 1,78 0,693 1,805 0,313 1,342 0,37 1,698 1,578 0,77 0,267 1,39 1,803 0,389 0,389 0,221 0,209 1,495 1,642 0,24 1,636 0,456 1,44 1,595 1,416 0,756 0,23 0,666 0,471 0,148 0,145 1,748 1,352 1,25 0,65 0,428 1,691 1,893 1,426 0,639 0,166 1,762 1,316 0,697 0,305 0,613 0,812 0,805 0,732 1,609 0,477 0,185 1,352 1,402 1,464 1,673 0,408 1,214 1,646 0,733 0,279 0,227 1,471 1,839 0,865 0,739 0,369 1,760 1,812 0,74 0,627 0,518 1,311 0,851 0,515 1,990 1,758 0,165 1,798 1,643 1,230 1,826 0,817 1,714 1,612 1,262 1,456 1,221 0,334 1,451 1,373 0,988 0,613 1,627 0,514 0,942 0,524 1,114 1,743 0,606 1,919 0,9 1,744 1,638 1,348 0,483 1,263 1,597 0,48 1,921 1,9 0,327 0,4 1,43 1,645 0,690 0,860 0,406 1,434 1,909 1,326 1,246 1,797 0,783 0,841 1,9 0,546 0,151 1,420 1,689 1,224 1,487 1,385 1,494 1,288 1,482 1,942 0,493 1,252 0,16 1,342 1,405 1,282 1,883 0,816 1,557 1,693 0,82 1,519 0,23 0,398 0,455 1,758 1,534 1,207 1,587 0,277 0,645 0,349 0,705 0,844 1,795 1,406 1,747 1,518 0,346 0,614 0,416 0,582 1,842 0,404 0,683 0,366 1,460 0,108 1,649 1,227 1,611 1,638 0,541 1,367 0,722 1,174 1,576 1,482 0,202 1,429 0,99 1,232 0,639 0,260 1,747 0,91 1,767 1,11 0,36 0,707 1,843 0,424 1,54 0,502 1,84 1,548 1,589 0,533 1,970 1,197 0,962 0,144 0,909 0,604 1,401 0,528 0,119 1,145 0,147 0,898 1,538 1,375 0,897 1,569 0,467 0,814 1,682 0,682 1,50 1,503 0,422 1,164 0,189 1,715 1,658 1,429 0,553 1,952 1,65 1,664 0,708 0,51 0,144 1,559 0,325 1,698 0,117 1,375 1,712 1,206 0,706 0,486 1,510 1,715 1,873 0,277 0,857 0,30 1,860 1,179 0,774 1,220 1,543 1,722 1,553 1,868 0,883 0,396 0,609 0,677 1,77 1,185 0,380 1,703 1,372 0,229 0,826 1,275 0,554 1,965 0,688 0,477 0,498 0,583 0,255 0,802 0,55 0,807 1,60 0,38 1,58 0,6 1,663 0,786 1,180 0,601 1,99 1,404 1,718 1,184 1,263 1,726 0,480 1,679 1,831 0,545 0,530 1,216 0,618 1,836 1,114 0,443 1,248 0,212 0,101 0,803 0,29 0,220 1,791 0,709 0,492 1,714 1,991 0,267 1,710 1,183 1,652 0,537 1,129 0,889 0,411 1,684 1,636 0,847 1,942 0,440 1,278 0,484 0,519 0,789 0,972 0,203 0,827 0,914 0,719 1,131 1,191 1,430 1,506 0,184 1,681 0,547 0,249 1,316 0,319 0,36 1,752 1,679 1,43 1,727 0,33 0,996 1,37 1,449 0,965 0,491 1,933 0,368 0,523 1,684 0,133 0,917 1,734 0,990 1,313 0,504 1,814 1,294 1,90 0,748 1,678 0,24 1,202 0,101 1,365 0,89 0,147 0,597 1,816 0,996 0,888 0,352 0,544 1,636 1,667 1,425 0,595 0,455 0,548 0,245 0,536 1,449 0,683 1,80 1,504 1,959 0,671 1,527 0,656 1,370 1,92 0,233 1,814 0,751 0,968 1,921 1,394 1,894 0,282 0,577 0,783 1,596 0,302 1,543 1,606 1,27 1,827 1,781 1,50 0,576 1,957 1,30 1,647 1,521 1,860 0,616 1,414 1,780 1,726 1,479 1,932 1,569 0,707 1,209 0,109 0,756 1,81 0,822 0,617 1,927 0,64 0,364 1,22 1,270 1,770 1,947 1,204 0,766 1,129 1,433 1,508 0,343 1,453 1,34 0,700 0,780 1,652 0,74 0,715 0,235 0,823 1,304 1,181 1,635 0,869 1,500 0,572 0,536 1,880 0,696 0,191 1,528 1,800 0,286 1,518 0,316 1,922 0,144 1,19 1,898 0,329 1,859 0,872 1,360 0,252 0,212 0,76 0,63 1,397 1,626 1,997 0,842 0,507 0,462 1,437 0,387 1,629 0,223 1,991 0,774 1,326 0,280 1,166 1,971 1,998 1,206 0,94 0,468 1,790 1,193 1,479 0,416 1,502 1,150 0,406 0,170 1,732 0,154 0,589 0,653 1,187 0,848 1,178 1,226 0,998 1,315 1,15 0,277 0,314 1,719 0,424 0,809 1,759 0,295 1,665 1,222 0,207 0,514 0,634 1,808 0,247 0,197 1,12 1,489 1,746 1,842 0,806 1,864 1,939 0,908 0,876 0,42 0,317 0,728 0,708 1,305 0,271 0,413 1,586 1,192 0,488 1,309 0,814 0,191 0,455 1,450 1,74 0,11 1,318 1,294 0,959 0,913 1,506 0,367 0,891 1,445 1,498 0,579 1,207 0,957 1,179 1,674 0,182 0,156 0,145 0,549 1,606 0,315 1,331 0,728 1,716 0,521 0,551 0,865 0,242 1,385 0,964 0,412 0,801 1,673 1,50 0,113 0,872 1,642 1,235 0,255 0,577 0,338 0,713 0,426 0,271 1,525 0,774 0,156 0,732 0,171 0,31 0,482 0,83 0,987 0,570 0,21 1,50 0,854 0,611 0,329 1,7 1,357 1,389 0,199 0,256 1,121 0,498 1,516 0,855 1,963 1,564 0,977 0,928 1,553 1,738 1,846 0,588 0,288 0,484 1,225 0,504 1,164 1,118 0,257 1,151 1,457 0,847 1,134 0,104 1,550 1,114 1,951 1,523 1,688 1,933 0,457 1,801 0,158 1,984 1,987 0,260 1,874 1,904 0,75 1,275 1,629 0,27 1,64 0,751 1,906 0,97 1,589 0,847 0,803 0,242 0,434 1,472 0,282 1,659 1,176 0,66 1,240 0,435 0,341 0,409 0,831 0,981 0,734 1,3 0,30 0,812 0,473 1,284 0,81 1,559 1,106 0,337 0,977 1,717 0,707 0,478 0,961 0,266 1,638 1,560 1,899 1,280 0,978 0,872 1,317 0,945 0,495 1,207 1,246 1,914 1,445 0,613 0,568 1,897 0,18 0,483 0,660 0,989 0,62 0,530 0,7 1,933 1,676 1,607 0,811 1,990 0,148 1,340 0,318 1,811 1,524 1,180 0,852 1,900 0,669 1,743 0,746 0,771 0,70 1,583 1,607 1,640 0,102 1,840 1,531 0,645 1,325 1,523 1,266 0,82 1,915 0,657 1,792 1,599 0,439 0,337 0,526 1,360 0,755 1,629 0,434 1,499 0,671 1,179 1,783 1,285 0,174 1,209 1,60 1,903 0,958 1,217 1,688 0,241 1,129 1,437 0,568 0,546 1,178 1,538 0,681 1,172 1,854 1,493 0,198 0,504 1,634 1,788 0,144 0,47 0,937 0,440 1,698 0,674 0,795 1,473 1,568 0,252 1,318 0,179 0,85 0,741 0,922 1,424 0,339 0,386 0,832 1,555 1,400 0,267 0,111 1,162 0,536 1,765 0,921 1,775 1,395 0,271 0,655 0,257 1,848 0,341 1,906 1,974 1,800 1,551 0,923 1,518 1,663 1,756 0,462 1,974 0,931 0,578 1,412 0,66 0,739 0,200 0,220 0,214 1,977 0,897 1,475 1,270 0,802 1,694 0,836 0,223 1,7 1,657 0,649 0,596 0,995 1,344 0,99 1,724 1,585 0,375 1,7 0,981 0,16 1,23 1,0 0,664 0,291 1,474 1,920 1,320 1,282 1,755 1,49 1,273 1,924 0,930 0,435 0,659 0,28 1,141 1,617 0,989 0,302 1,199 0,443 1,826 1,682 1,47 0,376 1,838 0,311 0,684 0,182 1,812 1,57 0,430 0,468 0,203 0,747 1,520 1,909 0,277 0,87 1,661 0,693 0,457 0,365 1,547 0,969 1,683 0,294 1,442 1,899 1,537 0,228 1,204 0,457 1,881 1,643 0,382 1,356 0,499 1,503 1,848 1,488 1,872 1,799 0,559 0,109 1,526 1,341 0,496 1,536 1,243 0,762 0,762 1,946 0,493 1,74 1,385 1,84 1,216 1,56 1,548 1,648 0,279 1,561 0,825 0,354 0,703 0,115 1,416 1,801 1,189 0,499 0,900 1,488 1,802 0,44 1,280 0,690 1,16 0,920 0,809 1,316 0,83 0,224 0,845 0,776 0,1 0,297 0,557 0,730 0,104 0,391 1,695 1,486 0,291 0,546 0,900 0,657 0,237 0,313 0,10 0,670 1,670 0,125 0,708 0,964 1,345 1,904 1,421 1,816 1,4 0,289 0,150 0,545 0,547 1,74 1,34 0,97 0,170 1,136 0,252 0,645 0,553 0,56 0,131 0,194 0,384 0,18 1,353 1,727 0,814 0,428 0,385 1,141 1,595 0,781 1,929 0,455 0,632 1,132 0,337 0,580 0,884 1,506 1,903 0,709 1,964 1,97 0,540 0,754 1,609 0,347 1,783 0,333 1,456 0,626 0,279 1,161 0,44 0,735 1,261 0,125 1,542 1,745 0,888 1,418 0,344 1,569 0,97 1,289 0,764 0,604 0,330 0,602 0,457 1,384 1,634 0,436 0,783 0,605 1,993 1,358 0,537 0,599 0,227 0,525 0,180 0,732 1,683 0,816 1,164 1,382 1,550 0,916 1,830 1,787 1,139 0,125 1,552 0,779 0,660 1,317 1,736 1,12 1,392 0,370 1,163 0,364 0,76 1,328 0,349 1,557 0,42 1,659 0,202 1,752 0,241 0,924 0,797 1,129 0,924 1,34 0,786 0,281 0,22 1,941 0,228 0,880 1,726 0,477 1,688 0,215 0,803 1,191 0,966 1,290 0,874 0,908 0,233 1,588 1,637 0,494 1,286 0,675 0,43 0,66 0,829 0,351 0,999 1,606 0,255 1,849 0,318 0,424 0,765 0,833 0,78 1,467 0,587 0,923 1,741 1,519 1,715 1,551 1,426 0,149 0,893 0,822 1,288 0,93 0,503 0,750 1,862 1,175 1,955 1,340 0,675 0,691 1,979 1,364 0,685 1,463 1,112 0,105 0,389 0,38 0,63 0,699 0,90 1,812 1,418 0,833 1,820 0,560 1,842 1,834 0,935 1,222 1,645 1,748 1,827 0,606 0,248 1,285 1,952 0,923 0,730 1,317 1,26 0,511 0,409 1,751 1,819 0,882 0,894 1,678 1,338 0,296 0,10 0,686 1,948 0,768 1,939 1,875 1,759 0,439 1,242 1,741 0,389 0,387 1,394 0,976 0,725 1,595 0,213 1,988 0,676 1,288 1,639 1,733 1,845 1,485 1,850 1,680 1,59 0,778 0,890 1,942 1,361 1,362 1,886 0,330 1,950 0,679 0,169 1,662 1,279 1,774 1,776 0,8 0,709 1,258 1,800 0,605 1,124 0,217 1,111 1,645 1,920 1,931 1,237 1,776 1,499 0,730 1,966 1,746 1,830 1,877 0,990 0,380 0,575 1,496 1,342 0,213 0,524 1,975 1,564 1,605 0,706 1,63 1,656 0,835 1,586 0,277 0,286 0,625 0,893 1,396 1,610 1,702 1,341 0,74 0,860 0,207 0,863 1,820 0,475 1,943 1,183 0,233 0,382 1,139 0,645 1,172 1,815 1,966 0,717 0,607 0,355 1,66 1,554 1,371 1,44 0,515 0,639 0,888 0,577 1,5 0,116 0,954 1,378 0,542 1,91 0,691 1,43 1,865 0,178 1,737 1,319 1,198 1,987 1,171 1,659 0,824 1,113 0,316 0,989 0,719 1,133 1,310 0,223 1,945 0,318 0,624 1,788 0,196 0,516 1,577 1,933 1,960 0,76 1,517 1,533 0,19 1,252 0,73 0,242 0,763 0,479 0,483 0,436 0,359 1,865 0,450 1,699 1,457 1,749 1,115 0,864 1,475 0,854 0,95 0,303 0,661 1,785 1,242 1,850 0,737 1,417 0,199 1,194 1,958 0,883 0,378 0,81 1,190 1,363 0,113 1,62 1,933 0,811 1,163 1,605 0,709 1,748 1,755 0,984 0,73 1,330 0,675 0,189 1,89 0,376 0,105 1,596 1,775 0,525 0,568 1,151 1,49 1,468 0,810 1,45 1,660 0,561 1,403 1,864 1,157 0,575 0,667 1,721 1,459 1,203 1,973 0,207 1,779 1,815 1,960 0,864 1,341 1,117 1,461 1,182 0,730 0,527 1,752 1,419 0,457 0,443 0,986 1,204 1,813 1,291 1,646 1,277 0,74 0,987 1,911 0,368 0,267 1,969 1,800 1,10 0,733 0,668 1,776 0,11 1,479 1,720 1,13 1,358 1,900 0,708 0,390 1,156 1,173 1,795 0,163 0,534 0,339 1,796 1,616 0,217 0,150 0,918 0,573 1,179 0,815 1,463 1,863 0,158 1,885 0,526 1,935 0,668 1,299 0,186 0,708 1,805 0,644 0,404 1,652 1,853 1,379 0,74 1,257 1,291 0,904 0,682 1,786 1,205 1,247 0,473 1,239 0,416 0,8 0,834 0,449 1,107 1,235 1,454 1,986 0,888 1,957 1,45 0,279 1,831 1,999 0,426 0,426 1,820 0,793 1,431 0,577 1,918 1,521 1,840 1,616 1,903 1,226 1,774 1,141 0,400 0,702 0,623 1,796 0,266 1,249 1,739 1,711 0,728 0,16 0,926 1,772 0,947 1,696 1,938 0,616 1,684 1,640 0,753 0,268 0,61 1,956 1,669 0,685 1,142 0,300 0,430 0,480 0,686 0,218 0,15 1,133 0,825 1,562 1,461 1,221 1,843 0,257 1,410 1,326 0,202 1,366 0,620 1,148 0,455 1,679 1,984 1,716 1,47 1,603 1,831 1,405 0,903 1,973 1,753 1,384 1,158 0,823 1,326 0,503 0,636 1,733 1,405 0,331 1,848 1,402 0,817 0,388 1,223 1,232 1,591 0,739 0,690 1,466 0,343 0,204 0,917 1,572 1,987 1,311 0,314 1,172 0,823 0,271 1,592 1,792 0,375 0,47 0,824 1,435 0,499 1,582 0,923 0,260 1,617 0,216 1,496 1,477 0,263 0,639 0,817 0,289 1,71 0,48 0,395 1,376 1,794 0,657 1,793 0,271 0,823 0,287 0,332 1,904 0,795 0,986 0,963 0,7 0,967 1,467 1,408 1,928 1,343 1,806 0,951 1,530 0,798 1,385 0,351 1,960 1,859 1,403 1,160 0,735 0,56 1,443 1,342 1,133 0,717 0,814 0,113 1,536 1,349 0,69 0,196 1,530 0,560 0,491 1,292 0,742 0,828 1,360 1,585 1,406 0,92 0,626 0,43 1,137 1,197 0,275 0,406 0,127 0,538 1,542 0,679 1,510 1,642 1,767 1,152 1,162 0,767 0,446 1,110 0,102 1,72 0,984 0,642 0,574 0,971 0,343 1,128 0,576 0,786 1,392 0,608 1,766 0,874 1,252 0,858 0,766 1,733 0,105 1,606 1,44 0,350 1,501 1,564 0,219 0,58 0,539 1,729 1,998 1,888 1,637 0,633 1,736 0,739 0,252 0,309 1,560 0,48 1,322 0,951 1,501 0,543 0,93 1,837 0,334 0,701 0,323 1,489 1,618 0,335 1,594 0,717 1,124 0,756 1,164 1,368 1,317 0,681 0,385 0,912 1,225 1,182 1,758 0,349 1,607 0,758 1,685 0,798 1,881 0,222 1,438 1,935 1,892 0,91 1,271 0,705 0,389 1,263 1,471 0,614 0,723 1,507 0,324 0,684 0,276 0,64 0,7 0,680 1,793 0,874 0,928 0,516 1,976 1,439 0,110 1,128 0,278 1,585 0,110 0,562 1,45 1,713 1,263 1,277 0,388 0,544 0,528 0,700 0,740 0,235 0,773 1,512 0,370 0,356 1,981 1,584 0,418 0,199 1,457 1,877 1,535 0,774 1,346 0,844 0,439 0,224 1,323 1,735 0,485 1,709 1,870 1,187 1,873 0,411 1,367 0,633 0,433 0,493 1,216 0,165 1,170 0,483 0,346 0,132 1,504 1,649 1,126 1,822 0,703 1,204 1,471 0,847 1,280 1,383 1,592 1,488 1,554 1,798 1,440 1,916 1,468 0,0 1,916 1,188 1,735 0,71 1,58 1,202 0,867 1,517 1,439 0,310 0,532 0,554 1,362 0,178 1,421 0,484 0,549 0,472 0,874 0,915 1,303 0,195 1,417 0,713 0,614 0,398 0,537 0,521 1,366 0,659 1,576 1,127 0,856 0,139 0,162 1,418 1,871 1,952 0,52 1,817 1,486 1,960 1,423 0,493 1,843 0,411 0,163 1,771 0,29 0,416 1,222 0,202 0,270 1,521 0,724 0,652 1,119 0,513 0,477 1,256 0,842 0,960 0,683 1,735 0,850 1,309 1,993 0,199 1,396 0,889 1,659 0,975 0,992 0,30 0,6 0,750 1,538 0,719 1,616 0,218 1,823 1,582 0,690 0,415 1,24 0,815 1,780 1,763 0,24 0,687 0,363 1,480 1,831 0,638 1,236 0,778 1,948 1,579 0,929 0,643 0,561 1,411 1,703 1,566 0,976 0,377 1,659 1,876 1,224 0,805 1,167 1,381 0,502 0,454 1,813 0,372 0,719 1,327 1,643 0,356 1,566 1,424 0,124 0,356 1,197 0,270 0,838 1,274 1,224 0,27 0,372 1,772 0,973 1,287 0,806 0,710 1,661 1,320 1,887 0,301 1,521 1,765 1,228 1,367 1,340 1,488 0,272 1,72 1,496 0,282 1,132 1,281 1,374 1,551 0,957 0,105 1,81 1,286 1,12 1,497 1,881 1,722 1,919 1,805 0,322 0,569 0,2 0,793 0,208 1,127 0,186 0,674 1,197 0,287 0,694 1,895 1,8 1,875 1,880 0,906 0,188 1,439 0,724 1,217 1,343 0,679 0,742 0,299 1,878 1,275 0,224 0,626 0,669 1,58 1,928 1,754 1,944 0,685 1,273 0,393 1,665 0,823 1,883 1,598 0,579 0,271 1,854 1,289 1,569 1,945 0,111 1,252 0,950 0,352 1,795 1,313 0,650 1,713 1,972 0,547 0,421 0,936 1,756 0,592 1,312 0,450 1,627 0,518 1,322 0,356 0,46 0,841 0,750 0,132 1,839 1,118 0,675 1,251 1,592 1,736 0,505 1,506 1,633 0,901 0,808 0,955 0,243 1,934 1,443 0,548 1,680 0,628 1,354 1,594 0,15 0,144 0,257 1,999 0,764 1,19 1,982 0,56 1,257 1,605 0,557 1,279 0,936 0,601 0,842 1,378 1,415 1,894 0,287 1,276 0,489 1,66 0,582 0,881 1,499 0,75 1,566 0,93 1,89 0,599 1,540 1,668 1,974 1,963 1,306 0,758 1,462 1,596 0,185 1,328 0,452 1,275 1,771 0,480 1,270 0,775 0,266 0,417 1,817 0,178 0,736 1,228 0,302 1,512 1,889 0,99 0,539 0,209 0,228 0,823 1,757 0,21 0,946 0,778 0,102 0,319 1,61 0,659 1,268 1,32 0,103 1,575 0,864 0,837 0,351 0,729 1,792 1,196 1,277 1,16 0,704 0,729 1,59 1,781 1,282 1,450 1,291 1,33 1,48 1,267 1,388 1,761 0,137 1,8 1,896 1,686 0,195 0,462 0,428 0,759 0,887 0,193 0,950 1,749 1,990 0,547 0,588 1,885 1,91 0,427 0,770 0,805 1,195 1,200 1,132 1,161 1,851 1,152 1,34 1,459 1,392 0,275 1,886 1,478 0,615 1,447 0,901 1,755 0,110 0,543 0,474 1,807 1,84 1,974 0,233 0,455 1,388 1,176 0,432 1,16 1,956 1,681 0,574 1,186 0,844 0,124 1,669 0,225 1,469 1,577 0,931 1,884 1,667 1,372 1,496 0,875 1,785 0,877 1,501 0,675 1,687 1,988 1,348 0,553 1,741 1,82 1,411 0,208 1,176 1,846 0,471 1,128 1,756 0,697 1,443 1,266 1,748 1,758 1,440 1,338 0,74 1,763 0,717 1,16 0,852 1,121 0,131 0,101 0,365 1,127 1,255 0,58 0,445 1,66 0,120 1,555 0,263 0,683 1,111 1,825 0,420 1,437 1,924 1,759 0,653 0,528 1,468 0,739 1,547 1,462 0,814 1,863 1,187 0,176 1,403 1,833 0,601 0,771 1,539 1,647 1,740 1,709 0,30 0,924 1,819 1,445 1,571 0,106 1,545 1,773 1,595 0,329 1,575 0,135 1,913 0,817 1,213 0,403 0,281 1,435 0,778 0,7 0,901 0,167 1,217 0,361 1,627 1,263 0,756 1,607 0,509 1,299 0,653 1,349 1,965 1,414 1,681 0,581 0,225 1,800 0,94 0,924 1,618 1,843 0,631 1,953 1,287 0,700 1,545 1,982 0,643 0,413 1,15 0,218 0,933 1,260 0,144 0,84 1,715 0,406 1,402 1,742 0,721 0,748 1,282 1,685 0,203 0,295 0,722 0,10 1,762 1,791 1,660 0,859 1,145 0,934 1,113 0,494 1,132 0,567 0,659 0,958 0,254 1,918 0,312 0,21 1,33 1,926 1,207 1,984 0,572 1,613 0,132 1,699 1,920 1,322 0,801 1,491 1,161 0,31 0,444 0,811 1,41 0,250 1,120 0,623 1,891 0,206 1,402 1,842 0,760 1,728 0,204 1,954 0,851 1,72 0,747 1,495 0,241 1,572 0,482 0,302 0,394 0,661 0,687 0,802 1,441 0,703 0,423 0,196 0,461 1,588 1,724 1,560 1,687 0,402 1,928 0,394 0,752 0,437 0,292 0,982 1,298 1,375 0,427 0,16 1,267 1,548 1,323 0,180 1,678 0,13 1,214 1,845 1,497 1,622 0,265 1,561 0,637 1,594 0,615 1,671 1,326 1,421 0,183 1,895 0,23 0,997 0,49 0,975 1,967 0,332 0,689 0,810 0,430 0,904 1,547 1,668 1,463 0,122 0,469 0,470 0,612 1,828 0,658 1,154 0,339 1,887 1,324 1,45 1,18 0,515 0,880 1,539 0,638 0,322 0,719 1,80 1,286 0,928 1,809 1,807 1,169 0,94 0,397 0,837 0,117 0,219 1,596 1,830 0,917 1,380 0,734 1,380 1,808 0,135 1,682 0,375 0,310 1,732 0,355 0,841 1,48 1,186 0,490 1,978 0,5 1,143 0,912 1,610 1,156 0,280 1,206 0,542 0,211 1,530 0,766 0,969 0,777 0,48 0,310 1,428 0,450 1,890 1,494 1,803 0,5 0,472 0,457 0,238 1,444 1,91 1,483 0,617 0,179 1,919 1,392 1,396 0,522 1,668 0,919 1,26 0,88 1,499 1,882 0,63 0,700 0,750 0,882 0,619 0,915 1,589 0,905 0,989 0,604 0,458 1,723 0,283 0,94 1,195 1,46 1,436 1,653 1,340 0,519 1,292 0,162 1,892 1,811 1,62 1,249 1,148 0,14 1,686 1,784 0,336 0,910 1,636 1,838 0,365 0,170 0,775 0,557 0,961 1,243 1,457 1,924 1,78 1,758 0,927 1,938 0,980 1,37 1,652 1,157 0,420 1,155 1,603 1,194 1,604 0,831 1,811 0,862 1,71 0,723 1,132 0,806 1,632 0,902 0,294 0,366 1,303 0,247 0,173 0,887 1,631 0,753 0,518 0,377 0,328 1,74 1,898 1,784 1,182 0,627 1,857 1,366 1,451 0,172 1,555 0,861 1,775 1,635 0,382 0,67 0,977 0,638 0,328 0,688 1,45 0,61 1,597 1,841 0,696 0,495 1,836 0,523 0,915 1,83 0,983 0,957 0,874 0,806 0,397 1,15 1,481 0,426 1,387 0,285 0,190 1,867 1,174 0,439 1,901 0,840 0,75 1,75 0,635 1,569 0,697 1,141 1,614 0,456 1,915 0,950 0,889 1,115 0,113 1,962 1,54 1,157 1,143 0,10 1,262 0,539 0,959 1,805 0,732 1,904 1,470 0,842 1,72 1,460 0,694 0,668 1,201 1,558 0,687 1,259 1,597 0,56 0,210 1,492 1,11 0,621 0,809 1,814 0,429 1,697 0,665 1,22 1,691 0,97 0,938 0,600 1,515 0,154 1,813 0,554 0,972 0,693 0,709 0,135 1,707 0,532 1,159 0,480 0,753 0,368 1,745 1,341 0,576 0,296 1,154 0,12 1,219 1,639 0,920 1,997 0,428 0,633 0,394 1,633 0,497 1,850 0,234 1,513 0,574 0,915 1,697 0,232 0,519 1,743 1,504 0,475 1,46 1,427 0,654 0,49 0,558 0,417 1,343 0,458 0,855 0,810 0,125 1,771 0,801 1,773 1,301 0,468 0,479 0,15 1,733 0,945 1,366 0,254 1,134 1,270 1,718 1,205 0,586 1,918 0,208 0,350 0,964 1,113 1,250 0,955 0,377 1,324 1,976 0,168 1,281 0,827 1,133 0,12 0,310 1,580 1,576 1,119 1,313 0,907 0,20 0,244 1,661 0,405 0,340 1,719 1,640 1,432 0,594 1,762 0,339 1,798 0,554 0,32 0,500 0,622 0,782 0,362 0,59 1,634 1,906 0,58 1,358 0,799 1,56 1,604 0,109 1,358 1,293 1,38 1,70 0,51 0,974 0,682 0,684 1,607 1,416 0,610 1,213 0,822 0,25 0,281 1,59 0,907 1,969 1,298 0,372 0,928 0,220 1,640 0,128 1,603 0,432 0,3 1,344 0,724 0,949 1,432 0,548 0,783 0,657 1,519 1,533 0,365 1,378 1,210 0,144 0,71 1,578 1,914 1,694 0,546 1,6 0,768 0,12 1,974 0,535 0,743 1,1 1,133 1,515 1,792 0,929 0,219 0,325 1,792 0,86 1,81 0,213 1,503 1,969 0,217 0,490 1,107 1,910 0,465 0,890 1,877 0,730 1,300 0,732 0,199 0,929 0,202 1,973 1,338 1,686 1,884 1,288 0,873 0,472 0,488 1,593 0,93 0,166 1,61 0,738 0,939 1,367 1,89 1,531 0,603 1,59 1,136 0,550 0,952 1,411 0,121 0,29 1,316 1,620 0,844 0,961 0,600 1,289 0,110 1,260 1,916 0,241 0,760 1,11 0,384 1,832 1,685 0,344 1,160 1,410 1,878 0,158 1,504 0,798 0,917 1,145 1,157 0,944 1,306 1,266 1,563 1,259 1,361 0,88 0,909 1,904 0,977 1,978 1,428 0,643 1,198 1,458 0,641 1,29 0,199 0,889 1,838 1,690 1,820 0,987 0,670 0,23 1,165 1,527 1,651 0,737 1,722 1,545 1,148 0,139 1,247 0,205 0,759 1,915 1,477 1,808 1,349 1,860 0,77 1,76 0,152 1,148 0,124 1,769 1,461 0,826 0,404 1,984 1,145 0,511 0,137 1,981 1,611 1,799 0,370 1,434 0,570 1,937 0,853 0,321 0,828 1,505 0,919 0,789 1,458 1,114 0,143 1,628 0,602 1,285 0,611 0,790 0,1 1,341 1,262 1,326 1,50 0,696 0,231 0,548 1,660 0,245 1,352 0,228 0,488 0,268 0,292 0,529 0,194 0,522 1,641 1,763 1,461 0,927 1,710 1,583 1,395 0,59 1,755 0,811 1,913 1,534 1,202 1,293 1,604 1,50 1,840 1,903 0,300 1,439 0,214 0,142 0,706 1,928 1,39 0,539 0,399 0,623 1,258 0,81 1,707 0,904 1,997 0,248 1,18 1,722 1,19 0,495 0,880 0,873 1,566 0,769 0,585 0,979 0,311 1,546 0,295 1,454 0,660 0,619 1,525 1,115 0,475 1,195 1,52 1,852 0,306 0,30 0,11 0,830 0,507 1,31 1,12 0,447 0,961 0,218 0,571 0,368 1,968 1,574 1,179 0,853 0,627 0,220 0,972 1,337 1,491 0,730 0,21 1,97 1,272 0,69 1,25 0,531 1,413 0,348 1,366 1,43 0,847 1,747 1,461 0,513 0,400 0,192 0,569 1,760 0,694 0,379 0,779 1,187 1,260 0,591 1,145 0,24 0,305 1,750 0,796 0,334 1,980 1,70 0,54 1,167 1,521 0,925 1,266 0,621 1,127 1,122 0,15 0,251 0,285 1,840 0,480 1,701 0,999 1,551 1,777 1,994 1,72 1,425 0,631 0,615 0,994 1,846 0,977 0,181 1,865 1,512 1,551 1,397 1,297 0,48 0,385 0,225 1,101 1,645 0,19 1,202 1,844 1,642 1,740 0,851 1,803 1,340 1,606 0,113 0,473 1,630 1,670 0,770 1,297 0,146 1,776 1,893 1,469 0,747 1,166 1,19 1,28 0,74 0,280 0,573 1,584 1,753 1,82 1,514 1,704 1,197 1,658 1,399 0,479 1,943 0,164 0,549 0,717 0,152 1,373 1,96 0,192 0,118 1,303 0,826 1,139 1,934 1,979 1,357 0,238 0,646 0,278 1,311 1,955 1,208 1,349 0,577 1,239 1,61 0,279 1,962 1,209 1,798 1,373 0,283 1,241 1,191 0,61 1,112 0,410 1,459 0,544 0,359 0,183 1,207 0,933 1,721 1,592 0,699 0,535 1,325 1,8 0,750 0,472 1,578 0,102 0,7 0,713 0,396 1,20 1,171 0,880 0,30 0,58 0,184 1,914 0,594 0,445 0,40 1,617 0,990 1,988 0,854 1,540 1,71 0,758 1,189 1,221 0,31 1,374 0,429 0,308 0,536 1,555 1,361 1,891 1,215 1,471 1,766 0,782 1,238 0,908 1,812 0,658 0,502 0,656 0,827 0,512 1,227 0,691 0,407 1,230 0,761 0,635 1,771 0,34 1,334 0,426 0,904 1,254 0,118 0,612 0,270 1,165 0,124 0,612 1,23 1,814 0,915 0,114 1,323 0,314 1,109 0,946 1,388 1,63 0,517 1,160 1,451 0,485 0,639 1,68 0,943 0,231 0,795 1,803 1,760 0,983 0,459 0,163 0,884 1,146 0,631 0,554 1,234 0,728 0,242 1,958 0,523 1,401 1,452 1,258 0,548 1,950 0,793 1,69 0,136 0,935 1,948 0,593 0,559 0,830 0,367 0,38 1,340 0,899 1,592 0,292 1,929 0,967 1,889 0,982 1,544 1,196 0,368 1,305 1,167 0,902 0,766 1,414 0,193 1,222 0,911 1,164 1,361 1,43 1,278 0,604 0,604 0,860 1,86 0,699 0,432 0,765 0,328 1,129 0,699 1,394 0,947 1,955 0,814 0,107 0,413 1,88 0,450 0,650 1,390 1,531 0,447 0,85 1,50 1,772 0,22 1,202 0,717 0,178 1,210 0,800 0,92 0,630 1,282 0,112 0,327 0,646 1,372 0,411 0,490 0,383 1,515 1,711 0,133 1,459 1,852 0,1 1,166 0,20 1,231 0,310 0,339 0,165 0,791 0,751 1,78 1,787 1,296 0,530 1,845 0,108 1,18 0,291 0,775 0,505 1,188 0,998 0,60 1,833 1,226 0,838 0,640 0,366 0,975 1,842 0,696 0,170 0,347 1,631 1,707 1,23 1,896 1,686 1,635 0,942 1,886 0,993 1,0 1,999 1,526 1,414 1,434 1,122 1,754 1,186 1,256 1,610 1,380 1,813 1,363 1,230 0,260 0,771 1,814 0,513 0,627 0,238 0,280 0,603 0,546 1,466 1,560 1,58 1,522 0,894 0,468 1,978 1,57 1,733 1,821 0,512 0,638 0,249 1,92 1,815 1,964 1,834 0,972 1,976 0,229 0,305 0,454 0,298 1,923 0,316 1,660 0,703 1,457 1,608 0,108 1,198 1,755 1,616 0,33 0,844 1,25 1,32 1,506 1,134 1,360 1,425 0,599 1,195 0,175 0,343 1,526 0,977 0,388 0,162 1,932 1,229 1,587 0,565 1,210 0,537 0,862 1,86 1,887 1,931 0,481 0,894 1,937 0,944 1,605 1,701 0,379 0,638 0,742 0,192 1,132 1,714 0,658 1,264 1,133 0,747 0,295 0,136 1,747 0,964 1,539 1,518 1,705 0,369 1,881 0,437 1,584 1,810 1,107 1,409 1,324 0,812 1,564 0,442 0,292 1,745 0,280 0,737 1,972 1,886 0,561 0,632 0,983 0,625 0,365 1,61 1,552 1,376 1,976 0,829 1,530 0,978 0,50 0,437 0,352 1,290 1,801 0,764 1,464 1,270 0,991 1,496 1,537 1,441 1,584 0,109 0,646 1,979 1,391 0,74 1,382 1,613 0,109 0,990 1,320 0,768 1,908 1,399 0,743 1,472 1,995 1,564 1,35 0,517 1,600 0,132 1,673 0,493 0,473 0,627 1,136 0,410 0,262 0,58 0,662 1,997 1,62 1,728 1,914 0,81 0,784 1,295 0,16 1,188 1,309 0,770 0,537 0,598 1,190 0,206 0,643 1,7 1,485 1,836 1,237 0,840 0,193 0,541 0,689 0,795 1,471 1,380 1,888 1,181 1,678 1,356 0,984 0,668 0,295 1,94 0,655 1,285 0,941 0,463 0,310 0,494 1,143 0,730 0,584 0,455 0,287 0,519 0,526 0,809 1,258 0,483 1,103 1,174 1,505 0,723 0,33 0,493 1,122 1,267 0,268 0,90 0,899 1,755 0,660 0,647 1,533 1,839 0,792 1,693 0,132 0,502 1,468 0,430 0,502 0,436 1,476 1,86 1,499 1,683 0,928 0,752 1,487 1,20 0,856 1,112 0,327 0,411 1,772 0,512 1,949 0,229 1,82 1,886 1,105 0,928 1,141 1,680 1,175 0,341 1,289 0,852 1,847 0,138 1,788 1,736 0,786 1,706 1,420 1,369 1,860 1,560 0,337 0,906 1,620 0,991 1,842 0,465 1,411 0,266 1,395 1,99 0,330 1,915 1,937 0,74 1,871 0,578 0,848 1,142 1,726 0,134 1,21 0,597 0,243 1,238 0,144 0,224 0,44 1,506 1,859 0,469 1,875 0,420 1,724 0,897 1,493 1,51 0,885 0,722 0,949 1,694 0,936 1,918 1,607 1,691 1,627 0,640 1,4 0,399 0,35 1,861 0,799 1,469 1,501 0,641 1,390 0,994 1,239 0,583 1,270 0,210 0,231 1,826 0,590 0,308 1,233 1,95 1,122 1,570 0,726 0,248 1,158 0,769 1,950 1,699 0,20 1,197 1,0 0,544 1,717 0,860 1,685 0,206 1,413 0,413 1,31 0,621 1,460 0,774 1,506 1,162 0,700 0,711 0,817 1,345 0,118 0,738 0,955 0,687 0,552 0,72 0,552 0,648 0,203 0,852 0,794 0,263 0,150 0,71 1,662 1,791 1,598 1,314 1,544 1,632 1,511 0,644 0,201 0,715 0,805 1,329 1,704 0,116 0,809 1,597 0,180 0,462 0,968 1,814 0,601 0,262 0,62 0,149 1,405 0,784 1,918 0,659 1,968 0,278 1,279 0,649 0,391 0,193 1,31 0,104 0,506 1,902 1,847 1,481 1,526 1,397 0,676 1,626 0,911 0,280 0,504 0,773 1,460 1,707 0,436 1,112 0,536 1,258 1,276 0,308 0,920 0,698 0,620 1,437 1,870 0,784 1,210 0,654 1,491 0,548 0,880 0,115 1,748 0,702 1,987 0,772 0,282 0,660 0,290 0,823 1,698 0,302 0,570 0,955 1,824 1,458 1,542 0,941 0,965 1,812 1,881 0,62 0,613 0,504 1,785 1,72 0,205 0,204 0,47 1,12 1,892 0,891 1,150 0,240 0,328 0,556 1,463 1,621 1,139 1,7 1,567 0,948 0,610 1,967 0,744 0,881 1,977 0,448 1,475 1,343 1,737 0,16 1,396 0,630 0,777 0,579 0,814 1,935 0,234 1,287 0,606 1,81 0,363 0,149 0,118 1,582 0,923 1,608 1,687 1,523 1,945 0,452 0,241 1,974 0,471 0,68 1,457 1,317 1,254 0,163 1,755 1,896 1,816 1,254 1,349 1,471 0,393 0,825 0,573 1,758 1,769 0,398 1,681 1,350 1,994 1,636 1,693 1,178 0,241 0,329 0,797 0,723 0,122 0,299 0,550 0,404 0,743 1,83 0,418 0,681 1,845 1,37 1,630 0,824 0,69 0,78 0,980 1,100 0,875 1,581 0,575 1,10 1,172 0,567 1,745 1,28 1,336 1,748 1,442 0,16 1,255 0,983 1,663 0,523 1,566 1,826 1,523 0,808 1,557 1,129 0,299 0,76 0,57 0,252 0,186 0,973 0,450 1,252 0,927 1,840 0,568 0,428 0,705 1,467 1,929 0,319 0,872 1,793 1,269 0,358 1,519 0,995 0,559 0,942 0,303 0,944 0,600 1,821 0,96 0,835 0,465 0,840 0,268 0,867 0,356 1,795 1,368 0,324 1,455 1,267 0,752 0,619 0,709 0,709 0,812 1,499 0,72 1,72 0,762 1,695 0,672 0,15 0,896 0,57 1,766 0,104 0,638 0,473 0,438 1,923 1,192 0,980 1,750 1,142 1,516 1,974 0,118 1,76 1,578 0,349 0,576 0,411 0,925 1,925 0,215 1,828 1,381 0,952 0,583 0,938 1,452 1,51 1,423 0,782 1,813 0,213 0,35 0,944 1,865 0,225 1,750 1,887 0,188 1,368 1,858 0,919 0,798 1,157 1,429 1,979 1,168 0,340 1,396 1,531 0,290 1,405 0,962 0,191 0,478 1,942 1,982 0,858 1,773 0,124 1,867 1,626 1,811 1,38 0,205 1,925 0,626 0,335 1,248 1,643 0,510 1,66 0,703 0,948 1,713 0,528 1,670 1,492 1,136 1,669 0,635 1,627 0,544 0,52 0,978 1,185 0,841 1,150 1,163 1,137 1,519 0,8 1,268 1,715 1,793 0,261 1,23 0,351 1,715 1,891 0,464 1,294 0,681 1,713 0,839 0,814 0,535 0,173 0,609 0,597 1,541 0,282 0,289 1,864 0,40 1,881 1,744 0,372 1,852 0,392 1,216 1,510 1,897 1,172 0,646 0,226 0,991 1,477 0,524 0,966 1,341 0,731 1,527 0,919 0,408 1,799 0,295 1,439 0,656 1,284 0,485 0,402 1,376 1,652 0,844 0,512 0,190 1,24 0,69 1,579 1,132 1,4 1,493 1,61 0,629 1,876 1,560 0,527 1,793 0,993 0,928 1,139 1,101 0,428 1,743 1,25 1,525 0,65 1,867 1,991 0,586 1,410 0,615 1,931 0,267 1,276 1,929 0,26 1,465 0,396 0,926 1,562 1,922 1,917 1,691 1,835 0,78 0,328 0,231 1,880 1,755 1,653 0,232 1,540 0,241 1,320 1,507 0,284 0,725 0,25 1,996 0,904 1,833 0,224 1,629 0,461 0,619 0,393 0,761 1,4 0,916 1,522 0,771 1,984 0,652 0,855 0,37 0,15 0,757 1,48 1,368 1,310 1,236 1,285 0,182 1,891 0,306 0,467 0,967 1,830 0,792 0,423 0,980 1,937 0,579 1,263 0,60 0,231 0,863 1,436 1,347 1,453 1,706 0,160 0,97 1,812 0,200 1,75 1,783 0,551 0,434 0,104 1,858 0,900 1,305 1,569 0,216 1,138 1,247 0,36 0,922 1,574 1,737 1,191 1,6 0,445 1,692 1,98 0,743 1,908 1,359 1,470 1,717 1,373 1,954 0,253 0,981 0,875 1,270 0,313 1,259 0,174 1,761 0,738 0,246 0,192 0,962 0,863 0,134 0,366 1,722 1,894 0,577 0,854 1,124 0,659 1,11 1,942 0,294 1,296 0,137 1,878 0,72 0,621 1,482 1,438 1,79 0,460 1,904 1,559 1,246 1,8 0,618 1,799 1,844 1,550 0,944 0,755 1,324 1,155 1,194 0,299 1,564 0,309 1,239 0,101 1,829 0,129 0,89 1,544 0,270 0,238 1,256 1,988 1,216 0,988 0,968 1,908 0,713 1,232 1,324 0,76 1,234 1,366 0,813 1,865 0,615 0,174 0,916 1,570 1,611 0,193 1,140 0,369 0,461 0,812 0,161 0,210 1,479 0,546 0,110 1,185 1,48 1,957 0,574 0,455 0,32 1,121 1,573 1,277 1,343 0,881 1,53 1,878 1,894 0,738 1,855 1,86 1,846 1,326 0,851 0,644 0,452 0,16 0,3 1,99 0,1 0,71 0,746 1,277 1,76 0,415 1,926 0,578 0,489 1,391 0,15 0,292 0,454 1,167 1,299 0,440 0,724 0,553 0,24 0,568 1,748 1,326 1,408 1,93 0,406 0,92 0,385 1,8 1,416 0,796 0,637 0,864 0,174 1,912 1,101 0,302 1,845 0,480 1,569 1,950 0,872 1,397 0,458 0,227 0,797 0,461 0,653 0,658 1,727 0,458 1,277 1,912 0,947 1,196 1,858 1,475 0,984 1,427 1,884 1,317 1,260 0,908 0,37 1,29 0,32 0,258 1,695 0,997 1,668 0,287 0,741 1,962 0,624 0,963 0,559 1,716 1,690 1,751 0,540 0,858 0,779 0,826 0,99 0,590 1,550 1,90 0,88 1,547 1,774 0,352 1,875 0,875 0,124 0,631 1,423 1,640 0,828 0,529 0,890 1,325 0,648 0,508 0,420 0,261 1,619 1,174 0,840 0,507 0,516 0,937 1,858 0,602 0,615 1,300 0,730 0,498 0,917 1,342 1,585 0,216 1,540 1,948 0,396 0,392 0,134 0,590 1,173 1,394 1,956 1,886 0,503 0,337 1,840 1,282 1,485 0,810 0,673 1,88 1,802 1,563 1,759 0,380 1,30 0,177 1,334 1,844 0,858 0,280 1,524 1,434 1,862 1,806 1,608 1,164 0,774 0,478 1,991 0,394 1,484 1,65 0,671 0,220 0,297 1,404 0,767 1,96 0,373 0,992 1,35 0,286 0,832 1,882 0,574 0,549 0,549 0,82 1,354 1,987 0,320 1,440 1,657 1,714 1,380 1,873 1,122 1,731 0,413 0,849 1,394 1,935 0,858 0,16 0,751 0,951 1,604 0,159 1,500 0,368 1,537 1,264 1,753 1,601 0,836 0,825 1,696 0,493 0,113 0,773 1,529 1,379 1,844 0,811 0,891 0,593 1,764 0,483 1,796 1,799 1,93 1,260 0,799 0,816 0,180 0,292 0,555 0,29 0,91 0,83 0,591 0,889 0,71 1,86 0,474 1,231 0,454 1,620 1,820 1,374 0,43 0,793 1,804 0,37 0,104 1,7 1,378 0,721 0,655 1,699 0,71 1,465 1,458 1,660 1,823 0,822 0,115 0,952 1,874 0,543 0,731 1,515 0,838 0,244 1,532 0,14 0,781 0,377 1,553 1,693 1,642 0,263 1,973 0,29 1,19 1,764 1,352 0,198 0,44 0,982 1,342 0,835 0,668 1,643 0,657 0,192 1,620 1,858 1,756 0,800 0,637 1,851 1,669 1,701 1,65 0,324 1,673 1,733 0,36 1,101 0,485 0,497 0,79 1,906 1,343 0,636 0,138 1,65 1,518 1,95 0,769 1,851 1,755 0,718 0,157 0,566 1,310 0,753 0,964 0,260 1,178 0,963 1,288 0,388 1,944 1,128 1,48 0,79 1,149 0,844 0,911 0,100 1,692 0,529 1,844 0,529 1,996 1,130 1,786 1,606 1,827 1,285 0,314 0,963 1,67 1,724 0,932 1,764 0,698 0,261 1,147 1,238 1,797 1,470 0,883 1,251 1,725 1,615 0,238 0,441 1,846 0,676 1,342 1,883 0,915 0,722 0,373 1,985 0,733 1,917 1,57 1,350 1,350 0,885 1,682 1,156 1,662 1,1 0,618 1,150 1,534 0,541 0,865 0,56 1,607 0,402 0,923 1,188 0,543 0,584 0,348 1,114 0,973 0,989 0,109 0,972 0,233 0,817 0,489 1,500 1,299 1,721 1,726 0,935 0,282 1,270 1,829 0,974 0,455 1,654 0,413 0,233 0,643 0,682 0,527 1,790 0,846 0,995 1,299 1,108 0,981 1,847 1,722 0,467 0,189 1,955 1,718 0,55 1,83 1,503 0,365 0,581 0,461 1,630 1,985 1,151 0,260 1,591 0,868 0,189 1,822 1,680 0,747 0,250 1,442 1,326 0,672 0,291 0,293 0,717 1,881 1,809 0,554 0,401 1,475 0,675 1,602 1,192 0,937 0,643 1,716 1,117 0,134 1,746 1,909 0,105 0,693 0,739 0,886 1,173 1,419 0,580 0,546 1,405 0,422 0,788 0,56 0,803 0,789 0,577 1,123 0,965 1,259 0,599 0,575 0,37 0,302 1,173 1,50 0,478 0,388 0,161 1,967 0,981 0,626 0,614 0,259 1,790 0,867 1,744 0,378 0,699 0,271 1,723 0,490 0,244 1,242 1,589 1,816 0,412 0,525 1,529 0,575 1,42 0,289 1,661 1,12 1,365 1,400 0,245 0,448 0,371 0,554 0,213 1,246 0,415 1,734 1,486 0,492 0,289 1,664 1,187 0,556 0,432 0,772 0,719 1,242 1,361 0,574 1,488 0,913 0,584 1,849 0,205 1,757 1,764 0,928 0,313 1,602 1,901 1,712 0,359 1,595 0,169 0,304 0,878 0,93 1,871 0,71 0,977 0,290 0,959 1,466 1,562 0,72 1,40 1,41 1,763 0,888 0,782 1,524 1,78 1,382 0,124 0,429 0,841 0,950 1,777 0,499 1,828 1,232 0,475 0,513 1,345 1,451 1,863 0,696 1,772 0,442 0,903 0,627 0,110 0,967 1,487 0,117 0,670 0,520 0,500 1,554 0,693 0,838 0,478 1,217 0,557 0,45 0,306 0,784 1,96 0,78 1,687 1,977 1,458 0,603 0,668 0,202 1,339 1,345 0,427 0,177 1,276 1,400 0,737 1,799 1,227 1,791 1,620 1,621 0,74 0,998 0,876 0,731 1,221 1,604 0,989 0,976 0,334 1,453 0,2 1,871 1,99 1,628 1,141 0,688 0,66 1,382 1,551 1,462 1,590 1,37 0,433 1,162 1,539 1,419 1,659 1,138 1,409 1,299 0,652 0,157 1,856 1,991 0,13 0,732 0,841 0,654 1,247 1,880 0,757 0,176 1,365 1,964 0,657 1,25 0,857 1,281 1,237 1,194 1,248 0,551 0,23 0,375 0,19 0,578 1,48 1,862 1,350 1,543 1,566 0,311 1,24 0,964 1,33 0,319 1,204 0,583 1,159 1,245 1,572 1,112 1,383 1,745 1,591 1,163 1,981 1,992 0,672 0,426 1,632 0,288 1,0 0,736 1,168 1,454 0,851 1,598 1,820 0,463 0,59 1,704 1,848 0,578 0,580 0,59 0,899 1,974 1,911 0,688 1,130 0,462 0,449 1,120 1,388 0,78 0,958 0,543 0,660 1,840 1,683 0,416 1,103 0,751 1,427 0,319 1,773 0,430 1,197 0,473 0,707 0,739 0,710 1,427 1,228 1,970 0,599 1,476 0,953 0,852 0,310 1,104 1,38 0,688 0,290 0,572 0,644 0,881 0,920 0,473 1,973 0,682 1,973 1,539 1,71 1,887 1,557 1,540 1,55 1,412 1,139 1,455 0,343 1,127 0,314 1,295 0,354 0,804 1,731 1,599 1,842 0,201 0,248 1,375 1,610 1,66 0,511 0,573 1,822 0,217 0,224 1,532 1,476 1,899 1,949 0,981 1,389 1,529 0,854 1,899 1,984 1,155 1,183 0,430 1,169 1,368 1,733 1,794 1,981 1,303 0,993 1,732 1,426 0,545 1,280 0,929 1,645 0,760 1,548 0,519 1,127 1,579 0,413 0,737 0,184 0,922 0,176 1,912 1,559 0,168 1,354 0,611 0,817 0,801 1,159 1,170 1,201 1,319 1,448 0,862 0,421 1,899 0,414 0,994 1,774 0,123 1,328 1,307 1,100 0,90 1,512 1,279 0,371 1,99 1,312 1,227 0,30 0,146 1,456 0,170 1,312 0,705 1,70 1,827 1,955 1,983 0,233 1,180 1,426 0,476 1,575 0,306 0,840 0,121 0,812 1,650 1,831 1,245 1,653 1,349 0,126 1,115 1,153 1,834 0,804 0,280 0,176 0,899 1,612 0,290 1,870 1,51 1,281 1,912 0,355 0,173 1,249 0,124 1,203 0,533 0,643 0,310 1,584 0,803 0,728 0,181 0,929 0,403 1,487 1,979 0,463 0,687 1,77 1,260 1,829 1,870 1,374 1,795 0,162 1,877 0,15 0,177 0,898 0,169 1,778 1,272 1,354 1,401 1,814 1,557 1,852 1,435 0,766 0,396 1,124 1,790 0,759 1,472 0,3 0,653 0,640 0,211 1,686 0,967 0,734 0,604 1,97 1,715 0,950 0,390 0,741 1,93 1,536 1,284 1,642 0,88 1,820 0,493 0,741 0,467 1,458 0,173 1,222 1,786 0,754 0,572 0,335 1,826 1,39 0,737 0,124 0,877 1,107 0,49 0,491 1,208 1,162 1,36 0,601 1,211 0,759 1,270 0,836 0,245 0,734 0,146 0,191 0,730 0,713 0,374 1,417 0,521 1,322 1,52 1,194 1,411 0,535 1,54 1,352 1,245 1,426 1,864 0,482 0,371 0,559 1,843 1,181 0,612 0,162 0,32 1,751 0,134 1,774 0,557 0,274 1,231 1,317 1,477 0,995 0,100 0,58 0,38 0,86 1,737 0,219 0,281 0,733 1,285 1,701 0,541 1,952 0,314 0,26 1,985 0,330 0,912 1,619 1,458 1,719 1,667 0,907 0,246 0,238 0,696 0,518 0,229 0,490 0,362 1,973 1,466 1,540 0,247 0,57 0,397 0,503 1,875 0,489 1,90 0,949 0,749 1,142 1,993 0,456 0,785 0,884 1,143 0,924 1,344 0,890 1,173 1,305 0,528 0,644 0,999 0,860 1,53 0,324 0,442 1,964 1,940 1,802 0,374 0,782 1,620 1,386 0,7 0,518 0,82 0,151 0,428 1,805 1,378 1,199 0,91 1,288 1,197 1,32 0,636 1,284 1,738 1,806 0,316 0,171 1,359 1,121 0,815 1,462 1,663 0,164 1,355 1,693 0,785 1,837 0,919 1,580 0,936 0,807 0,865 0,280 0,365 1,240 1,152 0,397 1,710 0,943 0,777 0,594 1,670 1,743 0,866 1,956 1,868 1,198 0,587 1,365 0,819 0,385 1,653 1,255 1,279 1,62 1,292 0,679 0,606 0,752 1,865 1,753 1,322 1,245 1,363 0,428 0,124 1,345 0,156 0,285 1,503 1,783 1,975 0,175 0,236 1,450 1,200 1,360 0,378 1,603 1,831 0,223 0,262 1,736 0,64 1,720 1,819 1,475 0,623 1,298 1,768 0,4 1,718 0,779 1,56 1,401 1,794 0,266 1,15 1,299 0,233 1,155 0,259 0,573 0,334 0,396 1,186 1,626 0,231 1,579 0,417 1,978 0,746 0,722 1,699 1,890 1,363 0,73 0,840 0,974 0,198 1,647 0,653 1,332 1,976 0,432 0,77 0,295 1,269 0,394 0,320 0,725 1,769 1,597 1,276 1,152 0,443 1,38 1,423 1,162 1,641 0,632 1,214 0,436 1,836 0,25 1,27 0,729 0,248 0,753 1,519 1,721 0,816 0,503 0,585 0,777 1,491 1,497 1,756 0,302 0,450 1,683 0,374 1,560 1,907 1,536 1,958 0,188 1,212 0,969 0,105 1,188 1,612 0,753 1,87 0,976 1,940 0,990 1,86 1,544 0,766 0,342 1,778 1,59 0,238 1,80 0,585 1,961 0,590 0,816 0,412 0,321 0,799 0,644 0,281 0,535 0,332 1,144 1,139 1,546 0,75 1,462 1,808 1,448 1,227 0,522 0,939 0,866 1,812 1,598 1,405 0,99 1,436 1,658 0,514 1,7 0,56 0,197 1,293 0,568 0,370 0,583 1,28 1,110 1,925 1,698 0,204 0,507 1,2 0,469 0,712 0,525 1,771 1,943 1,741 0,898 1,136 1,184 0,164 1,831 1,882 1,144 1,785 1,266 1,872 1,656 1,143 0,744 0,278 1,951 1,815 0,829 0,521 1,319 0,799 0,736 0,994 0,716 1,37 1,119 1,907 1,35 1,726 1,118 0,698 1,148 0,271 1,417 0,923 0,214 1,181 0,573 0,640 0,789 1,664 0,153 0,149 0,500 0,140 0,168 0,720 0,627 1,416 0,213 0,473 0,232 0,578 1,511 1,507 1,809 0,160 1,643 0,558 0,983 0,816 0,776 1,253 1,727 0,334 1,58 0,481 0,418 1,557 1,743 1,187 1,74 0,782 1,856 1,25 0,361 1,100 0,272 0,859 0,144 1,709 0,840 0,392 1,292 1,622 0,259 1,549 0,862 0,356 0,406 1,747 1,37 1,883 1,566 1,951 1,775 1,782 1,290 0,522 0,154 0,307 1,441 1,419 0,317 1,916 1,58 0,538 0,411 0,226 1,212 1,2 1,252 1,402 0,154 1,436 1,854 1,447 1,969 0,420 0,852 1,588 1,268 1,976 0,551 0,16 0,962 1,963 0,553 0,296 1,344 1,836 0,567 1,175 0,432 1,198 1,3 0,181 0,445 0,471 0,856 1,432 1,827 1,314 0,791 0,605 0,475 0,997 1,925 0,462 1,526 0,836 0,831 1,658 1,440 1,799 1,908 0,29 0,908 1,170 0,648 0,527 0,942 0,989 1,457 1,8 0,62 1,111 1,303 1,949 1,382 1,516 1,388 1,897 0,377 0,829 1,702 1,105 0,45 0,917 1,295 0,118 1,763 0,483 1,625 0,902 1,977 0,571 0,334 0,438 0,666 0,356 1,581 1,641 0,759 1,760 0,320 0,442 1,88 0,3 0,771 0,346 0,593 0,842 1,399 1,504 1,232 0,717 0,127 1,502 1,290 1,296 0,745 0,639 1,814 1,218 1,270 1,903 0,61 0,373 1,123 0,586 0,139 1,520 0,76 1,911 1,300 1,301 1,872 1,488 1,306 1,422 1,506 1,949 1,264 0,175 0,263 0,319 0,848 0,369 1,435 1,81 1,926 1,746 1,814 1,467 1,25 1,514 0,172 0,102 1,65 1,54 1,488 1,854 0,188 0,869 1,967 1,228 1,505 1,352 0,462 0,601 0,259 0,892 1,844 0,128 1,450 0,840 0,426 1,929 1,179 0,440 0,576 0,983 0,578 0,755 0,791 1,608 0,457 0,904 1,107 1,643 0,443 1,213 0,61 0,299 0,406 1,464 0,675 1,902 1,856 1,144 0,685 1,222 0,297 1,868 0,554 1,119 1,79 0,12 1,305 0,274 0,88 1,686 0,814 0,552 1,700 1,232 1,39 1,142 1,718 1,33 1,226 1,437 0,550 1,53 0,946 0,37 0,344 0,965 0,480 1,757 1,887 0,974 0,589 0,884 0,689 0,980 1,822 0,376 0,776 0,187 1,471 1,7 1,988 0,509 1,109 1,342 0,956 0,842 1,276 0,382 1,964 0,837 0,844 1,114 1,902 1,792 0,745 0,285 1,671 1,105 1,752 0,787 1,662 0,964 1,947 1,926 1,944 1,549 1,436 0,29 0,614 0,305 0,500 0,578 1,282 1,784 0,964 1,31 1,333 1,944 0,919 1,162 0,43 1,385 0,145 0,384 1,722 0,441 0,126 1,841 0,975 0,734 0,532 1,809 0,184 1,676 0,860 1,570 0,586 1,674 0,941 0,144 1,678 1,912 1,624 1,246 0,719 1,607 1,37 1,658 1,990 0,293 1,413 1,358 0,837 0,569 0,488 1,213 0,934 0,0 1,578 0,383 0,294 0,399 0,886 0,345 0,22 0,304 0,535 0,505 1,416 0,62 0,404 1,508 1,801 0,789 0,597 1,731 0,896 1,790 1,229 1,162 1,408 1,757 0,954 1,185 1,776 0,737 0,178 0,483 1,902 1,645 0,740 0,853 0,240 0,22 1,706 0,75 0,542 1,854 1,608 1,186 1,142 1,896 1,648 1,344 1,976 1,92 1,733 0,760 1,590 0,304 0,261 0,12 0,951 0,58 0,239 0,528 1,179 1,456 0,371 0,318 0,119 1,651 1,882 0,686 1,338 1,613 1,538 1,786 0,392 0,873 1,715 1,357 0,74 0,978 0,228 0,697 0,398 0,137 1,677 0,958 0,160 0,154 0,144 1,433 0,526 0,553 0,344 1,152 0,663 0,912 0,550 0,605 0,539 0,528 1,641 0,234 0,953 1,965 0,646 0,867 1,786 0,962 0,37 0,156 0,553 0,825 0,181 0,672 1,382 1,659 1,510 1,160 1,190 1,142 0,532 0,810 1,236 1,202 1,535 0,883 0,537 0,198 0,521 0,26 0,321 0,735 1,811 1,230 0,25 1,691 1,534 1,598 0,417 1,759 1,18 0,573 0,678 0,805 1,847 1,514 1,423 0,11 1,53 0,367 0,533 0,327 0,106 0,165 0,31 1,977 1,364 1,743 1,299 0,869 1,960 0,387 0,844 1,248 1,992 1,906 0,920 0,299 0,206 0,682 1,374 0,518 1,236 1,123 1,962 0,650 0,374 1,888 1,824 0,203 0,419 0,231 0,203 0,215 0,626 1,70 1,712 1,422 1,172 1,462 0,978 0,433 1,833 1,737 0,318 0,577 0,518 0,367 1,255 1,857 1,561 0,258 1,300 1,526 1,137 1,607 1,370 0,766 0,731 0,25 0,429 0,394 1,877 0,77 1,91 0,941 0,171 0,738 1,615 0,181 0,137 0,617 1,604 0,77 1,453 1,838 1,159 1,522 1,377 1,208 0,157 0,142 1,303 0,779 0,845 1,937 1,923 1,854 1,618 0,971 0,7 1,655 0,819 1,167 1,73 0,926 0,191 0,744 0,394 1,258 1,461 1,162 1,204 0,249 0,413 1,437 0,691 0,759 0,359 0,616 0,826 1,729 0,816 0,996 1,659 1,697 0,372 0,112 0,730 1,148 0,594 1,3 1,575 0,859 0,797 0,142 0,434 0,842 1,997 0,819 1,805 1,932 0,925 1,422 1,960 1,505 0,784 0,371 0,706 0,619 0,971 1,543 1,949 1,71 0,554 0,160 0,966 0,481 0,289 1,948 0,269 0,606 0,314 1,327 1,159 0,908 1,530 1,177 0,159 1,521 1,123 1,301 1,344 0,851 1,103 1,528 1,424 1,233 0,771 1,627 1,539 1,74 0,386 1,169 1,183 0,140 0,672 1,584 0,63 0,197 0,711 1,248 1,550 1,574 1,608 1,907 0,971 1,626 1,191 1,792 1,516 0,699 1,141 1,901 1,25 1,889 0,236 0,777 0,580 0,668 0,235 0,189 1,301 0,887 1,976 0,608 0,696 0,707 1,212 1,121 1,32 0,895 0,327 0,61 1,209 0,773 1,944 1,337 1,784 0,321 1,387 0,965 0,683 0,486 0,992 0,681 0,111 1,625 1,590 0,18 0,923 0,723 1,686 0,741 1,584 1,437 0,625 0,351 0,346 0,381 1,109 1,573 1,8 1,54 1,151 1,873 0,349 0,131 1,804 0,582 0,178 0,881 1,888 1,609 0,783 1,719 1,739 0,627 0,837 1,298 0,43 1,397 1,653 1,868 1,96 1,120 0,126 1,232 1,607 0,251 1,375 1,389 1,61 0,311 1,825 0,751 0,175 0,433 0,418 0,868 0,661 1,724 1,597 0,893 1,896 0,399 0,747 1,909 0,303 0,847 1,709 1,913 1,651 1,894 1,846 1,380 0,578 0,285 1,527 0,835 1,339 0,626 0,482 0,972 1,639 0,295 1,604 1,501 1,377 1,105 0,922 0,599 1,851 1,756 1,725 1,665 1,747 0,611 0,119 1,419 0,712 1,11 1,199 0,458 0,171 1,87 0,320 1,654 1,344 0,227 0,526 0,30 1,676 1,552 1,967 0,8 1,531 0,264 0,844 0,421 0,836 0,837 0,870 1,661 0,370 0,121 0,73 1,262 0,106 0,287 1,276 1,227 1,605 1,236 1,790 0,994 1,955 1,540 0,549 0,720 1,620 1,425 1,486 1,482 1,198 0,511 0,339 0,484 0,41 0,332 1,311 0,381 1,29 0,325 0,755 1,514 0,902 0,249 1,806 1,38 0,702 1,642 0,16 0,823 0,917 0,454 0,143 1,530 0,971 0,58 1,481 1,516 0,243 1,722 1,115 1,565 1,577 1,819 0,853 1,123 0,758 0,753 0,25 0,869 1,15 0,544 0,74 0,929 0,490 0,857 0,817 0,293 1,455 0,229 1,543 1,350 0,634 1,268 1,586 1,352 1,69 1,735 1,789 1,477 0,747 1,196 1,484 0,134 0,536 1,528 0,760 1,635 1,964 1,618 0,158 1,261 1,375 0,690 1,262 0,98 0,976 0,423 0,335 0,901 0,753 0,316 1,497 0,445 0,698 1,800 0,516 0,181 0,531 0,327 0,943 0,905 0,336 1,399 0,886 0,624 0,79 1,510 0,351 0,409 1,375 1,329 1,701 0,529 0,194 1,883 1,827 1,516 0,136 0,774 0,655 1,948 0,454 1,770 0,388 0,222 1,455 0,13 0,655 0,916 0,642 1,140 1,987 0,720 1,78 0,190 0,296 0,556 1,271 1,884 0,445 0,842 1,954 0,936 0,924 1,58 0,44 0,691 0,312 1,925 1,506 1,569 0,133 1,389 1,761 1,73 1,91 1,523 1,599 1,230 1,845 0,989 0,137 0,435 0,509 0,298 1,254 1,812 0,213 1,561 1,575 0,998 0,848 0,885 0,275 1,573 0,133 1,597 0,809 1,797 0,253 0,933 0,934 1,814 1,310 1,41 0,644 0,315 0,323 1,34 0,74 0,784 1,183 0,574 0,728 0,456 1,942 0,244 1,469 1,260 0,352 0,236 1,166 1,467 0,798 1,701 1,769 1,309 0,218 0,566 1,821 1,973 1,271 1,437 1,904 0,454 1,272 1,127 1,50 0,399 0,412 1,387 0,387 1,617 0,871 1,137 1,999 1,703 0,239 1,306 0,907 1,693 1,67 1,10 0,682 0,512 0,731 1,62 0,93 1,253 0,632 1,786 1,982 1,249 1,332 0,394 1,58 0,146 0,134 1,6 0,953 0,491 0,473 0,968 1,934 0,891 1,861 1,729 0,523 1,891 1,299 0,154 1,227 0,852 0,987 0,663 1,638 1,947 0,350 0,48 1,446 1,978 0,567 1,120 1,599 0,479 1,47 1,262 0,6 1,855 1,68 1,441 0,510 1,525 0,960 1,231 0,522 0,104 0,710 1,152 0,108 1,770 1,885 1,502 1,725 1,330 0,403 1,268 1,975 0,48 1,781 0,523 0,921 0,507 0,632 1,790 0,535 0,742 0,737 0,177 0,619 0,42 0,676 0,148 1,844 1,195 1,95 1,743 0,790 1,764 0,12 1,517 1,5 1,206 1,206 0,145 0,929 1,443 1,64 0,438 1,469 0,931 1,489 0,667 0,453 1,159 1,108 0,899 1,772 1,916 1,859 0,551 0,928 1,715 1,231 0,251 1,93 1,756 1,465 0,41 1,572 0,951 0,482 1,437 0,344 0,139 1,43 0,806 1,363 0,994 0,537 0,550 0,573 0,798 0,586 0,106 1,468 0,510 0,396 1,26 0,791 1,411 1,322 1,61 0,868 1,606 1,380 0,213 0,69 1,654 1,398 1,813 0,441 0,102 0,20 0,354 1,982 1,112 1,58 0,260 0,517 1,138 1,308 0,334 0,599 0,966 0,391 1,494 0,85 0,286 0,944 1,626 1,416 1,485 1,338 1,845 1,825 0,612 1,202 1,96 0,780 1,309 1,399 1,580 1,183 1,457 1,898 1,793 1,644 0,278 1,211 0,805 1,615 0,279 0,685 1,67 0,583 1,324 1,658 1,438 1,200 0,257 1,926 1,433 0,154 0,966 1,617 0,275 1,768 0,403 1,723 1,744 0,615 0,524 0,500 1,782 0,84 0,206 1,788 0,686 0,123 1,469 1,849 0,457 0,262 0,605 0,678 0,753 1,60 1,695 0,238 0,917 1,311 0,309 1,998 1,293 0,745 0,575 0,615 0,886 0,770 0,513 0,573 1,152 1,199 0,671 1,269 0,175 1,574 0,30 0,518 1,293 1,795 0,735 1,756 1,557 0,46 1,699 1,296 1,692 1,508 1,837 0,281 1,732 0,294 0,813 0,11 1,961 1,273 0,511 1,273 1,98 1,862 0,326 1,804 1,365 1,111 1,287 0,163 1,779 0,889 1,27 1,224 1,296 1,608 1,397 0,638 1,50 0,146 0,126 0,48 1,321 0,64 0,62 0,756 0,437 1,584 1,310 1,667 1,191 1,21 0,631 0,810 0,204 0,979 0,129 1,129 1,951 0,477 0,40 1,96 1,816 0,569 0,306 0,368 0,819 1,173 1,467 0,915 1,500 0,649 0,896 0,798 0,818 0,512 1,112 0,857 0,632 1,855 1,28 0,269 1,337 1,518 1,276 0,499 0,180 0,521 0,828 1,9 0,687 0,516 0,376 0,146 0,288 0,690 1,206 0,911 1,268 0,125 1,977 0,991 1,470 0,665 0,75 0,626 1,821 0,493 1,546 0,533 0,477 1,725 0,478 0,158 0,981 0,934 1,374 1,403 0,624 1,466 0,168 0,749 0,205 0,705 1,320 1,528 0,692 0,764 1,572 0,931 1,537 1,943 0,22 0,685 1,603 1,583 1,915 0,169 0,976 1,86 0,732 0,99 1,77 1,244 1,865 0,657 1,650 0,403 1,82 0,4 1,365 0,556 1,796 1,752 0,444 0,2 0,207 1,860 0,346 0,851 0,484 0,450 0,544 1,123 1,265 0,877 1,672 1,674 0,955 1,314 0,147 1,28 1,574 1,672 0,383 1,80 1,80 0,572 1,290 1,351 1,208 1,218 1,27 0,595 0,922 1,525 0,742 0,266 0,548 0,149 0,344 0,282 0,762 0,125 0,739 1,429 1,582 1,778 1,563 1,931 1,520 1,715 0,15 0,294 0,39 1,961 1,856 0,19 0,987 0,692 1,638 0,742 0,888 1,29 0,477 0,801 0,439 0,902 1,249 0,487 1,568 0,74 0,664 0,694 0,125 1,766 0,137 1,390 1,345 1,276 1,52 1,920 1,316 0,161 1,44 1,453 1,278 0,733 1,105 1,180 1,497 0,576 0,921 1,943 0,707 0,258 0,152 1,628 0,126 0,328 1,140 0,93 1,342 1,40 0,698 1,734 0,683 0,736 1,529 1,385 1,533 0,563 0,739 1,976 0,433 1,687 0,130 1,812 0,835 1,692 1,283 1,55 0,288 0,793 1,915 0,25 1,202 1,203 0,101 0,411 0,61 1,467 1,148 1,129 1,365 1,774 0,173 1,526 0,379 0,987 0,730 1,687 1,996 0,142 0,214 1,522 0,746 1,808 0,531 0,260 1,144 1,243 0,598 1,562 0,157 1,792 1,889 0,984 0,726 0,966 0,656 0,362 0,63 0,835 0,919 0,325 0,18 1,534 1,795 0,653 1,108 1,15 0,498 1,146 0,266 1,401 1,844 1,753 1,57 1,511 0,702 0,707 1,452 1,90 1,340 1,215 1,705 1,339 0,969 1,636 0,106 1,405 0,151 1,542 1,831 1,416 1,558 0,535 0,500 1,321 0,469 1,989 0,355 0,485 0,640 1,590 0,395 0,366 0,748 0,396 0,162 0,785 1,648 1,384 1,923 0,469 1,633 1,896 1,603 1,749 0,790 0,431 0,429 0,874 0,562 1,688 0,67 0,230 0,166 0,952 0,229 0,290 1,782 0,656 0,848 0,283 1,872 0,743 0,235 0,845 1,508 0,230 0,321 1,36 1,960 1,814 1,692 1,957 1,879 0,913 1,490 0,491 0,373 1,600 1,392 0,343 0,960 1,628 1,769 1,990 0,756 1,454 0,338 1,14 1,239 0,59 0,518 1,725 0,626 1,894 0,384 1,61 1,917 0,375 0,930 0,60 0,315 1,335 0,638 1,313 1,610 1,99 0,215 0,477 1,994 0,39 1,908 0,687 0,199 0,568 0,763 0,196 1,745 0,284 1,299 0,829 0,929 1,593 1,845 1,894 0,314 0,218 0,232 0,917 0,317 0,411 1,343 1,87 1,269 0,571 0,483 0,114 0,152 1,581 0,748 1,623 0,979 1,582 0,216 1,165 1,617 0,410 0,922 0,573 0,105 0,692 0,259 1,709 1,636 1,804 1,106 1,224 0,31 1,258 1,261 1,560 1,619 1,343 1,664 1,676 0,211 1,796 1,688 1,647 1,655 1,838 1,702 1,763 0,813 0,857 0,850 0,303 0,130 0,500 0,618 1,619 0,573 0,750 0,490 1,627 1,529 0,211 1,345 1,450 0,547 1,410 1,931 0,386 1,745 0,706 1,697 1,351 1,954 0,847 0,390 1,188 0,839 1,380 1,392 1,681 0,231 0,399 1,181 1,374 1,317 1,790 0,592 0,55 1,710 0,42 1,870 0,750 0,168 0,72 1,809 1,687 1,432 1,886 1,682 0,777 0,830 0,534 1,759 0,149 0,717 1,694 0,889 0,611 0,576 1,716 1,141 1,15 0,552 0,459 1,650 0,682 1,0 1,852 0,742 1,707 1,851 0,294 0,511 0,650 0,886 0,324 1,238 0,886 0,574 1,235 0,651 1,734 1,88 1,455 1,123 0,132 0,550 0,126 0,433 1,296 0,633 1,922 1,595 0,259 0,649 1,430 0,528 0,577 1,217 0,764 0,351 0,814 1,505 0,324 1,860 0,733 1,362 0,718 0,348 1,979 1,780 0,466 1,892 1,9 0,923 0,364 0,352 0,20 1,209 0,450 1,585 0,374 0,671 1,531 0,549 1,72 0,595 0,389 0,665 0,145 1,270 1,344 0,431 0,130 1,727 0,148 1,64 1,611 1,838 1,228 0,866 1,683 1,92 0,480 0,851 0,755 1,438 0,80 0,414 1,477 0,923 0,66 1,839 1,725 0,181 0,400 1,553 1,197 0,157 1,831 1,394 1,292 1,210 0,731 1,313 0,157 0,789 1,486 0,306 0,414 1,982 1,378 1,916 1,337 0,810 1,451 0,778 1,712 1,907 0,569 0,125 0,92 0,346 1,811 1,430 0,192 1,878 1,228 1,933 1,864 0,876 1,794 0,783 1,998 0,684 0,918 0,104 0,442 1,987 0,166 1,252 1,406 1,539 0,770 0,581 0,563 0,567 1,948 1,52 0,115 0,920 0,90 1,476 1,875 1,522 1,47 0,925 1,888 0,814 1,288 0,579 0,817 0,478 0,300 0,57 1,110 0,82 1,242 1,271 0,523 0,253 1,21 1,446 1,165 1,13 0,121 1,711 1,254 0,981 0,227 0,557 1,659 0,297 0,363 0,599 0,189 1,143 0,512 0,949 0,974 1,323 0,578 1,941 0,132 1,522 1,468 1,3 0,400 1,765 1,74 0,17 0,139 1,27 0,712 1,541 1,191 1,538 1,351 0,459 1,327 0,347 0,844 0,694 1,409 1,658 0,868 0,578 0,795 1,940 0,124 0,988 1,37 0,715 0,47 0,798 0,820 1,622 0,529 1,334 0,955 1,626 1,350 0,294 0,763 0,251 1,36 0,141 0,820 0,673 1,451 0,182 0,54 0,761 1,934 1,376 1,387 1,924 1,334 1,417 1,809 1,635 0,316 0,215 1,828 1,247 1,235 0,603 1,260 1,468 0,578 1,726 1,92 1,847 0,934 0,486 1,929 1,86 0,477 1,943 1,301 1,27 1,865 1,126 0,353 0,678 1,230 1,472 1,968 1,713 0,496 0,699 1,474 0,963 0,35 0,517 0,6 1,454 1,696 0,791 0,141 1,46 0,894 1,874 1,536 1,546 0,685 0,525 1,419 0,182 0,385 0,639 1,959 1,569 1,420 0,872 0,950 0,979 0,558 0,624 0,598 0,127 1,553 0,234 0,789 1,967 0,123 0,185 1,869 1,891 0,139 0,742 1,657 1,200 1,255 1,621 1,710 0,336 0,524 1,225 0,993 0,132 0,715 0,747 0,297 0,591 0,578 0,205 1,455 1,3 1,438 0,851 0,813 1,324 0,236 0,353 0,914 0,702 0,389 0,177 0,345 1,114 0,168 0,570 0,607 0,232 1,556 1,669 1,983 0,94 1,463 1,832 1,837 0,218 1,163 1,827 1,812 0,322 1,330 1,744 0,109 0,118 0,368 1,962 1,237 0,61 1,649 1,353 1,246 0,842 1,12 0,630 0,605 1,595 0,41 0,307 0,477 1,473 0,121 0,190 1,537 0,18 0,865 1,28 1,288 0,777 0,372 0,701 0,377 0,375 0,998 0,109 1,981 1,716 0,672 0,294 0,613 0,129 1,960 1,841 1,820 1,641 0,413 0,702 1,986 1,641 0,427 0,407 0,835 0,120 1,786 1,564 0,820 0,844 1,189 0,644 1,722 1,45 0,876 1,406 1,262 0,910 1,435 1,796 1,103 1,347 1,806 0,981 1,142 0,197 1,189 1,914 1,614 0,146 1,216 1,631 0,394 0,432 1,912 1,802 0,203 0,909 0,668 0,296 0,522 0,116 1,67 0,183 0,480 1,144 0,305 1,532 0,361 0,562 1,456 1,254 1,185 1,831 0,863 1,101 0,867 0,395 1,893 0,30 1,555 0,478 0,231 1,434 0,306 0,77 1,634 1,884 1,787 0,81 1,257 0,604 0,712 1,54 0,915 1,112 1,636 0,993 0,525 1,444 1,992 1,829 1,992 0,701 1,859 0,810 1,108 1,321 0,105 1,414 1,31 1,888 0,60 1,876 0,865 0,833 0,288 0,998 0,166 1,994 0,127 1,444 1,626 1,284 0,570 1,474 1,7 0,344 0,593 1,38 1,208 1,433 0,67 0,213 1,867 0,992 1,590 1,70 1,565 1,874 0,501 0,151 0,313 0,423 0,796 1,744 1,790 1,266 1,888 0,914 1,545 1,607 1,606 1,846 0,853 0,850 1,136 1,786 1,145 1,300 1,299 0,900 0,61 1,219 0,176 1,89 1,290 1,750 0,578 0,947 0,954 0,846 1,708 0,703 1,701 0,608 1,913 0,724 1,162 0,844 1,179 0,414 0,502 1,15 1,341 0,30 0,197 0,494 0,106 0,172 1,963 1,124 0,776 1,22 0,87 1,683 1,936 0,487 1,585 0,132 0,278 1,291 1,682 0,884 1,285 0,225 1,378 1,184 1,929 0,348 0,327 0,795 1,249 0,531 1,921 0,489 1,509 1,773 1,401 1,718 1,535 1,44 1,661 1,70 1,64 0,525 0,20 0,506 1,438 0,617 0,784 1,253 0,226 0,833 0,611 1,541 0,410 1,438 0,79 0,831 0,352 1,360 1,293 0,820 1,262 0,617 0,771 1,328 1,25 1,299 1,775 0,380 0,915 1,783 1,467 1,735 0,217 1,617 1,744 1,487 0,164 0,344 0,383 1,530 0,31 0,825 0,967 0,162 1,893 0,683 0,169 0,540 0,231 0,336 1,127 1,768 0,634 0,865 1,977 0,11 1,583 0,577 1,807 1,156 1,262 1,8 0,168 0,512 1,30 0,842 0,630 0,482 1,315 1,768 1,232 0,649 0,471 0,938 1,604 0,325 0,362 0,748 1,81 0,227 0,792 0,781 0,746 1,759 0,125 0,711 1,37 0,309 0,516 1,460 0,49 0,690 0,148 1,281 0,946 1,303 1,466 1,161 1,617 1,927 1,903 1,256 0,694 1,999 0,265 1,934 1,757 1,767 1,922 0,352 1,339 1,797 1,868 1,737 1,68 0,951 1,539 0,683 1,571 0,75 0,321 1,697 1,441 0,492 1,410 0,628 1,692 0,434 1,272 1,736 1,904 0,589 1,524 1,34 0,119 0,371 0,85 1,374 1,85 0,743 0,616 0,938 1,163 0,628 1,653 1,437 1,782 0,713 1,678 1,659 0,810 0,613 0,518 1,128 1,651 1,66 1,351 1,882 1,195 0,670 0,756 0,144 0,905 0,437 1,825 1,410 1,534 1,478 1,157 0,463 0,132 0,991 0,626 0,866 1,762 0,689 0,758 0,274 0,452 1,226 1,725 0,539 0,433 0,17 1,190 1,199 1,400 1,974 1,616 0,574 1,533 1,646 1,22 0,97 1,727 1,259 1,984 0,714 1,673 1,282 0,151 0,840 0,270 1,525 0,423 1,898 1,799 0,907 1,548 0,74 1,835 0,184 0,712 1,963 1,324 1,519 0,301 0,16 0,379 0,663 0,284 1,514 0,346 0,35 1,527 1,87 0,266 1,276 1,97 0,595 0,574 1,591 1,769 0,263 1,938 1,341 1,96 1,778 0,149 0,292 1,783 1,685 0,163 0,99 0,570 1,211 1,822 1,486 0,750 0,651 0,474 1,939 0,962 1,393 0,287 1,252 0,182 0,454 0,961 0,944 1,811 0,213 1,92 0,160 1,452 0,736 0,30 0,985 1,925 1,601 0,920 0,445 0,145 0,269 1,730 0,972 1,709 0,71 0,600 1,996 0,965 1,115 1,671 0,917 1,163 0,572 1,228 0,360 1,89 0,456 1,78 1,809 1,475 0,825 1,630 0,136 1,476 1,245 1,442 1,948 0,825 1,976 0,638 1,728 1,629 0,855 1,317 1,526 1,156 0,117 1,621 1,970 1,707 0,624 1,201 1,918 0,189 1,780 0,598 0,917 0,742 0,271 1,5 0,420 0,921 0,393 1,838 0,561 1,839 0,480 1,216 0,344 0,410 1,848 1,46 1,423 1,174 1,660 1,624 1,546 1,441 0,136 1,705 0,232 0,421 1,456 1,377 1,808 1,906 0,604 0,667 0,607 1,330 0,96 1,92 0,659 0,437 1,463 0,786 1,698 0,372 0,431 1,707 1,523 0,528 1,978 0,429 1,111 1,62 0,227 0,500 1,275 0,499 0,517 0,206 1,477 0,128 0,539 0,600 0,437 0,842 0,319 0,313 0,480 1,85 1,488 1,699 1,195 1,448 1,297 1,899 1,474 0,322 1,146 0,20 0,96 0,685 1,255 1,827 0,895 1,463 1,450 1,538 1,232 1,499 1,650 1,73 0,556 0,749 1,917 1,817 0,271 0,163 0,485 1,106 0,855 0,934 1,816 0,559 1,165 1,548 0,269 0,211 0,635 1,524 0,77 0,467 0,88 1,114 0,307 0,344 0,843 0,835 0,871 0,930 1,440 0,996 0,882 0,240 0,231 0,869 0,579 0,504 1,371 0,336 1,634 0,971 0,453 1,639 1,84 0,382 1,600 1,961 0,410 1,304 0,985 1,258 0,478 1,888 1,260 1,598 1,97 0,500 1,803 1,879 1,556 1,436 1,147 1,871 1,537 0,237 1,284 0,150 1,996 1,637 1,310 1,612 0,732 0,481 0,201 1,314 1,427 0,157 1,857 1,504 0,776 1,707 1,264 1,144 0,787 1,296 0,373 1,50 1,751 0,61 1,133 0,783 0,251 0,240 0,869 1,151 0,280 0,631 1,79 1,800 0,433 0,810 1,178 1,501 1,344 1,939 0,43 1,978 0,221 0,32 1,584 0,390 0,286 1,422 1,572 0,494 1,230 1,370 0,550 1,694 1,50 1,596 1,55 1,734 0,598 1,523 1,583 1,506 0,975 1,65 0,9 0,644 0,629 0,173 1,725 0,579 1,935 1,346 0,275 0,580 1,427 0,708 0,146 0,258 1,57 0,501 0,774 1,33 1,747 1,803 1,987 0,52 1,583 0,28 1,997 0,431 1,998 1,536 1,946 0,747 1,587 0,578 0,544 0,63 1,499 1,549 0,79 1,522 1,858 0,44 1,215 0,275 1,170 0,60 1,796 1,951 1,971 0,280 0,508 1,920 0,616 1,73 1,267 0,205 0,886 1,585 0,459 1,358 0,29 0,74 1,739 1,6 1,663 1,977 0,688 1,198 1,881 0,712 0,330 0,743 0,597 1,340 1,69 0,967 1,825 1,136 0,396 1,121 0,311 0,618 0,14 1,936 1,933 1,958 1,592 0,49 1,416 1,358 1,759 1,52 0,376 0,422 1,6 1,214 0,61 0,563 1,808 0,394 1,695 1,618 0,845 0,979 1,776 1,765 0,915 1,736 1,839 1,256 1,418 1,171 1,383 1,352 0,756 0,750 1,948 1,76 1,919 1,250 0,642 1,508 1,936 1,124 1,949 0,367 1,364 1,629 1,511 1,353 0,258 1,458 1,657 1,189 1,539 0,299 0,748 1,268 1,744 1,629 1,324 1,162 0,240 0,897 0,640 0,951 1,482 1,767 1,781 0,273 1,731 1,865 0,724 1,62 1,576 1,840 1,983 0,580 1,268 1,154 0,77 1,505 0,936 0,813 0,187 1,43 1,855 1,49 1,839 0,500 0,483 1,909 0,277 1,562 1,805 1,251 0,597 0,426 1,99 1,808 1,870 1,512 0,724 1,847 0,818 1,632 1,833 0,122 1,642 0,83 1,460 1,963 0,383 1,296 1,50 0,213 1,965 1,479 0,269 1,191 0,293 0,496 1,396 1,492 0,979 1,30 1,209 0,833 1,296 0,242 1,484 0,975 0,650 0,78 0,11 0,562 1,362 1,956 0,76 0,54 0,346 1,897 0,672 0,895 1,228 1,939 0,765 1,581 1,43 0,216 0,840 1,155 1,737 1,451 0,292 0,660 1,0 1,473 0,105 0,805 0,381 1,853 0,339 0,595 0,958 1,376 0,586 1,280 1,240 1,500 1,868 1,512 0,48 1,907 0,727 0,641 1,736 0,342 0,179 1,489 0,792 0,93 1,657 1,703 1,506 1,622 1,367 1,249 0,676 1,976 0,259 0,707 0,840 1,821 1,144 1,326 0,370 1,858 0,851 1,678 1,118 0,312 0,848 1,310 0,198 0,870 1,999 1,499 1,573 0,556 0,820 0,313 0,92 1,513 0,479 1,103 0,345 1,771 0,855 1,730 1,696 1,20 1,827 0,264 1,184 0,537 0,491 1,789 1,254 0,917 0,462 0,208 0,676 1,650 0,645 1,104 0,626 1,608 1,878 1,39 0,97 0,373 1,444 0,380 1,380 0,379 0,71 1,461 0,382 1,617 1,743 0,667 0,474 0,261 1,767 0,885 1,419 0,864 0,117 0,799 1,675 1,79 0,222 1,628 1,132 1,227 0,300 1,429 1,856 1,159 1,293 0,492 1,341 1,251 1,843 1,112 0,480 1,319 0,27 1,145 1,451 0,737 1,123 1,84 1,439 1,314 1,122 1,380 1,743 0,901 0,997 1,518 1,302 1,868 0,91 1,917 0,396 0,683 0,21 0,776 0,750 0,345 1,385 0,761 0,610 1,669 1,462 0,362 0,159 0,533 1,981 0,62 0,754 1,680 1,483 1,849 0,56 0,315 0,740 1,916 0,323 1,548 1,812 1,656 1,530 1,185 0,42 0,689 1,259 0,138 0,305 1,710 1,823 0,548 1,311 1,83 1,995 0,457 1,810 0,248 0,940 1,8 1,53 0,468 0,808 0,176 1,242 1,14 1,449 0,735 0,470 0,824 0,394 1,671 0,340 1,541 1,69 1,110 0,92 0,550 1,782 1,786 0,933 0,351 0,20 0,603 1,568 1,537 1,385 1,118 1,810 1,503 0,320 1,429 1,352 1,265 1,627 0,357 0,970 1,739 1,20 1,797 0,858 0,215 0,382 0,265 0,965 1,765 0,288 0,487 0,409 1,724 0,545 1,50 0,103 1,678 0,381 0,662 1,393 0,607 1,327 1,294 1,127 1,305 1,189 1,395 1,187 0,751 1,962 0,130 0,612 0,86 1,551 1,363 1,332 0,703 0,583 1,740 0,87 1,559 1,461 1,984 0,512 0,865 0,827 1,239 0,720 0,617 1,705 1,218 1,482 0,531 1,296 0,646 0,564 0,853 0,454 1,306 0,210 0,167 1,875 0,636 1,521 1,529 1,113 1,269 1,758 0,588 0,131 0,792 1,118 1,114 0,483 0,248 0,57 0,535 1,30 1,140 0,824 1,417 1,429 1,414 1,890 1,106 1,553 0,361 0,507 0,965 0,994 1,896 0,660 0,631 0,630 0,375 1,492 1,480 0,68 1,562 1,565 1,981 1,196 0,558 1,926 0,561 0,213 0,905 0,349 0,194 1,539 1,187 0,775 0,79 1,989 0,295 1,602 1,411 1,745 0,153 0,531 1,886 1,954 1,461 1,603 1,305 1,782 1,973 1,298 0,402 0,53 0,109 1,813 1,598 1,909 0,147 0,725 0,708 0,421 0,444 0,963 1,486 1,915 0,801 1,929 0,320 1,565 1,498 0,535 1,195 0,333 1,781 1,828 1,270 1,231 0,230 0,973 0,364 1,873 0,966 0,711 0,49 1,583 1,381 1,922 0,397 0,687 0,85 1,302 0,892 1,553 1,985 0,645 0,537 0,644 0,744 1,622 1,606 0,255 0,48 1,656 1,797 0,250 0,983 0,462 0,185 0,378 1,500 1,512 1,320 1,259 0,449 1,632 1,494 0,489 0,56 1,671 0,833 1,471 0,735 1,447 0,892 1,833 0,926 0,787 1,362 1,809 1,798 0,569 0,29 0,713 1,931 0,356 1,67 1,72 0,972 0,387 0,366 0,74 1,807 0,197 0,306 1,145 0,91 0,785 1,376 0,115 0,677 0,194 1,536 1,887 0,363 0,186 1,121 0,6 1,698 1,864 1,105 0,653 0,252 0,939 0,950 1,372 1,994 0,616 0,995 1,105 1,152 1,504 0,887 1,217 1,572 1,960 0,339 0,702 0,44 0,811 0,425 1,52 0,509 0,534 0,660 1,829 1,767 0,270 0,83 0,330 1,332 0,383 0,697 1,625 1,29 1,827 1,251 1,644 0,468 0,89 0,279 1,613 1,888 1,490 0,436 0,347 0,573 0,145 1,39 0,362 1,717 1,13 0,360 0,54 0,160 1,188 0,804 0,263 0,815 0,705 1,823 0,182 0,841 1,369 0,989 1,8 1,990 0,213 0,60 1,132 1,931 1,716 0,684 1,327 1,106 0,40 1,609 1,257 1,895 0,273 1,826 1,908 1,393 1,87 1,301 1,235 1,22 1,267 0,689 0,575 1,555 0,408 1,645 1,784 0,202 1,92 0,718 1,188 0,915 1,150 1,93 1,276 0,564 0,638 1,784 1,842 1,361 1,958 0,956 0,177 0,437 0,389 1,464 0,871 0,582 1,332 0,192 0,109 0,78 0,782 0,726 1,493 0,109 1,379 1,413 1,545 1,696 1,582 0,249 0,511 1,398 1,707 1,268 1,401 0,158 1,124 0,622 0,135 0,674 1,812 0,372 1,594 1,38 0,246 0,920 1,349 1,984 0,824 0,794 0,965 1,818 1,889 0,715 0,487 1,714 0,62 1,615 0,613 0,988 1,421 0,362 1,501 1,268 1,459 0,547 1,541 0,148 0,209 1,191 0,910 0,792 0,473 0,897 1,52 0,901 0,793 1,471 1,602 0,330 0,731 0,744 1,788 0,397 0,94 0,829 0,286 1,50 1,975 1,254 0,522 1,345 1,398 1,870 0,799 0,311 0,953 1,269 0,913 1,145 1,289 0,731 1,913 0,575 0,45 1,578 1,151 1,331 1,508 1,126 0,778 0,734 0,111 1,201 1,909 1,762 0,835 0,289 1,778 0,194 0,332 0,74 0,218 0,870 1,200 0,108 0,330 1,846 1,513 0,220 0,833 1,862 1,822 1,270 0,188 1,529 1,691 0,705 1,78 0,335 1,404 1,282 0,498 1,33 1,993 1,219 0,268 0,802 0,546 0,562 1,291 0,20 1,565 1,649 0,857 1,85 1,754 1,392 1,831 0,91 1,1 1,53 1,797 0,373 1,942 1,224 1,243 1,707 1,223 0,76 0,19 1,276 0,713 0,196 0,809 1,714 1,630 1,960 0,287 0,696 0,842 0,135 0,309 0,735 0,22 1,346 0,317 1,489 0,975 1,921 1,481 0,797 0,539 1,845 0,112 0,583 1,943 0,112 0,944 1,43 0,819 1,743 1,105 0,139 1,230 1,453 1,123 0,931 0,436 1,62 0,347 1,901 1,84 0,857 0,611 1,503 0,96 1,532 1,952 1,684 1,820 1,931 1,373 1,678 0,195 0,313 0,168 1,344 0,450 1,147 1,529 1,373 1,364 0,801 1,289 1,805 1,641 1,727 1,336 1,897 1,159 0,431 1,990 1,843 1,35 0,437 1,892 0,270 1,562 1,455 0,555 1,698 1,868 1,799 0,631 0,758 0,402 0,0 0,70 0,969 0,210 1,86 1,554 1,22 1,186 0,180 0,593 1,251 1,17 1,219 1,803 1,34 1,963 0,307 0,702 0,809 0,774 1,799 1,690 1,1 1,75 0,812 0,659 0,388 1,828 0,530 1,368 0,294 0,186 0,437 1,316 1,207 0,64 1,455 1,890 1,223 1,30 0,261 1,492 0,549 0,142 0,915 0,675 0,946 1,83 0,747 1,650 1,938 0,789 1,136 0,981 1,523 0,76 0,36 0,785 1,674 1,672 0,913 0,305 0,15 1,377 0,263 1,161 0,767 0,744 1,151 1,357 0,586 0,843 1,623 1,504 1,177 1,249 1,722 0,125 0,454 1,521 0,168 1,885 0,401 1,817 0,255 0,291 1,238 1,138 1,560 0,596 1,451 0,725 1,166 0,174 1,436 0,460 1,640 0,944 1,177 0,776 0,326 1,946 1,939 0,343 1,524 0,255 1,553 1,399 1,702 0,257 0,882 0,36 1,892 1,196 0,495 1,598 1,233 0,245 1,251 0,847 1,114 0,126 1,437 1,760 1,311 1,539 0,330 0,147 1,324 0,895 1,11 0,168 1,12 1,519 1,449 1,460 1,710 0,347 0,846 0,552 0,295 0,960 0,612 1,10 1,564 1,45 0,34 1,552 0,414 0,787 1,599 1,17 0,937 1,767 0,257 0,608 0,745 1,90 0,346 1,138 1,482 1,583 1,594 0,896 1,883 0,866 1,258 0,466 0,751 1,703 0,579 1,336 0,6 1,861 0,358 1,309 0,578 0,355 1,477 1,12 1,66 0,389 1,80 0,252 0,668 0,967 1,497 0,442 1,630 1,329 0,859 0,237 1,502 1,69 1,172 0,272 1,373 0,565 1,289 1,937 1,489 1,769 1,691 0,79 0,39 1,414 0,684 1,754 0,561 1,697 0,881 0,285 1,266 0,223 1,892 1,56 0,542 1,824 0,562 0,806 0,285 1,874 1,745 0,936 1,248 0,887 0,131 1,166 1,702 0,528 1,319 1,872 1,111 0,623 0,787 0,122 0,101 0,702 0,292 1,884 1,893 1,569 0,44 0,723 1,415 1,218 0,957 0,766 1,169 1,287 1,803 1,682 0,31 1,153 0,213 0,146 0,31 1,884 1,113 0,130 1,436 0,499 1,113 1,677 0,268 1,957 1,14 0,226 0,933 1,473 1,248 1,837 1,315 1,257 0,296 0,779 1,484 1,157 1,152 0,363 0,407 0,690 1,324 0,880 0,809 1,428 1,486 1,727 1,117 1,430 1,159 1,811 1,639 1,595 0,353 1,12 0,177 1,557 1,33 1,451 1,121 1,157 0,123 1,671 1,445 1,737 1,245 1,117 1,896 1,283 1,992 1,777 0,4 0,474 0,939 0,887 1,45 0,635 1,300 0,94 0,627 0,188 1,94 1,488 0,32 0,944 1,792 1,471 0,124 0,821 1,366 0,944 0,58 0,889 1,786 0,800 0,264 0,704 0,952 0,965 0,456 1,723 0,300 0,937 1,438 1,125 1,400 1,132 1,589 1,477 0,43 1,698 1,655 0,123 1,429 1,316 0,108 0,941 1,30 0,159 0,941 0,925 1,730 0,600 1,571 1,721 0,681 0,26 0,318 1,706 1,58 0,558 1,818 0,103 0,625 1,784 1,932 0,151 1,708 1,394 0,487 1,341 0,756 1,494 0,276 1,613 1,655 1,578 0,993 0,858 1,272 1,850 1,271 0,847 0,879 0,231 0,176 0,222 1,724 1,22 1,530 1,400 1,505 1,72 0,223 0,825 1,290 1,808 0,404 0,740 0,809 1,462 0,403 1,680 1,166 0,240 0,860 1,996 0,571 1,862 0,270 1,584 0,55 0,168 0,285 1,171 1,952 0,152 0,789 0,564 0,418 1,568 1,542 1,46 1,426 1,865 0,485 1,548 1,709 1,22 0,694 1,237 0,393 1,574 0,661 1,131 1,210 1,0 0,935 0,614 0,852 1,947 1,716 0,601 1,854 1,939 0,723 0,757 0,800 0,524 1,171 1,800 0,148 1,212 1,124 0,935 0,34 1,470 0,815 1,9 1,838 1,792 1,381 1,344 1,88 1,633 0,24 1,370 0,79 1,316 0,813 1,112 0,674 0,788 1,658 1,499 0,161 0,764 0,334 1,234 1,162 1,774 0,281 0,337 0,266 0,470 0,630 0,325 1,486 0,391 0,283 1,37 0,829 0,428 1,313 1,872 0,265 1,372 1,960 1,259 1,580 1,736 1,633 1,332 0,571 0,471 1,765 1,726 1,53 0,781 0,474 1,570 1,156 1,691 1,356 1,32 0,963 0,154 1,907 0,273 1,982 0,404 1,125 0,223 0,746 0,85 0,627 1,999 0,991 1,193 0,65 1,977 1,244 0,133 0,352 1,113 0,11 1,559 1,481 0,394 1,742 0,672 0,605 1,291 0,912 0,327 1,175 1,584 0,720 0,596 0,8 0,975 0,94 0,889 1,749 0,925 1,57 0,885 0,318 0,652 1,105 0,6 0,553 1,191 0,956 1,61 1,965 0,189 0,233 0,552 1,942 0,190 1,576 0,520 0,900 0,606 1,537 0,475 1,756 0,518 1,699 0,119 1,312 0,358 1,739 1,452 1,223 1,835 0,903 1,229 0,695 1,342 0,583 0,311 0,695 0,253 1,160 1,425 0,372 1,345 1,777 1,416 1,885 1,553 1,331 1,156 1,40 0,104 1,800 0,347 1,773 0,755 1,955 1,912 1,441 0,104 1,245 0,818 1,970 0,474 0,439 1,23 0,176 1,820 0,802 0,509 0,420 0,700 1,542 1,345 0,26 0,416 0,858 1,728 1,231 0,223 1,853 0,99 0,103 0,932 1,760 0,548 0,955 0,234 0,561 0,320 1,933 0,569 1,878 0,756 0,186 0,969 1,778 1,533 0,485 0,423 1,135 0,125 0,644 0,953 0,943 0,437 0,759 0,897 1,424 0,957 1,985 0,918 0,604 0,386 0,452 1,776 0,782 0,684 1,770 0,646 1,820 0,867 1,818 0,189 0,125 0,896 1,920 0,166 0,854 1,613 0,532 1,858 1,179 1,731 0,903 1,927 0,603 0,414 0,753 1,339 0,556 0,261 1,30 0,76 1,740 1,262 1,260 1,115 1,582 1,7 0,801 0,210 1,176 1,183 0,364 0,165 0,214 1,199 1,164 1,176 0,125 1,129 0,5 1,374 1,189 0,86 1,398 0,397 0,360 0,850 0,815 1,954 0,226 0,302 1,233 0,284 0,68 1,947 1,383 0,408 0,91 0,322 1,738 0,907 0,718 0,551 1,724 1,796 0,247 1,568 0,666 0,585 1,821 0,640 0,138 0,969 1,383 1,884 1,930 1,220 1,765 0,42 1,654 1,571 0,307 0,963 0,79 0,672 1,429 1,180 0,364 1,5 0,466 1,907 1,147 0,822 1,225 0,387 0,320 0,712 1,497 1,607 0,744 0,801 1,770 1,174 0,765 0,571 1,223 1,123 1,590 0,802 0,36 0,137 1,459 0,986 0,358 1,613 1,817 1,913 0,875 1,344 0,843 0,436 0,251 0,500 0,244 1,660 1,686 0,78 1,752 1,240 1,816 1,419 0,111 1,760 0,316 0,977 1,754 0,98 1,408 1,410 1,893 0,343 0,417 0,22 0,378 1,538 0,901 1,963 0,122 1,218 1,950 0,918 1,921 1,259 1,842 0,353 0,859 1,847 0,360 0,819 1,791 0,193 1,236 1,924 0,930 1,612 0,642 0,932 0,880 0,49 1,136 0,645 1,168 0,491 1,832 0,772 1,926 0,471 0,97 1,833 1,881 0,817 1,350 1,12 0,624 0,507 0,476 1,247 1,812 0,393 1,214 0,872 0,424 0,496 1,263 1,115 0,599 1,4 0,338 0,93 1,70 1,946 0,41 0,504 1,912 1,166 0,192 0,655 0,142 1,820 1,868 1,636 0,461 0,278 0,136 0,746 1,885 0,784 1,692 1,864 1,649 1,915 1,104 0,926 1,550 1,313 1,61 0,21 0,413 0,317 1,512 1,844 0,552 0,620 1,521 1,735 1,204 1,310 0,355 1,421 1,529 1,850 0,733 1,813 0,615 1,358 0,973 1,195 1,319 1,206 0,692 1,682 1,286 1,857 0,763 0,415 0,121 0,809 1,97 0,978 0,306 0,508 0,369 1,867 0,777 1,870 0,755 1,18 0,44 1,267 1,156 1,743 1,643 1,740 0,522 0,969 0,255 1,367 1,254 1,457 1,409 0,161 1,478 1,911 1,759 1,735 1,150 1,796 0,80 0,515 1,586 0,762 0,199 0,314 0,53 0,644 0,822 1,548 1,885 1,540 1,663 0,699 0,893 1,149 0,711 1,954 1,624 1,567 1,114 1,957 1,172 1,942 1,344 1,188 0,498 1,146 1,511 1,30 0,240 0,14 0,239 0,683 1,239 1,166 1,585 0,962 1,6 1,948 1,969 1,923 0,688 0,303 0,239 0,638 0,5 0,970 1,700 1,861 1,86 1,820 1,18 1,815 0,152 1,49 0,139 1,864 0,954 1,429 0,600 1,372 0,300 0,470 0,183 0,469 1,955 0,472 0,607 1,648 0,720 0,678 1,867 1,448 0,839 1,663 0,161 0,816 1,837 0,825 0,436 1,85 1,613 1,261 0,460 0,109 1,354 1,671 0,165 1,479 1,937 0,735 1,865 0,392 1,246 0,911 0,777 1,486 0,413 0,191 0,464 1,112 0,144 0,651 0,741 1,468 1,787 0,85 1,686 0,475 0,36 0,402 1,750 0,644 0,902 1,275 0,533 0,92 1,351 0,348 0,784 1,759 0,334 1,554 0,825 0,451 0,722 1,818 0,22 1,712 1,905 1,141 0,641 0,210 1,884 0,842 0,663 1,685 0,184 0,70 0,935 1,277 1,496 0,118 0,409 1,619 1,933 0,723 1,193 1,629 0,386 0,554 0,843 1,767 1,344 0,638 0,869 1,818 0,706 1,670 1,463 0,329 0,586 1,741 1,216 0,187 0,165 0,510 0,871 1,351 0,166 0,767 0,989 1,209 0,712 0,796 1,859 1,18 0,924 1,758 0,816 1,114 1,381 1,14 0,817 0,17 1,55 1,320 0,770 0,690 1,367 1,810 0,345 1,689 0,90 0,494 1,62 1,667 1,236 1,859 1,734 0,253 0,948 1,201 0,579 1,526 0,818 1,317 0,234 0,952 1,751 0,0 1,41 1,508 1,978 0,219 0,900 1,388 1,293 1,118 0,291 0,75 1,776 0,652 0,113 0,560 1,421 0,430 0,861 1,665 0,822 1,458 1,551 0,398 1,837 1,667 1,95 0,848 1,845 0,201 0,489 1,262 0,861 0,149 0,177 0,282 0,468 1,537 1,316 0,355 0,917 1,448 0,262 1,306 1,72 0,946 0,82 0,383 0,709 1,739 0,330 1,4 0,167 0,57 0,900 1,536 1,739 1,125 0,302 1,480 0,535 0,217 0,604 1,812 1,451 0,523 0,179 0,373 0,138 0,666 0,433 0,768 1,434 1,31 0,348 0,97 1,709 0,133 0,932 0,138 1,232 0,335 0,27 1,931 0,677 0,407 1,476 1,908 0,174 1,421 1,697 0,595 1,72 1,189 0,107 1,886 0,326 0,635 1,156 0,589 0,421 0,451 0,11 0,842 1,434 0,661 1,810 0,988 1,846 1,40 1,920 0,277 1,382 1,0 0,676 1,208 1,93 0,935 1,465 1,966 1,495 0,525 0,825 1,894 1,191 0,80 1,764 0,754 0,387 1,184 1,173 1,839 1,692 0,78 0,345 0,498 1,334 1,223 0,425 0,698 0,29 0,92 0,836 1,896 0,73 1,139 1,967 0,919 0,327 1,23 0,36 1,996 0,654 1,961 0,760 0,134 0,219 1,907 0,989 0,278 0,877 1,80 1,478 1,716 0,622 1,83 0,865 1,163 1,265 1,920 0,523 0,95 0,756 1,802 1,361 0,941 0,960 1,744 0,623 1,678 1,795 0,684 1,729 1,220 1,814 1,243 1,359 0,659 1,145 0,472 1,381 1,448 0,414 1,349 0,91 1,490 0,270 0,784 0,443 0,452 1,140 0,639 1,857 0,883 0,755 1,903 1,90 0,704 0,442 0,418 0,670 1,850 0,820 1,695 0,765 1,252 0,935 0,227 0,8 1,993 1,816 0,136 0,827 0,848 1,900 1,282 0,173 1,915 0,184 1,953 1,108 0,857 0,376 0,287 1,826 1,657 1,747 0,105 1,738 0,125 0,560 1,820 0,549 0,236 1,229 1,139 0,886 1,840 1,676 1,787 0,596 0,454 0,762 1,631 0,389 0,679 0,352 1,209 1,440 0,425 1,417 0,186 0,302 1,944 0,789 1,493 0,276 1,216 1,64 0,210 0,154 0,670 0,959 0,641 1,796 1,17 0,804 0,151 1,22 1,463 1,262 1,454 1,648 1,754 0,857 0,801 0,755 1,291 0,654 1,514 0,152 1,661 0,632 0,92 0,332 1,670 1,261 1,594 1,628 1,310 1,514 1,330 0,488 0,353 1,408 0,492 0,180 0,501 0,402 0,893 0,60 1,530 1,438 1,57 1,887 1,25 0,24 1,559 0,450 1,261 1,248 1,345 0,453 0,602 1,949 1,580 0,101 1,270 1,932 0,476 1,674 1,985 1,398 1,741 1,718 1,602 0,760 1,387 1,917 1,995 1,995 0,200 0,492 0,245 0,12 1,542 0,111 1,241 1,573 0,287 0,904 1,888 1,972 1,309 1,696 1,255 1,276 1,987 1,363 0,946 0,752 1,447 0,265 0,233 1,484 1,231 0,30 0,927 1,232 0,477 0,720 1,431 0,802 0,140 1,197 0,358 0,431 0,629 1,667 0,930 1,210 1,951 1,820 1,329 0,288 1,972 1,460 1,919 0,783 0,546 1,727 1,955 0,754 1,90 0,398 0,350 0,372 1,645 1,43 0,406 0,201 1,122 1,830 0,350 0,259 1,163 1,699 1,971 0,943 0,218 0,907 1,899 1,315 0,121 1,34 0,495 1,278 0,532 1,989 0,873 0,948 0,645 1,958 1,617 0,378 1,508 1,117 0,686 1,972 0,881 0,413 1,88 0,694 0,571 0,553 1,594 0,653 1,578 0,180 1,163 1,27 1,210 0,698 1,250 1,375 1,249 0,167 0,928 1,349 0,569 0,494 0,631 0,280 1,591 1,128 0,507 0,197 1,979 0,43 1,695 1,478 0,245 1,397 0,485 1,595 0,61 1,245 0,860 1,563 0,244 0,915 1,807 0,544 0,304 1,516 1,963 1,997 1,969 0,974 0,767 0,505 0,504 1,403 0,990 0,19 0,582 1,692 0,470 1,110 1,336 0,632 1,617 1,944 1,60 0,128 1,370 0,871 0,320 1,352 1,334 0,989 0,64 0,631 1,224 0,846 0,813 0,473 1,666 1,41 1,535 1,617 0,232 1,622 1,509 0,962 1,799 0,3 0,633 0,467 0,201 1,562 0,876 0,999 0,885 1,981 0,952 1,648 0,815 1,873 0,540 1,165 1,868 0,766 0,164 1,438 1,259 0,371 0,701 1,787 0,304 1,882 1,432 1,204 1,125 0,614 1,475 0,596 0,512 1,616 0,729 0,463 0,273 1,571 0,133 1,978 0,123 0,315 1,543 1,414 0,626 0,376 0,625 1,352 1,458 0,907 1,368 0,324 1,360 1,566 0,972 0,504 0,371 0,194 0,235 0,744 0,114 1,132 0,23 0,809 1,15 1,279 0,757 0,583 0,612 0,348 1,839 1,34 1,796 1,843 0,119 1,673 1,589 1,418 1,586 0,885 0,309 1,604 1,851 0,164 0,959 1,579 0,376 0,664 1,629 1,898 1,504 1,128 1,852 1,839 1,698 0,999 1,439 0,80 0,203 0,663 0,21 0,990 1,143 1,216 0,597 0,187 1,408 0,658 1,822 1,652 0,671 1,749 0,541 0,356 0,125 0,499 0,97 0,499 1,603 0,830 1,795 1,327 1,894 0,380 0,502 0,61 1,746 1,825 0,658 1,536 0,278 1,30 1,876 1,44 0,234 1,629 0,145 0,221 1,36 0,217 1,385 1,996 1,129 1,346 1,319 1,312 0,309 1,793 1,164 0,585 1,973 0,621 1,116 0,639 0,237 1,378 0,562 1,387 0,658 1,853 1,636 0,215 1,256 1,111 0,89 0,125 0,637 1,118 0,442 1,844 0,230 1,386 1,298 0,887 1,586 1,8 1,928 0,158 0,391 1,685 1,844 0,448 0,846 1,337 0,394 1,767 0,156 1,725 0,980 1,655 1,542 1,66 0,761 1,878 0,789 1,81 0,659 1,96 1,473 0,496 1,579 1,252 1,302 1,924 0,180 1,805 1,638 1,425 0,153 1,986 1,54 1,420 0,558 1,693 0,403 1,531 1,910 0,744 0,132 0,370 0,478 0,897 0,662 1,510 1,910 1,672 0,273 0,72 0,309 1,198 1,307 1,420 0,253 0,565 1,973 1,690 0,812 1,136 1,939 0,902 0,837 0,766 1,527 0,183 0,222 1,281 0,267 1,729 0,760 0,127 0,446 0,894 1,546 0,370 1,654 1,89 1,296 0,919 0,41 0,15 1,158 0,369 1,848 0,449 1,220 1,810 0,804 1,166 1,125 1,353 1,682 0,115 0,944 1,983 1,660 0,366 0,762 0,740 1,244 1,280 1,8 0,614 1,336 0,127 0,12 1,374 1,564 0,266 0,255 0,621 0,673 0,382 0,786 0,420 0,535 1,729 1,110 1,163 1,606 0,883 0,716 1,110 0,544 0,716 0,981 1,333 0,989 0,973 1,762 1,134 0,524 1,751 1,336 0,530 1,940 0,847 0,928 1,769 0,434 0,689 0,293 0,509 0,10 0,665 0,217 1,753 0,342 1,292 0,656 1,348 0,611 1,464 0,600 0,173 1,823 0,778 0,542 0,489 1,762 0,18 0,953 0,662 0,362 0,626 1,896 0,29 1,877 0,67 1,612 0,556 1,580 1,599 1,731 1,145 0,22 1,974 0,482 1,547 0,351 1,52 1,270 1,852 0,560 0,239 0,119 1,548 1,760 1,112 1,9 1,45 1,305 0,706 1,134 1,878 0,777 0,591 0,393 1,312 1,830 0,308 0,679 0,83 1,468 0,771 0,476 0,295 1,533 1,943 0,437 0,938 0,603 0,757 1,770 0,956 0,657 0,431 0,106 0,788 1,554 0,632 1,507 0,250 0,240 1,462 1,3 1,945 1,292 1,122 1,987 0,96 1,188 1,34 1,111 0,9 0,123 1,700 0,233 1,492 0,240 0,189 0,818 1,383 0,955 1,15 1,41 1,541 0,838 1,158 0,291 1,346 0,636 0,600 0,532 0,920 0,483 1,793 0,589 0,579 0,311 0,156 1,995 0,441 1,721 0,757 1,113 0,85 0,689 1,192 1,111 1,878 0,779 0,692 0,420 1,743 1,498 0,763 0,911 0,158 0,831 0,312 1,180 1,552 0,482 1,900 0,180 1,19 1,292 0,908 1,467 0,870 1,418 0,652 1,435 1,469 0,234 0,911 1,464 1,295 0,235 1,866 1,511 1,682 0,505 0,189 0,132 0,105 0,694 1,514 1,172 1,426 1,876 1,540 1,996 0,790 1,26 1,725 1,865 0,167 1,195 1,86 1,244 1,144 0,208 0,882 0,118 1,638 0,467 1,691 1,546 1,746 1,510 0,234 0,147 0,874 0,667 1,195 1,740 1,918 1,275 1,428 1,24 0,554 0,948 1,216 1,213 1,628 1,380 0,930 1,707 1,23 0,813 1,518 0,100 1,751 1,816 0,802 1,760 1,685 1,738 1,412 0,911 1,675 0,839 0,804 0,943 1,264 1,632 1,189 0,561 1,398 0,479 0,790 1,546 1,320 0,263 1,174 1,973 0,424 1,179 1,791 0,124 1,507 1,678 1,944 1,987 0,565 1,428 0,853 0,396 1,857 0,751 1,434 1,142 0,661 0,334 1,147 1,378 0,358 0,450 1,599 0,560 0,92 0,378 0,186 1,39 1,727 0,982 0,898 0,400 1,474 0,773 0,364 1,264 0,340 0,319 0,46 1,340 0,158 1,896 0,418 1,958 0,327 0,183 0,67 0,968 0,416 1,411 1,564 0,370 1,218 0,122 0,440 1,531 1,309 0,186 1,320 0,817 1,166 1,165 0,415 0,334 1,401 1,119 0,476 1,506 0,784 0,600 1,84 0,10 0,847 0,123 0,491 1,160 1,810 1,986 1,200 1,525 0,139 0,796 1,617 1,241 0,946 1,818 1,131 0,639 0,230 1,134 0,808 1,565 0,614 1,187 1,2 1,857 0,396 1,298 1,693 0,555 1,333 1,590 0,986 0,171 0,557 1,120 0,700 0,125 1,703 1,952 0,268 1,534 1,231 0,269 1,762 0,474 1,748 1,484 0,586 0,787 1,820 0,211 0,597 0,993 0,343 0,880 0,496 1,248 0,316 1,781 1,595 0,529 0,692 1,253 0,694 1,565 0,287 0,708 0,468 0,211 0,89 0,457 0,426 1,667 1,431 0,99 1,31 1,176 0,710 1,896 1,189 0,35 1,495 0,218 1,141 0,585 1,74 1,774 0,981 1,286 1,619 1,27 1,144 0,918 1,466 1,403 1,835 0,568 1,478 0,408 1,530 0,954 0,400 1,589 1,803 1,298 0,119 0,800 0,952 1,714 0,490 0,615 0,936 0,737 0,627 0,942 1,701 0,540 0,490 1,17 0,919 1,784 1,216 1,395 1,762 1,556 1,585 1,357 0,69 0,687 1,349 0,947 0,542 1,444 1,133 0,881 1,121 0,715 1,515 0,32 0,951 0,605 0,522 1,205 1,917 0,117 0,230 0,262 1,912 1,164 0,681 1,605 1,506 0,190 0,161 1,185 0,43 1,186 1,949 1,18 0,351 1,572 0,403 1,769 1,572 1,11 0,175 0,29 1,933 1,189 1,554 0,101 0,597 1,700 0,904 0,577 1,656 1,204 1,375 1,473 0,622 1,777 1,857 1,924 1,683 1,838 0,307 0,359 0,878 1,282 0,975 1,49 1,863 0,947 1,835 0,570 1,989 1,838 0,974 0,109 1,980 1,298 0,514 1,846 1,956 1,709 0,15 0,597 1,555 1,708 1,575 0,447 0,2 1,138 0,574 0,732 0,839 0,634 0,676 1,501 0,770 1,592 1,155 0,622 1,445 0,160 1,550 0,772 1,244 0,133 0,174 1,807 1,611 0,608 1,525 0,827 1,157 0,719 1,741 1,737 0,147 1,702 0,585 0,585 0,21 0,646 1,306 1,811 0,248 0,655 1,867 0,155 1,372 0,890 1,915 1,379 0,752 0,760 1,651 0,321 0,932 0,157 1,262 0,203 1,563 0,335 0,90 0,17 1,606 1,271 1,201 0,831 1,364 1,569 1,354 1,894 0,739 1,368 0,510 0,631 1,223 1,302 0,163 1,397 1,435 1,526 0,520 0,389 1,107 0,547 0,811 1,805 0,259 0,713 0,575 1,176 1,551 0,389 0,206 0,179 1,972 1,252 0,770 1,104 1,241 0,86 0,446 1,255 0,580 0,131 0,775 1,262 0,431 1,284 0,0 1,131 0,929 1,194 0,218 0,756 0,172 0,397 0,996 1,818 1,577 1,626 0,971 1,99 0,234 1,237 1,18 0,819 1,73 1,493 0,365 1,266 1,872 1,19 0,878 0,451 1,839 1,86 1,521 0,659 0,317 0,880 0,987 1,178 0,66 1,949 0,205 1,949 1,558 0,889 0,515 1,469 1,572 0,694 1,648 1,351 1,537 0,538 1,904 1,406 1,436 0,588 0,796 0,640 1,893 0,73 1,374 0,946 0,466 0,439 1,681 1,894 1,289 1,567 1,337 1,665 0,367 0,699 1,21 0,806 0,544 1,869 1,705 1,123 0,927 0,260 1,150 1,481 0,384 1,754 1,444 0,378 0,354 1,685 0,603 1,443 1,675 1,309 0,593 0,615 0,422 0,965 0,316 1,282 0,617 1,499 0,372 1,260 0,753 0,140 0,95 0,386 0,633 0,138 1,614 1,265 1,740 0,96 1,25 1,36 1,303 0,711 0,499 0,78 0,938 1,907 1,1 0,325 1,973 0,467 0,227 1,757 0,984 1,448 1,764 0,41 1,81 1,211 1,70 0,406 0,616 1,290 0,966 1,490 1,502 0,281 0,958 1,972 0,199 1,278 1,257 0,109 1,652 0,313 0,238 1,146 1,839 1,747 0,720 1,243 0,539 1,388 0,787 1,947 0,481 1,879 0,86 0,48 0,782 0,817 1,441 1,833 0,465 1,78 0,15 1,359 1,84 0,474 1,436 1,349 0,147 1,943 1,805 0,938 0,156 0,512 1,488 0,686 0,549 1,632 1,298 0,110 1,59 0,213 0,422 1,244 0,690 1,562 1,818 1,602 0,760 0,75 1,914 1,226 1,471 1,954 0,416 1,878 0,42 0,290 0,734 0,939 0,760 1,412 0,861 1,215 1,100 1,993 0,838 1,460 0,85 0,512 1,375 1,709 1,254 1,77 0,830 0,848 0,860 0,145 0,57 1,946 0,552 0,477 1,824 1,49 0,906 1,726 1,456 1,381 1,777 0,232 1,941 1,161 1,898 0,378 1,804 0,880 0,350 1,63 1,595 1,898 0,531 1,227 1,14 0,858 1,845 1,769 0,711 0,492 1,722 1,99 1,571 1,243 0,771 1,401 1,565 0,169 1,233 0,554 0,190 1,932 1,39 0,534 0,716 1,281 1,116 1,118 0,64 1,607 0,417 0,434 1,860 1,795 0,680 0,532 1,680 1,574 1,5 0,214 0,258 1,39 1,426 0,172 0,396 0,775 1,840 1,134 0,162 1,385 0,176 0,585 0,148 0,860 1,497 0,803 0,955 0,276 1,510 0,754 0,586 0,933 0,201 0,570 0,779 1,607 0,720 1,920 1,539 0,538 0,659 1,169 0,990 0,394 1,495 1,455 0,536 0,102 1,288 0,828 0,861 0,91 0,293 0,151 0,480 1,56 1,828 0,49 0,573 0,472 1,336 1,588 0,775 0,498 1,596 1,995 0,339 0,50 0,850 0,743 0,389 0,777 1,624 0,985 1,137 1,10 0,658 1,807 0,802 1,244 0,621 1,374 0,988 0,639 0,100 1,666 1,992 0,264 0,935 1,532 1,422 1,69 0,493 1,655 0,610 1,569 1,597 0,158 1,474 0,278 0,831 0,461 1,354 0,258 0,270 0,792 1,91 1,770 0,300 1,300 1,757 0,468 1,181 1,794 0,392 1,990 1,918 1,423 1,539 1,300 1,372 1,828 1,408 1,496 1,276 1,929 1,497 0,934 0,125 0,43 1,74 0,163 1,304 0,548 1,630 1,869 1,378 0,437 1,254 1,159 1,88 0,447 1,689 0,660 1,461 0,317 1,683 1,963 1,573 0,204 1,959 0,846 1,385 0,19 1,99 0,393 1,41 1,471 0,654 0,119 1,599 1,174 0,446 1,254 1,598 0,306 0,188 1,810 0,43 0,587 0,793 0,964 1,428 1,216 1,689 1,177 1,177 1,464 1,393 1,872 1,83 1,456 0,297 0,553 0,857 1,289 1,372 1,339 1,459 1,908 1,674 0,285 1,241 0,57 0,637 0,118 1,309 1,252 0,279 1,743 0,69 0,531 1,443 1,769 0,553 1,573 1,452 1,795 1,522 0,539 0,735 0,359 1,591 0,505 0,562 0,646 1,730 0,520 1,161 1,985 0,615 1,364 0,903 1,23 1,384 1,564 1,776 0,26 0,43 1,800 0,40 1,505 0,374 1,191 0,58 0,979 0,737 0,928 1,856 1,960 1,955 1,470 1,329 1,613 0,699 1,911 0,503 0,423 1,492 0,256 1,487 1,63 1,40 1,972 1,466 1,422 0,556 0,917 0,120 0,305 1,437 1,611 0,276 0,254 0,574 1,328 0,216 1,610 0,788 0,760 0,120 1,790 0,234 0,914 0,522 1,468 1,518 1,844 1,666 0,858 0,293 0,430 0,238 1,52 1,777 1,855 0,83 1,610 0,114 1,570 1,662 1,635 1,978 0,331 1,650 0,652 0,296 1,627 0,191 0,859 1,582 1,305 1,22 1,495 0,944 0,632 1,588 0,864 0,885 0,150 0,529 0,570 1,744 0,802 1,802 1,895 0,138 1,918 0,424 1,279 0,276 1,271 1,638 1,434 1,11 0,175 1,132 0,170 1,412 0,614 1,890 0,172 1,589 0,622 1,235 1,630 1,302 0,249 0,350 1,672 0,539 0,658 1,463 0,790 1,839 1,701 0,132 0,791 1,439 1,598 0,208 1,314 1,304 0,419 1,391 1,255 0,219 0,295 1,922 1,774 1,157 1,317 0,517 0,418 1,802 0,628 0,500 0,389 0,887 1,517 1,509 1,124 1,101 1,566 1,486 0,695 1,53 1,124 0,642 0,288 1,340 0,677 1,107 1,712 0,868 0,296 1,565 0,229 0,652 0,709 1,236 1,30 1,478 0,53 0,775 0,326 1,101 0,764 0,89 0,902 1,761 0,631 1,295 1,467 1,364 1,785 0,667 0,640 0,144 1,564 1,752 1,332 1,187 1,397 1,309 1,847 1,458 0,501 1,469 1,375 0,797 0,80 0,143 1,474 1,52 0,838 0,470 1,564 0,530 1,819 1,486 0,488 1,49 0,393 1,219 0,467 1,683 0,931 0,526 1,740 0,488 0,231 0,277 1,123 1,124 1,870 1,317 0,493 1,251 1,810 1,412 1,114 0,984 1,781 1,574 0,878 1,758 1,57 1,724 0,831 1,196 1,746 0,951 0,828 1,492 0,622 0,374 1,511 0,133 0,136 1,620 1,353 1,484 1,872 0,415 0,858 0,841 0,4 0,114 0,182 0,132 0,450 1,371 1,716 1,803 0,575 0,527 1,12 1,612 1,877 0,542 0,530 0,804 1,356 0,726 1,151 1,850 1,628 0,897 0,890 1,130 1,941 1,63 0,825 1,525 0,800 1,924 1,307 0,974 1,86 0,997 0,919 1,778 1,767 0,136 0,604 0,266 0,317 0,682 1,755 1,583 0,16 0,534 1,809 0,531 1,543 0,912 1,756 0,720 0,240 0,693 1,82 0,950 0,517 0,416 1,810 0,443 0,976 0,570 1,886 1,741 1,277 0,35 1,442 1,676 1,362 1,261 1,853 1,934 0,199 0,419 0,210 1,1 1,417 0,416 0,784 1,386 1,738 1,232 1,588 0,186 0,947 1,788 0,914 1,267 0,645 0,411 1,648 0,839 0,29 0,289 1,121 1,198 0,835 1,151 0,815 1,20 0,767 0,744 1,790 0,973 0,499 0,444 0,48 1,966 0,89 0,12 0,803 1,494 0,471 0,432 1,593 0,157 1,584 1,696 1,363 1,424 0,849 0,768 1,410 1,607 0,181 0,493 1,572 0,840 1,971 1,310 1,625 0,856 0,689 1,630 0,662 0,412 1,357 1,964 0,527 0,712 0,379 1,880 0,656 1,482 1,100 0,673 1,859 0,866 1,397 0,254 1,302 0,111 1,537 0,208 0,991 0,412 0,228 0,573 1,617 0,144 1,598 1,473 1,245 0,803 0,728 1,364 1,473 1,504 1,140 1,361 0,793 0,261 1,828 0,695 0,562 1,631 1,592 0,258 0,40 1,706 0,122 1,63 1,181 0,963 0,215 1,572 0,463 0,200 1,533 1,431 0,516 1,355 0,182 1,47 1,401 1,998 1,836 0,152 0,456 1,743 0,963 0,980 0,565 0,889 1,928 1,937 1,989 0,242 1,868 0,0 0,480 1,206 0,16 1,819 1,49 1,473 0,87 0,630 1,627 0,471 1,14 0,501 1,279 0,798 1,68 0,942 0,297 1,146 1,885 0,809 1,327 0,209 1,262 0,486 1,377 0,58 1,760 1,399 1,514 1,165 1,273 0,23 1,920 1,579 1,857 0,630 0,787 1,796 0,587 1,951 1,225 1,935 0,660 0,231 1,624 0,68 1,61 1,333 1,85 0,817 0,174 1,872 0,749 1,577 1,728 0,402 1,693 0,33 1,707 0,208 0,937 1,782 1,183 1,75 0,420 1,339 0,772 1,371 1,87 1,550 1,302 1,975 1,528 0,288 1,311 1,900 1,805 1,377 0,161 1,740 1,370 1,503 1,877 1,367 1,192 1,728 0,916 0,574 1,719 1,722 0,816 0,818 1,401 0,307 0,334 1,894 1,108 0,767 1,575 0,609 1,508 1,291 1,466 0,640 0,520 0,5 0,147 1,842 0,856 0,508 1,148 1,673 0,53 1,482 0,696 0,746 1,544 1,644 0,838 1,965 0,616 1,467 0,274 1,821 1,373 0,366 1,30 1,102 0,300 1,794 0,366 0,712 1,134 0,744 0,852 0,764 1,280 0,820 0,108 0,238 0,194 0,159 1,61 1,233 0,30 0,461 0,774 0,876 0,189 0,622 1,540 0,385 0,294 0,714 1,514 1,303 1,667 1,483 0,978 0,482 0,387 1,928 0,182 0,237 1,813 0,780 1,623 1,245 0,630 0,278 1,159 0,443 1,507 1,722 1,473 0,533 0,290 1,968 0,308 1,182 0,967 0,740 0,340 1,781 0,24 0,895 0,314 1,935 0,919 1,52 0,879 0,167 0,730 0,868 0,194 1,64 1,101 0,459 1,523 0,555 0,598 1,165 0,923 1,256 0,238 1,834 1,184 1,564 0,544 0,300 1,995 0,185 1,599 1,379 0,959 0,797 1,514 1,307 0,201 0,189 1,884 1,543 0,144 0,38 0,478 1,198 1,137 1,128 0,672 0,658 1,746 0,643 0,174 0,565 0,316 0,819 0,970 0,790 0,559 1,983 0,263 0,240 1,452 1,994 1,488 1,317 0,159 1,190 0,992 0,129 0,51 0,852 1,505 0,752 0,826 0,291 1,56 0,53 0,653 1,62 0,363 0,416 0,731 0,101 0,362 1,174 0,261 1,345 0,289 1,474 1,87 0,899 0,166 0,418 1,982 1,692 0,9 1,759 0,835 1,389 1,5 0,738 0,821 1,524 0,174 0,161 1,684 1,641 1,802 0,53 1,665 0,502 0,386 0,81 1,954 0,732 1,128 1,558 0,778 0,276 0,151 0,412 0,23 0,882 0,666 0,924 0,684 0,976 0,204 1,974 1,510 1,787 0,959 0,355 1,957 1,268 0,866 1,276 0,504 1,139 0,744 0,748 1,523 0,924 0,184 1,283 0,619 1,672 0,531 1,4 1,672 1,233 0,329 0,633 1,794 1,271 1,687 0,558 1,691 1,877 1,831 1,860 0,636 1,994 0,918 0,34 0,928 0,974 0,861 0,825 0,914 0,892 0,668 0,330 1,438 1,35 0,547 1,508 1,551 0,982 0,18 1,15 1,81 1,906 1,620 0,449 0,692 1,109 0,905 1,47 0,938 0,155 0,611 0,958 1,419 0,43 1,471 1,14 0,229 1,191 1,996 1,882 1,887 1,747 0,204 1,853 1,18 0,707 0,819 0,672 1,165 0,491 1,941 0,96 0,44 1,214 1,609 0,91 0,465 0,640 0,175 0,625 0,822 0,177 1,343 1,972 0,809 0,84 1,161 0,922 0,960 0,469 1,110 0,181 1,668 1,61 1,714 1,763 1,777 0,230 0,40 0,193 1,403 0,164 0,66 1,795 0,664 0,182 0,543 0,592 0,760 1,562 1,94 0,637 0,921 0,366 0,835 1,968 0,551 1,83 0,494 1,410 0,845 1,607 1,777 0,350 1,865 0,842 0,861 0,178 1,395 1,98 0,44 1,433 0,645 0,387 0,171 0,998 0,589 1,819 1,683 0,968 1,964 1,645 0,301 1,562 1,3 1,198 0,731 1,983 1,779 0,134 1,102 1,879 0,354 0,672 0,569 0,570 0,947 1,676 1,905 0,715 0,409 1,428 1,54 0,86 1,266 1,978 0,159 1,883 1,297 1,420 0,238 1,226 1,856 0,469 1,151 1,664 1,709 1,345 1,975 0,439 1,822 0,650 1,155 0,998 0,827 0,918 0,192 0,894 1,532 0,806 0,198 1,777 1,265 0,320 0,732 0,795 1,114 0,106 0,241 1,884 0,81 1,414 1,781 0,287 1,600 1,953 0,938 1,542 1,21 0,304 0,278 1,688 1,49 1,400 0,735 1,628 0,910 1,489 0,196 0,326 1,784 0,932 0,510 0,279 0,319 1,674 0,572 1,916 0,969 0,877 0,261 0,549 1,57 1,988 0,108 1,413 1,664 0,214 1,31 1,818 1,319 0,781 1,458 1,611 1,856 0,306 1,946 1,897 0,690 0,468 1,527 1,584 0,727 0,944 0,501 1,430 0,908 0,47 1,33 0,443 0,20 0,145 1,753 1,119 0,686 0,226 0,354 1,646 0,756 1,382 0,79 0,361 0,280 1,939 1,706 0,819 1,924 1,852 0,393 0,19 0,659 1,136 1,806 1,461 1,233 0,470 1,90 0,260 0,697 0,331 1,717 1,68 1,142 0,34 1,751 0,573 1,504 1,277 0,58 0,762 1,800 1,126 0,791 1,696 0,121 0,339 0,436 0,618 1,105 0,967 0,854 1,516 0,155 0,428 0,626 1,165 0,872 1,428 1,589 0,446 1,876 0,942 0,429 1,572 0,630 1,478 1,772 0,731 1,292 1,928 0,810 0,520 0,837 0,919 0,298 0,345 0,832 0,850 0,405 0,690 0,715 0,288 0,833 0,397 0,579 1,238 1,45 1,462 0,363 0,878 1,384 0,431 0,686 1,900 1,21 1,931 0,770 1,656 1,900 1,659 0,431 1,458 0,527 0,963 0,944 0,474 0,253 0,555 0,290 0,378 1,971 1,814 0,772 0,410 1,352 1,342 0,273 1,542 0,216 1,710 1,301 1,776 1,759 1,371 1,473 1,472 0,589 0,953 1,488 1,574 0,126 1,831 1,273 0,791 1,840 0,148 1,47 0,50 1,686 0,387 1,624 1,341 1,91 0,909 1,472 0,897 0,812 0,552 0,502 0,320 1,310 0,228 0,332 1,82 0,49 1,136 0,534 1,41 1,597 1,408 1,499 0,177 1,288 0,411 0,191 1,853 0,945 0,931 1,529 0,905 1,408 1,741 1,506 0,135 1,488 1,146 1,185 0,192 1,885 1,522 1,586 0,517 0,976 0,136 0,650 0,292 1,164 0,694 0,597 0,45 1,488 0,219 1,978 1,347 0,655 1,785 0,528 0,440 1,656 0,91 1,404 1,164 0,963 0,278 0,521 0,532 1,488 0,723 1,109 0,978 0,690 1,921 1,399 0,71 0,265 0,30 0,261 1,648 1,212 1,516 0,474 0,287 0,357 0,52 0,332 1,570 0,728 1,105 0,464 0,460 1,251 1,2 0,786 1,768 0,527 1,591 0,979 0,653 1,750 1,647 1,193 0,682 0,945 1,225 1,127 0,60 0,873 0,950 0,486 0,126 0,647 0,713 1,387 0,124 1,362 0,91 1,242 1,16 1,926 0,569 0,712 1,719 0,651 1,736 1,890 1,160 1,861 0,502 1,699 1,40 0,641 0,906 1,25 1,840 0,407 0,388 0,231 0,653 1,764 0,271 1,448 1,846 1,920 0,676 0,457 0,8 1,794 0,974 1,794 0,765 1,568 0,633 0,911 1,33 1,984 0,184 1,519 0,918 0,231 1,949 1,348 0,978 0,348 0,254 0,540 0,184 0,847 0,633 0,241 1,227 0,583 0,314 1,695 1,730 1,873 1,157 0,377 0,850 0,139 0,679 1,632 1,525 1,833 1,813 0,503 1,408 0,655 1,969 0,311 1,160 1,908 0,737 0,483 1,130 0,407 1,785 1,606 0,295 1,997 0,513 1,522 0,367 1,665 1,191 0,192 1,242 1,42 0,292 0,569 0,560 1,963 1,181 1,426 1,538 0,946 1,185 0,105 0,302 1,527 1,775 1,82 0,55 0,100 0,424 1,315 0,59 0,202 1,798 1,343 1,654 1,790 0,735 1,516 0,639 0,750 1,385 1,788 0,177 1,993 1,812 1,106 0,18 1,803 0,801 0,349 0,754 1,7 0,222 0,995 0,855 1,253 0,636 0,622 1,359 1,998 0,658 1,699 1,768 0,528 1,365 0,917 1,177 0,666 0,267 1,366 1,322 1,171 1,265 0,538 0,711 0,482 1,156 0,913 1,419 0,935 1,139 0,81 0,843 0,408 0,197 1,109 0,730 0,33 1,55 1,287 1,472 1,305 1,17 1,398 1,520 0,774 1,928 0,299 0,903 1,157 0,463 1,50 1,100 0,453 1,851 1,90 0,237 0,879 1,777 1,697 0,704 0,390 0,888 0,880 1,368 0,850 0,977 0,969 1,713 1,874 0,729 0,901 1,802 0,68 0,828 0,732 1,409 1,980 1,667 1,121 0,766 1,642 0,726 0,492 1,671 1,578 0,11 0,570 1,334 1,548 1,857 0,90 0,824 1,787 0,659 1,65 0,237 1,263 0,23 0,335 1,761 1,264 1,818 0,886 1,554 0,975 0,953 1,730 0,766 0,875 0,820 1,636 1,642 1,981 0,327 1,234 1,270 1,594 0,343 1,940 0,2 1,733 1,455 0,956 0,243 0,713 1,58 1,653 0,895 1,867 0,990 1,13 1,771 0,802 1,110 1,556 1,565 0,809 0,855 1,920 0,896 0,358 1,813 1,820 1,859 1,136 1,320 1,564 1,762 1,8 0,399 0,788 1,753 0,441 1,797 0,38 0,701 1,243 1,34 1,253 1,569 1,936 0,952 0,87 0,121 1,939 0,346 0,474 1,911 0,797 1,330 0,879 0,525 0,480 0,968 0,681 1,787 0,618 1,573 0,774 1,404 1,890 0,675 0,446 1,883 0,451 1,918 0,522 0,207 0,279 1,318 1,930 1,606 1,811 1,31 0,222 0,736 1,527 0,417 1,474 0,874 0,877 0,3 0,648 1,180 0,787 0,806 1,2 0,96 0,872 0,500 0,55 1,819 1,539 0,903 1,265 0,870 1,520 1,991 1,871 1,927 0,464 0,994 1,341 0,873 0,584 0,519 1,457 1,852 1,46 1,771 0,649 0,360 1,726 1,71 1,109 1,663 1,686 1,864 1,39 1,756 1,589 1,347 1,221 0,3 1,444 1,903 0,554 1,571 1,979 0,72 0,516 0,617 0,145 0,879 1,993 1,127 1,506 1,205 0,759 1,344 1,380 1,702 1,435 0,240 0,958 1,308 1,302 0,37 0,520 1,969 1,996 0,204 0,47 0,900 0,303 1,900 0,87 1,935 1,81 1,705 1,614 1,141 1,234 1,753 0,843 0,164 1,121 0,931 0,667 0,493 1,507 1,484 1,344 1,304 0,924 1,555 0,652 0,117 0,319 0,792 0,209 0,257 0,401 1,871 1,887 0,117 0,267 1,755 0,269 1,579 1,771 1,56 1,461 1,835 1,399 1,210 1,106 1,721 0,717 1,223 0,988 0,945 1,24 1,362 0,699 1,914 1,378 1,730 1,608 0,649 1,970 1,31 0,401 1,58 1,899 1,277 0,255 0,180 0,490 1,905 1,365 1,476 1,799 0,896 1,662 0,514 1,476 0,740 1,331 1,541 0,985 0,85 0,764 0,814 0,918 0,994 0,573 0,837 1,494 0,329 0,769 0,143 0,385 0,988 1,899 0,240 1,651 1,395 0,890 0,785 1,450 0,699 1,621 1,308 1,518 1,689 1,710 0,489 1,707 0,495 1,189 0,666 0,973 0,598 1,871 0,351 1,851 1,792 0,720 0,47 0,813 0,643 0,90 0,971 1,988 1,883 0,426 1,986 1,340 0,23 1,855 1,390 0,999 1,463 0,889 1,872 1,960 0,852 0,505 1,482 0,111 0,718 1,875 0,238 0,29 0,938 0,549 0,218 1,95 1,43 0,610 0,713 1,955 0,411 0,614 0,137 1,65 1,510 0,629 1,180 0,293 1,3 0,828 1,663 0,588 0,742 0,758 0,507 1,45 1,548 0,147 0,421 0,313 1,651 1,140 0,438 1,488 1,480 1,945 0,456 1,137 0,914 1,463 0,111 1,630 1,679 1,512 1,669 1,305 1,918 0,198 0,356 0,430 1,2 0,797 1,956 1,271 0,639 0,911 1,293 1,763 1,512 0,108 0,715 0,699 0,824 0,338 0,493 0,749 0,577 0,981 1,325 1,368 0,99 0,456 0,990 1,366 1,637 1,39 1,202 1,836 0,418 1,444 1,545 0,369 0,576 0,724 1,74 0,271 0,984 0,610 1,975 0,730 0,10 0,89 1,843 0,723 1,331 0,386 1,643 0,136 1,564 0,232 1,158 1,549 0,24 1,852 1,3 0,65 1,374 1,805 1,133 1,740 1,911 0,380 1,411 0,261 0,215 1,562 0,815 1,965 1,990 1,585 1,450 0,268 0,135 0,276 0,734 1,533 0,669 1,950 0,142 0,971 1,290 1,969 0,162 1,473 0,196 1,677 0,392 0,721 1,627 0,757 0,104 0,443 1,887 0,81 0,487 1,44 1,827 0,263 1,452 0,124 0,364 0,807 0,359 1,206 1,462 1,934 1,555 1,56 0,528 1,633 1,561 1,611 0,831 0,731 0,55 1,966 1,939 1,950 1,681 0,979 1,942 1,458 0,769 1,727 1,478 1,378 0,601 1,303 1,15 0,546 1,802 0,365 0,881 1,291 0,261 1,304 0,213 1,767 1,972 1,195 1,17 1,62 0,796 1,466 0,83 1,447 0,616 0,499 0,498 1,207 1,981 1,551 1,427 0,496 0,213 0,184 1,166 0,942 0,951 1,962 0,124 0,960 1,320 0,535 1,505 0,523 1,453 1,113 1,542 1,461 1,98 1,711 1,794 1,535 1,872 1,736 1,512 0,50 0,710 1,517 1,845 0,34 0,521 0,489 1,721 0,804 0,462 0,473 1,120 1,206 1,0 0,805 1,576 0,807 0,609 0,899 0,864 0,276 0,801 0,671 1,791 1,293 0,179 1,295 1,789 0,216 0,201 0,358 1,263 1,807 0,672 1,322 0,199 0,268 0,871 0,106 1,246 0,860 0,635 1,333 0,802 1,979 1,155 1,169 0,814 1,624 0,540 0,273 1,909 0,49 1,645 1,740 0,464 0,986 1,818 1,479 0,246 0,633 0,452 1,992 0,741 1,295 1,485 0,381 0,280 0,954 0,287 1,758 1,782 1,301 1,383 0,499 0,45 1,352 1,352 1,596 0,231 0,741 0,165 1,900 1,944 1,647 0,437 1,61 1,578 1,173 1,806 0,631 1,44 1,371 1,164 0,804 0,760 0,651 1,397 0,718 1,361 1,873 0,685 1,366 0,370 0,846 1,617 1,498 1,271 0,879 1,21 1,248 0,703 0,172 0,567 1,238 0,150 0,64 1,687 0,178 0,959 0,116 1,672 1,656 1,157 1,204 0,880 0,575 0,470 0,450 1,463 0,489 1,625 1,837 1,906 1,39 0,434 1,556 1,682 1,701 0,719 1,735 0,725 0,732 1,604 0,166 1,35 1,867 1,976 0,66 0,732 0,210 0,392 1,526 1,662 0,179 0,712 1,80 0,338 0,710 0,83 1,490 0,128 0,676 0,470 1,264 0,893 0,332 1,639 1,974 0,200 1,169 1,775 0,70 1,273 0,627 0,927 0,915 0,355 0,748 0,708 0,830 0,980 0,265 0,791 0,66 0,36 0,88 1,196 0,676 1,221 0,340 0,268 0,880 0,664 1,986 0,289 0,692 0,134 0,104 0,332 0,948 0,177 1,568 0,373 0,173 0,770 0,942 0,257 1,541 1,268 0,985 1,904 0,945 0,685 0,664 0,189 1,467 0,411 1,283 0,664 1,931 1,503 0,992 1,338 0,153 0,931 1,983 0,469 1,843 0,439 0,440 1,973 1,532 0,749 0,187 1,553 1,207 0,778 1,795 0,962 1,983 1,946 1,198 0,243 0,66 1,870 1,15 1,183 1,811 0,996 1,946 1,560 0,962 0,639 1,496 0,693 1,993 0,158 0,472 0,772 0,910 1,865 0,222 0,124 0,607 1,45 0,786 0,904 0,226 1,229 1,974 0,0 0,199 0,100 0,567 0,139 1,805 0,145 0,780 0,458 0,518 1,558 1,762 1,483 0,106 0,256 1,12 0,773 1,304 0,588 1,998 1,140 1,20 0,97 0,974 1,137 1,241 1,766 0,458 1,369 0,420 1,771 1,292 1,339 1,328 1,336 0,376 0,54 1,153 1,706 0,97 0,600 0,928 1,5 1,530 0,417 0,399 1,323 0,75 0,34 1,944 0,162 0,367 0,374 0,213 1,71 1,201 1,997 0,437 1,356 0,737 1,65 1,363 0,890 1,87 0,858 0,784 0,568 1,956 0,693 0,192 1,757 0,623 1,401 0,562 1,37 0,529 1,433 1,769 1,814 1,746 1,75 1,802 0,506 1,549 1,748 1,858 1,140 0,874 1,159 1,77 0,772 1,643 1,491 1,997 0,486 1,952 0,640 1,372 1,465 1,678 0,665 1,853 1,660 0,113 0,887 0,666 0,772 0,971 1,605 1,115 0,272 0,272 0,36 1,838 0,512 0,418 1,158 1,324 1,575 0,411 1,528 0,540 0,843 0,618 0,49 0,272 1,372 0,844 1,183 1,373 0,984 0,948 0,338 0,314 0,179 1,92 0,409 0,103 1,806 0,685 1,838 0,277 0,538 0,13 1,885 1,756 1,435 1,22 1,15 0,324 0,305 0,41 0,323 1,335 1,176 0,707 0,741 0,467 1,770 0,568 1,945 0,667 0,311 1,896 1,529 1,540 0,124 1,230 0,170 0,159 0,446 0,764 0,901 1,337 0,566 0,534 1,403 0,566 0,912 1,458 0,148 0,815 0,253 0,932 0,162 0,609 1,475 1,958 1,614 0,646 0,352 0,493 1,351 1,283 0,214 0,592 0,527 1,401 0,179 1,918 0,777 1,846 1,368 1,857 0,58 0,906 0,109 0,898 1,723 0,488 1,66 1,664 1,146 0,570 0,673 0,399 0,803 0,923 0,640 0,574 1,187 0,526 0,187 0,256 0,836 0,831 0,107 0,586 1,766 0,891 0,720 0,560 1,904 0,652 1,741 1,533 0,402 1,955 0,457 0,937 1,329 1,432 1,926 1,846 0,669 1,692 1,845 0,43 1,940 0,475 1,290 1,36 0,620 0,399 0,585 0,941 0,269 1,197 0,435 0,139 1,368 1,930 1,798 1,993 0,382 1,685 0,163 1,363 1,847 0,916 1,562 1,146 0,798 0,451 0,752 0,388 0,414 1,579 1,532 1,938 1,645 0,278 1,733 1,867 0,793 1,566 1,490 1,981 0,181 0,863 1,720 0,782 0,309 1,514 1,437 0,165 0,35 1,363 0,715 0,818 0,476 1,545 1,404 1,637 0,961 0,772 0,896 1,590 0,376 1,209 1,859 0,951 1,505 1,850 1,888 1,259 0,777 1,207 1,179 0,573 0,173 1,407 1,508 1,378 1,314 1,988 0,356 0,387 1,534 1,429 1,758 0,855 1,918 1,818 1,238 1,124 1,120 1,870 1,56 1,103 1,541 0,60 1,580 0,437 1,635 0,46 0,451 0,5 0,207 1,151 0,888 1,274 0,489 0,171 0,126 1,251 1,570 1,254 1,974 1,316 0,57 1,804 0,406 1,238 1,143 1,783 0,681 1,662 0,482 1,561 1,401 0,154 1,399 1,940 0,852 1,847 0,841 1,310 1,889 1,897 0,111 1,356 0,9 1,768 0,626 0,864 0,639 1,340 1,147 1,371 1,425 1,377 1,779 1,357 1,236 1,760 0,543 0,893 0,143 0,700 1,440 0,436 0,605 1,695 1,360 1,745 0,981 1,905 1,375 1,111 1,200 0,952 1,228 1,676 0,966 0,504 1,474 1,25 1,353 1,977 0,494 0,433 1,15 0,580 1,305 0,821 1,988 0,517 0,328 1,78 1,707 0,327 0,820 0,262 1,181 1,950 1,930 1,19 0,717 0,443 1,781 0,539 0,562 0,557 1,275 1,590 1,230 0,830 0,328 0,359 0,923 0,940 0,490 1,753 0,858 0,942 0,299 1,148 1,793 1,333 0,499 0,240 1,121 1,123 1,571 1,1 1,9 0,108 0,873 0,978 1,336 0,550 1,505 0,468 0,925 1,193 1,405 1,794 0,755 1,260 0,618 0,968 0,71 1,455 0,271 1,381 1,132 1,570 0,347 0,59 0,780 1,87 1,403 1,305 0,731 0,26 1,94 0,543 0,925 0,757 1,685 0,89 0,710 1,886 0,955 0,26 1,310 1,782 1,695 0,413 1,801 0,985 1,6 0,513 0,607 0,396 1,248 0,189 0,291 0,482 1,613 0,933 1,928 0,937 0,562 0,560 1,724 0,954 1,493 1,794 1,784 0,68 0,871 1,555 1,358 0,740 1,574 1,656 0,184 1,598 0,924 0,30 1,883 0,750 0,519 1,752 1,333 1,600 0,600 0,683 1,493 1,969 0,832 0,85 0,290 0,938 0,116 1,299 0,413 1,4 0,895 0,195 0,886 1,426 0,781 0,293 0,757 0,453 0,415 1,883 1,575 0,516 0,56 1,236 1,404 1,607 0,792 0,466 0,287 1,215 1,390 1,152 0,4 1,773 1,885 1,367 0,372 0,845 0,308 0,417 0,18 1,306 0,916 1,59 0,936 0,399 0,123 0,819 0,103 0,322 1,590 1,618 0,543 0,807 1,492 1,932 0,42 0,403 1,18 0,244 1,34 0,77 0,769 1,67 1,676 1,460 1,113 1,679 1,986 0,203 1,761 0,798 1,23 1,879 0,607 0,0 0,471 0,888 0,852 0,313 1,284 1,451 1,965 1,741 0,691 0,664 0,417 1,70 0,137 1,857 0,898 1,864 0,27 0,523 1,481 1,975 0,608 1,602 1,223 1,528 0,677 0,989 0,940 0,928 0,484 0,547 0,23 1,340 0,864 0,163 0,282 0,624 1,300 0,288 1,543 1,151 0,663 1,177 0,7 0,570 0,773 1,826 0,646 1,666 0,228 1,71 1,46 0,732 0,637 0,608 0,830 1,628 0,845 0,31 0,62 1,131 0,465 1,480 0,891 0,284 0,508 1,528 0,77 0,137 0,182 1,59 1,902 0,175 1,392 0,814 1,956 0,26 1,979 1,686 1,169 0,8 1,619 1,629 0,204 1,127 1,961 1,953 0,59 1,808 1,16 0,649 1,205 0,411 1,631 0,496 0,310 1,102 1,948 1,168 1,204 0,901 1,498 1,53 0,846 0,870 1,207 1,112 1,224 0,605 1,312 1,987 0,272 1,338 1,732 1,986 0,728 0,495 1,582 0,725 0,576 0,209 1,300 0,649 0,927 1,715 1,229 0,600 1,380 1,811 0,119 0,957 1,39 1,694 1,485 0,953 0,957 0,42 1,487 1,631 0,240 1,855 0,291 0,157 0,115 0,616 0,661 0,517 0,486 1,799 1,149 0,482 1,991 1,198 1,608 0,476 0,259 1,437 1,462 0,432 0,175 0,363 0,578 0,119 0,331 0,509 0,251 0,707 0,422 1,783 1,849 0,397 1,725 1,774 0,707 1,412 0,56 1,316 0,903 0,492 1,460 1,673 0,836 1,814 1,903 1,417 1,22 0,256 0,359 0,266 0,985 0,302 1,626 0,262 1,196 0,625 0,746 0,691 0,453 1,536 1,851 0,906 1,291 1,864 0,112 0,400 0,110 0,709 1,394 0,422 1,958 1,525 1,757 1,640 1,845 0,766 0,339 1,382 1,333 1,721 0,896 0,5 1,840 0,248 0,141 0,293 1,418 0,862 0,68 1,292 0,480 1,657 0,550 0,931 1,464 0,299 1,937 1,112 1,742 0,153 0,818 0,698 0,527 1,691 0,338 1,79 0,579 0,846 0,352 1,630 1,48 0,643 0,289 0,797 0,217 1,433 0,769 0,202 1,462 1,915 0,559 0,481 1,818 1,686 1,101 1,269 1,745 1,12 0,454 0,112 0,201 0,442 0,693 0,625 0,722 1,448 0,754 0,123 1,73 1,623 1,14 1,35 0,931 1,89 0,957 1,130 0,733 1,551 1,346 1,436 0,658 1,143 1,247 0,281 0,494 1,338 0,745 1,49 1,282 0,69 1,442 0,587 1,118 0,150 1,1 0,3 1,983 0,410 0,15 0,601 1,179 0,241 1,906 1,94 0,209 1,448 0,70 1,130 1,467 0,18 0,663 1,460 0,447 1,390 1,578 0,159 1,477 1,947 1,20 1,13 1,200 1,199 0,385 0,34 1,340 0,919 0,230 0,940 1,745 0,305 0,258 0,877 1,607 1,852 0,850 0,249 1,334 0,25 1,44 1,100 1,759 0,647 0,395 1,236 1,236 0,270 0,771 1,468 0,94 1,656 1,6 0,806 1,724 0,32 0,559 1,934 1,518 0,549 0,729 1,825 1,728 0,324 1,478 0,932 0,458 0,638 0,257 0,530 1,755 1,955 0,569 1,307 1,172 1,364 1,607 1,996 1,184 1,181 1,844 1,886 1,209 1,524 0,773 1,413 0,737 0,663 1,459 1,903 1,284 1,603 0,101 1,173 1,884 1,8 1,360 1,593 0,15 0,108 1,608 1,416 0,783 1,967 0,957 0,939 1,95 1,460 1,952 0,885 0,5 1,911 1,226 1,772 1,32 1,441 1,763 1,747 0,176 1,390 0,604 0,328 0,973 0,769 0,723 0,810 1,276 0,828 0,101 1,140 1,204 1,660 0,423 1,568 0,412 1,478 0,47 1,177 0,523 0,237 0,344 0,252 1,512 1,457 1,306 0,938 1,851 0,762 1,784 1,909 0,17 0,178 0,365 0,769 1,800 0,511 1,70 0,107 1,775 1,149 0,592 0,87 1,229 0,247 0,643 0,17 0,427 1,725 0,85 0,796 1,380 1,782 0,44 1,974 0,858 1,55 0,334 1,960 1,73 0,158 0,437 1,686 0,678 0,875 0,932 0,868 1,969 0,417 1,775 0,357 1,733 1,434 1,880 0,621 1,56 1,406 1,428 1,752 0,237 1,786 0,720 0,288 1,135 0,59 0,753 1,306 0,552 0,379 0,864 0,751 1,483 0,526 1,193 0,906 1,523 0,584 0,433 1,132 1,823 0,699 1,9 1,423 0,539 1,714 1,362 1,875 1,66 0,996 0,97 0,301 1,807 0,903 1,425 0,671 0,465 1,259 1,35 1,24 1,818 1,730 1,894 1,75 1,258 1,816 0,51 1,266 1,720 1,663 1,486 0,705 0,606 1,329 1,874 0,404 1,956 0,52 0,503 0,386 1,866 1,932 0,522 0,92 1,838 1,858 0,269 1,554 1,218 0,942 0,476 1,726 1,730 0,369 1,711 1,534 1,858 1,788 1,794 0,34 1,577 0,441 1,58 0,712 0,536 1,803 1,596 0,446 1,105 0,169 0,680 0,131 0,249 0,160 0,330 0,86 0,726 1,257 0,13 0,132 1,472 0,67 1,117 1,973 1,880 1,450 1,773 1,147 0,448 0,749 0,410 0,880 0,71 1,90 0,17 1,528 1,984 1,639 1,669 1,609 0,508 0,934 1,317 0,265 0,392 1,626 1,790 0,559 0,893 0,619 1,806 1,778 0,92 1,615 1,297 1,758 0,923 0,91 0,877 1,932 1,794 0,773 0,634 0,469 0,328 0,165 1,744 0,976 0,177 0,702 0,60 1,305 1,86 0,378 1,411 0,196 0,880 0,889 0,139 0,910 1,634 0,580 1,930 0,860 1,696 0,194 1,341 1,824 0,333 1,913 1,724 1,119 0,842 1,578 0,273 1,751 0,989 1,179 1,23 1,448 1,489 0,661 1,525 0,537 1,315 1,849 1,971 1,487 1,865 1,310 1,492 0,889 0,674 0,918 0,921 0,904 1,462 0,8 1,426 1,562 0,525 1,210 0,989 0,176 1,300 0,288 1,622 1,586 0,127 1,589 1,946 1,663 0,664 0,43 1,604 1,500 0,157 1,772 0,572 1,853 0,483 0,883 1,17 1,309 1,579 0,582 0,740 1,861 1,358 0,561 1,813 0,641 0,982 0,658 0,82 1,253 0,277 1,840 0,349 1,490 0,968 1,488 0,316 0,931 1,16 0,682 0,267 0,598 0,264 1,947 0,996 0,96 1,730 0,805 1,35 0,318 0,410 0,422 0,822 1,65 1,960 0,392 0,174 1,84 1,582 1,167 0,66 1,256 1,715 1,21 1,3 1,511 1,345 0,629 1,133 0,887 0,619 0,600 1,781 1,858 0,172 1,416 0,492 0,254 1,437 1,681 0,439 1,392 1,339 1,424 1,393 0,60 1,327 1,115 1,678 1,171 1,201 1,55 1,228 0,388 1,800 0,73 1,713 0,391 1,623 0,272 1,260 1,105 0,2 1,192 1,900 1,406 0,413 0,190 1,861 0,706 0,889 1,325 0,117 0,730 0,710 0,979 1,61 0,804 1,77 1,500 1,874 0,473 0,797 0,352 1,968 0,494 0,965 1,976 0,418 1,685 0,865 0,497 1,43 1,551 1,443 0,606 0,210 1,540 0,689 1,355 1,716 1,874 1,628 0,130 0,28 1,317 1,41 1,42 0,396 1,174 0,562 1,541 1,238 0,257 0,772 1,446 0,565 1,508 0,117 1,241 0,876 1,752 0,355 0,892 1,923 0,847 1,335 0,191 1,244 1,329 0,303 0,944 1,373 1,554 0,170 1,290 0,179 1,550 0,346 1,648 1,185 0,332 1,135 0,207 1,995 0,498 0,138 0,464 1,650 1,351 1,908 0,513 1,168 1,991 0,884 1,987 0,745 1,803 1,740 0,645 1,611 0,21 0,374 0,93 1,217 0,3 1,639 1,320 1,660 1,146 1,339 1,509 0,317 0,836 0,845 0,861 1,390 0,233 0,404 0,119 1,107 1,978 0,434 1,439 0,509 1,759 0,632 0,241 1,80 0,219 0,244 0,91 1,344 0,296 0,660 1,869 0,935 0,612 1,947 1,515 0,389 0,276 0,234 1,495 0,360 0,989 1,414 0,641 1,988 1,279 0,854 1,255 1,889 1,34 1,143 1,624 1,839 0,416 1,705 0,365 0,282 0,894 0,642 0,537 0,733 1,757 1,206 0,810 1,697 1,847 1,448 1,781 1,242 1,779 1,882 1,509 0,37 1,973 1,895 1,608 0,847 0,674 1,123 1,680 0,336 1,896 1,295 1,269 0,46 1,268 0,886 0,848 1,196 1,151 1,198 1,677 1,132 1,234 1,680 0,763 0,790 1,910 0,150 0,475 1,266 0,937 0,270 0,18 1,202 0,833 0,460 0,250 0,860 0,46 0,17 1,725 0,189 1,157 0,963 0,157 0,449 1,809 1,920 1,58 1,464 0,763 0,685 1,4 0,568 0,975 0,421 1,549 1,686 0,888 1,214 1,625 0,529 1,67 1,751 0,998 1,706 1,304 0,726 1,815 1,26 1,758 0,811 0,659 0,94 0,104 0,337 0,203 0,282 0,378 1,939 1,278 1,181 1,428 1,61 1,1 0,859 1,277 0,811 1,737 1,459 1,366 0,876 0,380 0,390 0,362 0,19 0,245 0,529 0,209 0,828 1,945 1,788 1,50 1,469 1,783 1,991 1,591 0,275 1,437 1,837 0,759 1,130 1,104 1,24 1,127 1,334 0,336 0,489 1,132 1,177 0,191 0,187 0,97 1,996 1,487 0,117 1,8 1,843 1,763 1,290 1,122 1,998 0,56 1,162 0,617 0,954 0,264 1,504 1,153 1,731 0,848 0,440 1,978 1,533 1,526 0,822 0,546 0,957 0,897 1,303 1,260 1,672 0,675 0,868 1,496 0,134 1,936 0,747 1,513 1,58 1,906 1,247 1,444 1,765 0,652 1,812 1,939 0,665 0,727 0,264 1,661 0,755 1,365 1,118 0,253 0,529 1,81 0,202 1,852 0,982 1,128 1,998 1,923 1,413 0,159 0,684 1,592 0,705 0,906 1,936 0,995 1,519 1,822 1,409 0,174 1,653 1,954 1,69 0,673 0,680 0,414 1,613 0,643 1,529 0,502 1,241 1,787 1,820 1,996 1,919 0,751 1,156 1,756 0,730 0,485 1,575 1,283 1,599 0,840 0,59 0,902 0,579 1,108 1,533 1,980 0,211 1,418 0,69 1,110 1,775 1,600 0,790 1,45 0,283 0,862 0,953 1,559 1,663 1,177 0,64 1,789 1,711 1,206 1,206 0,151 1,185 0,985 0,246 1,724 0,364 1,754 0,667 0,220 0,892 0,779 1,141 0,326 0,600 1,207 0,463 0,439 1,426 1,144 1,801 0,333 1,967 1,55 1,625 0,819 1,403 1,734 0,476 1,7 1,561 0,683 0,159 1,310 0,501 0,98 1,178 0,199 1,352 1,177 1,646 1,140 0,280 1,979 1,796 0,873 1,996 0,586 0,981 1,197 1,43 1,540 0,230 0,455 0,322 1,438 1,79 0,988 0,999 1,41 0,200 1,4 0,956 0,792 0,103 1,599 0,197 1,811 0,331 0,777 0,357 1,107 0,297 1,891 0,875 1,991 0,624 1,438 0,150 0,0 0,358 1,946 0,376 1,302 1,249 1,548 0,924 1,387 1,199 0,571 1,404 0,667 0,614 0,939 1,112 0,535 1,41 0,263 1,27 0,285 0,928 0,384 1,236 1,946 1,609 0,122 0,856 0,561 1,591 0,960 1,793 1,946 0,158 0,605 1,685 1,530 1,56 1,573 1,185 1,696 1,391 0,515 1,92 0,753 0,599 0,227 1,667 1,256 0,874 1,89 1,216 1,49 0,28 0,189 1,220 1,98 1,791 1,385 1,888 0,66 0,343 0,895 1,386 0,294 1,389 1,92 0,791 1,943 1,565 1,78 1,313 0,46 0,665 0,238 0,392 0,407 1,311 0,715 1,226 1,487 1,617 0,515 0,44 0,240 0,207 1,788 0,944 0,272 0,16 1,158 1,569 1,691 0,816 1,142 1,717 1,219 0,300 0,236 1,100 1,40 0,430 1,984 1,684 0,810 1,854 0,978 1,147 0,942 0,551 0,292 1,111 0,297 1,238 1,579 0,894 1,136 1,895 0,898 1,564 1,805 0,129 0,441 0,494 0,882 0,536 1,304 0,910 0,778 1,648 0,99 1,398 1,36 0,753 1,429 0,752 0,657 0,669 0,183 1,208 0,622 0,556 0,442 0,658 1,25 0,434 0,518 1,733 0,337 1,922 0,137 0,398 1,261 0,47 0,411 1,893 1,728 0,568 0,372 0,253 1,149 0,221 0,710 1,618 0,473 0,476 0,963 1,425 1,595 0,804 0,1 0,495 0,239 1,547 1,260 0,906 0,675 0,742 0,915 1,849 0,916 0,752 0,512 0,715 1,415 1,605 0,116 1,162 1,231 1,152 1,273 0,14 0,407 0,89 0,960 1,917 1,786 0,680 0,806 1,142 0,833 0,742 0,192 0,284 0,683 0,742 0,713 1,401 0,424 1,605 1,188 0,593 0,525 0,981 0,213 1,747 0,377 1,385 0,880 0,247 0,196 0,239 0,65 0,530 0,942 0,799 1,776 1,922 0,777 1,181 1,847 1,578 1,393 0,639 1,532 0,582 0,178 0,927 0,612 0,524 1,493 0,727 1,356 1,728 1,425 1,97 1,749 1,1 0,866 0,304 0,285 1,294 0,173 0,796 0,794 1,355 1,677 1,249 1,140 1,464 0,713 1,837 1,625 0,161 0,946 0,351 1,283 0,618 1,453 1,406 1,313 0,125 0,136 0,510 1,316 0,254 1,962 1,229 0,464 1,596 0,967 0,50 0,98 1,327 0,166 1,508 1,811 1,146 1,682 1,6 0,971 1,878 0,10 0,729 0,428 0,390 0,589 1,884 1,616 1,92 0,295 1,587 0,459 1,186 1,880 1,739 0,455 1,65 0,708 0,684 0,196 1,654 0,37 1,182 1,919 0,648 0,414 1,132 0,335 0,639 0,415 0,269 0,91 0,323 1,160 0,120 0,562 0,890 1,40 0,569 1,458 0,359 0,483 1,981 1,78 1,562 1,457 1,122 0,467 1,579 0,133 0,616 1,917 0,740 1,69 0,161 1,766 0,560 1,697 1,338 0,459 1,291 1,677 0,914 0,113 0,304 0,914 1,732 1,584 1,668 0,46 1,239 0,952 1,612 0,831 1,166 1,151 0,4 0,287 1,907 0,954 0,2 1,823 1,413 1,894 1,255 0,975 1,822 0,475 0,546 0,159 0,142 0,748 1,788 1,402 1,197 1,18 0,790 0,888 0,576 1,253 1,964 1,910 1,914 1,939 1,556 0,172 0,48 1,662 0,840 0,953 1,111 1,770 0,497 0,886 1,173 1,392 0,577 1,625 0,162 0,534 1,983 1,133 1,903 1,758 1,914 1,238 1,592 0,925 0,839 0,122 0,354 0,140 1,554 1,715 1,669 0,391 1,489 1,104 0,270 1,650 1,338 0,778 0,994 1,12 1,73 1,978 0,453 0,276 1,760 1,912 0,952 1,148 1,619 0,510 1,701 1,596 0,259 0,798 1,431 1,49 0,664 1,201 1,577 0,600 0,102 0,893 0,567 1,601 1,777 0,966 0,522 0,475 0,303 0,242 1,458 1,684 0,948 1,805 1,831 0,990 1,41 0,94 1,760 0,890 1,558 0,216 0,971 1,650 0,610 0,504 0,866 1,763 0,751 1,781 0,844 0,400 0,192 0,493 0,981 1,510 1,452 0,870 0,954 1,242 1,962 1,883 1,985 1,3 1,743 0,334 1,452 0,276 0,164 1,579 0,779 1,404 1,928 1,706 0,993 0,998 1,10 0,38 1,287 0,332 1,741 0,686 0,796 1,515 1,64 0,634 1,363 1,702 1,904 1,542 0,81 0,789 1,242 0,458 1,564 0,455 1,968 0,798 1,776 0,190 1,165 1,970 0,121 1,420 1,968 0,545 1,992 1,32 1,241 0,913 0,635 1,245 1,106 0,941 0,398 1,485 1,93 0,167 0,967 1,318 1,677 0,429 0,316 1,570 0,975 0,621 1,251 1,95 1,409 1,526 1,985 1,42 1,780 1,944 1,379 0,420 0,392 0,314 1,676 1,147 0,901 0,537 0,122 0,502 1,413 0,245 0,629 1,244 0,655 0,489 1,246 1,571 0,779 1,227 1,968 0,715 1,277 1,969 1,461 0,346 0,393 1,464 1,778 1,926 1,528 1,839 0,78 1,217 1,390 0,635 0,226 0,410 0,281 1,167 0,364 0,501 0,465 0,11 0,128 0,766 1,470 0,995 1,477 0,749 1,188 0,712 0,494 1,179 1,129 1,866 1,502 1,527 1,779 1,400 0,603 1,220 0,912 0,147 0,699 0,368 1,699 0,479 1,856 0,628 1,735 1,181 1,730 1,329 0,758 0,426 1,586 1,377 1,97 1,552 1,407 0,643 1,768 1,901 0,52 0,588 0,203 1,771 0,966 1,878 1,266 0,916 0,909 0,632 1,412 1,696 1,245 0,60 1,553 1,159 1,783 1,482 0,818 1,369 0,677 1,320 0,231 0,123 0,522 0,63 1,488 1,582 1,671 0,620 1,751 0,848 1,747 0,628 0,952 0,968 0,66 0,560 0,457 1,97 0,208 0,240 0,828 0,629 1,76 0,223 0,716 1,86 1,625 0,941 0,895 1,884 1,68 0,718 1,345 1,762 1,832 1,117 0,689 1,239 0,263 0,83 1,600 0,95 0,966 1,445 1,789 0,487 1,535 1,226 0,545 1,245 1,820 1,116 0,403 1,597 0,51 0,707 0,946 1,38 0,81 0,995 1,386 0,835 0,814 0,442 1,610 1,644 0,761 0,854 0,76 0,176 1,757 1,879 1,348 0,883 0,28 1,726 1,41 1,472 0,740 1,791 0,748 0,207 0,888 1,618 0,996 1,774 1,148 0,762 0,886 1,693 1,187 1,686 1,524 0,967 0,84 0,873 1,548 1,186 1,964 1,34 1,701 1,587 0,177 1,562 1,155 0,447 0,552 0,88 0,507 1,243 1,628 0,726 0,426 0,593 0,579 1,135 1,864 0,728 0,579 0,808 0,599 1,237 1,632 1,969 0,588 1,776 0,249 1,90 0,169 0,662 0,641 0,573 1,384 1,835 1,329 1,19 1,108 1,414 1,789 1,534 1,24 0,503 1,98 1,884 1,216 0,110 1,791 0,346 1,145 0,987 1,497 1,956 0,799 1,694 0,0 0,706 0,145 0,947 0,930 1,755 1,690 0,287 0,823 0,242 1,468 1,678 1,51 0,207 0,298 0,444 0,624 0,338 1,147 0,232 1,923 1,272 0,245 1,129 0,505 1,850 1,291 1,140 1,663 1,95 1,127 0,524 0,435 0,661 0,855 0,662 0,152 1,412 1,391 0,791 1,334 1,591 1,902 0,436 1,816 0,425 1,170 1,810 1,560 0,905 1,771 1,968 0,740 1,916 1,56 0,420 1,582 0,974 1,956 0,823 0,660 0,608 0,949 0,804 0,679 0,547 1,419 1,274 1,324 1,289 1,444 1,792 0,578 1,296 1,121 0,985 0,386 0,217 0,913 0,249 0,954 1,728 0,943 1,15 0,510 1,281 0,506 0,227 0,918 1,598 1,340 1,640 1,558 0,933 1,912 1,169 0,630 1,548 0,92 1,812 1,529 1,662 0,648 0,832 0,741 0,987 1,948 0,452 0,479 0,965 0,382 1,908 0,494 0,251 1,973 0,451 0,748 0,770 1,15 1,965 0,733 1,613 0,857 1,78 0,979 1,600 0,504 1,167 1,254 1,421 1,787 0,487 0,864 1,833 1,987 0,274 1,548 1,511 0,123 1,688 1,767 0,950 1,342 0,832 0,494 1,714 0,912 1,95 1,785 0,263 0,182 0,552 1,640 0,856 1,281 1,42 1,42 0,653 1,161 0,329 1,345 0,782 1,739 1,25 1,146 1,18 0,821 0,684 0,836 1,277 1,682 1,615 0,743 1,503 0,402 1,751 0,24 1,710 1,861 0,90 0,69 0,504 0,818 0,116 0,742 1,985 0,986 1,18 0,955 1,97 0,192 1,447 1,252 1,783 0,627 0,964 1,242 0,426 1,176 1,262 1,989 1,579 1,241 0,308 0,792 0,683 1,357 0,438 0,252 0,434 0,874 0,550 0,840 1,297 0,337 0,329 1,267 0,897 0,862 0,340 1,818 0,153 0,356 0,583 1,5 0,697 0,181 0,986 1,305 0,145 0,658 0,781 0,888 0,745 1,639 1,517 0,39 1,764 1,685 1,275 1,271 1,728 0,92 1,695 1,917 0,935 0,647 1,313 1,718 1,292 1,491 1,212 0,891 1,250 1,104 0,637 1,941 0,859 0,937 1,996 1,433 0,840 0,907 0,568 0,320 1,512 0,598 1,372 1,134 1,58 1,712 0,291 1,810 0,579 0,470 0,876 1,795 1,146 0,549 1,166 1,482 0,657 1,809 0,690 1,141 0,325 0,80 1,243 0,840 1,465 0,58 0,655 1,610 0,258 1,166 1,740 0,491 1,180 1,742 1,647 1,681 0,391 1,552 1,106 1,434 1,862 0,480 1,383 1,974 1,270 0,995 0,396 1,288 0,383 1,704 0,496 0,697 1,573 0,12 1,38 1,211 1,741 0,901 0,942 0,445 1,366 0,762 1,350 1,543 1,918 1,425 0,251 1,916 1,494 1,831 0,461 1,367 1,239 0,679 1,569 0,826 0,853 0,412 0,210 0,701 0,54 1,365 0,876 0,738 0,368 1,457 1,231 1,624 0,267 1,122 1,770 0,737 0,35 1,576 1,607 0,232 1,378 1,171 0,531 0,397 1,977 0,363 0,648 1,718 1,143 0,510 0,454 1,807 1,502 1,252 0,166 0,188 0,699 1,228 0,665 1,896 1,320 0,402 0,685 1,600 0,197 0,526 0,866 0,660 1,691 0,148 0,391 1,541 1,85 1,327 0,879 1,438 1,325 1,870 0,890 0,858 1,358 1,721 0,98 0,878 1,613 0,433 0,65 1,111 0,456 0,189 1,81 1,556 0,0 1,122 0,227 1,96 0,729 0,721 0,18 1,436 1,286 1,611 1,949 1,213 1,863 0,457 1,398 1,587 0,283 1,691 1,786 0,204 1,945 1,548 1,385 0,251 1,677 0,237 1,174 1,670 0,365 0,968 0,175 1,888 1,161 0,749 0,113 0,585 1,31 1,721 0,248 1,218 1,68 0,928 1,582 1,133 1,613 0,566 1,825 0,838 0,955 1,437 0,902 1,439 1,611 0,391 1,328 1,202 1,242 1,129 0,137 0,725 1,845 0,623 1,346 0,539 1,37 0,256 0,621 1,400 0,35 1,476 0,518 0,901 1,740 1,104 0,141 0,919 1,470 0,384 0,415 0,256 0,955 1,713 1,516 0,193 0,447 0,449 1,419 1,237 0,770 1,642 1,791 0,961 0,90 0,885 0,945 0,930 1,268 1,395 1,56 1,132 1,251 1,158 0,582 0,614 1,315 1,111 1,260 0,780 0,602 1,60 0,984 0,464 0,155 1,851 1,292 0,903 0,179 0,12 0,664 0,962 1,754 0,226 0,191 0,798 0,414 1,824 0,723 0,249 1,698 1,400 1,915 1,517 0,663 0,148 1,164 0,60 1,626 1,450 1,331 1,543 0,743 1,543 1,593 1,750 0,541 1,508 0,267 1,855 0,926 1,127 0,548 0,45 1,941 1,213 1,783 1,88 1,903 1,835 0,0 1,889 1,300 1,603 1,438 1,877 0,399 0,591 1,458 0,360 0,397 1,969 0,5 1,311 0,159 1,747 0,578 0,142 1,685 1,705 0,611 1,910 1,299 1,510 1,437 0,263 0,672 1,503 0,564 0,91 1,957 1,89 0,678 0,852 1,104 0,535 0,553 0,193 1,771 1,903 1,192 0,516 1,876 1,299 1,443 0,853 1,944 0,456 0,950 0,575 0,397 1,803 0,667 1,759 0,93 1,529 1,434 1,9 1,529 1,605 0,680 1,668 0,796 1,126 1,114 0,814 0,730 0,558 1,674 1,993 1,85 0,344 0,983 1,507 0,269 1,712 0,167 0,968 0,961 1,235 0,144 1,829 1,365 0,157 0,972 0,180 0,871 0,242 1,299 1,122 1,375 1,176 0,250 1,80 1,596 1,975 1,664 1,449 1,30 1,150 0,465 0,918 0,121 1,616 1,784 1,171 0,556 0,481 1,267 0,610 0,41 1,979 0,670 0,485 0,396 1,248 0,407 1,744 1,328 1,432 1,8 0,434 1,147 1,427 1,155 1,431 0,272 0,83 1,66 0,903 1,181 0,972 1,161 0,328 0,26 0,306 0,878 1,668 1,720 0,286 0,267 0,784 1,130 1,111 1,552 0,83 1,916 0,169 1,930 0,34 1,576 0,806 1,851 0,6 0,588 1,547 1,854 0,229 0,626 1,512 1,209 0,351 0,859 1,510 0,876 0,12 0,291 1,286 0,902 0,877 1,324 0,678 1,243 0,223 0,926 0,197 1,862 1,597 1,774 1,917 0,896 0,617 0,783 1,893 0,957 0,529 0,582 0,23 0,174 1,124 0,387 0,667 0,312 1,312 1,855 0,513 0,463 0,89 1,474 1,380 1,451 1,623 0,751 0,470 0,753 0,508 0,475 0,769 0,119 0,452 0,392 0,549 1,193 0,171 1,102 1,425 0,333 0,916 0,875 1,698 0,20 0,845 1,6 0,216 0,613 0,260 1,431 1,459 1,23 0,893 0,991 1,190 0,373 0,589 1,796 0,95 0,865 0,208 0,383 0,800 1,582 0,726 0,316 0,346 0,690 0,728 1,790 0,22 1,25 1,606 1,390 1,464 0,792 1,781 0,856 0,488 0,150 1,420 0,193 0,773 1,999 1,103 1,559 0,624 1,191 0,922 0,707 1,464 1,234 1,950 1,148 1,215 1,776 0,226 1,154 1,147 0,710 1,742 1,586 0,826 0,375 0,209 1,712 1,660 0,495 1,194 1,614 0,166 1,675 0,405 1,523 0,249 1,898 1,897 0,279 0,267 1,446 0,803 1,89 1,326 0,101 1,189 1,932 0,129 1,697 0,296 1,623 0,576 0,47 1,178 1,747 1,289 1,402 1,468 1,388 0,245 1,360 1,622 1,77 1,77 1,799 0,416 1,145 1,851 0,514 1,105 0,748 1,585 0,494 1,721 0,143 0,848 0,959 1,92 0,336 1,578 1,169 1,615 0,555 1,800 0,972 0,339 0,868 1,68 0,362 1,36 1,633 1,531 1,947 1,407 1,448 0,113 1,323 1,224 0,675 0,964 0,355 0,256 1,727 1,583 1,223 1,883 1,499 0,857 1,808 1,260 1,106 0,451 1,83 1,338 1,526 1,479 0,454 1,979 1,130 1,813 1,685 1,851 0,826 0,262 1,648 0,587 1,830 1,587 0,234 0,973 0,277 1,202 1,96 1,588 1,774 1,923 0,842 0,325 1,673 0,809 1,244 0,693 0,666 0,884 1,513 0,319 0,127 0,634 1,73 0,606 1,724 1,187 1,264 0,584 1,670 0,392 1,658 0,1 0,43 1,444 1,40 1,345 0,91 0,207 1,136 1,270 0,399 0,234 1,357 0,748 0,940 1,348 1,328 1,2 1,467 0,761 1,400 1,330 0,457 1,529 0,289 0,115 0,892 1,764 0,870 0,647 1,263 0,301 1,19 0,510 1,564 0,542 0,373 1,936 0,219 1,293 1,729 0,601 1,9 1,648 1,190 1,232 1,216 0,295 1,811 1,211 1,885 1,159 0,832 0,474 1,404 1,0 1,551 1,296 1,255 1,741 0,693 0,537 1,774 1,700 0,953 0,857 1,725 1,162 0,747 0,313 0,658 1,807 0,373 1,283 1,133 0,967 1,895 1,905 0,993 0,769 0,872 0,504 1,655 1,594 1,628 0,292 0,108 1,248 1,612 0,323 1,581 0,277 0,326 0,596 0,883 0,755 1,522 1,402 0,354 0,770 0,10 0,986 1,874 1,66 0,138 0,14 0,831 0,484 0,269 0,424 1,793 0,550 1,681 0,329 1,261 1,461 1,48 0,54 0,941 1,767 0,819 0,961 1,450 0,633 1,544 0,564 0,328 0,153 1,60 1,513 1,782 1,30 0,66 1,93 0,791 0,526 0,816 1,886 0,688 1,830 1,487 1,366 1,465 1,173 1,255 1,642 1,263 1,814 0,352 0,700 0,551 1,670 1,816 0,644 0,510 0,814 0,713 0,359 0,95 1,577 1,800 0,878 0,420 1,972 1,841 1,85 0,842 1,816 1,980 1,984 1,939 0,838 0,159 1,547 0,103 1,762 1,353 1,876 1,24 0,987 0,236 1,56 0,453 0,859 1,118 0,604 0,485 1,675 1,753 0,843 0,358 0,300 0,394 1,238 1,862 1,262 1,977 0,575 1,220 0,702 0,222 0,523 0,135 0,424 0,505 1,659 1,907 1,178 0,987 0,342 1,426 1,165 0,875 0,420 1,446 0,685 1,576 1,608 1,893 1,851 1,175 0,224 1,449 0,444 0,580 0,648 0,3 1,898 1,558 1,138 0,433 0,373 0,531 1,595 0,890 0,566 0,920 0,819 0,991 0,368 0,207 0,287 0,601 0,983 1,181 1,817 0,232 0,695 1,355 0,938 0,834 1,995 1,512 0,243 0,497 0,625 0,876 1,884 1,507 1,514 0,445 0,126 0,823 0,344 1,62 0,279 0,717 1,660 0,859 1,339 1,633 1,586 1,93 1,780 0,521 0,288 1,675 0,323 0,986 0,345 0,677 0,275 1,578 1,27 0,9 1,277 0,16 0,89 1,780 1,836 1,800 0,381 1,297 1,330 1,600 1,907 0,972 0,15 0,733 1,811 1,546 1,584 1,142 1,178 1,35 0,602 1,366 0,592 1,483 0,893 0,313 0,299 0,85 1,798 1,614 0,55 1,786 0,148 1,707 0,223 0,875 1,582 1,557 0,839 1,307 0,325 0,978 1,571 1,237 0,957 1,820 1,450 1,510 1,359 1,424 1,546 0,932 1,441 1,167 0,561 0,401 0,952 1,963 1,431 0,266 0,899 0,949 1,844 0,276 0,669 1,96 0,253 0,379 1,61 0,866 0,740 0,639 1,635 0,89 1,34 1,531 1,440 1,635 1,343 0,845 0,233 1,738 0,433 0,628 1,127 0,39 0,705 0,797 1,561 1,523 1,538 0,931 1,89 0,157 0,720 1,364 1,652 0,151 1,249 0,442 0,581 1,804 1,445 1,925 0,22 1,920 0,472 1,826 0,167 1,905 1,508 0,127 0,325 0,439 1,393 0,922 0,199 0,248 1,880 1,206 0,217 1,295 1,214 0,347 1,94 0,677 0,548 1,568 0,14 1,620 0,595 0,917 0,495 1,408 0,724 0,462 1,785 1,859 0,897 0,110 1,899 0,519 0,652 1,134 1,501 1,710 1,581 0,636 1,748 0,480 0,729 0,580 1,326 0,828 1,22 1,566 1,126 1,796 1,558 1,879 0,944 1,897 1,662 1,520 1,103 0,514 0,347 1,475 0,982 1,711 0,501 0,676 0,35 0,163 1,903 1,453 0,474 0,278 1,354 1,181 1,127 0,942 0,225 1,935 0,131 1,603 0,884 0,540 0,182 1,830 0,300 0,85 0,795 1,463 1,46 0,184 0,156 1,232 0,399 1,303 1,864 1,455 0,639 0,557 1,981 1,352 1,10 1,673 1,565 1,711 0,256 1,736 0,458 0,344 0,888 1,934 1,610 1,968 1,235 1,458 1,697 1,39 0,968 1,881 0,639 0,733 0,671 0,442 0,556 1,335 0,39 0,543 1,491 1,710 0,988 1,249 1,817 1,187 1,223 0,677 0,240 0,660 0,306 1,941 0,564 0,328 0,584 1,486 0,898 0,399 0,660 0,728 0,87 1,57 1,972 0,421 1,170 1,323 0,65 1,622 1,922 1,261 0,869 0,402 1,904 0,228 0,498 1,316 1,63 0,86 0,78 0,360 0,970 0,370 0,64 0,340 0,160 0,892 0,502 1,808 1,656 1,819 0,712 0,360 1,833 1,394 1,832 1,852 1,911 0,782 0,196 1,399 1,990 0,567 0,614 0,892 0,494 0,731 0,110 1,455 0,144 0,602 0,789 0,437 1,565 1,764 0,349 0,606 0,183 0,839 1,403 0,938 0,154 0,502 0,909 1,482 1,422 0,690 0,382 1,717 0,56 0,493 1,67 0,861 0,648 1,469 1,589 0,576 1,290 1,70 0,976 1,760 0,770 0,886 0,986 0,770 0,330 0,277 1,848 1,845 1,407 1,497 1,321 1,929 0,839 1,233 1,684 1,292 0,957 0,634 0,735 1,650 0,613 0,326 0,867 0,388 1,591 1,645 0,615 0,5 0,39 0,725 1,962 0,819 0,330 0,942 0,383 1,705 0,774 1,193 0,685 1,316 1,897 0,231 1,692 0,974 1,483 0,625 1,63 1,930 0,82 1,619 0,748 0,515 0,883 0,245 0,139 0,17 0,190 0,493 0,481 1,90 1,207 1,360 0,462 1,616 1,760 1,967 1,891 0,522 1,251 1,304 1,940 1,515 0,449 1,463 1,83 0,393 1,409 0,732 0,73 1,678 1,163 0,896 0,931 0,843 1,975 1,905 0,921 1,461 0,550 0,304 0,164 0,139 1,484 0,364 0,490 0,575 1,663 1,710 1,346 1,38 0,672 1,523 1,973 1,346 0,685 1,74 0,970 0,772 0,676 0,487 0,61 0,375 1,502 1,622 1,823 1,413 0,444 0,180 0,506 1,164 0,348 0,976 1,336 0,762 0,600 0,647 1,777 0,194 1,433 0,746 0,502 0,635 1,690 0,300 0,670 1,211 0,749 0,903 1,48 1,377 1,503 0,6 0,424 0,387 1,584 1,262 1,96 1,335 0,123 0,802 1,359 0,312 1,776 0,775 1,85 1,347 1,551 0,627 1,393 1,789 0,985 0,575 0,320 0,23 0,945 1,680 0,950 0,785 1,970 1,499 0,965 0,649 1,687 0,896 1,40 1,407 0,210 1,930 1,773 1,49 1,306 0,424 0,476 0,311 0,389 1,801 0,345 1,851 1,526 0,781 0,839 1,792 1,824 0,943 0,325 1,577 1,507 0,748 0,92 0,975 1,931 1,106 1,930 1,371 1,506 1,596 0,6 0,344 1,77 0,875 0,856 1,506 1,207 0,310 1,247 1,70 0,382 1,222 0,819 0,600 1,596 0,908 1,841 0,857 0,1 1,601 0,131 1,768 0,631 0,574 0,769 1,883 0,892 0,680 0,902 0,272 1,857 0,488 0,630 0,882 0,984 0,13 1,267 0,311 0,209 0,613 1,789 0,158 1,616 1,441 1,941 1,502 0,118 0,271 1,432 1,813 0,48 0,395 1,361 1,234 1,708 0,650 0,983 0,333 0,339 0,233 1,614 1,375 1,507 1,783 0,424 1,645 1,250 1,531 1,941 1,919 0,456 0,195 0,765 0,389 1,421 0,820 0,953 1,559 0,830 0,656 0,619 1,416 0,983 0,358 1,982 1,247 1,760 1,350 0,483 0,901 1,471 1,197 0,731 1,31 1,613 0,265 0,255 1,185 1,449 0,555 1,232 1,138 1,198 0,614 0,700 0,87 0,189 0,793 1,263 1,420 1,787 1,181 1,79 0,550 1,371 1,221 1,259 1,681 0,512 1,718 1,352 1,201 0,933 0,182 0,536 0,901 1,35 0,457 0,322 1,951 1,735 0,980 1,323 1,351 0,305 1,278 0,716 1,853 0,74 1,267 0,253 0,74 1,668 1,117 0,228 1,102 0,146 1,515 1,262 1,490 0,82 0,558 1,558 1,562 0,27 1,73 0,749 0,625 1,356 1,23 0,131 0,880 1,281 0,258 0,166 0,156 1,703 1,433 0,667 0,892 1,982 0,656 1,509 1,65 1,689 0,945 1,499 0,614 1,954 0,147 1,459 0,469 0,517 1,533 1,232 1,131 1,784 0,721 1,199 1,610 1,232 1,730 0,92 0,373 0,852 0,172 1,896 0,654 0,949 1,749 0,789 1,428 1,613 1,382 1,297 1,412 1,719 1,635 0,520 0,888 0,978 0,267 0,555 1,179 0,58 1,414 1,609 1,424 0,341 0,238 1,472 1,199 1,639 1,912 1,33 1,593 1,586 0,835 0,337 1,474 0,854 0,100 0,506 1,631 0,544 1,89 0,299 1,192 0,374 0,799 0,973 0,471 1,89 0,461 1,379 0,107 1,360 0,831 0,327 1,540 0,521 1,234 1,264 0,481 1,32 1,416 1,408 0,502 0,510 0,768 1,963 0,835 0,472 0,356 0,561 0,591 1,573 0,920 0,176 0,909 1,80 1,884 0,229 0,326 1,492 1,255 1,990 1,722 1,536 0,419 1,878 0,622 1,283 1,213 1,7 0,813 0,688 0,26 0,348 0,927 0,982 0,309 1,797 1,303 0,620 0,376 0,645 1,263 0,443 0,867 0,711 0,141 0,485 1,806 0,971 0,536 0,24 1,838 0,859 0,646 1,495 1,333 0,407 1,414 0,517 0,993 1,143 1,230 0,557 1,941 0,899 0,145 0,26 0,333 0,201 0,163 1,880 0,855 1,957 0,760 1,273 0,334 0,32 1,300 1,652 1,758 1,750 0,237 0,455 0,830 0,536 1,160 0,698 0,199 0,10 0,28 0,639 0,873 1,979 0,141 1,324 1,608 0,734 1,486 0,859 0,121 0,236 0,419 0,229 1,955 1,359 0,862 1,39 0,932 1,151 1,553 0,496 0,28 1,413 1,174 1,638 1,46 1,442 0,904 1,615 1,912 0,773 0,363 1,799 0,118 0,628 0,441 0,521 0,6 0,734 1,830 0,144 0,656 1,462 1,66 1,278 1,890 0,718 0,283 1,993 0,377 0,457 1,225 0,215 0,373 0,823 0,367 1,129 1,720 1,34 1,304 1,708 0,650 0,556 0,290 0,124 1,609 0,744 0,193 1,625 0,905 0,714 0,162 0,866 1,836 1,561 0,283 1,573 0,865 1,329 0,439 0,208 1,585 0,81 1,649 1,717 0,519 1,380 0,765 1,390 0,582 1,175 1,37 0,713 1,934 1,468 0,606 0,335 1,130 0,694 1,357 0,810 0,726 0,426 1,426 1,702 1,148 0,6 1,895 1,964 0,825 1,586 0,843 0,979 0,468 1,426 0,296 1,937 1,547 1,590 0,324 0,477 1,457 1,851 0,693 1,25 0,218 0,230 1,60 0,565 1,723 1,663 0,386 1,917 0,723 1,949 0,704 0,369 0,145 1,698 1,593 1,321 1,226 1,795 1,842 0,177 0,0 1,453 1,129 1,333 1,134 0,157 0,67 1,688 0,751 1,806 1,113 0,718 1,451 0,0 0,835 1,206 1,280 0,97 0,870 1,945 0,529 1,57 0,607 0,289 1,906 0,134 0,647 1,653 1,884 0,586 1,212 1,836 1,928 1,315 0,358 0,1 1,566 1,577 0,997 1,544 0,464 1,571 0,421 0,67 1,378 1,784 0,33 1,746 1,733 1,115 1,662 0,663 1,437 1,377 0,692 0,320 1,878 0,117 0,953 1,804 0,611 1,594 1,107 0,493 1,516 1,136 1,922 0,520 0,870 0,577 1,88 1,721 1,21 0,758 1,250 0,407 0,838 0,863 1,222 0,234 1,722 1,867 0,806 1,352 0,786 1,999 0,240 0,968 1,333 0,748 1,371 1,111 1,910 0,807 1,704 1,508 0,598 1,75 1,354 0,530 1,218 1,202 0,553 0,847 1,851 0,762 0,311 0,680 0,371 0,446 0,2 1,487 1,719 0,392 0,203 1,303 1,879 1,790 0,669 0,430 1,327 0,707 0,227 1,201 0,540 0,972 0,324 1,511 1,458 0,745 0,220 0,85 0,583 0,636 1,350 1,8 0,137 1,290 1,441 1,161 1,163 0,376 0,349 0,600 1,286 0,350 0,20 0,292 1,329 1,195 0,606 0,766 1,23 1,9 1,828 0,659 1,379 0,816 1,871 0,115 1,980 0,788 0,106 1,533 0,838 0,743 0,983 1,612 0,335 1,539 0,274 0,699 0,438 1,345 0,949 1,41 0,630 1,132 0,267 1,422 0,545 0,383 0,688 1,690 1,955 1,293 1,228 0,952 0,424 0,690 1,273 1,290 1,352 1,631 0,494 0,922 1,259 1,329 0,131 1,916 0,23 1,964 0,284 0,403 0,242 0,936 1,157 0,9 0,830 1,248 0,70 1,565 1,39 1,178 1,717 1,819 0,816 0,427 0,544 0,516 0,535 1,831 0,15 0,88 1,110 1,882 1,983 0,964 1,79 1,385 1,395 1,919 1,93 1,844 0,848 0,990 0,75 0,75 1,727 0,343 0,488 1,21 1,774 0,347 1,69 1,493 1,205 1,543 1,240 1,936 1,143 0,647 1,706 1,366 1,729 1,728 0,848 0,572 1,329 1,198 1,517 1,991 0,925 1,606 1,323 0,274 1,946 0,971 0,466 0,292 1,817 0,969 1,593 0,732 0,488 1,912 1,176 0,283 0,664 1,224 1,877 0,325 1,110 1,204 1,255 1,549 1,810 1,105 0,80 0,419 1,459 1,916 1,548 0,517 0,772 0,737 0,848 1,39 0,935 0,180 0,808 1,188 0,47 1,355 0,279 0,717 1,90 1,251 0,357 0,658 1,859 0,249 1,10 0,773 0,979 0,867 1,266 1,338 0,450 0,396 1,841 0,734 1,346 1,232 1,362 1,576 0,435 1,263 0,169 0,668 0,762 0,114 0,131 1,208 1,300 1,338 1,335 0,698 1,260 0,744 1,37 0,604 1,83 0,262 0,869 0,764 0,769 1,484 1,12 1,820 1,846 0,275 1,29 1,687 1,907 0,726 1,978 0,75 1,670 1,654 1,270 0,281 1,846 1,156 1,602 1,232 0,357 1,377 1,151 1,202 0,26 1,270 1,22 1,380 0,942 0,92 1,685 1,554 1,238 0,103 1,631 1,427 1,902 1,56 0,181 0,684 0,251 0,866 1,400 0,846 1,848 1,597 1,384 0,495 1,311 0,618 1,884 0,369 0,812 0,295 1,518 0,636 1,577 0,528 1,788 0,389 0,9 0,324 1,800 0,998 1,558 1,397 0,605 1,960 0,820 0,524 0,578 0,187 0,926 1,229 1,405 0,29 1,43 0,279 1,777 0,349 0,629 0,700 1,513 0,45 0,446 1,955 1,901 0,311 1,884 0,747 0,591 0,927 1,807 0,671 1,518 1,757 0,30 0,148 1,279 0,867 0,458 0,717 0,282 1,894 0,716 0,815 0,427 0,488 1,860 1,635 0,141 0,662 0,84 0,670 0,374 0,271 1,248 0,78 0,786 0,115 1,953 0,410 0,777 1,622 0,317 0,925 0,715 1,777 0,616 0,595 0,247 1,964 0,971 1,883 0,442 0,231 1,291 1,585 0,440 1,486 0,587 1,609 0,432 1,7 1,71 1,137 0,220 1,5 1,367 0,479 0,827 1,842 0,14 1,302 0,977 1,450 1,448 0,95 0,119 0,784 1,671 1,132 1,154 0,218 0,246 0,602 0,549 1,935 1,440 1,315 0,551 0,516 1,419 1,98 1,726 0,984 1,961 0,850 1,128 0,348 1,675 0,627 1,491 1,889 0,292 1,162 1,439 0,998 0,971 1,835 0,717 0,108 0,909 0,225 1,738 0,661 0,960 1,801 0,458 1,472 1,72 1,720 1,740 1,539 1,679 0,573 1,896 0,51 1,797 1,651 0,135 1,443 0,576 1,505 0,511 0,558 0,387 0,588 0,777 1,623 0,221 0,646 1,12 1,587 0,148 1,987 1,286 0,627 1,574 1,885 1,761 0,212 0,242 1,665 1,873 0,139 0,187 0,869 1,378 0,108 1,192 0,839 0,459 0,421 1,821 0,411 0,811 0,246 1,660 1,741 0,645 1,402 1,392 0,515 1,627 0,692 0,907 1,408 1,290 0,813 1,705 1,876 1,814 0,249 1,932 1,147 0,483 1,500 0,248 0,623 1,33 1,600 0,722 1,121 0,632 1,966 1,755 0,213 0,282 0,839 1,441 0,45 1,688 1,671 1,264 1,213 0,845 1,532 1,276 0,928 0,614 0,177 0,331 1,823 1,795 0,791 0,477 1,103 0,37 0,892 0,944 1,131 0,381 1,659 1,466 1,451 1,21 0,725 1,961 1,978 0,508 1,402 1,789 0,777 1,666 0,108 0,901 1,903 1,639 0,806 0,970 0,554 1,371 0,466 1,357 1,871 0,969 1,833 1,854 0,133 1,960 0,380 0,268 0,423 0,763 1,445 0,307 0,443 1,401 0,815 1,957 0,982 0,225 0,960 1,531 0,359 0,346 0,82 0,160 1,185 0,76 1,294 1,823 0,641 1,895 0,906 0,460 0,962 1,30 0,157 1,439 0,397 1,234 1,82 1,114 0,302 1,677 1,646 0,119 0,932 1,652 1,848 1,388 0,683 1,217 1,840 1,771 1,708 0,468 0,900 1,243 1,103 1,857 0,910 1,781 1,811 1,443 0,437 0,678 0,885 0,873 1,790 1,775 0,249 1,15 1,858 1,726 1,870 0,479 0,295 1,472 1,987 0,390 0,265 1,636 0,109 1,958 0,361 0,187 1,707 1,370 0,429 0,858 0,109 0,5 1,81 1,712 0,621 1,381 1,949 0,48 1,227 1,958 0,622 1,253 1,136 0,661 0,730 0,14 1,165 1,659 1,790 1,357 1,301 0,905 0,17 0,131 1,106 1,826 0,49 1,952 1,11 1,427 0,232 1,957 0,925 1,803 0,681 1,454 0,894 0,202 1,258 1,683 0,40 1,151 0,253 1,858 1,217 0,401 1,387 1,981 1,453 1,806 0,759 0,268 0,750 0,558 1,360 1,909 1,31 0,123 1,342 0,217 0,374 1,865 0,665 0,515 1,313 1,715 1,161 0,775 0,770 0,988 1,196 0,581 1,937 0,509 0,838 0,849 1,703 1,447 1,911 0,223 0,608 1,880 1,133 0,437 0,984 0,991 1,144 1,342 1,414 1,859 0,884 1,676 0,107 1,370 1,351 1,676 1,477 1,665 1,600 1,609 0,62 0,368 0,951 0,589 1,581 1,539 0,542 0,55 0,932 0,465 1,365 0,253 1,332 0,944 1,901 1,785 0,220 0,587 1,666 0,750 1,346 1,997 1,523 1,927 1,758 1,988 1,586 1,345 0,561 0,865 1,285 1,293 0,960 1,230 0,123 0,145 0,107 0,619 1,418 0,629 0,35 1,15 1,354 1,962 1,622 1,178 1,363 0,971 0,610 0,376 1,258 0,872 0,166 0,675 0,793 1,627 0,729 1,255 0,179 0,437 0,72 1,140 0,823 1,462 0,607 0,375 1,149 1,650 1,725 0,930 1,222 0,482 0,129 0,499 1,817 0,810 0,381 1,578 1,440 1,5 0,60 1,70 0,275 1,758 1,237 1,161 0,150 1,271 1,508 0,750 1,833 1,840 0,695 1,638 0,108 1,867 0,654 0,719 1,337 0,930 1,535 0,122 1,805 1,666 1,563 0,45 0,65 1,440 1,389 1,152 0,407 1,739 0,102 1,457 0,600 1,579 0,987 0,599 1,626 1,951 1,615 0,959 0,852 0,152 1,753 0,479 0,564 0,599 0,895 0,193 0,985 0,968 1,68 0,511 0,276 0,6 1,530 1,178 0,908 1,706 0,343 0,974 1,989 1,656 1,899 1,734 1,559 1,879 1,842 0,898 1,330 1,967 1,538 0,916 0,633 0,34 1,649 0,742 1,462 1,529 0,760 1,46 0,489 0,165 0,305 1,757 0,103 0,679 1,623 0,359 0,610 1,704 0,811 0,432 1,831 0,808 0,413 1,662 1,984 0,42 0,332 0,498 0,580 0,536 1,366 0,347 1,448 1,165 0,988 0,608 1,892 1,156 1,930 0,839 1,717 0,232 0,364 1,419 0,447 0,661 0,975 0,468 0,572 1,378 1,878 1,505 0,919 0,939 0,271 0,894 0,498 1,217 0,247 1,198 1,873 1,569 1,196 1,772 0,425 0,32 1,48 0,174 1,710 1,662 1,722 0,142 0,19 1,484 1,966 1,25 1,86 1,564 1,616 0,256 0,609 1,901 1,257 1,690 0,295 0,229 1,903 0,460 1,628 0,830 0,920 0,411 1,489 1,434 0,288 1,601 1,885 0,74 0,438 0,684 0,569 1,678 1,211 0,828 0,703 0,635 1,738 1,229 0,894 0,167 1,819 1,483 0,124 0,716 1,720 0,264 1,253 0,866 0,809 1,808 0,561 1,250 0,154 0,680 1,475 1,350 0,482 1,72 1,601 0,825 1,247 0,206 0,872 1,709 0,671 0,702 0,769 1,695 1,167 1,526 1,714 1,835 1,237 1,603 0,68 0,288 0,959 1,104 0,48 1,182 1,198 1,693 1,310 1,173 1,215 1,84 0,180 0,626 0,380 0,250 1,463 1,61 0,98 1,309 0,260 1,901 0,462 0,974 0,371 1,948 0,373 1,338 0,860 1,106 1,828 0,1 0,326 1,322 0,739 0,450 1,112 0,388 1,41 1,122 0,759 0,630 0,32 1,686 0,810 0,92 0,75 1,998 0,142 0,780 0,597 0,343 1,83 1,60 1,920 1,549 1,941 1,236 1,959 0,508 1,379 0,12 0,62 0,525 0,34 0,423 1,438 1,300 0,163 0,839 0,227 0,180 0,292 1,818 0,416 1,686 0,631 0,760 0,496 0,344 1,530 0,996 1,70 0,928 1,287 1,755 0,139 0,434 0,268 1,739 1,986 1,96 1,948 1,267 0,841 0,329 1,329 0,507 1,948 0,659 0,335 0,507 0,722 1,57 0,188 0,346 1,456 1,957 1,624 1,309 1,564 1,319 0,824 1,678 0,573 1,520 1,974 0,595 1,787 1,317 1,970 0,229 1,770 0,788 0,371 1,691 1,600 0,606 0,177 1,741 1,979 0,165 1,524 0,315 1,850 0,21 0,58 0,529 1,697 1,302 0,194 0,386 1,207 0,146 1,774 1,677 0,647 0,888 0,692 0,287 1,271 1,954 1,556 1,157 0,441 1,83 0,445 0,901 1,562 0,976 1,794 0,604 0,537 0,333 0,127 0,384 1,326 1,106 1,752 1,444 0,277 0,288 0,465 0,94 0,50 1,39 0,664 1,670 1,507 0,379 0,828 0,340 0,157 1,740 0,337 1,660 1,40 0,99 0,404 1,418 0,510 0,512 1,368 0,391 1,845 0,47 0,936 1,657 0,864 0,203 1,491 1,355 0,292 0,314 1,562 0,70 1,503 0,501 1,118 1,371 0,61 0,255 1,199 0,586 0,221 0,203 0,847 0,532 0,255 1,51 0,897 0,989 1,568 0,863 1,870 1,271 0,911 0,255 1,695 1,118 1,305 0,82 1,330 1,843 1,204 0,768 0,455 0,345 0,608 1,195 0,468 1,614 1,647 1,352 1,329 0,185 0,899 1,862 1,327 0,186 0,532 1,65 0,57 1,522 0,308 0,136 0,607 1,917 1,311 1,557 1,80 1,229 1,203 0,654 0,373 0,457 0,678 1,173 0,577 1,992 1,197 1,993 0,440 0,22 0,845 1,145 1,136 0,830 0,584 1,103 1,443 1,13 0,720 0,708 0,266 1,548 0,225 1,465 0,944 0,534 0,330 1,381 1,759 1,884 0,26 0,791 0,934 0,559 0,811 0,448 0,996 0,379 1,702 1,273 1,361 0,838 0,304 1,559 1,971 0,238 0,355 1,64 0,636 1,101 0,144 0,791 1,756 0,711 1,89 0,469 0,924 0,263 0,94 0,201 1,994 1,80 0,733 0,282 0,549 1,971 1,915 0,52 0,924 1,803 0,213 1,680 1,658 1,413 0,371 0,267 0,840 1,797 0,724 0,640 1,175 0,213 1,898 0,550 0,506 0,249 0,319 1,342 0,6 1,292 0,375 0,534 0,351 0,740 1,581 1,295 0,860 1,852 0,864 0,479 0,850 1,355 1,672 0,10 0,158 0,74 1,932 1,526 0,646 1,424 1,468 1,89 1,278 0,772 1,495 1,60 1,735 1,564 1,319 1,6 0,90 0,569 1,449 1,189 0,209 0,607 0,946 0,969 1,198 0,61 1,971 0,642 1,550 0,940 0,217 1,442 1,854 0,848 0,913 0,899 0,526 1,217 0,378 0,154 0,553 1,82 1,798 1,734 0,179 0,194 1,755 0,241 1,413 0,17 1,491 1,768 0,282 1,200 0,61 0,369 0,440 0,547 1,603 0,832 1,131 1,184 0,555 0,523 0,19 0,643 0,123 1,859 0,66 0,46 0,404 0,222 0,915 1,750 1,580 0,114 0,743 0,713 1,669 1,49 0,71 0,705 1,781 1,18 1,748 0,980 0,188 1,570 0,251 1,6 1,460 1,342 1,291 1,449 1,611 1,443 0,346 1,619 0,585 0,688 1,2 0,262 1,713 1,709 1,77 0,146 1,295 1,352 0,571 1,963 1,865 0,62 1,724 0,542 0,572 1,618 0,37 0,502 1,104 1,553 1,235 0,92 1,931 0,226 0,458 1,737 0,177 1,467 1,615 0,684 0,151 0,524 0,874 0,2 1,435 1,425 1,735 0,543 1,492 0,605 1,108 1,462 0,269 0,894 0,862 0,687 0,657 1,291 1,258 0,474 0,775 1,789 1,492 1,272 0,878 1,319 1,913 0,820 0,278 1,954 0,410 1,487 1,64 0,919 0,468 1,42 0,758 0,235 1,501 1,705 1,436 1,788 1,760 0,561 0,398 1,625 0,976 0,94 1,978 0,408 0,328 1,362 0,772 0,556 0,285 1,73 0,924 0,955 1,795 1,450 1,429 0,97 0,833 0,194 0,112 0,476 0,507 0,706 0,617 1,294 0,485 1,212 1,748 0,742 1,15 0,799 1,915 0,870 1,811 0,493 1,552 1,866 1,519 0,258 0,812 0,561 1,215 1,498 0,781 1,101 1,231 1,166 1,678 1,303 0,760 1,190 1,56 1,132 0,154 1,819 1,612 1,159 0,271 1,630 1,628 1,353 1,494 0,634 1,763 1,847 0,826 1,210 0,776 1,701 0,732 1,580 1,295 1,466 1,846 0,84 1,664 1,541 1,832 0,920 1,519 1,319 1,430 1,129 0,35 0,537 0,596 1,354 0,86 0,681 0,332 1,369 0,586 1,876 0,811 1,698 1,437 0,108 0,19 1,312 0,166 1,377 0,393 0,182 0,258 0,583 1,660 1,758 0,286 0,169 1,255 1,688 1,357 0,221 1,481 1,458 1,775 1,988 0,749 1,459 1,646 1,318 1,651 1,404 0,97 0,565 1,398 0,850 0,429 1,768 1,879 0,331 0,910 1,317 1,2 1,798 0,967 0,413 1,261 0,551 0,63 0,485 1,858 1,398 0,393 1,715 1,596 1,209 1,67 1,261 1,342 1,172 0,604 0,956 1,675 1,261 1,970 1,18 1,612 1,783 0,274 0,249 1,481 1,939 0,12 1,508 1,226 1,125 0,201 1,932 0,802 0,417 0,102 1,200 0,401 1,674 0,554 0,404 0,967 0,24 1,371 0,523 0,499 1,337 0,451 1,794 0,41 0,443 1,456 0,946 0,905 1,716 1,107 0,75 1,759 0,444 1,113 0,5 0,349 0,964 1,41 1,672 1,313 0,154 0,761 1,665 1,386 0,958 1,821 0,119 0,717 0,279 0,776 1,551 1,124 1,434 1,339 1,951 0,263 1,908 0,716 0,779 0,820 1,534 0,532 0,241 0,522 1,799 0,905 1,383 1,461 0,159 0,535 0,901 1,538 0,544 0,278 1,844 0,733 0,106 1,950 1,919 0,619 0,796 0,215 1,586 0,996 1,266 0,556 1,149 1,166 0,470 1,825 1,704 0,792 0,256 1,495 0,589 1,419 0,314 1,320 1,664 1,333 0,395 0,293 1,975 0,612 1,647 0,941 0,405 0,505 0,465 0,352 1,400 1,906 0,287 1,45 1,1 0,817 0,31 0,557 1,633 0,974 1,31 1,24 0,256 0,715 0,283 1,910 0,943 0,821 0,572 1,448 0,445 0,696 0,46 0,928 1,185 1,551 1,738 0,159 0,706 1,493 0,411 1,104 0,44 0,944 0,976 1,794 0,56 0,763 0,863 1,337 1,326 1,926 1,486 0,898 1,79 1,894 1,293 0,779 1,727 1,455 1,519 1,306 0,801 1,625 0,728 0,640 0,736 1,360 0,588 0,753 1,277 1,831 1,488 0,141 0,0 1,701 1,875 1,868 0,847 0,372 0,96 1,726 0,189 1,975 1,682 1,707 0,320 1,290 1,722 1,467 1,307 0,60 0,126 1,783 1,832 0,60 1,520 0,73 0,260 0,933 1,87 0,988 0,664 1,262 1,743 0,752 0,921 1,453 1,673 1,167 1,195 1,461 1,675 0,680 1,724 0,838 0,593 0,46 1,985 1,659 1,701 1,717 1,92 1,308 0,465 0,521 1,926 0,337 1,660 1,801 1,835 1,872 1,993 0,111 1,774 0,469 0,319 1,755 0,291 1,358 0,41 1,287 0,314 0,685 1,3 1,682 0,635 0,703 0,887 0,769 0,505 1,603 0,829 0,644 1,331 1,698 0,869 0,853 0,57 1,180 1,916 0,729 0,77 1,982 1,218 1,573 0,364 1,15 1,72 1,664 1,607 1,31 1,529 1,692 1,939 1,159 0,170 0,716 1,82 1,361 1,190 1,138 0,358 1,774 0,799 1,579 0,462 1,897 0,564 0,766 1,196 1,74 0,49 1,869 1,548 0,226 0,457 0,662 0,664 0,605 0,240 0,98 0,903 0,599 0,98 1,896 0,476 1,606 0,679 1,302 1,688 0,729 1,452 0,995 0,730 1,602 0,11 1,177 0,709 1,920 0,909 0,315 1,966 1,919 0,223 0,865 1,92 1,373 1,599 1,854 0,465 0,625 0,718 0,634 1,223 1,483 1,258 0,917 0,30 0,122 0,487 0,612 0,497 1,655 1,892 0,917 1,777 1,23 0,915 0,98 0,659 1,670 0,275 0,495 0,750 1,937 0,730 1,578 0,150 0,720 1,238 1,236 1,896 0,345 0,817 0,873 0,557 0,577 0,96 0,528 0,77 1,254 0,796 0,683 0,291 0,217 0,885 1,684 0,619 0,507 0,790 0,396 0,596 1,615 1,347 0,831 0,773 0,367 1,650 0,651 1,941 0,394 0,822 1,512 1,810 1,196 0,164 0,748 0,866 1,635 0,133 1,643 0,805 0,299 0,538 0,387 1,906 1,192 1,411 1,517 1,256 0,877 1,64 1,943 0,145 0,958 0,57 0,99 1,728 0,951 1,688 1,792 1,965 1,145 0,928 0,940 1,686 0,288 0,222 0,159 0,806 0,917 1,394 1,733 0,163 1,773 0,302 0,567 0,994 1,757 1,676 0,457 1,588 0,644 1,184 1,220 0,686 0,403 1,219 1,192 0,301 0,14 1,846 0,467 0,172 0,198 1,653 0,86 1,902 0,472 0,746 0,75 0,240 1,288 0,410 0,220 1,887 0,54 0,463 1,383 1,766 0,344 0,274 1,759 0,367 1,515 0,459 0,49 1,611 1,746 1,315 1,119 0,370 1,578 0,543 1,583 0,488 1,361 1,883 0,952 0,85 1,752 1,911 1,388 0,85 0,514 1,534 0,734 0,939 0,738 0,722 1,588 0,611 1,511 0,453 0,508 0,76 1,331 1,255 1,941 0,736 0,932 0,928 1,651 1,61 1,65 1,898 1,75 1,369 1,311 0,312 1,916 1,480 0,123 0,359 0,966 0,725 0,146 1,378 1,410 1,209 0,700 0,131 0,294 0,592 1,638 0,548 1,737 0,66 1,82 1,947 0,120 1,339 0,496 0,196 1,142 0,248 0,836 0,53 1,811 0,528 1,357 1,574 0,803 0,749 0,386 1,546 0,841 0,32 1,222 0,439 1,952 1,732 0,439 1,641 1,893 0,111 0,596 1,821 0,919 1,655 0,965 0,184 0,570 1,54 1,690 1,691 0,87 1,16 1,318 0,99 0,653 1,3 1,832 0,449 1,879 1,319 1,360 1,427 1,105 1,779 1,476 0,961 1,671 1,521 1,279 0,813 0,441 0,347 1,264 1,735 1,582 0,126 1,203 1,60 1,954 1,941 0,236 0,377 0,330 1,754 1,522 1,4 1,54 1,768 1,671 1,213 1,534 0,13 0,900 1,476 0,123 1,797 0,339 1,112 1,565 1,340 0,21 0,186 0,458 1,939 0,66 0,782 0,808 0,863 1,84 1,198 1,96 0,858 0,221 1,921 0,782 1,559 1,270 1,134 1,325 1,543 0,704 0,29 1,238 0,813 1,205 0,735 1,623 1,915 0,257 0,283 0,343 1,278 1,230 0,462 0,92 0,805 0,171 1,745 1,700 0,185 0,724 0,401 1,360 0,942 1,116 0,879 0,91 0,6 1,607 1,826 1,654 0,861 0,871 1,906 1,831 1,584 1,438 0,187 1,702 0,537 1,700 0,4 0,534 0,811 0,554 0,224 0,478 0,326 0,888 0,61 0,42 1,199 1,655 0,322 1,621 1,86 1,436 1,27 0,911 1,499 1,293 0,564 1,310 0,746 0,409 0,110 1,818 0,842 0,76 1,42 1,720 1,996 1,47 1,183 0,497 0,431 0,233 0,874 1,990 1,719 0,702 0,55 0,146 0,547 0,40 1,835 0,780 0,994 0,619 1,651 0,533 1,949 1,849 0,168 0,550 0,446 1,56 0,52 1,477 0,910 0,695 1,331 0,459 0,426 1,750 0,798 0,67 1,696 1,141 0,994 1,576 0,633 0,168 1,135 0,298 0,131 1,568 0,331 0,721 1,28 0,703 1,372 0,100 0,994 0,271 1,521 1,433 1,748 0,518 1,837 0,621 1,104 0,809 1,152 0,184 1,639 0,627 0,938 0,962 1,281 1,910 0,326 1,637 0,723 1,745 0,921 1,680 1,60 1,780 0,51 1,177 0,679 0,998 0,463 0,739 0,615 0,124 1,465 0,844 0,695 1,890 1,522 1,973 0,857 1,22 0,780 0,940 1,508 1,799 1,787 1,113 0,546 1,642 0,97 0,923 1,547 1,56 1,98 0,927 1,713 0,537 1,108 0,647 0,737 0,990 1,91 0,743 1,264 1,138 1,570 0,326 0,555 0,728 1,326 0,587 1,705 0,926 0,502 0,250 1,92 0,833 0,801 0,393 0,638 0,127 1,834 0,678 1,352 1,510 0,68 0,505 0,293 0,108 0,733 0,47 1,791 1,290 1,890 0,252 1,576 0,720 1,805 0,561 0,388 1,752 0,683 1,622 1,627 1,97 1,528 0,155 0,678 0,690 1,633 1,585 0,369 0,917 1,663 0,880 1,140 0,118 0,247 0,661 0,263 1,897 0,523 0,225 0,717 0,463 0,116 1,186 0,502 1,687 1,88 1,886 1,519 1,325 0,902 1,943 1,893 0,565 0,813 0,572 1,229 0,348 0,370 1,268 0,179 1,270 0,843 0,977 0,334 0,841 0,387 0,998 0,285 1,37 0,820 0,964 1,224 0,252 0,44 1,506 0,434 0,360 1,271 0,684 1,636 1,138 1,799 1,384 0,558 0,85 1,157 1,506 0,418 0,279 1,644 1,630 0,719 0,861 1,434 1,207 1,135 1,970 1,997 0,338 0,682 0,334 0,685 1,702 0,815 1,788 0,877 0,587 0,136 1,492 1,555 1,11 0,354 1,561 1,586 0,131 1,510 1,474 1,145 1,820 0,439 1,162 1,766 0,282 1,2 0,165 1,576 1,448 0,558 1,976 1,487 0,717 1,877 0,657 0,931 0,318 0,97 1,58 0,732 1,266 0,66 1,207 0,682 1,202 0,153 1,337 0,797 0,124 1,160 0,942 0,127 1,191 1,992 1,672 1,961 0,0 0,200 0,771 0,605 1,567 0,202 1,590 0,308 1,836 0,740 0,315 1,894 0,839 0,128 0,500 1,101 1,638 1,361 0,457 0,492 0,228 1,984 1,218 0,736 1,165 1,334 0,265 1,706 1,373 1,686 0,8 1,611 0,52 1,478 1,760 0,312 1,776 1,778 1,601 0,435 0,437 0,127 0,38 1,391 0,299 0,476 1,905 0,950 0,659 1,59 1,279 0,330 1,98 1,135 1,408 0,761 0,660 0,186 0,555 1,581 0,913 1,697 1,926 1,488 0,319 1,873 1,600 1,257 0,777 1,609 1,468 0,492 1,511 1,607 1,561 1,45 0,747 1,774 0,290 1,977 1,987 1,22 0,852 1,50 1,537 1,819 0,650 0,279 1,223 0,238 1,438 0,554 1,380 1,828 1,319 0,919 0,137 0,205 1,140 1,234 1,607 0,777 1,592 1,699 0,291 0,635 1,527 0,873 0,548 1,609 1,115 0,648 1,229 0,331 0,413 1,281 0,312 0,65 0,122 0,434 0,970 1,307 1,464 1,875 0,119 1,550 0,650 1,177 0,888 0,407 0,19 0,11 1,993 1,76 0,515 1,896 0,924 1,408 0,513 1,698 1,840 1,849 0,694 0,756 0,221 0,810 1,927 0,870 0,986 0,230 1,514 0,639 0,47 0,911 1,628 0,192 0,593 1,238 1,616 1,879 1,847 0,423 0,928 0,603 0,465 1,824 0,634 1,252 0,681 0,160 0,777 1,824 1,320 1,508 0,385 0,244 0,46 0,111 0,374 1,290 1,470 0,663 1,989 1,670 0,192 1,104 1,700 1,837 0,457 1,60 0,579 1,547 1,218 0,50 0,88 1,816 0,413 1,62 1,130 1,818 1,479 1,633 0,866 1,787 0,669 1,944 1,537 0,635 0,642 0,66 1,47 0,924 1,923 1,213 0,955 1,85 0,991 0,153 0,979 1,463 0,462 0,192 0,123 0,549 0,930 1,435 0,236 1,758 1,202 0,141 1,486 1,296 1,633 1,798 1,959 1,501 0,395 1,136 0,493 1,47 0,625 0,972 0,182 1,404 0,0 1,875 0,538 0,142 1,232 1,720 0,602 0,532 1,45 1,376 0,198 1,302 0,866 0,275 0,307 0,869 1,839 0,553 0,935 0,926 0,821 1,82 1,190 0,500 1,426 0,770 1,384 1,474 1,845 1,370 0,231 1,431 1,319 1,327 1,127 1,80 0,383 0,550 0,534 1,391 0,789 0,949 0,506 0,523 0,979 1,483 0,154 1,106 1,618 1,50 0,628 0,745 1,793 0,326 0,800 0,497 1,972 1,939 1,570 0,638 1,708 1,953 0,590 0,480 1,721 0,895 1,620 0,390 1,800 1,605 1,356 0,438 1,147 1,980 1,601 1,954 1,58 0,419 0,433 0,21 1,853 0,290 1,821 1,844 1,32 1,57 1,749 1,217 0,53 1,275 0,352 0,969 1,677 0,957 1,524 0,481 0,849 1,997 1,710 1,390 0,643 1,917 0,182 0,411 1,427 0,222 1,608 0,132 1,376 1,403 1,445 1,423 1,316 1,211 0,812 0,93 1,777 1,364 0,994 1,701 0,656 1,60 1,831 1,724 0,387 0,581 0,602 0,592 0,347 0,314 1,524 1,855 0,769 0,243 0,213 0,932 1,983 0,798 1,193 0,775 1,310 0,856 1,679 0,126 0,112 0,392 0,916 0,909 0,374 0,727 1,48 0,105 1,282 0,531 0,841 1,266 1,406 1,387 0,821 0,857 1,52 1,114 0,981 1,374 1,23 0,304 0,873 1,605 1,808 1,460 0,294 0,487 0,91 1,971 0,927 1,418 1,407 0,238 1,797 1,907 1,238 0,69 1,138 0,993 1,391 1,595 0,358 1,307 0,95 0,659 1,582 1,862 0,894 0,882 0,553 0,496 1,311 1,8 1,558 1,590 1,755 0,911 1,195 1,748 1,555 1,7 0,281 0,841 0,673 1,942 0,732 0,753 1,945 0,728 0,809 0,437 0,84 0,717 1,92 0,304 0,159 1,311 1,737 0,120 1,303 0,890 0,114 0,328 0,256 1,886 1,34 1,646 1,724 1,267 0,132 1,993 1,583 0,814 1,158 1,61 1,15 0,699 0,976 0,223 1,668 0,712 1,589 0,570 0,80 0,900 0,829 0,831 1,724 0,343 1,338 0,989 1,809 1,275 1,601 0,110 1,182 0,296 1,516 0,398 1,638 0,962 0,795 0,450 1,601 0,814 0,291 1,251 1,288 1,95 0,195 1,549 1,458 0,667 1,247 0,19 0,536 1,289 1,424 0,617 0,888 1,759 1,423 1,805 1,322 1,987 0,48 0,511 1,815 1,22 0,224 0,517 1,485 1,612 1,303 0,117 1,479 0,329 1,563 1,889 1,367 0,214 1,259 0,819 1,926 0,381 1,766 0,854 1,534 1,413 1,774 0,19 1,303 1,791 0,320 0,621 0,404 0,338 0,289 0,817 1,386 1,434 1,214 1,347 0,274 0,992 1,88 0,349 0,194 1,43 0,909 1,788 1,424 1,100 0,475 0,204 1,62 0,784 1,96 0,767 0,508 1,18 0,93 1,499 1,115 0,145 0,712 1,78 0,714 1,668 1,322 0,55 1,52 1,526 0,746 1,499 0,493 0,80 0,858 1,210 0,532 0,474 1,469 1,445 1,676 1,160 0,780 1,388 0,576 0,885 0,264 1,955 1,832 1,606 0,7 1,291 1,935 0,99 0,0 0,348 1,674 1,359 0,106 0,778 1,217 1,847 0,952 1,352 1,567 1,141 1,637 1,225 1,513 1,766 1,625 1,307 0,61 1,189 1,177 0,936 0,266 1,293 1,80 0,713 1,959 1,669 1,716 1,376 1,522 0,601 0,201 0,55 1,538 0,609 1,399 1,379 1,957 0,856 1,133 1,184 0,911 1,547 1,697 0,458 0,757 0,307 1,267 1,305 1,258 0,498 1,945 0,939 1,584 1,531 0,68 1,484 1,84 0,201 0,220 1,270 0,326 0,386 0,578 1,882 1,32 0,161 0,268 0,978 0,934 1,579 1,590 1,314 1,852 0,873 0,737 0,404 1,860 1,104 0,978 0,190 1,843 1,731 1,54 0,145 1,175 1,284 0,49 1,673 1,543 1,230 0,870 0,938 1,199 1,422 1,60 0,79 1,189 1,750 1,94 0,101 1,724 1,728 0,265 1,845 1,930 1,188 0,206 0,472 1,2 0,887 0,628 0,429 0,493 1,49 0,279 1,668 1,506 0,381 0,118 1,188 1,290 1,561 0,618 0,486 0,110 0,73 1,453 0,22 0,290 1,194 0,893 1,932 0,307 1,840 0,125 0,929 0,867 0,279 1,774 1,718 1,23 0,823 1,796 0,590 0,274 0,348 0,584 1,222 1,329 0,578 1,227 1,151 1,309 1,73 0,333 1,327 0,552 0,866 1,174 0,936 1,474 1,76 1,334 1,356 0,462 1,670 0,747 1,254 0,64 0,538 0,166 1,181 0,575 0,410 0,564 0,297 0,885 0,278 0,173 0,679 0,271 0,844 1,368 1,78 1,861 1,4 1,1 0,8 0,427 0,365 0,32 1,703 1,423 0,272 0,687 1,506 1,298 0,321 1,555 1,833 1,976 1,794 0,500 1,524 1,908 0,346 0,999 0,437 1,382 0,307 1,22 0,808 1,133 1,176 1,282 0,677 0,644 0,349 1,706 0,814 1,722 0,339 1,898 1,473 0,985 1,589 1,183 0,34 1,460 0,311 1,523 1,330 0,311 1,889 1,620 1,830 1,955 1,914 0,996 0,242 0,766 1,543 1,15 0,463 1,33 1,914 1,192 1,600 0,410 1,994 1,14 1,402 1,259 0,837 0,373 1,673 1,988 1,330 1,632 0,364 1,402 0,829 1,191 0,441 1,308 1,928 1,946 0,199 0,665 1,546 1,528 1,847 1,271 0,793 0,289 0,565 1,775 1,345 0,820 0,517 1,603 1,271 1,361 0,354 0,748 1,229 0,668 0,991 0,3 0,569 0,549 0,670 0,412 0,307 1,753 1,703 1,780 0,663 0,576 0,925 1,836 1,381 1,505 1,546 1,860 1,864 1,465 0,798 0,590 1,97 0,453 1,518 0,79 0,166 1,719 0,300 1,836 1,579 0,525 1,618 0,282 1,950 1,184 0,194 0,115 1,100 0,877 1,55 1,637 0,883 0,75 1,399 0,943 0,458 0,7 1,637 1,871 1,127 1,455 0,985 0,824 0,799 1,184 1,562 1,372 0,11 0,398 1,851 1,615 0,213 1,146 0,65 0,180 1,945 1,546 1,764 0,700 1,751 1,695 1,516 1,559 1,517 1,876 1,172 0,999 0,993 1,434 1,235 1,596 0,285 1,209 0,212 0,788 0,707 0,178 1,494 1,890 1,153 1,385 1,952 0,688 0,398 0,877 1,431 0,798 1,162 1,303 1,354 0,714 1,523 0,805 0,208 0,554 0,137 1,352 1,822 0,639 1,958 0,36 1,747 1,630 0,829 0,992 0,834 0,585 1,89 1,314 0,24 0,656 1,266 0,936 0,37 0,835 1,658 1,882 0,42 0,459 0,693 0,915 1,19 1,351 1,188 1,812 0,577 0,471 0,178 0,87 0,370 0,193 0,958 0,589 1,462 0,248 1,771 1,198 1,863 0,307 1,822 0,109 1,232 0,25 0,227 1,760 1,882 0,279 1,360 0,139 0,926 1,27 0,634 0,585 1,936 0,945 1,190 1,445 1,410 1,821 0,429 0,704 0,163 1,411 0,877 0,588 0,178 1,26 0,738 0,632 0,812 1,263 1,795 0,646 0,99 1,514 0,24 1,129 1,336 0,839 0,663 0,954 1,529 1,535 0,78 1,517 0,177 0,972 1,968 1,813 1,883 0,509 1,788 1,747 0,890 0,501 0,780 1,783 1,860 0,89 1,936 1,283 0,887 1,706 1,535 0,191 1,528 1,388 1,411 1,747 0,170 1,163 1,241 0,605 1,300 1,288 1,913 0,234 0,990 1,559 1,820 0,92 1,763 0,604 1,529 0,574 0,489 0,787 0,743 1,529 0,439 0,677 0,707 1,243 0,788 1,496 0,70 1,158 1,941 0,586 1,584 1,840 0,509 1,791 1,145 1,402 1,328 0,751 0,641 1,376 0,677 1,709 1,117 1,653 0,658 1,848 1,494 1,883 1,244 1,467 0,772 1,958 1,173 1,377 0,953 0,362 0,289 1,379 1,8 1,333 1,658 0,655 0,967 0,654 0,910 0,685 0,286 0,413 1,783 1,302 1,446 1,173 1,11 1,724 0,687 1,894 0,128 0,500 0,987 1,249 0,400 0,785 0,167 0,831 1,209 1,749 1,562 1,710 1,121 0,538 1,114 0,107 0,935 1,610 0,151 1,197 1,76 1,775 1,285 0,653 1,543 1,476 0,759 0,589 1,35 0,687 0,300 1,633 1,697 1,826 1,270 0,935 1,623 0,863 0,985 0,313 1,69 0,34 0,718 1,238 0,181 1,265 1,513 1,880 0,118 0,736 1,524 0,972 0,763 0,75 1,548 1,511 0,343 0,797 1,288 1,18 0,921 0,237 1,967 1,594 1,965 1,807 0,906 1,713 1,111 1,581 1,13 1,269 0,491 1,219 0,498 1,866 0,869 1,682 0,813 0,508 0,540 1,734 1,305 0,479 0,803 1,833 0,544 1,591 1,434 0,883 0,381 1,752 0,939 0,676 1,356 1,749 0,357 1,782 1,833 1,404 0,76 0,913 0,346 1,938 0,982 0,651 1,855 0,570 0,248 1,174 0,426 0,312 1,757 0,783 0,707 0,733 1,368 0,654 1,265 1,125 1,14 1,180 1,902 1,822 0,26 1,977 0,369 1,428 0,87 1,667 1,172 1,983 1,597 0,24 1,949 0,135 1,667 1,886 1,200 1,729 1,857 1,166 0,226 1,67 1,775 1,978 0,931 1,367 1,859 0,414 0,183 0,758 0,638 0,286 1,951 0,176 0,790 1,31 1,208 0,439 1,607 1,804 0,722 1,729 1,454 0,44 0,534 0,513 0,266 1,741 1,156 1,444 0,151 1,781 0,711 1,875 1,711 0,226 1,96 1,189 0,41 1,340 0,580 0,824 1,368 0,213 1,896 1,148 1,895 1,796 0,723 0,509 1,881 1,162 0,161 1,504 1,996 0,580 0,679 1,259 1,967 0,27 1,837 0,741 1,264 0,121 0,410 0,248 0,788 1,832 0,202 0,981 0,882 1,608 0,322 1,894 1,42 0,535 0,752 0,671 0,591 0,813 1,391 1,477 0,309 1,281 1,362 0,232 0,525 0,786 1,262 1,291 1,773 0,769 1,137 0,925 1,470 0,935 1,789 0,550 1,160 1,956 0,231 1,916 0,667 1,365 1,468 0,561 0,714 1,224 1,188 1,748 0,820 0,583 1,523 0,116 0,844 1,513 0,638 0,134 0,981 0,347 1,212 0,511 1,933 0,992 1,555 0,487 1,634 1,299 1,938 0,756 0,446 1,745 0,252 0,573 0,900 0,940 0,888 1,521 1,326 0,187 0,91 0,453 1,559 1,689 0,998 1,87 1,692 1,64 0,804 1,452 0,293 0,589 0,651 0,704 1,741 0,740 0,573 1,548 1,185 1,643 0,582 1,97 1,146 1,375 1,161 1,147 1,909 1,800 0,913 1,573 0,137 1,985 0,443 1,502 0,330 1,16 0,124 0,98 1,18 0,186 0,142 1,714 0,787 0,729 1,522 1,388 1,627 0,940 1,368 1,49 1,985 0,49 1,721 1,747 1,789 0,258 0,388 1,593 1,257 0,988 1,396 1,610 0,22 1,165 1,685 1,540 1,818 0,677 0,472 1,788 0,220 0,956 0,386 1,975 0,548 0,28 0,616 1,916 0,224 1,690 0,162 0,212 0,916 1,417 1,695 0,481 0,74 0,651 0,358 1,210 1,705 1,465 0,404 0,948 0,349 0,489 1,959 0,120 1,953 1,925 1,412 1,58 1,456 0,631 0,131 1,719 0,14 0,707 1,582 0,873 1,605 0,529 0,262 0,147 1,983 1,21 1,151 1,21 1,328 1,447 0,40 0,581 0,379 1,415 0,489 0,39 1,251 0,230 0,531 1,675 1,285 0,19 1,869 1,56 0,674 0,667 1,590 0,183 0,641 1,281 1,171 1,302 0,762 1,834 1,213 1,797 0,934 0,955 1,272 0,764 1,367 1,184 0,986 0,810 1,939 0,914 0,7 0,71 1,933 1,312 0,984 0,379 1,949 1,263 0,632 1,304 1,118 0,389 1,790 0,241 0,329 0,780 0,863 1,610 0,647 1,64 1,66 1,505 1,960 0,620 1,180 0,262 1,974 1,767 0,792 0,279 0,910 0,85 1,321 1,577 1,34 0,959 1,154 0,350 1,638 0,182 0,891 1,447 1,265 1,638 0,327 1,195 1,460 0,642 1,928 0,614 1,509 1,606 0,217 0,510 1,518 0,889 0,908 0,570 0,382 0,986 1,692 0,439 1,266 1,368 1,27 0,684 1,813 0,522 0,452 0,94 0,473 0,953 0,127 1,914 1,747 0,92 1,53 0,910 1,860 0,516 1,128 0,698 0,84 1,822 1,823 1,385 0,415 1,727 0,567 1,290 1,374 0,375 0,727 1,665 0,880 1,927 1,369 1,11 1,220 1,114 1,598 0,59 1,904 1,633 0,436 1,633 0,678 0,630 0,734 1,235 0,208 0,48 1,27 0,305 0,714 0,306 1,846 1,50 0,686 0,291 0,435 0,542 0,759 0,342 1,62 0,311 0,648 1,746 1,84 1,838 0,982 1,422 1,813 1,570 0,453 1,704 0,899 1,865 0,633 1,766 1,12 1,216 1,681 1,14 1,915 1,926 0,854 1,400 0,267 0,171 0,954 0,870 0,69 0,4 0,734 0,911 1,447 0,109 0,37 0,136 0,911 0,890 1,610 1,682 0,262 1,191 0,35 1,819 1,365 1,581 1,132 1,260 0,173 0,29 1,35 0,339 0,947 0,708 1,783 1,390 1,422 0,537 1,865 0,317 1,297 1,407 1,487 1,149 0,298 0,463 0,894 1,777 1,481 1,531 0,861 0,660 1,307 0,910 1,246 1,730 1,762 0,264 1,595 0,400 0,966 0,817 1,954 0,934 0,600 0,676 1,616 1,85 1,484 0,160 0,503 0,389 0,155 1,671 0,879 0,51 1,461 0,941 1,329 1,893 0,705 1,232 1,995 1,809 1,624 1,353 0,140 0,227 0,844 1,404 0,432 1,477 1,788 0,3 0,455 0,616 0,966 1,786 0,284 0,367 1,264 0,937 1,396 0,534 0,816 1,884 0,274 1,781 1,874 1,243 1,362 0,56 0,623 1,839 1,578 1,875 1,568 0,126 1,140 1,689 1,129 1,357 0,765 0,538 1,51 1,389 0,344 0,589 1,433 1,207 0,968 1,984 1,741 0,730 0,57 0,960 1,57 0,432 0,93 1,201 0,657 0,66 1,366 1,29 0,605 1,109 0,880 1,680 1,848 0,349 0,421 0,214 0,704 0,28 1,469 0,979 0,601 0,839 1,27 0,850 0,737 0,536 0,108 1,337 1,363 0,659 0,687 1,817 0,781 1,165 0,53 0,872 0,596 1,896 1,973 1,28 0,354 1,468 1,642 0,537 0,771 0,493 0,116 0,889 0,78 1,348 1,629 0,689 0,805 0,950 0,160 1,603 0,677 1,140 0,356 1,839 1,447 0,495 0,742 1,867 1,973 0,412 1,539 1,528 1,5 0,527 0,239 0,499 1,637 1,426 0,704 1,245 1,799 0,21 0,278 0,899 0,822 0,985 0,901 1,269 1,908 0,853 0,783 0,395 0,281 0,653 1,540 0,711 0,926 0,283 1,591 1,247 0,395 1,179 1,517 0,212 1,234 1,210 0,104 0,86 0,119 1,186 0,105 1,433 0,944 0,580 1,244 0,845 1,285 0,196 1,738 0,533 0,805 1,938 0,769 1,657 1,692 1,604 1,320 0,367 0,429 1,151 0,617 1,777 0,937 1,843 0,29 0,678 0,96 1,383 1,209 0,540 0,666 0,446 1,499 1,81 1,34 0,159 1,389 1,392 0,330 0,221 0,747 0,747 0,825 0,727 0,30 0,160 0,896 0,726 0,968 1,885 1,846 1,221 0,427 1,859 0,942 0,659 1,897 0,715 0,154 0,183 0,572 0,155 1,125 0,234 1,836 1,357 0,425 0,519 0,589 0,775 1,187 0,914 0,120 0,125 1,438 1,955 0,30 1,490 0,194 0,451 1,572 0,673 1,498 1,402 0,726 0,351 1,824 0,767 1,59 0,28 0,561 1,928 1,247 0,937 0,642 0,985 1,545 1,41 0,947 0,909 1,800 1,266 1,49 1,458 0,228 1,585 1,257 1,622 1,411 0,334 1,803 1,826 1,640 1,336 0,947 0,194 0,992 0,302 0,381 1,705 0,641 1,217 0,32 1,586 1,292 1,111 1,834 1,523 0,151 1,154 0,367 0,903 1,392 1,253 1,932 1,137 1,713 1,362 0,106 0,545 0,806 0,138 0,458 1,429 1,982 1,195 0,894 0,520 0,305 0,394 1,614 1,931 0,800 0,706 0,297 1,227 0,16 0,719 1,115 1,402 0,406 0,540 1,277 1,871 1,777 1,918 0,426 0,476 1,197 0,541 0,317 1,125 1,499 0,884 0,478 0,999 0,510 1,180 1,8 1,159 0,372 1,417 0,107 1,413 0,793 0,706 1,540 0,825 1,513 0,276 0,393 0,71 1,89 0,171 0,390 1,139 1,745 0,938 0,409 0,308 0,893 1,500 0,294 1,285 0,438 1,887 1,173 0,477 1,828 0,31 1,582 0,629 1,523 0,365 1,181 1,929 0,457 0,36 0,728 1,185 1,992 0,158 1,109 0,125 0,423 1,566 0,916 0,807 1,534 0,406 0,418 0,861 1,71 0,462 0,669 1,113 1,391 1,441 0,409 1,804 1,170 0,773 1,699 0,391 0,133 1,718 1,476 0,211 0,827 0,718 0,514 0,343 0,783 0,918 1,850 1,196 0,508 0,848 1,346 0,552 1,726 0,55 1,248 0,365 0,403 1,720 1,80 1,366 1,563 1,515 0,714 0,323 1,902 1,130 0,219 0,51 0,833 0,293 0,531 0,484 1,525 0,753 0,926 0,483 1,291 1,602 0,734 0,18 1,343 0,432 0,743 0,615 1,720 1,330 1,329 1,961 1,857 0,522 0,470 1,888 1,44 1,406 0,575 0,223 0,522 0,941 0,920 0,640 0,316 0,421 1,282 1,439 0,106 1,578 1,901 1,310 0,639 1,482 0,487 0,182 0,76 0,347 0,752 0,234 1,247 0,394 1,711 1,388 0,578 0,304 0,786 1,529 0,51 1,873 0,927 1,409 0,784 1,789 0,75 0,597 1,896 1,436 1,233 1,517 1,369 1,247 1,486 0,490 1,897 0,247 0,89 0,585 0,164 1,698 1,150 1,629 0,161 1,31 1,43 1,586 1,750 0,99 1,676 1,913 0,669 1,501 1,778 1,532 0,197 1,262 0,462 1,46 1,80 0,631 1,331 1,775 0,656 0,103 0,942 1,633 0,695 0,453 1,438 0,463 0,824 0,985 0,843 1,588 1,39 1,917 0,942 1,850 1,31 0,845 0,494 0,115 1,763 0,327 1,119 0,793 0,696 1,333 1,406 0,992 1,404 1,393 0,287 1,976 0,399 0,206 1,778 1,748 1,584 1,439 1,666 1,643 0,597 1,532 0,626 1,200 1,817 1,211 1,574 1,220 0,765 0,542 0,728 1,170 0,49 1,463 1,539 0,91 0,226 1,19 1,521 0,257 0,173 0,809 0,533 1,381 1,443 1,877 1,77 0,160 1,861 0,247 1,39 0,218 0,989 0,258 1,143 1,26 1,924 0,235 1,911 0,100 1,237 1,21 1,274 0,3 1,382 1,737 0,640 1,69 0,201 0,63 0,6 1,257 0,91 0,994 1,507 0,929 0,145 1,636 0,516 0,328 1,825 0,199 0,444 1,547 1,552 1,243 0,512 1,234 0,731 0,30 0,867 1,104 0,109 1,984 1,823 0,219 0,628 0,496 1,603 1,748 1,257 0,947 0,959 1,45 0,265 1,606 0,793 1,37 0,440 1,795 1,731 1,428 1,322 1,252 1,277 1,822 0,77 1,622 1,56 0,384 0,19 1,171 1,840 0,201 1,40 1,127 1,906 1,699 1,350 1,928 1,159 0,493 0,4 1,632 1,982 0,242 0,920 1,734 1,209 1,625 1,33 1,787 1,863 0,577 1,349 0,802 0,785 0,275 1,22 1,549 0,842 0,932 1,468 0,284 0,249 1,518 1,806 1,707 0,951 0,180 0,71 0,924 1,561 0,502 1,141 0,578 1,697 0,10 1,772 0,621 0,116 0,619 0,495 0,523 0,489 0,222 0,524 0,540 1,401 1,907 0,600 1,47 0,528 1,642 1,858 0,119 1,675 1,747 1,26 1,758 1,925 1,77 1,669 1,414 1,718 0,242 1,847 1,119 0,577 1,152 1,219 1,435 1,806 0,737 1,117 0,597 0,126 0,337 0,874 1,361 0,263 1,338 0,293 0,516 1,356 1,923 0,794 0,301 0,589 1,323 0,775 1,534 0,555 1,926 0,947 0,777 1,419 0,942 1,243 0,711 1,635 1,124 1,489 1,707 0,718 1,568 0,796 0,895 1,968 0,516 0,296 0,212 1,470 1,600 1,753 1,936 0,715 1,127 0,668 1,889 1,552 0,739 0,180 0,249 0,51 0,584 1,545 0,675 1,247 0,970 1,616 1,96 0,14 1,993 0,433 0,650 0,500 1,443 0,764 0,670 1,213 1,386 0,44 0,52 1,103 0,735 0,998 1,399 1,545 0,224 1,606 1,179 0,478 1,369 1,147 1,89 0,913 1,944 1,857 0,920 1,548 1,365 0,971 0,588 1,668 1,392 1,175 1,183 0,31 0,378 1,969 0,728 1,868 0,784 1,265 1,506 1,298 1,167 0,292 0,966 0,794 0,408 0,650 1,253 0,49 0,113 1,477 1,665 1,290 0,648 0,865 1,955 1,488 1,352 0,957 1,795 1,10 0,455 0,263 1,215 1,761 0,942 1,356 1,838 1,446 0,501 1,907 0,934 0,623 1,418 1,291 1,669 0,613 1,526 0,952 1,540 0,636 1,372 0,689 0,486 0,375 1,309 0,802 0,830 1,898 0,658 1,309 1,660 0,270 0,790 1,612 1,664 1,288 1,385 1,668 1,454 0,422 0,364 0,138 0,886 0,26 0,138 0,501 1,201 1,95 0,147 1,419 1,789 0,289 1,513 1,499 0,687 1,423 1,426 0,808 0,673 1,70 0,174 0,94 1,284 1,457 0,113 1,948 1,973 1,116 0,156 0,940 1,682 1,607 0,734 0,2 1,863 0,87 0,125 1,870 0,946 1,829 0,906 1,872 0,37 0,953 0,520 1,576 0,556 1,349 1,747 1,328 0,138 0,647 1,166 0,323 1,867 0,768 1,632 0,393 1,261 0,795 1,987 0,907 1,857 0,932 1,473 0,416 1,720 0,287 0,686 1,835 1,100 0,79 0,814 0,647 0,549 0,426 1,479 0,827 1,184 0,803 0,570 1,176 0,72 1,625 0,229 1,184 0,417 1,347 0,510 0,233 0,269 1,367 0,142 1,716 0,309 1,707 0,166 0,335 1,294 0,111 1,372 0,326 1,631 1,480 1,349 1,9 0,746 0,231 1,392 1,262 0,589 1,7 1,204 1,198 1,559 1,104 0,282 1,707 0,619 0,75 0,214 1,642 0,173 0,569 0,904 1,408 1,922 0,10 1,604 0,82 1,504 0,971 0,963 1,201 0,381 0,580 0,714 1,275 1,997 0,654 1,67 1,198 0,450 1,147 0,880 1,809 0,566 1,155 0,555 0,183 0,651 0,667 0,391 1,551 0,406 1,569 0,811 1,127 1,272 0,584 1,512 0,233 1,445 0,945 1,886 0,241 0,43 1,32 1,53 1,997 0,841 0,949 0,279 1,320 1,150 1,118 1,707 1,141 0,183 1,401 1,314 1,757 0,369 1,812 0,1 0,658 0,48 1,603 1,846 0,827 0,933 1,45 0,283 1,582 0,642 1,538 1,170 0,849 1,729 0,86 0,580 0,181 1,76 0,440 0,825 0,191 1,263 1,958 0,727 0,479 1,217 0,162 0,450 1,200 0,681 1,444 1,734 0,739 0,291 0,952 0,311 1,317 0,222 1,983 0,201 0,743 1,577 0,271 0,53 0,304 1,568 1,638 1,409 1,220 0,758 0,188 1,501 1,775 0,360 1,52 0,935 1,77 0,621 1,664 1,559 0,94 1,449 0,345 1,814 0,320 0,674 1,671 1,555 1,484 1,914 1,994 1,596 1,169 0,411 0,529 0,695 1,597 0,805 0,416 1,946 1,45 0,368 1,25 0,890 0,623 1,636 0,484 1,479 1,618 0,900 1,590 0,79 1,744 0,217 1,710 0,294 1,409 1,26 0,50 0,114 1,329 0,610 0,617 0,186 0,615 1,804 1,775 0,411 0,38 1,12 0,127 1,939 1,596 1,119 1,264 1,590 1,369 1,990 1,12 1,942 1,802 0,763 1,723 0,130 0,485 1,371 1,845 1,9 0,199 1,508 1,259 1,650 0,368 1,670 0,666 1,133 1,988 0,903 1,495 1,383 0,473 0,333 1,557 0,371 1,457 0,315 0,566 1,549 1,557 1,627 0,458 1,900 0,540 0,26 1,821 0,488 0,867 0,135 0,924 1,470 1,861 0,898 1,497 0,690 0,624 0,709 0,406 0,927 1,739 0,164 0,556 1,145 1,851 1,124 0,333 1,608 1,749 1,914 0,272 0,843 0,917 1,152 1,345 0,546 0,445 0,212 1,555 1,929 0,943 1,765 1,314 0,673 0,319 0,584 0,672 0,303 1,116 0,115 1,813 1,525 1,11 0,195 1,989 1,57 0,489 0,584 0,483 0,192 0,934 0,155 0,373 1,82 0,526 1,655 0,475 0,696 0,407 0,426 1,881 1,351 0,607 0,927 0,637 0,411 1,150 1,613 1,865 0,690 1,877 0,379 0,251 0,429 1,31 1,995 1,450 0,205 0,334 0,36 1,396 0,847 1,820 0,239 1,655 1,266 0,540 1,726 0,242 0,540 1,504 0,631 1,615 0,564 1,696 1,431 0,391 0,333 1,117 0,577 0,983 1,696 0,231 1,200 0,29 1,991 0,258 1,688 0,805 1,425 0,830 1,193 1,843 0,164 0,271 0,299 0,569 1,431 0,922 0,305 0,479 0,571 1,660 0,300 1,522 1,800 0,763 0,905 1,420 1,397 0,997 1,331 1,172 1,839 0,814 1,757 1,494 1,420 1,387 1,376 0,76 1,660 1,891 1,731 0,859 0,960 1,633 0,273 0,30 0,817 0,911 1,456 1,47 0,849 1,367 1,218 0,490 1,566 0,668 1,386 0,779 1,370 1,246 1,160 1,480 0,30 1,842 0,541 1,610 1,429 0,634 1,982 0,914 0,974 1,965 1,712 0,280 1,796 0,699 0,295 0,645 1,982 1,366 0,938 0,259 1,576 0,336 1,609 1,863 0,490 0,421 0,236 0,964 0,924 0,504 0,370 1,25 0,738 0,271 1,106 1,437 0,153 1,5 1,466 1,234 0,370 0,626 1,775 1,343 1,473 0,649 1,282 0,52 1,443 0,899 0,210 1,113 1,616 1,779 1,154 1,648 0,541 1,729 1,597 0,191 1,951 1,671 0,366 1,710 1,252 0,183 0,283 1,708 1,348 1,132 0,773 1,194 0,854 1,549 0,351 1,897 1,263 1,872 1,671 1,896 1,412 1,504 0,398 1,665 0,956 1,417 1,51 0,52 1,131 1,2 0,289 1,350 0,547 1,207 0,987 1,944 0,361 1,190 0,66 1,409 0,407 1,487 0,249 0,416 0,234 1,682 1,614 1,223 1,109 1,247 0,222 0,919 1,625 0,244 1,490 0,465 0,921 1,621 0,611 1,898 1,694 1,437 1,528 0,32 0,199 0,99 1,854 1,500 0,554 0,576 1,336 0,412 1,564 0,476 0,37 0,219 1,428 1,611 0,3 1,940 0,992 0,21 0,60 1,781 0,501 0,964 1,180 0,752 1,610 0,846 0,593 1,619 1,288 1,912 1,407 1,240 0,632 0,645 0,236 1,741 0,91 0,900 1,947 0,110 1,63 1,856 1,563 1,195 1,718 1,353 0,111 0,550 0,858 1,819 0,523 1,773 0,164 1,767 0,580 0,344 0,409 0,495 0,709 0,272 0,98 1,297 0,863 0,240 0,176 0,160 1,362 1,793 0,466 1,474 0,562 1,724 0,175 1,629 0,56 1,406 1,74 0,146 1,504 0,453 0,360 0,21 1,853 1,853 1,107 0,479 0,728 0,529 1,521 0,74 1,120 0,424 0,93 0,407 1,182 1,347 1,212 1,271 1,153 1,106 0,506 1,783 0,661 0,517 0,440 0,619 1,927 0,145 1,256 1,625 1,381 0,644 0,284 1,703 0,882 0,549 0,983 1,965 0,408 0,680 0,673 0,89 0,264 0,156 1,914 0,751 0,251 0,161 1,28 1,47 0,361 1,780 1,276 1,37 0,868 0,788 1,728 0,560 0,944 0,590 0,183 1,725 0,307 1,412 1,634 1,644 1,600 1,435 1,481 0,266 1,583 1,801 1,396 0,688 0,185 1,291 1,403 0,295 0,901 1,230 1,497 0,28 0,632 1,605 0,115 0,971 1,13 0,592 1,763 0,421 0,407 0,28 0,751 1,407 0,164 1,211 1,943 1,595 1,370 0,10 1,428 1,592 0,464 1,813 0,615 1,715 1,688 1,613 1,394 1,636 1,302 1,135 1,837 0,490 0,344 1,925 0,837 0,998 1,756 0,732 1,594 0,766 1,418 1,305 1,821 0,514 1,383 0,309 1,156 0,877 0,66 0,981 0,891 1,532 0,795 1,197 1,961 0,973 1,375 0,415 0,726 0,59 1,357 0,372 1,429 1,352 0,534 0,832 1,497 1,347 0,629 0,493 1,882 1,361 0,130 0,410 1,885 1,721 0,223 1,425 1,180 0,276 1,848 1,812 0,401 1,93 0,240 1,329 0,46 0,247 1,935 1,999 1,257 0,969 0,498 1,672 1,671 1,357 1,27 1,299 0,805 1,601 0,954 0,68 0,761 1,973 1,699 1,515 0,151 0,255 1,527 0,427 0,474 1,344 1,966 1,840 1,174 1,128 1,400 0,471 0,712 1,26 0,213 0,216 1,88 0,132 1,676 1,352 1,685 1,980 0,738 1,920 1,821 0,658 0,799 0,383 0,718 1,484 0,241 0,388 0,80 1,797 0,439 1,225 1,747 1,36 1,1 1,118 0,432 0,407 0,835 0,463 1,96 1,286 1,351 1,717 0,826 1,891 1,712 1,154 0,623 1,378 1,633 1,42 1,50 0,861 0,302 1,773 0,29 0,324 0,346 1,850 0,440 1,308 1,211 0,694 1,847 1,466 0,355 0,636 0,395 1,813 1,997 0,742 1,723 0,749 1,982 1,389 0,178 1,437 1,377 0,404 0,784 0,308 1,978 1,979 1,64 1,828 0,843 0,645 1,191 0,719 1,980 0,634 0,153 0,181 1,664 1,954 1,218 0,983 1,953 1,237 0,418 1,324 1,805 0,445 1,180 1,562 1,936 0,402 0,610 0,255 1,181 0,210 0,194 1,513 1,117 1,667 0,136 1,165 0,623 0,985 1,590 1,413 1,587 1,968 1,667 1,16 1,649 1,430 0,247 0,159 1,511 1,436 1,935 0,359 0,563 1,813 1,92 0,126 1,854 1,103 1,712 1,346 1,953 0,163 1,49 0,537 0,478 1,185 1,839 1,750 0,229 1,77 0,983 0,931 1,290 0,155 0,725 1,791 1,824 0,700 1,697 0,391 0,174 0,563 0,622 1,647 1,508 0,0 0,4 0,615 0,651 0,912 1,604 1,162 0,853 0,441 1,41 0,227 0,546 1,353 1,385 1,113 1,797 1,822 0,890 1,739 1,969 0,263 1,563 1,759 0,208 0,473 1,694 0,156 1,59 0,752 0,766 0,174 1,860 1,86 0,69 0,455 1,432 1,468 0,508 1,712 0,893 0,327 1,387 0,734 1,189 0,190 1,965 1,492 1,717 0,678 0,901 0,934 1,935 0,601 1,413 0,677 1,205 1,843 0,241 0,771 0,120 0,413 1,747 1,382 0,37 1,390 0,533 0,164 0,301 0,911 0,116 0,774 1,162 1,301 1,985 1,866 1,768 0,751 0,935 0,857 0,539 1,6 1,408 0,610 0,541 0,718 1,545 0,344 1,123 0,383 0,68 0,454 1,650 1,671 1,442 1,183 1,79 0,617 1,976 0,810 1,281 1,805 0,960 1,667 0,825 1,866 1,683 1,783 0,612 0,862 1,889 0,412 0,950 1,741 1,523 0,246 1,338 1,167 1,436 1,722 1,198 1,653 0,572 0,887 1,110 0,463 0,957 0,427 1,826 0,431 1,913 1,570 1,963 1,934 0,647 1,501 1,940 0,268 0,889 0,208 0,574 0,248 0,674 0,5 1,135 0,420 0,219 1,986 1,959 0,161 1,67 1,327 0,338 1,129 1,546 1,926 0,725 1,193 1,798 1,836 0,833 1,388 1,390 1,51 0,191 0,605 0,223 0,912 0,366 0,594 0,321 1,896 1,133 0,318 1,68 1,251 0,259 0,336 0,809 1,303 0,765 1,134 1,209 0,719 0,10 1,700 0,361 1,979 1,171 1,763 1,436 0,517 1,890 1,429 1,518 0,118 0,380 1,473 0,925 1,909 1,286 0,818 0,140 1,690 1,914 1,711 0,484 0,98 0,201 0,841 0,318 0,202 0,946 0,970 1,243 1,138 0,748 1,787 0,304 1,18 1,741 0,816 0,591 0,540 0,577 0,129 0,43 0,708 1,457 1,54 1,260 1,282 0,219 0,811 0,707 1,684 0,565 0,501 1,385 1,292 1,234 0,646 0,507 0,55 0,933 1,904 0,109 0,755 0,633 1,677 0,980 1,749 1,615 1,288 1,532 1,859 0,629 1,799 1,494 1,170 0,763 0,688 1,525 0,813 1,399 0,525 0,198 0,502 1,897 0,415 0,353 1,498 0,572 1,507 0,921 0,324 0,397 1,941 1,590 1,594 0,312 1,784 1,275 0,328 1,556 0,666 0,847 0,762 0,499 1,176 0,986 1,468 1,373 0,76 0,362 0,131 0,871 0,707 0,310 0,377 1,772 1,817 1,6 0,286 1,194 0,744 1,494 1,790 1,401 1,204 1,763 1,334 1,500 1,454 0,165 0,616 0,226 0,810 0,815 1,35 0,422 1,126 1,965 1,547 1,492 0,127 1,53 0,389 1,216 1,602 0,636 1,875 0,422 1,641 0,266 0,728 1,748 0,543 0,210 0,225 1,255 1,678 0,166 0,261 0,981 1,646 0,47 1,910 0,192 1,976 1,53 1,626 1,480 0,667 1,642 1,770 1,886 0,246 0,835 0,857 0,871 1,602 1,256 1,557 1,694 1,461 1,329 0,913 0,949 1,935 0,172 0,713 1,727 1,280 0,860 0,992 1,940 0,224 0,808 1,579 1,991 0,661 0,966 0,546 1,677 1,36 0,809 1,926 1,600 0,102 0,555 1,829 0,694 0,302 1,538 1,637 1,598 1,248 0,202 0,324 1,219 0,273 1,991 1,362 1,706 0,426 0,879 0,294 0,757 0,99 1,82 0,694 0,80 1,886 0,471 1,312 0,594 1,212 0,118 0,582 1,647 0,588 1,513 0,419 0,810 1,516 0,157 1,141 1,512 1,192 0,949 0,687 1,257 0,700 1,109 0,225 1,267 0,174 1,808 1,398 0,61 1,193 1,3 0,223 1,744 1,835 1,360 0,498 0,485 0,559 1,797 1,182 0,317 1,264 0,742 0,465 1,18 0,45 0,814 0,283 0,839 1,575 0,549 1,458 0,669 1,907 0,656 1,425 1,330 1,21 0,861 0,66 0,560 1,558 0,346 1,405 0,378 1,989 0,487 1,448 0,700 1,953 1,958 1,491 0,355 1,859 1,144 0,644 1,783 1,729 1,320 0,158 1,635 0,698 1,209 0,951 0,78 1,998 1,290 1,285 1,504 1,725 0,560 0,939 0,757 1,552 1,48 1,95 0,275 1,839 1,901 1,285 0,415 0,264 1,70 1,573 0,981 0,966 0,917 0,904 0,943 1,782 1,507 1,533 0,164 1,820 0,208 0,417 0,455 0,290 1,360 0,736 1,810 0,612 0,106 0,261 1,631 1,852 1,165 1,126 0,5 0,982 0,661 0,197 1,299 1,189 0,142 1,777 1,303 0,436 0,679 1,949 1,409 0,694 1,345 0,590 0,376 0,84 0,408 1,620 0,848 1,749 1,438 0,52 0,713 0,24 0,712 0,219 0,918 0,685 1,693 0,665 0,445 0,929 1,824 1,923 0,332 1,528 0,354 1,116 1,394 1,314 0,604 0,79 0,121 0,94 1,134 0,969 0,72 0,740 0,696 0,403 1,493 0,368 1,854 0,461 1,504 1,308 0,868 0,253 0,461 0,43 1,478 0,265 0,526 0,959 0,87 1,303 1,813 0,646 1,506 0,358 0,107 0,201 0,330 0,226 1,925 1,400 0,196 0,905 1,147 0,199 1,861 1,640 1,175 0,124 1,186 0,924 0,79 0,330 1,583 0,782 1,464 0,777 0,501 1,905 1,127 0,464 1,839 0,64 0,277 0,7 0,23 1,338 0,364 1,11 1,313 0,951 0,76 0,714 1,948 1,459 0,368 1,601 1,992 0,16 1,721 0,858 1,284 0,133 0,362 1,704 0,666 0,842 1,531 0,900 1,864 1,780 1,966 1,805 1,855 0,509 0,505 0,80 1,205 1,365 1,461 0,531 1,677 0,331 1,417 1,739 1,683 0,312 1,664 0,881 1,315 1,874 0,937 0,62 1,488 1,864 0,236 1,770 1,350 1,708 0,764 0,181 0,16 1,205 1,692 0,682 1,778 1,8 1,455 0,339 1,260 0,99 0,881 0,410 0,603 1,335 1,113 1,465 0,171 0,616 1,585 1,220 0,781 0,203 1,216 1,930 1,358 1,145 1,815 0,439 0,386 0,169 1,823 0,647 0,609 1,917 0,198 1,302 1,432 1,342 0,984 1,349 1,450 0,80 0,257 1,965 0,887 1,726 0,781 1,642 0,107 1,854 1,808 1,814 1,224 0,644 0,925 1,532 0,974 0,150 1,457 0,44 1,522 0,257 1,323 0,934 0,939 0,492 1,40 1,40 1,992 0,877 1,772 0,25 0,674 1,937 0,439 1,362 1,873 0,735 0,384 0,516 1,574 0,335 0,276 1,52 1,775 0,387 0,288 0,869 0,367 1,10 1,797 1,847 0,792 0,885 0,919 1,344 1,752 1,571 0,774 1,446 1,65 0,518 0,942 0,883 0,626 1,559 0,655 1,10 0,884 1,925 0,681 1,994 0,240 1,212 0,996 1,403 0,802 0,200 1,272 0,770 0,712 1,952 0,435 0,573 0,212 0,520 1,795 1,492 0,331 1,644 0,312 1,877 1,736 0,996 1,491 1,199 1,591 1,522 1,655 0,823 1,64 1,932 1,822 1,526 1,704 1,41 1,285 1,388 0,333 0,0 0,789 0,389 1,451 1,28 0,885 1,629 1,13 1,214 1,649 0,878 0,786 0,619 0,905 1,133 0,979 0,516 1,175 0,77 1,911 0,252 1,526 1,767 1,723 0,641 0,644 0,60 1,117 0,178 0,919 0,932 0,779 1,160 0,637 1,122 0,681 0,54 0,140 1,721 0,572 0,991 0,335 0,673 1,676 0,469 1,559 1,580 1,150 1,996 1,205 0,604 1,277 1,479 1,31 0,945 0,264 0,724 0,883 0,941 1,524 1,477 1,737 0,529 0,824 1,872 1,633 0,241 0,842 0,587 0,650 1,457 0,673 0,767 1,511 0,853 0,247 0,509 0,981 1,254 0,630 0,56 1,63 0,850 0,751 0,446 1,530 1,164 0,568 1,786 1,953 1,517 1,708 0,561 0,782 1,739 1,7 1,483 0,566 1,524 0,457 0,3 0,298 1,659 1,299 1,580 0,809 0,698 1,144 1,727 0,308 1,2 1,694 0,957 0,132 0,849 0,200 0,146 1,439 0,417 0,101 1,409 0,925 1,954 0,810 1,888 1,639 1,598 0,513 1,602 1,558 0,684 1,88 1,944 1,744 0,58 1,78 0,290 0,862 1,3 1,681 0,423 1,940 1,776 1,125 0,61 1,763 0,766 1,252 0,843 1,353 0,891 0,790 0,673 1,871 0,564 0,864 0,665 0,945 1,802 0,404 0,91 1,804 0,347 1,156 1,840 1,454 1,347 0,613 1,197 0,841 0,624 0,397 1,125 1,882 1,261 1,687 1,681 1,452 1,586 1,736 0,265 0,236 0,155 0,210 1,139 1,465 0,923 0,136 0,79 0,46 1,661 0,464 0,109 1,499 1,322 0,207 0,175 0,516 0,870 1,640 0,245 1,556 1,384 1,104 0,388 1,737 0,95 0,915 0,67 1,502 0,238 1,796 1,199 1,626 0,643 1,739 0,444 0,985 1,686 1,352 0,792 0,658 1,337 1,424 0,3 1,707 0,858 1,16 1,473 1,539 1,444 0,460 0,278 1,390 1,747 0,151 1,998 0,282 0,211 1,184 1,110 0,658 1,11 1,511 1,339 0,900 0,487 0,906 0,381 1,397 0,537 1,571 0,585 1,420 0,634 0,162 0,203 1,763 1,933 1,918 1,7 1,237 1,180 1,825 0,191 0,847 1,87 1,737 0,414 1,156 0,953 0,379 0,411 0,804 1,692 0,34 1,115 0,27 0,917 0,869 0,761 1,223 1,634 1,93 0,678 1,264 1,359 1,956 0,556 0,336 1,249 1,200 1,132 1,249 0,702 0,938 1,76 0,756 0,841 0,60 1,675 1,275 0,753 0,689 0,453 0,280 1,62 0,947 0,922 1,661 0,613 0,938 1,92 1,822 0,894 0,683 1,344 1,888 0,550 1,325 1,40 0,744 1,642 0,228 1,178 0,605 1,53 1,30 0,218 1,380 0,980 0,860 0,186 0,625 1,549 0,285 1,961 1,315 1,272 0,277 1,297 1,222 1,587 1,690 0,436 0,65 0,63 1,476 1,325 1,868 0,120 1,702 0,695 1,424 0,138 0,894 0,945 1,538 0,832 1,755 1,785 1,79 1,482 0,186 1,461 0,104 0,300 1,494 0,292 1,250 0,458 0,735 0,939 1,813 0,789 0,789 1,704 1,909 1,449 0,41 0,17 0,225 0,217 1,931 1,345 0,273 1,711 1,630 1,128 1,378 0,620 1,682 0,955 1,423 0,899 0,466 0,759 0,663 0,203 1,136 0,345 0,325 1,627 0,855 0,229 1,653 0,217 1,318 1,555 0,253 0,704 1,592 1,306 1,599 1,784 1,921 1,944 0,216 0,32 1,914 1,255 0,876 0,775 0,910 1,19 0,260 1,840 1,878 1,581 0,135 0,799 0,144 0,478 0,873 1,766 0,178 1,52 1,998 1,611 1,48 1,384 0,868 0,8 1,544 0,823 0,130 0,161 0,867 0,53 0,395 0,402 0,374 0,562 0,551 1,910 1,175 1,360 0,375 0,990 1,951 0,686 0,167 1,711 1,821 0,907 1,442 1,233 0,361 0,150 1,128 0,338 0,469 0,427 1,338 1,29 1,716 1,151 0,594 0,314 1,795 0,818 0,19 0,559 1,89 0,606 1,813 0,173 0,816 0,259 1,854 1,508 1,158 0,862 1,861 1,686 1,559 0,211 0,824 0,393 0,691 0,580 0,617 1,189 0,172 1,689 1,187 1,685 1,639 1,838 1,818 1,126 1,555 0,996 1,17 1,691 0,712 1,691 0,699 0,283 0,709 1,47 1,794 0,741 1,237 1,856 1,391 1,93 1,1 1,211 1,135 1,176 1,991 0,833 0,640 0,941 0,282 0,528 1,194 1,173 0,690 0,725 1,768 0,955 1,644 0,828 1,603 0,271 1,48 1,989 0,931 1,527 1,895 1,704 0,75 1,26 1,960 1,512 1,708 1,517 0,432 0,69 0,643 0,225 0,133 0,984 0,125 0,387 0,359 0,176 0,243 0,718 1,934 0,298 1,567 1,546 1,462 0,894 0,949 0,704 0,729 1,933 1,81 1,779 0,441 1,561 0,111 0,38 0,394 1,34 1,929 1,759 1,831 0,868 1,40 0,168 0,246 1,568 0,245 0,978 1,498 1,93 0,58 0,836 1,686 0,882 1,835 0,357 1,128 1,173 0,386 0,719 0,447 0,414 0,396 0,110 1,529 1,55 0,275 1,728 1,507 1,617 1,988 0,159 1,762 1,213 0,409 0,886 0,721 1,932 1,157 0,937 1,107 0,674 1,197 0,546 0,780 0,161 0,779 0,681 1,94 1,327 1,584 1,54 1,426 1,356 1,489 0,576 1,315 0,962 0,735 1,766 0,216 1,56 0,203 0,14 0,397 1,25 1,273 1,276 0,248 0,950 0,95 0,20 0,546 0,276 1,665 0,720 1,409 0,854 1,719 0,81 1,52 0,326 1,71 1,367 1,868 1,207 0,307 1,101 1,705 1,402 1,77 1,226 1,940 0,956 1,713 0,557 1,710 1,452 1,294 0,395 1,973 0,319 1,611 1,418 1,238 0,624 1,415 0,540 1,590 0,380 0,147 1,207 0,284 0,446 0,401 1,857 1,948 1,10 1,729 1,557 1,950 1,385 1,42 0,453 1,156 0,787 0,233 1,11 0,210 0,875 0,53 0,865 1,87 0,876 1,213 1,723 0,585 1,566 0,341 0,388 1,631 0,559 1,841 0,438 0,542 1,588 0,809 0,270 0,650 0,808 0,463 1,418 0,711 0,592 1,696 1,496 0,51 1,205 1,602 0,650 0,605 0,95 0,581 0,764 1,122 1,483 1,626 0,409 1,202 0,825 0,16 1,355 1,983 0,813 0,227 0,724 0,896 1,154 1,671 0,645 0,10 1,871 0,530 0,681 1,661 1,641 0,0 1,480 0,366 1,821 0,545 1,385 0,541 0,936 0,418 1,584 0,614 0,303 0,584 0,855 0,885 1,270 1,875 0,264 1,962 0,174 0,946 0,873 0,417 1,423 1,45 1,12 1,429 0,783 0,968 1,584 0,879 0,128 1,118 1,272 1,384 1,659 1,800 1,614 0,657 0,908 0,589 1,651 0,258 0,62 1,84 1,809 1,834 1,924 0,289 1,554 0,784 1,227 1,310 1,78 0,589 0,404 0,830 1,458 0,688 0,543 0,460 0,697 0,291 1,143 1,631 1,1 1,132 1,379 0,815 1,539 1,646 1,583 1,764 0,926 0,932 1,594 0,972 1,261 1,708 0,80 0,883 0,452 1,156 0,29 0,127 0,347 0,489 0,633 1,26 0,343 1,637 0,998 1,723 1,20 1,458 0,655 0,927 0,32 1,959 0,300 1,832 0,790 0,218 1,53 1,305 1,790 0,627 0,474 1,945 0,293 0,311 1,563 1,481 1,526 0,508 1,601 1,784 0,118 1,771 0,820 0,565 1,294 0,977 1,48 0,674 1,870 0,360 1,383 0,105 1,427 1,296 1,61 0,876 0,201 0,778 1,933 1,771 0,942 0,761 1,742 0,510 0,761 0,963 0,478 0,952 1,242 1,641 0,669 1,939 0,580 1,392 0,365 1,728 1,518 0,476 1,282 1,430 1,851 1,63 1,700 0,257 1,17 1,642 1,900 1,343 0,88 0,443 1,803 0,324 0,424 0,648 0,120 0,136 1,603 0,704 0,468 0,853 1,642 1,976 1,192 1,180 0,687 0,376 1,933 0,883 0,375 0,167 1,576 0,287 1,906 0,207 0,741 0,82 0,306 1,337 1,418 1,665 1,228 0,416 1,648 1,998 1,836 0,529 1,916 0,39 0,134 0,903 1,572 1,394 0,979 0,920 0,713 1,571 1,220 1,500 0,596 0,441 1,441 1,617 0,843 1,617 1,634 0,897 1,203 1,104 1,314 0,118 0,968 1,719 1,105 1,205 1,277 1,55 1,806 1,721 1,252 0,865 0,228 0,898 1,457 1,900 1,402 0,614 0,221 0,505 0,786 1,478 0,297 0,407 0,924 1,865 1,772 0,822 0,761 1,444 0,903 1,451 1,936 0,134 0,959 1,455 0,327 0,552 1,956 1,388 0,873 0,70 1,774 1,857 1,998 0,675 0,228 0,775 1,336 0,552 1,30 1,480 1,338 0,848 1,47 1,891 1,602 1,865 1,106 0,542 0,784 0,740 1,38 1,783 0,951 0,394 0,699 1,551 0,994 1,349 1,560 0,754 1,499 0,429 1,586 1,737 0,153 0,904 0,225 0,677 1,295 0,627 0,395 0,8 1,453 0,768 1,307 1,643 0,563 1,394 1,538 1,994 1,633 0,567 1,795 1,980 0,705 0,616 1,251 0,457 1,615 1,86 0,319 1,834 1,314 0,913 0,367 0,876 0,499 1,844 0,835 1,28 0,359 1,930 1,717 1,472 0,277 0,2 1,821 1,783 0,281 0,851 1,896 1,8 0,500 0,81 1,759 1,762 1,616 0,959 1,187 0,51 0,318 1,388 0,578 0,344 0,451 0,415 1,391 0,586 1,773 1,174 1,250 1,984 1,37 0,358 0,803 0,419 0,943 1,576 1,944 1,114 0,613 0,796 1,485 1,469 0,919 1,769 0,841 1,400 1,876 1,214 0,321 0,601 1,369 1,62 0,900 1,784 1,826 0,246 1,926 0,118 1,613 0,835 0,782 0,73 1,934 0,379 0,185 0,887 0,175 1,869 0,264 0,31 1,846 0,330 1,254 1,431 0,253 1,237 0,970 0,31 1,579 1,226 0,242 1,387 0,942 0,205 1,436 1,271 0,391 0,757 1,701 1,66 0,729 1,89 0,781 0,583 0,234 0,273 0,924 0,23 0,186 1,585 1,464 0,525 0,86 1,275 0,40 0,26 1,788 0,93 1,854 0,739 0,882 1,193 0,922 1,226 0,4 1,23 0,679 0,923 1,134 0,937 1,376 0,758 0,494 1,414 1,603 0,731 1,240 0,679 0,387 0,389 1,633 1,666 0,257 1,643 0,320 0,18 0,41 1,446 1,289 1,764 1,808 1,869 1,617 1,905 1,953 0,599 0,291 0,969 0,976 1,156 1,149 1,665 1,520 0,189 1,672 1,793 0,77 0,937 0,134 0,898 1,629 0,26 0,519 0,719 1,269 0,909 1,280 1,629 1,139 1,689 1,1 1,628 1,494 1,915 1,45 0,575 1,221 0,427 1,89 0,7 1,285 0,164 0,989 0,557 1,592 1,502 1,107 1,68 1,479 0,848 1,785 0,945 1,983 0,966 0,485 1,395 0,617 0,373 1,106 1,506 1,104 0,52 0,93 1,173 1,528 1,350 0,568 1,731 0,735 1,988 0,629 1,187 0,732 0,579 0,383 1,496 1,985 0,752 0,715 0,246 0,779 1,909 1,356 0,817 0,684 0,190 0,187 0,656 1,278 0,394 1,261 1,606 1,367 0,110 0,850 1,654 0,467 0,616 1,966 0,376 0,67 1,420 1,840 0,179 1,659 1,329 1,252 1,35 0,64 0,177 1,709 1,243 1,18 0,312 0,57 1,824 1,408 0,541 1,760 0,600 1,511 1,831 1,584 1,174 1,949 0,87 0,716 1,380 1,452 1,669 1,595 0,478 1,420 0,727 1,923 0,975 0,63 0,465 1,34 1,240 1,525 1,815 1,973 1,876 1,176 0,791 1,179 0,829 1,762 1,988 1,984 0,859 0,622 1,107 0,738 0,965 1,924 1,11 0,274 0,735 0,294 1,222 0,98 0,460 1,344 1,850 0,697 0,11 0,663 1,737 1,683 0,869 1,658 0,789 1,800 0,550 1,603 1,981 0,421 1,691 1,915 1,635 0,163 0,273 1,652 0,919 0,806 0,310 0,700 1,415 1,325 0,873 1,374 1,247 1,522 1,108 1,51 0,455 0,695 1,259 0,252 0,695 1,120 0,374 0,450 1,877 0,417 1,642 1,420 1,942 0,390 1,43 0,603 1,938 1,103 1,465 0,38 1,386 1,485 1,363 1,103 1,984 0,884 1,842 1,633 1,295 1,641 0,380 1,710 1,782 0,592 0,942 0,405 1,510 0,530 1,666 1,986 0,648 0,366 1,752 0,373 1,866 1,453 0,566 1,543 0,477 0,920 1,362 1,369 1,540 1,155 1,676 1,66 0,55 1,813 1,280 0,346 0,892 0,732 0,512 0,80 0,62 0,178 0,727 1,592 1,907 1,701 1,670 1,320 1,172 1,20 1,657 1,685 0,339 1,343 0,428 1,625 0,226 0,258 0,143 1,641 1,159 0,395 1,17 1,303 1,989 1,188 1,898 1,689 1,115 0,913 0,566 0,328 1,175 0,822 0,364 0,65 0,102 1,628 0,964 0,552 1,742 1,652 0,180 1,477 1,55 1,806 1,644 0,251 0,278 0,508 0,537 0,762 1,889 1,268 1,188 1,924 1,304 0,49 0,331 0,659 1,827 0,145 0,737 1,745 1,736 0,281 0,187 0,245 1,127 1,682 1,444 1,504 0,429 0,548 0,522 0,161 0,87 1,498 1,225 1,976 1,950 1,680 1,117 0,15 0,680 1,951 0,359 1,110 1,175 0,92 0,747 1,151 1,719 0,736 0,875 1,741 1,84 1,327 1,136 1,981 1,935 0,337 0,624 1,841 1,723 1,64 1,103 0,787 1,637 0,627 1,201 1,95 0,420 1,110 0,316 1,232 1,999 0,706 0,830 0,46 0,305 0,507 0,376 1,919 0,553 1,933 0,235 1,34 0,573 0,905 0,247 1,257 1,903 1,76 0,257 1,880 0,699 1,487 0,821 1,86 0,543 1,746 0,529 0,393 1,516 0,504 0,214 1,631 0,144 1,447 0,232 0,979 1,115 0,269 1,762 0,370 1,325 1,512 0,810 0,838 1,979 0,31 1,79 1,525 1,936 1,722 1,388 1,829 0,912 1,533 1,184 0,631 0,933 1,196 0,882 1,904 0,394 0,198 1,239 1,975 0,22 1,71 1,89 1,122 0,843 0,291 1,887 1,835 0,973 1,602 1,60 1,893 0,262 1,771 1,69 1,613 1,545 1,806 0,322 0,501 0,605 0,650 0,951 1,791 1,903 1,793 0,203 1,914 1,411 1,164 1,126 1,384 1,717 1,990 1,871 0,177 0,509 0,490 1,544 1,679 1,69 1,584 0,397 1,631 1,278 0,247 0,171 0,473 1,891 1,725 1,471 1,268 1,160 0,481 0,924 0,413 0,122 1,571 1,161 1,713 0,112 1,248 1,939 0,464 1,719 0,795 0,181 0,310 1,586 0,29 1,287 0,323 0,762 0,551 0,472 0,491 1,121 1,674 0,665 0,409 1,70 1,556 1,772 1,221 1,764 1,266 1,965 0,615 0,422 1,498 0,481 0,486 0,829 1,690 1,103 0,398 0,23 1,522 0,765 1,940 1,120 1,794 1,703 0,360 1,276 0,774 0,8 0,297 0,275 1,204 1,884 0,156 1,9 1,736 0,97 1,605 1,468 0,952 0,171 1,208 1,292 1,909 0,537 1,840 0,394 0,898 0,49 1,850 0,842 0,920 1,978 1,537 0,147 1,740 1,161 0,398 0,642 0,21 1,914 0,625 1,238 1,953 1,644 0,517 1,184 1,890 0,6 0,473 1,747 0,393 1,76 0,659 1,819 1,688 0,815 0,327 0,752 0,303 1,361 0,123 1,363 1,45 0,996 0,284 1,846 1,293 1,82 0,422 1,958 0,64 0,542 0,494 0,198 0,873 0,109 1,833 1,480 1,892 1,558 1,732 1,406 0,647 0,68 1,693 0,829 0,581 1,42 1,303 1,444 0,455 1,694 0,145 0,812 0,313 0,749 0,120 0,456 0,95 1,781 1,228 0,225 1,806 0,949 0,882 1,292 1,967 0,385 1,25 1,810 1,690 1,738 0,243 0,735 0,285 0,974 1,5 0,830 1,174 1,996 0,408 1,840 0,737 0,559 0,239 0,11 1,397 0,997 0,83 0,890 1,760 0,604 1,686 0,379 1,687 0,518 0,194 1,671 0,141 0,446 0,245 0,451 1,604 0,270 1,776 0,1 0,554 0,124 1,763 1,864 1,509 0,254 0,133 0,488 0,966 1,575 0,518 1,275 0,711 1,817 0,12 1,862 0,275 1,159 1,337 1,764 0,769 0,309 1,648 1,793 1,615 0,877 0,291 1,961 1,96 0,343 1,869 1,637 1,306 0,301 1,186 0,268 0,941 1,750 1,561 0,337 0,592 0,430 1,984 0,132 0,250 0,240 1,770 1,920 0,983 0,535 0,103 1,591 1,990 0,848 1,740 1,28 0,43 1,940 0,898 1,233 1,627 0,508 0,188 0,413 0,107 0,446 0,213 0,990 1,786 1,77 0,585 1,929 1,942 0,236 0,951 1,922 1,499 1,125 0,326 1,719 1,811 0,502 0,990 0,821 0,299 0,509 0,725 0,622 1,571 0,956 0,353 0,675 0,289 1,609 1,276 1,364 1,850 0,219 1,652 0,457 1,60 1,187 1,680 0,709 1,580 1,174 0,592 1,5 1,206 1,7 1,375 1,715 0,293 0,616 0,879 1,360 0,909 1,502 0,323 0,981 0,920 0,502 0,375 1,498 0,5 0,80 0,537 0,903 1,336 1,903 0,462 1,102 1,789 1,742 0,744 0,715 0,458 0,698 1,461 0,101 1,593 0,312 1,503 0,988 0,825 0,79 0,37 0,9 1,528 0,839 0,433 1,183 0,16 0,649 0,787 1,819 0,705 0,670 0,117 0,640 1,260 0,246 1,642 0,845 1,807 0,735 1,54 0,776 1,139 1,200 0,630 0,380 1,589 1,578 1,91 0,597 1,795 1,249 1,371 0,617 0,832 0,897 1,593 0,34 1,251 1,969 1,883 0,396 0,778 1,483 1,682 1,446 0,604 0,809 1,76 1,163 0,528 1,220 0,717 1,143 0,629 0,345 1,996 1,879 1,407 1,670 0,687 1,150 0,198 0,179 0,467 1,402 1,8 1,258 1,573 1,689 1,814 0,775 0,732 1,284 1,716 0,976 0,673 0,595 1,318 1,178 1,309 1,174 0,554 1,151 0,935 1,114 1,515 0,171 0,338 1,136 0,872 0,760 1,9 1,554 0,617 1,289 0,453 1,286 1,18 0,215 0,975 1,716 0,695 0,536 1,217 1,166 0,302 0,929 1,853 0,560 1,909 0,154 0,973 0,327 1,898 1,379 0,442 0,400 1,199 1,436 0,477 1,73 0,915 1,659 0,133 0,113 1,98 1,389 0,14 0,993 1,853 1,148 0,781 0,343 1,861 1,230 1,93 0,404 1,599 1,867 1,866 1,438 0,698 0,973 1,298 0,716 0,715 1,747 0,315 1,999 0,401 1,606 0,301 0,607 1,692 0,864 1,299 0,630 1,125 0,699 0,872 1,843 1,978 1,523 0,237 1,108 0,841 0,896 0,102 0,348 1,913 0,559 1,972 1,751 1,242 0,224 1,483 1,591 1,666 0,693 0,143 1,891 0,74 0,997 1,234 0,687 0,861 0,787 0,876 1,287 0,434 1,471 0,510 0,798 0,935 1,343 1,355 0,815 0,794 1,972 1,844 0,17 1,40 1,664 1,396 1,69 1,215 1,517 0,387 1,913 1,148 1,901 0,177 0,321 1,800 1,63 1,529 0,341 1,247 1,552 0,146 0,669 0,702 1,961 0,915 1,920 0,7 1,70 0,619 0,968 0,424 0,388 0,812 1,57 1,195 1,145 1,923 0,267 1,281 0,826 1,514 1,649 1,542 0,674 1,507 1,105 0,3 0,924 1,28 1,872 1,687 0,786 0,58 0,965 0,736 1,138 1,375 1,372 1,232 0,601 0,212 0,519 0,459 1,585 1,305 0,847 1,273 0,207 0,346 1,371 0,92 1,709 1,33 1,983 1,670 1,588 0,824 0,814 0,348 0,41 1,445 1,692 1,79 1,108 1,349 1,842 1,915 1,181 1,852 0,450 0,878 0,192 0,410 0,84 1,903 0,150 1,502 1,916 0,523 0,671 1,315 0,686 0,848 1,913 1,60 0,772 1,874 1,954 0,103 0,711 1,391 1,673 0,372 1,286 0,52 1,42 0,252 0,414 1,85 1,71 0,373 0,774 1,69 0,569 0,938 1,900 0,346 1,93 0,432 0,270 0,859 1,569 0,318 0,666 0,858 0,972 0,144 0,346 1,145 0,682 1,898 1,16 0,762 0,439 1,881 0,188 1,596 1,655 1,956 0,822 0,195 0,951 0,674 0,149 0,694 1,550 1,472 0,419 1,679 1,628 1,486 0,914 0,420 1,500 1,383 1,260 1,515 0,750 1,363 1,339 0,453 1,300 0,324 1,70 1,52 0,176 1,880 1,258 0,465 1,310 0,579 1,95 0,138 1,950 0,171 1,93 1,504 0,964 0,835 1,748 1,267 0,176 1,574 1,989 1,103 1,726 0,131 1,550 1,47 0,845 0,755 1,311 1,152 0,874 0,695 0,372 0,328 1,591 1,152 1,831 1,323 0,428 0,674 1,630 0,764 1,283 0,654 1,806 1,732 1,813 0,468 1,443 0,408 0,585 1,47 1,900 1,275 0,125 1,145 1,368 1,578 0,584 0,157 0,952 1,26 0,715 0,75 0,304 1,671 0,592 0,334 1,519 0,107 1,733 1,20 1,804 0,739 1,931 1,530 0,162 1,223 1,905 0,952 0,567 0,211 0,517 1,438 0,881 0,277 1,460 1,250 1,688 0,43 0,304 0,949 1,132 1,583 1,578 0,365 1,220 0,685 0,164 1,695 1,495 0,851 0,552 1,417 1,92 1,83 0,195 1,401 0,949 1,132 0,115 1,762 1,742 1,760 1,416 1,531 0,305 1,798 1,910 0,976 1,93 0,935 0,283 0,574 1,615 0,837 1,613 1,134 1,863 1,501 0,824 1,160 0,572 1,19 1,835 1,564 0,990 1,783 0,990 0,235 0,74 1,635 0,588 0,754 0,446 0,388 1,962 1,129 0,708 0,195 0,883 0,775 1,99 0,146 1,645 1,158 1,138 1,194 0,691 1,901 0,264 1,992 1,693 0,118 1,81 1,361 1,733 0,229 0,942 1,616 1,156 0,670 0,369 0,802 0,607 0,621 0,869 1,295 1,363 0,719 1,94 1,451 1,819 0,709 0,56 1,813 1,441 1,946 1,762 1,524 0,583 0,520 0,922 1,160 0,978 0,240 1,399 1,686 0,900 1,6 0,647 0,729 1,540 0,164 1,130 0,825 1,284 0,22 0,603 1,315 1,66 1,473 1,866 0,853 0,437 0,929 0,291 1,473 0,466 1,481 1,80 0,605 0,597 0,883 1,363 1,555 1,559 0,643 0,712 1,243 1,114 1,385 0,713 0,781 0,40 1,188 1,74 1,70 0,753 1,163 0,416 1,854 0,350 0,579 1,120 0,544 0,452 0,584 1,351 0,922 1,537 1,618 1,209 0,311 0,27 0,135 1,538 1,193 1,605 1,380 1,25 0,363 0,549 0,864 1,514 1,804 0,888 0,410 0,934 0,22 1,79 1,480 0,418 1,174 0,323 0,700 1,838 1,390 0,654 0,168 1,178 1,211 1,46 1,524 1,438 1,160 1,678 1,752 0,157 0,202 1,539 0,238 1,823 0,379 1,284 1,819 0,619 1,283 1,643 0,225 1,583 0,313 0,367 0,763 1,396 1,413 1,418 1,257 1,872 0,246 0,402 1,743 1,557 1,519 1,275 0,219 1,307 0,684 1,593 1,749 0,619 1,792 0,728 0,176 0,17 0,478 0,191 1,457 0,802 1,631 0,906 0,185 0,575 0,417 1,768 0,476 0,217 0,378 0,12 0,529 0,955 0,142 0,490 0,647 0,262 0,360 0,499 1,502 0,519 0,53 0,235 1,707 1,189 0,187 1,52 1,647 0,311 0,651 1,148 0,98 1,222 1,588 0,62 1,6 1,692 1,32 0,764 0,904 1,656 1,499 0,957 0,690 1,543 1,463 0,979 1,548 1,242 0,201 0,80 1,360 1,393 0,153 1,884 1,324 1,697 0,224 0,556 0,473 0,476 1,363 1,237 0,122 1,256 1,838 1,699 0,991 0,798 1,562 0,565 1,711 1,661 1,828 1,902 1,165 0,823 1,127 0,60 0,382 1,544 1,91 0,498 0,344 0,982 0,313 0,794 1,96 1,530 0,187 1,525 1,351 0,346 1,589 0,876 1,466 1,400 1,281 0,618 0,121 1,275 1,321 0,607 1,978 1,273 1,323 1,690 1,141 0,893 0,356 0,712 0,508 0,289 1,917 0,61 1,183 1,688 0,553 0,304 1,440 1,669 0,913 0,429 1,751 0,858 0,7 0,878 0,161 0,261 0,905 1,785 1,863 0,637 1,41 0,652 0,815 1,46 0,109 0,17 0,899 0,763 0,202 1,63 0,911 0,735 0,835 0,130 0,199 1,75 1,508 0,542 1,91 0,990 1,762 1,615 1,952 0,478 1,118 1,891 1,43 1,608 1,951 0,372 0,637 1,867 1,937 0,773 0,759 0,135 1,458 1,382 1,74 0,172 0,677 0,724 0,989 1,437 1,119 1,704 1,44 1,602 1,877 1,714 0,312 0,825 1,289 0,163 1,809 0,335 0,28 0,408 0,552 1,338 0,307 0,162 0,218 0,612 0,309 1,346 1,965 1,471 1,127 0,8 0,125 0,872 1,920 0,540 0,672 0,505 1,150 0,194 0,402 1,628 1,943 0,582 0,977 0,408 0,610 0,642 1,764 1,356 1,848 0,227 0,298 1,91 1,770 1,768 0,235 0,908 1,606 0,623 1,884 1,989 0,826 0,202 1,959 0,680 1,772 1,628 1,925 1,757 0,947 0,428 1,662 1,221 1,8 1,233 1,216 1,859 1,106 1,628 0,781 1,707 1,854 0,83 1,438 1,183 0,638 1,823 0,740 0,35 1,600 0,275 1,915 0,979 1,597 0,273 1,664 1,872 0,963 1,949 1,958 1,557 0,889 1,733 1,538 1,505 0,303 0,266 1,1 1,765 1,980 0,924 0,611 1,85 1,221 1,642 0,532 0,227 1,507 0,962 1,384 1,547 0,387 0,881 0,107 1,439 0,697 0,996 0,286 0,627 1,789 0,950 1,739 1,46 0,94 0,654 1,560 0,311 0,292 1,881 0,852 1,232 1,58 1,84 0,822 1,979 1,976 1,410 1,6 1,224 1,508 0,693 0,537 0,825 1,913 0,552 0,891 1,668 1,933 1,908 1,420 1,263 0,517 1,704 1,77 0,779 0,951 0,467 1,290 1,147 1,704 0,867 0,991 0,162 1,521 1,947 1,854 0,773 1,50 1,6 0,895 1,659 0,185 0,28 0,171 1,513 0,681 1,875 1,234 0,714 0,246 1,54 0,996 0,752 1,133 0,895 1,539 1,587 0,444 1,145 1,216 1,460 0,285 0,491 0,908 0,396 1,399 1,147 1,711 1,213 0,194 1,283 1,118 1,428 1,537 0,670 1,116 0,48 0,586 1,329 1,88 1,910 0,212 0,889 1,488 0,427 0,794 0,693 0,180 1,763 0,301 0,558 0,229 1,52 0,361 0,801 0,506 0,556 1,142 0,908 1,394 0,790 1,928 1,206 0,933 0,190 1,257 1,260 0,423 0,635 1,859 0,420 0,179 0,299 0,343 1,147 1,819 0,114 1,83 1,785 0,841 0,485 1,190 0,271 1,215 0,922 0,220 0,572 1,9 1,59 0,91 0,445 1,538 1,939 1,446 0,500 0,971 0,393 1,702 0,166 1,837 1,460 0,199 0,831 1,230 0,485 0,613 0,711 0,614 1,652 1,967 0,265 1,584 0,96 1,506 0,62 0,923 1,716 0,851 1,830 1,522 1,276 1,332 0,846 0,774 0,196 0,190 1,159 0,348 1,65 1,396 0,285 0,410 0,667 0,895 0,277 0,986 0,129 0,935 1,947 0,581 1,756 0,805 1,900 1,519 0,871 1,711 0,69 0,149 0,147 1,233 0,670 1,583 1,305 1,226 0,220 1,250 1,151 0,271 1,640 1,469 0,382 1,510 1,643 0,537 0,731 1,581 0,997 0,470 1,159 1,746 1,386 0,142 1,995 1,138 1,379 0,979 0,775 0,448 1,660 1,792 1,448 0,241 1,192 0,748 0,261 0,851 1,271 0,893 1,931 0,770 0,394 0,773 1,662 0,445 0,385 0,302 1,52 0,296 0,560 1,53 1,458 0,277 0,508 1,186 0,731 0,551 1,704 0,53 0,787 1,804 1,595 1,255 1,614 0,593 1,248 0,713 0,535 1,142 1,210 0,898 0,292 0,698 0,265 1,553 1,172 0,261 1,355 0,740 1,752 0,436 0,535 1,804 1,441 1,26 0,929 0,59 1,570 0,751 1,509 0,206 1,858 0,376 0,663 0,258 0,530 1,360 0,796 0,962 0,253 1,652 0,558 1,905 0,985 0,680 1,579 1,728 1,331 1,306 0,605 0,544 1,405 0,326 1,334 1,768 1,245 1,117 0,165 1,47 0,553 1,722 1,830 0,245 1,962 0,441 1,367 1,870 0,952 1,511 1,262 1,521 1,450 1,85 1,570 0,153 0,225 1,575 1,630 0,106 0,776 1,609 0,468 0,88 1,346 1,769 1,622 0,195 1,124 1,710 1,183 0,200 0,136 0,432 1,610 1,832 1,841 1,433 0,534 1,296 0,335 0,630 0,68 1,977 1,200 1,712 0,575 0,190 0,21 0,582 0,515 0,424 1,999 1,468 0,697 1,979 1,856 0,514 1,34 1,665 0,904 1,58 0,867 1,56 0,315 0,924 0,833 0,536 0,96 1,605 1,563 0,509 0,503 1,139 1,123 1,828 1,437 1,453 0,46 0,678 1,479 0,895 1,101 1,719 1,785 1,3 1,210 0,875 1,986 1,808 0,99 1,115 0,152 0,232 1,52 1,120 0,256 1,610 0,838 1,496 0,773 0,119 0,439 0,430 0,914 0,561 1,275 0,919 1,950 0,633 0,993 1,43 0,70 0,8 1,242 0,445 0,425 1,980 0,66 1,977 1,571 1,300 1,271 0,411 0,77 1,483 1,244 0,81 1,599 1,196 0,920 1,599 1,353 1,664 1,468 1,708 0,230 1,514 0,863 0,985 1,550 0,467 0,979 0,205 1,54 0,177 0,305 0,996 0,159 0,463 0,166 1,481 0,613 0,660 0,456 1,665 0,951 0,647 1,588 0,325 1,926 0,989 0,700 1,414 0,616 0,834 1,505 0,306 0,665 1,250 0,610 1,670 0,429 0,398 1,964 0,768 0,902 0,821 0,794 1,538 0,67 1,781 0,126 1,617 1,310 0,414 1,54 0,448 1,754 1,158 0,267 0,928 0,438 1,42 1,776 1,774 1,70 1,7 0,495 0,141 0,377 0,726 1,193 1,558 0,225 0,527 1,86 1,801 0,131 1,834 1,509 0,325 1,996 1,332 1,523 0,833 0,774 1,529 0,570 0,952 1,771 0,341 1,923 0,53 0,127 1,347 1,578 1,739 1,670 1,236 0,740 1,69 0,385 1,547 1,418 0,966 0,995 0,936 1,32 1,835 0,185 0,726 1,30 1,755 0,443 1,73 1,763 1,690 1,791 1,325 1,549 1,162 1,883 0,611 1,212 1,213 0,713 0,728 0,136 0,507 1,89 0,601 0,439 0,539 0,430 1,267 0,680 0,395 1,887 1,926 1,414 1,278 0,542 0,946 1,622 0,870 1,427 1,505 0,952 1,848 0,266 0,120 0,853 0,926 1,764 1,515 1,201 1,395 1,81 0,581 1,679 1,74 1,105 1,446 0,565 1,327 0,478 0,663 1,940 0,256 1,736 0,57 1,807 1,337 0,864 0,41 1,346 1,161 1,305 0,757 1,570 1,226 1,425 1,849 0,26 1,127 1,5 0,531 1,976 1,138 0,354 1,247 1,705 0,813 0,42 0,271 0,551 0,49 0,690 0,970 1,492 1,838 0,655 1,577 0,200 0,67 1,922 1,188 1,167 1,784 1,745 1,321 0,900 0,65 1,884 0,360 1,485 0,148 1,976 0,91 0,121 0,734 0,708 0,562 1,524 0,889 1,391 1,985 1,762 1,810 0,23 0,370 1,210 1,396 0,190 0,542 0,419 1,927 0,122 1,485 0,202 0,727 1,654 0,701 1,956 1,211 1,832 1,699 1,410 1,644 0,57 0,153 1,665 1,989 0,769 0,655 1,390 1,136 0,170 1,621 0,983 0,620 0,628 0,474 1,690 0,990 1,897 0,508 0,644 0,219 1,867 0,382 0,906 1,423 0,91 1,370 1,650 1,573 1,413 0,506 0,64 1,576 1,397 1,803 0,318 1,962 0,702 1,264 0,316 0,68 0,397 0,440 0,147 1,250 1,25 0,899 0,180 1,974 0,421 1,65 0,749 1,320 1,286 1,79 1,981 0,981 0,311 1,370 1,355 1,413 1,318 1,294 0,494 1,291 1,210 0,727 1,451 0,62 1,345 1,894 1,282 0,286 0,2 1,4 1,548 0,206 1,622 1,996 0,550 1,246 1,332 1,171 0,573 1,346 0,867 1,488 0,341 0,584 1,474 1,129 1,785 1,916 1,13 1,871 0,287 0,692 0,528 0,744 0,882 0,787 0,981 0,996 0,195 0,39 1,437 0,194 0,985 0,403 0,461 1,314 0,848 0,427 0,528 0,632 1,691 0,89 0,741 0,501 0,589 0,995 0,763 0,430 1,861 1,289 0,633 1,231 0,36 0,355 1,609 0,943 1,908 0,102 1,684 0,548 0,656 0,166 0,134 0,362 1,309 1,920 1,243 1,126 0,981 1,955 0,289 0,162 0,278 1,247 0,957 1,429 0,404 1,733 1,609 0,644 1,713 1,788 1,700 0,915 0,703 1,484 0,570 0,339 0,695 0,712 1,363 1,74 1,904 1,951 1,280 0,995 1,804 0,357 1,286 1,236 1,670 1,631 0,920 0,288 0,899 1,154 0,589 1,948 0,381 1,675 1,491 0,484 1,27 0,833 0,18 0,112 1,660 0,285 1,693 1,346 0,404 1,498 0,254 1,107 0,975 1,527 0,991 1,607 0,326 0,864 1,400 1,929 1,775 1,976 0,802 0,195 0,472 0,277 1,749 1,958 0,890 0,368 0,78 1,294 1,345 0,960 1,580 0,707 1,491 0,270 0,893 0,841 0,988 0,720 1,473 0,606 0,358 0,823 1,788 0,261 1,825 1,742 0,86 1,142 1,822 0,717 1,719 0,814 1,202 1,284 1,634 1,169 1,128 1,548 0,360 0,880 0,666 0,21 0,160 0,435 0,470 1,939 0,951 0,389 1,85 0,190 0,114 1,891 1,408 1,863 0,316 1,948 0,170 0,247 1,161 1,274 0,399 0,613 1,405 1,650 1,95 0,706 1,824 1,400 1,572 0,646 0,500 0,255 1,503 0,3 0,94 0,61 0,0 0,778 0,390 1,942 0,751 1,98 1,267 0,147 0,459 1,288 0,371 1,232 1,0 1,529 0,788 0,692 0,100 0,716 1,619 0,942 1,361 1,531 0,791 0,158 1,787 1,131 0,840 1,182 0,92 0,913 0,191 0,932 1,993 0,403 0,738 1,28 0,769 1,436 0,143 0,365 0,288 1,896 1,580 1,307 1,334 0,530 1,764 0,839 1,162 1,890 0,484 0,270 1,28 1,570 0,956 0,180 0,398 1,561 1,499 0,100 0,90 1,264 0,669 1,760 1,767 0,777 1,645 0,821 1,736 0,883 0,205 1,466 0,673 0,912 1,721 1,57 0,803 0,746 0,512 1,663 1,253 1,864 0,122 1,727 1,795 0,95 1,816 0,338 1,749 0,961 0,218 0,369 1,981 0,931 1,476 0,765 1,743 1,130 1,511 0,12 1,413 1,68 0,164 1,735 1,252 0,234 1,492 0,127 0,842 0,13 1,89 0,676 1,941 0,739 0,948 1,569 1,865 1,294 0,46 0,551 1,410 1,19 0,254 0,148 0,574 1,792 1,105 0,774 1,328 0,219 1,249 1,232 0,671 1,41 1,40 0,827 0,328 0,970 1,826 1,540 1,625 0,946 0,465 1,568 1,582 0,88 0,24 1,59 1,405 0,954 0,915 1,975 0,925 1,245 0,752 0,816 1,864 1,260 1,129 0,520 0,674 0,420 0,19 0,142 1,365 0,972 1,110 1,821 0,772 0,386 1,411 0,601 0,842 1,556 1,819 1,553 0,243 0,854 1,758 1,522 1,187 0,89 0,403 1,1 0,695 0,626 1,99 1,714 0,934 1,746 1,946 0,830 0,429 0,148 0,86 1,895 1,407 0,392 0,759 1,390 0,883 0,280 0,516 1,1 1,36 0,768 0,153 1,123 0,768 1,694 0,523 0,751 1,534 0,62 0,655 0,387 0,818 0,720 1,453 1,548 0,365 1,217 1,905 0,431 1,986 1,862 1,238 1,635 1,660 0,47 0,814 1,659 0,696 1,79 1,716 0,621 1,706 1,542 1,771 1,430 0,727 0,727 0,945 0,721 0,353 0,135 0,962 0,556 0,332 1,802 1,734 0,728 1,996 1,170 0,412 1,533 1,143 0,188 1,75 1,123 0,464 1,608 1,580 0,510 0,263 1,174 1,975 1,669 1,391 1,725 1,162 0,647 1,430 1,350 0,938 0,508 0,84 0,956 1,610 0,954 1,356 0,618 1,181 1,863 1,115 1,442 0,611 1,724 1,504 0,763 1,427 0,170 0,647 1,263 0,933 0,274 0,715 0,441 0,47 0,631 0,901 1,962 1,903 1,965 0,841 1,707 0,516 1,447 0,736 1,173 0,27 0,529 0,851 0,615 1,443 0,567 0,614 0,294 0,660 1,28 1,996 1,475 0,662 1,845 1,220 1,863 0,503 1,523 1,714 0,185 0,79 1,764 0,223 1,378 1,622 0,960 1,866 0,719 0,620 0,962 0,800 0,974 0,303 0,40 0,562 1,336 1,373 1,769 0,792 1,565 1,465 1,495 0,893 1,409 0,650 1,6 0,695 1,226 0,864 1,48 0,661 0,451 1,6 0,460 1,268 0,912 1,681 1,278 0,872 1,604 1,706 0,554 0,727 0,972 1,661 0,911 0,51 0,807 1,743 1,882 1,574 1,952 0,606 1,149 0,35 0,151 1,395 0,627 1,635 1,399 0,64 0,966 1,186 0,200 0,168 0,136 0,110 1,470 1,762 0,698 1,630 1,814 0,463 1,935 1,264 0,934 0,851 1,958 1,408 1,33 1,59 1,94 1,980 0,372 0,751 0,240 0,782 1,894 1,818 0,508 1,890 0,234 0,626 0,10 1,166 0,645 1,603 1,391 0,763 0,794 0,574 1,131 1,797 0,495 0,757 0,924 1,591 0,818 1,368 1,526 0,170 0,796 0,654 1,509 1,97 0,729 0,675 0,853 1,178 0,134 0,893 0,483 1,976 1,283 1,568 0,148 1,721 0,965 0,580 0,293 1,61 0,593 1,808 1,597 1,64 0,684 0,343 0,82 0,328 0,897 0,556 1,733 1,166 0,651 1,753 1,991 1,32 1,313 0,971 0,938 1,889 0,837 0,547 1,538 1,484 0,311 0,18 0,591 1,16 0,585 0,388 1,150 1,961 1,497 0,718 0,992 1,212 0,86 1,853 0,42 0,321 0,131 0,39 0,69 1,221 0,93 1,559 1,91 0,280 0,845 1,769 0,717 0,63 0,950 1,221 1,169 1,179 1,804 1,620 0,615 0,295 1,404 0,358 1,516 1,535 1,291 0,788 0,920 1,361 0,868 1,524 1,990 1,715 0,427 1,224 1,354 1,716 1,307 1,197 0,401 0,809 1,813 1,113 0,476 1,126 0,145 1,529 0,237 1,265 0,585 0,446 0,672 0,366 1,975 1,482 0,849 0,40 1,494 0,381 0,240 0,615 0,616 1,791 0,630 1,308 1,572 1,347 1,962 0,184 0,865 1,524 1,531 0,997 1,496 0,536 1,84 0,266 0,438 0,102 0,388 0,593 0,799 1,347 0,925 0,614 1,697 0,965 1,436 1,330 0,389 0,750 0,363 1,196 0,245 1,982 0,742 1,985 0,917 0,227 1,820 0,518 1,406 0,830 0,572 0,203 1,616 1,364 1,265 0,637 0,733 0,837 0,278 0,450 1,182 0,464 1,220 0,520 0,993 0,789 0,121 1,26 0,112 0,768 0,920 1,306 0,118 1,550 1,792 0,758 1,331 0,630 0,16 1,741 1,502 0,159 1,242 1,542 1,596 1,68 0,159 1,800 1,523 0,17 0,830 0,766 1,408 0,995 1,900 1,698 0,373 0,866 1,933 1,834 0,464 1,454 0,896 0,381 1,312 1,32 1,872 0,280 0,379 1,446 0,679 0,557 0,466 1,481 1,880 1,74 0,579 1,564 1,200 1,868 1,139 1,421 1,146 0,937 1,907 1,296 1,386 0,678 0,289 0,686 1,305 0,251 0,116 1,311 0,823 0,473 1,862 1,91 1,774 1,948 0,751 1,100 0,147 0,611 0,465 0,165 0,183 0,954 1,161 0,425 1,690 0,492 1,10 0,163 0,215 0,480 1,560 0,230 0,267 1,973 1,715 1,504 1,514 0,692 0,804 1,978 1,231 1,657 0,68 0,771 0,190 1,713 0,611 0,760 1,427 0,530 0,206 0,673 0,199 1,733 1,396 0,586 1,38 1,950 0,899 0,716 1,630 0,631 1,350 0,236 0,167 1,90 1,408 1,578 0,978 1,114 1,196 1,936 0,317 0,434 0,497 1,135 0,439 0,426 1,340 0,961 1,351 1,182 1,424 1,77 1,647 1,554 0,522 0,375 1,792 0,193 0,305 1,92 1,803 1,462 1,540 0,549 0,734 0,892 0,776 0,639 1,31 1,602 1,401 0,356 0,445 1,399 1,199 1,924 0,301 1,195 0,65 1,440 1,659 0,380 0,565 1,125 0,658 1,358 0,639 0,202 1,665 1,96 0,702 0,482 1,557 1,272 1,652 1,885 0,369 1,83 0,442 1,73 0,45 1,625 0,259 0,963 1,409 0,978 0,969 1,736 0,181 0,449 0,178 1,889 1,916 1,464 0,826 1,19 0,454 0,691 0,801 0,760 1,358 1,179 0,607 0,939 0,77 0,603 0,768 0,459 0,440 1,6 0,883 1,117 0,158 0,956 0,988 1,334 1,532 0,981 1,631 1,630 0,349 1,484 0,4 0,916 1,512 1,644 1,490 0,766 0,978 1,740 1,914 1,880 1,619 1,589 0,563 1,575 1,933 1,933 0,859 1,929 0,106 1,941 1,837 0,296 0,775 1,834 0,354 1,704 1,199 1,660 1,872 1,550 0,941 0,485 0,871 1,544 1,880 1,899 0,675 0,348 1,224 0,869 0,907 0,325 0,274 0,478 1,721 1,153 0,136 0,530 1,582 0,576 1,595 1,289 0,863 0,524 0,930 1,566 0,592 0,674 0,728 1,200 1,972 1,181 0,441 0,420 0,772 1,585 0,429 1,998 1,168 0,387 0,262 1,196 0,822 0,530 1,985 0,108 1,917 1,287 1,608 1,362 0,778 1,45 0,850 1,345 1,514 1,642 0,141 1,605 0,662 1,619 1,274 1,419 1,381 0,925 0,757 1,34 1,635 0,190 0,348 0,554 0,656 1,207 1,817 1,581 0,382 1,205 1,788 1,528 1,638 0,938 1,524 1,777 1,395 1,728 1,955 0,25 1,59 1,679 0,119 1,526 1,195 1,593 1,711 1,728 1,518 0,310 0,44 0,509 1,473 0,402 1,939 0,32 1,359 0,9 1,907 0,350 1,525 0,198 1,711 1,467 0,708 1,364 1,458 1,375 1,823 1,46 0,131 0,211 0,634 1,629 0,396 0,990 0,992 1,789 1,844 1,927 1,200 0,588 1,44 0,910 0,180 1,733 0,166 1,85 0,730 1,764 0,686 0,242 0,170 0,835 1,921 0,792 0,352 0,288 0,251 1,38 0,185 0,556 0,559 0,390 0,628 0,133 1,382 0,116 0,668 0,461 0,164 0,677 1,117 1,596 1,533 1,381 1,767 1,499 0,954 1,508 1,717 0,312 1,349 0,365 1,698 1,536 0,327 1,324 1,984 1,613 1,496 1,988 0,693 1,455 0,355 0,942 0,902 1,663 1,123 1,473 0,183 1,200 0,2 0,538 1,797 0,455 0,647 1,243 0,21 1,875 1,723 0,974 0,587 0,174 1,471 1,243 1,470 1,541 1,6 0,995 1,625 0,944 0,87 1,866 1,750 1,31 1,49 1,968 0,182 1,745 1,77 1,217 0,522 0,391 0,387 0,560 0,318 0,965 1,726 1,60 1,802 1,671 1,785 1,328 1,196 0,169 1,43 1,583 0,244 1,2 0,524 0,542 0,487 1,526 0,124 0,361 0,690 1,243 1,562 0,568 1,79 0,893 1,556 1,519 0,384 1,389 0,241 1,415 1,396 0,589 0,9 1,622 1,185 1,769 0,379 1,104 0,159 0,268 1,887 0,902 1,420 1,996 0,70 0,644 1,973 1,471 1,548 1,968 0,245 0,205 1,725 1,227 0,222 1,123 1,121 1,849 1,178 0,373 1,499 0,655 1,386 1,49 1,212 1,528 1,22 1,420 0,542 0,65 1,965 1,828 1,486 0,393 1,156 1,502 0,628 1,700 1,176 1,220 1,436 1,175 1,864 1,274 0,538 1,700 1,310 1,579 1,302 0,210 1,979 0,812 1,430 1,300 1,292 0,872 1,920 1,691 0,339 1,701 0,149 0,997 1,843 1,975 0,805 0,386 0,754 1,378 0,428 0,848 0,161 1,994 0,791 0,847 1,653 0,272 0,305 1,494 0,446 1,978 0,835 0,674 0,833 0,888 0,887 0,20 0,562 0,32 0,367 1,925 0,390 1,520 1,154 0,3 0,942 0,618 0,41 0,260 1,605 1,831 1,837 0,754 1,688 0,200 0,519 0,407 0,375 0,653 0,920 1,623 1,190 0,765 0,910 1,623 1,513 0,552 1,117 0,657 0,856 0,971 0,610 1,573 1,776 0,375 1,673 1,661 1,909 0,99 1,562 1,182 0,796 1,463 0,596 0,348 0,114 0,287 1,195 1,568 1,888 1,589 0,180 0,123 0,739 0,649 0,66 0,125 1,562 0,178 1,685 0,423 0,567 1,365 1,899 1,187 0,673 0,151 1,110 1,555 1,34 0,666 1,307 1,762 0,965 0,921 1,872 1,58 0,220 0,277 0,980 1,114 0,25 0,795 1,575 1,343 1,110 0,36 1,943 0,829 1,792 0,371 1,11 0,899 1,501 1,223 1,478 0,199 1,915 1,827 0,238 0,222 0,410 1,761 0,21 1,932 1,928 0,726 0,46 1,482 0,333 1,828 0,651 0,483 1,369 0,242 0,775 1,589 0,201 0,281 0,876 1,861 0,428 1,663 0,538 1,204 1,171 1,193 1,600 0,524 1,176 0,514 1,241 0,43 1,586 1,757 0,221 1,380 0,853 0,757 1,286 1,87 0,384 1,110 0,414 0,830 1,935 1,478 0,816 1,752 0,786 0,431 0,989 0,866 0,914 0,422 0,99 0,185 0,131 0,554 1,710 0,705 1,270 1,817 0,552 1,185 0,321 0,968 0,358 0,102 1,217 1,859 0,101 1,347 1,721 0,665 1,101 1,419 1,603 0,446 1,627 0,983 1,919 1,530 1,687 0,518 0,165 0,635 1,558 1,457 0,452 0,877 1,87 1,131 1,908 0,911 0,446 0,190 1,368 1,462 1,83 0,25 1,608 0,981 1,520 0,883 0,920 0,427 1,149 1,315 1,676 0,746 0,820 0,279 0,289 1,301 0,929 0,0 1,793 1,508 0,521 0,135 1,144 0,406 0,431 1,925 1,136 1,400 0,113 1,316 0,228 0,570 1,526 1,998 1,166 1,415 1,872 1,125 0,484 1,845 0,570 0,717 1,660 1,721 0,972 0,695 0,880 1,163 1,228 1,274 1,977 1,656 0,300 1,318 1,186 0,432 1,884 0,746 0,141 1,310 1,531 0,685 1,164 1,600 1,193 0,71 0,90 1,722 0,780 1,846 1,683 0,728 0,514 1,601 0,219 1,91 1,816 1,446 0,474 0,488 1,832 1,824 1,681 0,437 1,534 1,110 0,464 1,183 0,565 1,833 1,286 1,590 1,369 0,136 1,614 0,693 0,841 0,169 0,357 1,266 1,465 1,676 1,663 0,660 1,192 1,176 0,366 0,761 0,198 1,12 0,643 0,685 0,509 1,698 0,110 0,414 1,157 0,986 0,372 1,914 1,113 0,348 1,599 1,593 0,544 0,274 1,657 0,963 1,831 0,875 1,768 1,541 0,831 1,164 0,873 0,584 1,607 0,37 0,36 1,111 0,12 1,57 0,380 1,685 1,563 0,100 0,430 0,566 0,760 1,303 0,392 0,507 1,266 0,962 0,710 0,662 0,673 1,928 1,300 1,313 0,187 0,405 0,871 0,267 0,640 0,879 0,413 1,558 0,194 0,56 1,954 1,850 0,742 1,239 0,328 0,961 0,530 0,774 0,119 1,319 1,441 1,731 1,2 1,738 0,64 1,676 1,881 1,244 0,288 1,258 1,58 0,682 1,396 0,454 1,427 0,251 1,235 0,995 1,818 1,286 0,540 0,557 1,744 1,713 0,469 1,821 0,113 0,64 0,419 0,976 0,872 1,421 0,86 0,750 0,722 1,220 1,454 0,658 0,543 1,999 0,196 1,231 1,740 1,830 1,732 0,735 0,413 1,139 0,412 0,425 0,42 1,938 0,566 0,808 0,882 1,754 1,888 0,552 0,676 0,215 0,881 1,96 0,139 0,613 1,411 1,610 1,392 0,958 0,343 0,347 0,857 1,45 1,446 0,76 1,887 0,144 0,516 1,676 1,514 1,831 0,529 1,782 0,134 0,775 0,267 0,787 1,542 0,415 0,466 0,296 0,352 0,741 0,254 1,455 1,673 0,450 0,239 0,513 0,723 0,197 0,964 0,21 1,581 1,93 0,267 0,771 0,382 0,259 0,275 0,311 0,130 0,199 1,116 0,255 1,141 1,919 0,604 0,428 0,998 1,407 1,44 1,392 0,49 0,219 0,592 0,637 1,85 1,62 1,108 1,84 1,489 1,178 0,434 1,984 1,768 0,586 1,273 1,953 0,109 1,674 1,730 0,582 1,25 0,695 0,805 1,313 1,363 0,510 1,287 1,141 0,888 1,608 0,913 1,905 0,515 1,85 1,462 0,122 1,333 0,948 0,558 1,315 0,771 0,771 1,4 0,753 1,231 0,918 0,602 1,98 1,293 0,618 1,241 0,955 0,770 0,145 1,40 1,593 1,438 0,815 0,168 1,153 0,317 0,855 0,71 0,793 1,764 0,7 1,456 0,574 1,39 1,520 1,400 0,548 0,73 1,451 1,851 0,293 1,586 0,384 1,683 1,568 0,757 0,374 1,253 1,629 1,300 0,201 0,248 0,176 0,906 0,378 1,741 1,851 1,450 0,452 1,990 0,64 1,221 0,306 0,115 1,778 0,656 0,291 1,372 1,468 1,545 1,530 0,578 0,257 0,345 0,966 0,22 1,2 1,822 1,456 0,852 1,711 1,842 1,864 0,929 0,768 1,46 0,324 0,641 0,29 0,947 0,958 0,403 0,129 1,951 1,317 0,167 1,315 0,479 0,163 1,169 1,229 0,589 0,302 1,2 0,78 0,352 0,941 1,905 1,122 0,179 0,97 1,794 0,662 1,295 1,208 0,365 1,819 1,948 0,152 0,518 1,305 0,908 1,783 1,411 1,650 1,548 0,641 1,373 1,865 1,257 0,266 1,253 0,139 1,522 0,915 1,879 1,210 0,745 0,742 1,70 1,459 0,160 1,463 1,320 1,522 1,923 0,892 0,130 0,401 0,140 1,382 0,849 1,83 1,113 1,784 1,647 1,648 1,343 0,900 1,132 0,722 0,388 1,398 1,101 0,989 1,977 1,425 0,228 0,330 0,168 1,270 1,242 0,694 0,33 0,507 0,528 0,716 0,965 1,720 0,80 1,342 1,387 0,723 0,459 0,714 1,305 1,211 1,408 1,924 1,638 0,834 1,488 1,964 1,265 1,937 1,127 0,327 1,909 1,826 1,686 1,487 1,720 1,667 0,93 0,758 0,732 0,951 1,507 0,813 0,299 1,834 1,638 0,648 0,615 1,861 1,751 1,649 0,73 0,930 1,513 1,379 1,300 0,200 0,704 0,239 1,636 1,768 0,529 0,500 0,181 0,483 0,990 0,977 1,134 1,339 0,312 0,48 0,812 0,928 0,241 0,238 1,274 1,113 1,139 1,648 0,566 0,313 1,779 1,942 1,996 0,234 0,442 1,503 1,620 1,163 0,76 1,821 0,694 0,908 1,851 1,467 0,495 1,133 1,368 0,63 0,500 0,221 0,432 1,393 1,899 0,420 0,203 1,536 1,363 1,555 1,21 0,309 1,752 0,196 1,851 0,935 0,443 1,394 0,433 1,58 0,108 1,403 1,451 1,91 0,932 1,485 0,561 0,406 0,820 0,585 0,589 0,213 0,760 1,285 1,347 1,994 1,705 0,216 0,87 0,90 1,534 1,44 0,641 0,232 0,325 0,125 0,710 0,818 0,465 1,458 1,604 0,768 1,529 1,131 1,260 1,900 0,725 0,654 1,969 1,601 0,794 0,803 0,294 1,705 0,341 1,612 0,315 1,6 1,454 0,994 0,216 1,175 0,619 1,651 0,266 0,512 1,901 1,841 1,598 1,342 1,979 1,741 1,939 0,68 0,978 1,628 1,319 0,933 1,919 0,172 1,572 0,546 1,77 0,251 0,542 1,633 1,352 1,332 1,154 0,287 0,796 0,694 0,725 1,451 0,18 0,480 1,125 0,777 1,299 0,264 0,338 0,985 1,538 0,155 0,433 0,280 0,607 1,139 1,428 1,299 0,942 1,991 1,434 0,281 1,850 0,932 0,757 0,443 0,160 1,556 1,641 1,636 0,601 0,922 0,77 1,350 1,756 0,608 1,520 0,967 0,175 1,921 1,490 0,543 1,844 1,586 1,369 0,675 1,475 1,784 0,135 1,72 0,330 0,207 0,95 1,132 0,479 1,460 1,806 0,428 1,795 1,178 1,28 1,926 1,745 1,256 1,586 0,991 1,119 0,598 0,160 1,678 0,579 0,694 0,111 1,779 1,847 1,513 0,330 1,555 1,903 0,809 1,996 1,859 1,203 1,948 0,713 0,983 1,723 0,744 0,235 0,957 0,203 0,940 1,255 0,357 1,834 1,370 1,624 0,926 0,203 1,916 0,130 0,237 1,495 1,169 1,649 0,283 0,632 1,287 0,642 0,9 0,673 1,758 1,632 1,672 1,463 0,506 1,654 1,850 0,88 0,170 0,731 1,258 0,304 1,24 1,362 0,312 1,374 1,141 1,123 0,172 1,335 1,658 0,527 1,53 1,585 0,65 1,874 0,783 0,397 1,958 0,988 0,628 0,705 1,458 0,885 0,247 1,599 0,322 0,472 0,241 0,307 0,174 0,717 1,763 1,377 1,524 1,521 0,218 1,91 0,488 0,474 1,354 0,899 1,141 0,201 0,949 0,848 1,101 1,255 0,245 1,142 1,646 0,209 0,596 0,665 0,109 0,586 0,34 0,44 0,486 0,584 1,342 1,569 1,966 1,158 1,108 0,971 0,832 0,897 0,158 1,918 0,746 0,102 0,192 1,460 0,966 0,268 1,708 0,775 0,707 1,465 1,670 1,840 0,337 0,888 1,676 1,610 0,480 0,966 1,945 1,346 1,481 0,973 0,754 1,84 1,38 1,102 1,876 0,955 0,227 1,24 0,221 1,428 0,378 1,764 0,804 1,176 1,452 0,261 1,334 1,503 0,236 1,913 0,979 1,91 0,106 1,425 1,161 0,750 0,472 0,926 1,914 0,165 0,70 0,804 0,131 0,702 1,625 0,356 0,967 1,895 0,728 1,703 1,973 0,518 1,372 0,856 0,574 1,392 0,735 1,287 0,211 0,992 1,266 0,437 1,140 0,724 1,353 0,85 1,925 1,170 0,257 0,994 1,739 0,118 1,106 1,921 1,419 0,397 1,406 0,925 1,255 1,672 0,594 0,352 1,192 0,238 0,288 0,975 0,730 1,647 0,756 0,251 1,100 1,612 1,696 1,896 0,742 0,873 0,392 0,32 1,568 1,658 1,387 1,387 0,41 0,263 1,511 0,52 0,514 0,475 0,667 0,365 0,368 0,763 1,798 1,573 1,544 1,264 1,306 1,406 0,214 1,929 1,79 1,565 1,851 1,401 0,798 1,347 0,101 1,129 0,719 0,715 1,617 1,938 1,179 0,13 0,545 1,155 0,83 0,44 0,888 0,131 0,127 1,532 1,536 1,800 1,569 0,30 0,481 0,860 0,442 0,526 0,866 0,658 1,359 0,338 0,130 0,409 1,48 1,952 1,30 0,956 1,938 0,65 0,968 1,990 0,731 0,413 0,13 0,65 0,83 0,750 1,994 1,915 1,160 1,257 1,278 1,96 1,495 1,381 1,620 1,123 0,596 1,54 1,895 1,198 1,511 0,821 0,684 0,644 0,552 1,478 0,351 0,197 1,259 0,963 0,261 0,154 0,817 0,452 0,923 0,309 0,365 0,983 1,869 0,739 1,507 0,532 0,745 0,996 1,295 0,890 0,399 1,644 0,269 0,75 0,590 1,280 1,865 0,248 1,349 0,979 0,377 1,231 1,240 1,167 1,187 1,297 0,957 0,911 0,139 0,241 0,712 0,700 0,396 0,129 1,742 1,72 0,276 0,392 0,935 1,302 0,901 1,360 1,200 1,851 1,406 1,305 1,561 1,297 1,345 0,58 0,465 0,122 1,304 1,825 0,183 1,629 0,173 0,433 1,464 0,411 0,191 1,556 1,979 1,338 1,158 1,454 0,75 1,99 0,314 0,191 0,301 1,226 0,720 1,778 1,988 0,661 1,47 0,404 0,31 0,451 1,364 1,924 1,241 1,371 1,784 1,604 1,242 0,122 1,517 0,624 1,614 1,46 1,778 1,517 1,372 0,656 1,89 1,327 0,314 0,958 0,89 1,824 0,396 1,143 1,72 0,172 1,385 1,445 1,695 1,583 0,200 1,869 0,213 0,393 0,18 0,487 0,877 1,257 0,222 1,769 1,577 1,566 0,834 1,823 0,458 1,990 1,55 1,819 1,835 1,355 0,526 0,818 0,673 1,81 1,755 1,889 0,256 1,961 1,617 0,485 1,947 1,997 0,97 1,752 0,686 1,327 1,617 1,245 0,625 1,545 1,87 0,641 0,995 0,24 1,935 1,204 0,543 0,356 0,272 1,993 0,369 0,435 0,23 1,356 1,542 0,1 0,286 0,377 0,858 0,806 1,945 0,437 0,358 1,663 1,277 1,840 0,799 0,704 0,916 0,119 0,45 0,484 0,192 0,659 0,51 0,515 0,91 0,141 0,637 1,378 1,558 1,197 0,254 1,20 1,782 0,990 0,625 1,86 1,760 0,937 1,271 0,115 1,114 0,361 0,219 0,197 0,875 1,663 0,278 0,335 0,241 1,188 1,393 0,990 0,491 1,484 0,911 1,75 1,26 0,465 1,574 1,764 0,390 0,154 1,463 0,140 1,506 1,252 1,11 1,50 0,202 0,211 0,235 0,816 0,436 1,145 0,538 0,365 1,215 1,301 0,965 1,532 1,335 0,489 1,70 0,988 1,696 1,910 0,133 0,820 1,70 1,924 1,40 0,225 1,488 1,366 0,99 0,748 1,447 0,354 0,559 0,566 1,235 0,586 1,482 1,414 0,730 1,229 0,916 1,636 1,233 1,409 1,387 0,246 0,238 0,418 0,499 1,139 0,560 0,942 0,171 0,459 1,408 0,317 1,596 1,545 1,566 1,682 0,154 0,311 0,270 1,481 1,15 1,400 1,936 0,319 1,948 0,823 1,554 1,663 0,68 0,645 1,431 0,429 1,538 0,157 0,180 0,251 1,941 0,253 1,207 1,811 1,212 1,457 1,930 0,754 0,1 0,282 1,279 0,766 1,492 0,254 1,234 1,816 1,782 1,217 0,942 1,559 1,209 1,341 1,712 0,534 1,633 1,507 0,729 1,384 1,3 1,980 1,881 1,810 1,101 1,576 0,23 1,140 1,589 1,157 0,216 1,453 1,234 0,377 1,92 1,584 0,224 1,104 0,321 1,650 1,757 1,65 0,114 1,330 1,154 0,921 0,122 0,962 1,822 0,827 1,620 0,477 1,649 1,821 0,114 1,645 0,924 1,65 1,409 0,409 0,223 0,111 1,319 1,461 0,639 0,147 0,404 0,640 1,648 1,321 0,631 1,58 0,587 0,743 0,313 1,77 1,89 0,766 0,746 1,752 0,376 1,259 1,863 0,639 0,21 1,28 0,772 0,271 0,99 0,523 0,300 0,929 0,297 0,137 0,265 0,632 0,477 0,760 0,253 0,476 0,68 1,908 0,3 1,606 1,245 1,313 1,644 0,63 1,743 1,563 0,526 0,954 1,130 0,9 0,587 1,824 1,479 0,334 1,517 1,271 1,258 1,130 0,644 0,717 1,886 0,339 0,0 1,951 1,731 1,689 0,672 0,44 1,235 1,414 0,261 1,886 1,717 0,515 0,913 1,537 1,797 1,873 1,436 1,127 1,105 1,379 0,955 0,684 0,211 1,60 0,631 1,871 1,410 1,602 0,586 1,867 1,520 1,160 1,122 1,574 1,965 0,446 1,186 1,641 1,92 0,476 1,487 1,55 0,279 0,611 0,490 0,385 0,881 0,571 0,949 1,264 0,401 1,307 0,835 0,109 1,456 0,782 0,737 1,731 0,530 0,219 0,882 1,623 0,264 0,769 0,331 1,726 0,514 0,314 0,937 0,180 0,498 1,741 1,688 0,678 1,949 1,599 1,519 1,102 1,212 1,527 0,921 0,175 1,603 0,815 1,345 1,165 0,22 1,552 1,82 0,691 1,258 0,696 1,793 0,620 0,833 1,458 0,686 0,531 1,125 1,814 1,639 0,955 0,952 1,974 1,852 1,609 0,694 1,554 0,693 0,572 1,105 1,792 0,710 0,529 1,951 0,328 0,0 0,169 1,880 1,418 0,742 1,318 0,54 1,379 0,525 0,569 0,760 1,981 1,571 1,275 0,776 0,301 0,926 0,997 0,44 1,161 0,11 0,15 1,404 1,919 1,896 1,561 1,918 0,839 1,167 1,721 0,115 1,339 0,360 0,777 0,929 1,929 0,473 0,548 1,85 0,198 0,664 0,541 1,965 1,46 0,597 1,947 0,127 1,264 1,95 0,203 0,790 1,189 1,665 0,968 1,764 0,526 0,479 1,350 1,333 1,101 0,690 0,780 1,971 0,497 1,747 0,629 0,288 1,957 0,971 1,334 0,862 0,457 0,406 0,49 1,489 0,856 0,751 0,946 0,807 0,707 0,753 1,243 1,502 1,326 0,180 1,169 1,179 1,606 1,821 1,341 1,31 0,448 1,502 0,455 1,906 1,885 1,133 0,71 0,224 1,616 0,201 1,217 0,737 1,4 1,857 1,851 1,437 0,890 1,896 0,793 0,683 0,808 0,706 0,148 0,268 0,82 1,703 1,730 1,891 0,836 1,533 1,329 1,990 1,54 1,823 0,291 0,529 0,542 0,604 0,791 1,19 1,304 1,124 0,340 1,644 0,290 0,117 0,598 0,240 0,854 1,856 0,14 0,943 1,989 1,489 0,202 0,820 0,326 0,457 0,418 0,633 0,982 1,92 1,888 0,17 0,469 0,614 1,884 0,885 0,915 1,383 1,123 1,132 1,877 0,422 0,126 0,39 1,382 0,770 1,912 1,413 1,607 0,590 1,562 1,501 1,666 0,775 1,485 0,290 0,89 0,692 1,984 1,145 0,511 1,409 0,54 0,611 1,155 0,358 1,925 0,158 1,921 0,967 1,847 0,745 1,327 1,933 0,326 0,307 0,636 1,516 0,369 0,100 0,28 1,597 0,181 0,889 1,812 1,538 1,675 1,317 1,218 0,268 0,53 0,703 1,185 0,56 1,566 1,141 1,522 0,674 0,663 0,296 0,297 0,905 1,720 1,768 1,434 0,349 0,992 0,335 0,249 1,795 1,445 1,419 0,569 1,368 0,564 0,419 0,350 1,946 1,965 0,845 1,1 0,492 1,984 0,775 1,378 0,577 0,19 1,739 0,647 1,753 1,548 1,282 0,483 1,647 0,89 1,91 0,120 1,506 1,522 1,230 1,834 0,199 0,589 0,945 0,127 1,224 1,754 0,821 1,180 1,814 0,618 1,192 0,624 0,867 1,610 0,825 0,831 0,22 1,743 0,428 1,957 0,24 1,4 1,895 1,391 0,556 0,102 1,828 1,7 0,533 0,50 1,662 1,973 1,689 0,686 1,871 1,157 1,475 0,125 1,788 0,583 1,346 1,587 0,12 1,855 0,337 0,294 0,729 1,692 1,313 0,795 0,265 1,69 0,664 1,762 0,804 0,974 0,316 1,269 0,575 0,412 1,961 1,420 0,923 1,407 0,814 0,91 1,984 1,275 0,942 0,763 1,113 1,272 0,711 0,977 1,888 1,658 1,664 0,593 1,650 0,910 0,391 0,76 0,648 0,220 0,135 1,266 1,467 0,498 0,662 1,732 0,144 1,356 0,175 1,621 1,353 0,633 0,494 0,21 1,687 1,265 0,178 1,319 1,1 1,113 1,983 1,102 1,792 0,954 0,35 0,216 0,941 1,999 0,586 1,287 0,810 0,418 0,900 0,696 0,714 0,204 0,661 1,662 1,658 1,41 1,907 0,960 0,688 0,248 1,555 1,188 0,188 1,745 1,589 1,119 1,272 1,524 1,871 1,385 0,25 1,451 1,501 0,231 1,317 1,93 0,367 1,306 1,565 1,171 0,684 1,765 0,62 1,308 0,53 0,84 0,395 1,556 0,696 1,149 0,734 0,23 1,671 1,580 0,608 1,637 1,989 1,825 0,126 1,749 1,647 1,530 1,656 1,53 1,708 1,421 1,124 1,715 1,461 1,550 1,143 0,349 1,548 1,307 0,634 0,124 1,60 0,287 0,739 0,377 1,854 0,397 0,764 0,400 0,645 0,54 0,984 1,902 1,866 0,569 0,199 1,602 1,895 0,862 0,592 1,779 0,972 1,972 1,223 0,983 1,420 1,167 0,496 0,838 0,281 0,178 1,714 1,227 1,966 1,805 0,644 0,281 0,38 0,125 0,439 1,877 0,797 0,349 1,190 0,521 0,158 0,695 1,232 1,372 1,652 1,194 0,857 0,233 0,405 1,979 1,802 1,596 0,281 1,556 0,248 1,474 0,639 1,494 0,668 1,643 1,551 1,608 1,708 0,542 0,727 0,499 1,258 1,128 0,458 0,288 1,888 0,866 1,934 0,57 0,707 0,33 1,824 0,775 0,332 1,476 1,533 0,400 0,651 1,491 0,543 0,929 1,962 1,831 1,499 1,764 1,888 1,369 1,357 1,677 1,979 0,711 1,852 1,986 1,901 1,28 0,221 0,936 1,403 0,628 1,441 0,704 1,454 1,858 0,343 0,417 1,880 1,978 1,37 0,770 1,765 0,971 1,636 1,190 1,955 0,753 1,581 0,464 0,581 1,419 1,40 0,567 1,971 1,859 0,315 1,328 1,230 0,853 0,643 0,904 1,951 0,308 0,220 0,625 1,547 0,761 1,819 1,397 0,629 0,720 0,603 1,481 1,97 0,152 0,100 0,519 0,501 0,800 1,650 0,499 1,523 0,879 0,686 0,768 0,574 1,550 1,509 1,195 1,976 0,212 0,734 1,476 1,342 0,750 1,583 0,937 1,563 1,418 1,315 0,519 1,819 1,524 1,791 1,135 0,857 0,797 0,833 1,107 1,703 1,861 0,557 0,651 1,626 1,634 0,628 1,356 0,936 0,27 1,403 0,836 1,195 0,230 1,322 0,254 1,673 1,197 1,606 0,487 0,368 1,884 0,871 0,289 1,135 0,73 1,62 1,701 1,430 1,466 0,410 1,34 0,826 0,451 1,224 0,897 1,992 1,870 1,449 1,402 0,885 1,487 1,874 0,492 1,881 1,776 1,386 1,139 0,558 0,671 0,454 0,166 0,375 1,278 1,59 1,298 0,593 0,222 0,232 0,676 1,320 0,227 1,12 0,585 0,563 0,705 0,456 0,294 0,171 0,983 1,925 1,756 0,741 1,818 0,423 0,658 1,772 1,695 0,899 0,840 0,179 1,992 0,230 1,157 1,471 1,356 0,669 1,817 1,931 1,356 0,861 1,107 0,377 0,785 1,195 0,544 0,117 1,527 0,965 0,849 1,140 1,102 0,478 0,639 1,614 1,384 0,126 0,220 1,192 0,212 1,276 1,187 1,103 1,767 1,231 1,166 1,329 0,940 1,408 0,82 0,367 0,945 1,379 1,901 1,420 0,536 1,102 1,705 0,323 0,334 0,385 1,237 1,161 0,135 1,476 0,671 0,393 1,831 0,949 1,971 0,589 0,470 1,283 0,1 0,292 0,858 1,717 1,680 1,166 0,968 0,616 1,698 0,347 1,194 1,502 1,462 1,946 0,665 1,211 1,885 1,438 0,311 1,846 0,687 0,390 1,646 1,222 1,612 1,829 1,501 1,864 0,184 0,391 0,748 0,180 1,551 0,599 0,174 0,436 0,823 0,811 0,777 1,569 1,954 0,221 0,789 0,270 1,299 0,304 1,718 0,622 0,39 0,479 0,215 0,810 1,658 0,83 1,208 1,393 1,488 0,581 0,73 0,502 0,963 0,916 1,314 0,511 0,531 1,515 1,226 1,672 0,816 0,801 1,66 0,471 1,202 0,51 0,159 0,566 0,326 1,537 1,777 1,682 1,254 0,341 0,169 0,600 1,800 1,282 0,593 1,898 1,423 1,88 0,114 0,223 0,836 0,919 0,501 0,742 1,810 1,312 1,536 1,323 0,19 1,633 1,120 1,477 1,332 1,31 0,492 1,610 0,654 0,999 0,951 1,78 1,90 1,264 1,826 1,167 1,627 0,562 1,274 0,867 0,604 1,552 1,862 1,660 0,297 0,508 0,456 0,275 0,782 1,155 0,443 0,933 0,290 1,33 0,292 1,415 1,680 0,556 0,6 0,589 1,192 0,662 1,315 0,662 1,551 1,964 0,604 1,740 1,364 0,397 0,495 1,100 0,202 0,786 0,196 0,162 0,432 0,278 1,342 0,566 1,136 0,377 0,940 0,377 1,650 0,127 0,601 1,763 1,802 0,908 1,851 1,63 1,217 0,442 1,911 0,659 1,957 1,129 0,482 0,96 0,272 1,534 0,998 0,926 1,485 0,135 1,206 0,64 0,404 1,77 0,234 0,249 1,953 1,220 1,47 0,795 0,610 1,712 0,842 0,524 0,632 0,340 1,144 0,34 0,246 1,138 1,800 1,50 1,417 0,570 0,207 0,744 0,621 1,516 1,893 1,516 1,552 0,720 0,800 1,552 1,663 0,616 1,353 0,661 0,571 1,756 1,369 1,73 1,482 1,608 1,308 0,464 0,237 1,3 0,326 0,778 1,724 1,181 0,750 1,303 1,328 1,655 1,858 0,868 0,655 1,640 0,441 0,14 1,964 1,893 1,32 1,238 1,70 1,19 0,671 1,108 0,211 0,658 0,106 0,69 1,474 1,533 0,819 1,218 1,524 0,432 1,703 1,258 1,776 1,86 0,866 1,746 1,315 1,851 1,293 0,889 1,799 0,813 0,652 0,76 0,13 0,113 0,407 0,111 1,845 1,343 1,484 0,703 0,938 1,131 0,705 1,168 1,670 0,709 0,232 1,880 0,734 1,152 0,877 1,939 1,390 1,772 0,785 0,525 1,741 0,318 1,640 1,862 1,880 0,145 0,589 0,579 0,722 0,935 1,357 0,480 1,37 1,969 0,574 1,867 1,743 0,887 0,136 1,740 0,798 1,245 1,373 0,927 0,671 0,726 1,594 1,207 1,804 1,896 0,291 1,474 1,564 0,481 0,482 0,821 1,677 1,574 1,257 1,801 0,163 0,917 0,825 1,567 0,861 0,909 0,255 1,889 0,224 1,12 0,73 1,394 1,14 0,178 0,265 1,97 0,106 0,379 0,88 0,131 0,815 1,374 1,782 1,337 1,569 0,828 1,800 1,853 0,232 0,636 1,733 0,301 0,138 0,754 0,220 0,291 1,179 1,977 0,990 0,929 1,819 0,285 0,124 1,605 1,188 0,297 1,181 1,434 0,542 0,171 0,997 0,268 1,476 0,254 1,701 1,604 0,511 0,468 1,505 0,512 1,659 0,446 1,203 1,764 1,115 1,449 1,111 0,502 0,856 1,57 1,241 0,741 0,482 1,946 0,779 1,342 0,90 1,670 1,290 0,803 0,197 1,273 1,762 1,448 1,86 0,261 0,2 0,519 1,764 1,75 1,394 0,42 0,549 1,412 1,460 1,288 0,45 0,289 1,488 1,187 1,104 1,115 0,817 0,973 0,400 0,106 1,101 1,479 1,937 1,287 1,885 1,292 1,220 0,238 1,253 1,387 1,649 1,686 1,908 0,490 0,530 0,825 1,483 0,131 1,142 0,379 1,190 1,264 0,620 1,563 1,444 1,909 1,228 1,451 1,719 1,193 0,984 1,760 1,757 1,587 0,768 1,466 1,529 0,779 1,434 1,289 0,567 1,475 1,652 1,1 0,499 1,527 1,817 0,537 1,833 0,976 0,343 0,538 1,392 0,85 0,116 0,108 1,682 0,163 1,644 0,807 1,405 1,538 1,971 1,807 1,780 1,860 1,249 0,64 1,959 0,161 0,821 0,222 1,167 0,615 0,863 0,918 1,923 1,757 1,872 0,150 0,47 0,551 1,617 1,650 0,517 0,764 1,284 0,670 0,281 1,712 1,773 0,442 1,931 1,661 1,943 0,722 0,770 0,24 0,21 0,859 0,89 0,330 1,540 0,251 0,979 0,202 0,447 0,459 0,439 0,116 0,382 0,924 0,526 0,227 1,607 0,493 0,453 0,776 1,764 1,781 0,517 1,667 1,65 1,374 0,987 0,508 0,694 0,326 1,915 1,688 0,930 0,849 0,888 0,701 0,753 1,949 1,876 1,91 0,334 1,720 0,416 1,185 1,947 1,355 1,664 1,698 1,120 0,843 1,111 1,531 0,649 1,961 0,260 1,25 1,449 1,235 1,86 0,525 0,75 0,945 0,959 1,641 0,423 1,586 1,365 1,628 1,415 1,968 0,440 1,184 1,411 0,299 1,117 0,698 0,651 1,158 1,623 0,889 0,650 1,519 0,220 1,81 1,186 0,751 1,42 1,568 1,96 1,692 1,593 0,86 0,906 0,777 0,609 0,44 0,509 0,989 1,159 0,843 1,61 0,421 0,791 0,713 0,191 0,283 0,91 1,415 1,218 1,829 1,49 0,239 1,367 0,685 0,750 1,698 1,510 0,480 0,764 1,107 1,854 0,360 0,734 1,514 0,946 0,448 1,793 1,582 0,115 0,792 1,123 0,365 0,177 1,289 1,84 0,175 0,407 1,618 1,327 1,229 0,363 1,143 1,963 1,409 0,469 1,780 1,263 0,282 0,666 0,628 0,414 1,442 1,669 1,359 0,346 0,953 1,584 1,870 0,509 0,283 1,285 0,194 1,326 0,990 0,655 0,944 1,839 0,273 0,489 1,35 1,202 1,765 1,616 1,805 0,792 0,303 1,732 1,251 0,476 1,601 0,427 1,49 1,660 0,238 1,588 0,524 0,780 0,804 1,466 1,335 0,216 1,358 0,649 1,685 1,70 1,274 0,8 0,264 1,786 0,714 1,685 1,537 1,997 1,134 0,306 1,163 0,568 0,786 0,764 1,908 1,420 0,466 0,757 0,404 0,724 1,834 1,704 1,541 1,667 1,547 0,304 1,563 1,807 0,899 0,714 0,441 0,589 0,846 0,827 0,988 1,840 1,896 0,664 1,55 1,334 0,668 1,699 0,640 0,258 0,893 0,821 0,66 0,207 0,929 1,745 1,404 1,785 1,66 0,424 0,806 1,403 1,424 1,894 1,383 0,827 1,274 0,951 0,210 0,91 0,177 1,596 1,141 1,306 1,586 0,18 1,551 1,662 1,303 0,213 0,11 0,612 1,327 0,252 0,574 1,356 0,205 0,394 0,263 1,404 0,206 1,251 0,396 0,837 1,584 0,309 1,94 0,993 0,602 1,492 1,776 1,713 1,604 1,506 1,28 0,274 1,619 0,871 0,413 1,84 0,549 1,451 0,804 1,769 0,280 1,745 0,190 1,699 1,686 1,912 0,29 1,99 1,687 1,347 0,699 0,761 0,586 0,238 0,953 1,653 1,387 1,109 1,167 0,771 0,378 1,197 1,856 0,362 0,827 1,562 1,196 0,254 1,683 1,819 0,299 1,162 0,836 1,664 0,659 1,1 1,29 1,246 1,387 1,987 1,244 0,108 1,175 1,478 1,346 0,974 0,537 1,833 1,421 0,897 0,609 0,568 0,287 1,0 1,418 0,362 1,400 0,978 1,345 1,951 0,217 0,997 0,27 1,232 1,661 0,349 0,706 1,713 0,324 1,451 1,941 0,120 0,356 0,924 1,403 0,560 1,97 1,522 1,932 0,705 1,799 1,494 0,95 1,316 0,532 1,764 1,883 1,355 1,522 0,746 0,926 1,675 0,822 0,286 1,349 1,987 0,502 0,796 0,340 1,755 0,419 1,225 0,320 0,771 0,371 1,935 1,145 1,131 1,873 0,236 0,234 0,578 0,559 1,667 1,924 0,77 1,589 0,110 1,407 1,208 0,38 1,403 1,456 1,84 1,797 0,406 0,726 1,639 0,221 1,170 0,255 1,357 1,492 0,335 0,25 0,129 0,68 0,999 1,550 1,496 0,387 0,482 1,947 1,722 1,89 1,496 1,185 0,916 1,603 1,890 1,274 0,74 1,295 1,59 0,628 1,388 0,547 1,154 1,885 1,235 1,563 0,640 0,953 0,129 0,908 1,465 0,475 1,949 0,723 0,336 1,978 0,759 0,235 0,291 1,688 0,962 1,544 1,829 1,848 1,249 0,340 1,987 1,582 0,886 0,264 1,41 1,98 1,987 1,550 0,923 1,622 0,479 0,759 1,266 1,996 0,254 1,389 1,990 1,557 1,446 1,931 1,211 1,6 0,464 1,695 0,957 0,442 1,0 0,269 1,707 0,658 0,947 1,528 0,175 1,667 1,744 1,566 0,406 1,723 0,442 0,449 1,326 1,584 0,303 0,106 1,550 1,879 0,14 0,236 0,907 0,299 0,622 1,975 1,762 0,422 1,120 0,686 0,187 1,517 0,533 0,512 1,233 0,699 0,240 0,973 0,931 0,621 0,837 0,888 0,294 0,474 1,480 0,755 1,845 1,807 1,600 0,607 0,202 0,310 0,35 0,563 1,310 1,437 1,131 0,786 0,10 0,861 0,903 1,138 1,767 0,163 0,538 1,73 1,513 1,293 1,6 0,978 1,989 1,71 0,923 1,486 1,249 1,183 1,40 0,902 0,774 1,248 0,334 1,507 1,433 0,697 1,329 0,17 1,570 0,572 1,291 0,406 1,192 0,234 1,863 0,882 1,421 1,183 1,355 0,436 1,27 1,176 1,99 0,424 1,866 1,541 1,528 1,747 0,521 0,49 0,596 1,416 1,126 0,951 1,790 1,622 1,443 0,104 0,297 0,646 1,705 1,540 0,871 0,848 0,425 0,688 1,700 0,899 1,912 1,145 0,344 1,648 0,233 1,16 0,756 0,791 1,589 1,5 1,873 1,83 1,959 1,510 1,782 1,793 0,967 0,700 1,137 0,846 0,756 0,392 1,312 0,413 1,454 0,855 1,875 1,658 0,890 0,418 0,982 1,148 0,774 0,222 0,375 0,556 1,121 1,978 1,341 0,860 1,490 1,298 0,725 0,320 0,667 1,890 0,609 1,994 1,874 0,372 0,399 1,66 0,280 1,205 1,164 1,817 1,833 0,623 0,631 0,205 1,986 1,121 1,78 1,50 1,119 0,3 0,736 0,27 1,953 1,489 1,776 0,26 1,44 0,852 0,668 1,32 0,208 0,295 1,30 1,126 0,710 1,410 1,348 1,854 0,983 1,95 1,262 1,946 1,758 0,196 1,72 0,486 1,993 0,51 0,921 0,247 1,712 1,4 0,71 1,942 0,451 0,591 0,839 0,109 1,452 1,510 1,893 0,622 1,184 1,397 1,605 0,849 0,259 0,666 0,0 1,510 0,19 1,195 0,6 1,793 1,150 0,661 0,683 1,35 0,842 1,426 0,946 1,681 1,518 0,849 1,981 1,123 1,782 0,985 1,199 1,629 0,677 0,133 1,861 0,609 1,999 0,789 0,127 1,492 0,696 1,230 0,776 1,575 0,89 0,534 1,569 0,67 0,526 1,263 0,278 1,880 1,590 1,405 1,369 0,188 0,488 1,379 1,372 1,105 0,452 1,665 1,760 0,456 0,200 0,804 0,183 1,983 1,564 0,526 1,39 1,426 0,104 0,342 0,600 1,715 0,621 1,813 1,639 0,544 1,775 0,577 1,570 0,845 1,122 0,503 0,7 0,202 0,198 1,771 1,121 0,64 0,893 0,997 0,859 0,211 1,475 0,554 1,308 1,153 1,492 1,378 1,42 1,793 0,933 0,371 0,950 0,455 0,714 0,714 1,486 1,202 1,559 1,299 1,646 1,172 1,332 1,853 0,467 1,816 0,507 0,122 0,644 0,714 1,229 1,790 0,748 0,871 1,675 1,682 0,494 0,105 0,355 1,718 0,957 1,765 0,268 1,789 0,475 1,97 1,713 0,836 1,728 0,324 0,356 0,518 0,545 1,15 1,841 1,434 1,467 0,816 0,644 1,372 1,55 1,873 1,574 0,412 1,836 0,251 0,984 1,454 1,982 1,575 0,543 1,768 0,848 1,320 0,338 1,985 0,29 1,889 0,865 0,889 0,574 1,842 0,404 0,769 0,321 1,662 0,793 0,770 1,298 0,54 1,425 0,137 1,564 1,775 1,447 0,77 1,98 0,314 1,820 1,413 0,345 0,661 1,387 0,842 0,315 1,285 1,780 1,405 0,304 0,255 1,694 1,841 0,289 0,682 1,220 1,66 1,248 0,725 1,911 1,815 0,483 0,640 1,234 0,395 0,987 0,782 0,841 1,648 0,83 0,767 1,888 0,69 1,983 1,878 0,4 0,48 0,719 0,253 0,596 0,654 1,262 0,388 1,258 0,214 0,53 0,304 0,47 1,904 1,980 1,321 0,255 1,520 0,224 0,851 0,977 0,362 1,236 0,971 0,242 0,952 1,942 1,889 1,225 1,80 1,623 1,386 1,522 1,640 0,648 0,38 1,921 1,574 1,770 0,146 0,655 1,930 1,169 0,45 0,317 1,550 0,662 1,679 0,146 0,903 1,987 0,503 0,117 1,332 1,129 0,345 0,285 0,817 1,216 0,274 0,806 0,560 1,72 0,454 0,63 1,26 0,881 0,740 0,902 1,359 0,358 0,668 1,764 0,313 1,556 1,651 0,202 0,526 1,265 0,85 0,566 0,201 1,79 1,810 0,877 0,854 1,432 0,119 0,508 1,105 0,34 0,997 0,298 0,856 1,491 0,287 0,394 1,538 0,901 0,169 1,836 1,508 1,390 1,62 0,262 0,662 0,327 1,376 0,109 1,792 0,568 1,169 1,429 1,709 0,977 0,913 1,645 1,782 0,985 1,596 0,835 0,242 1,70 1,917 1,764 1,766 0,468 1,8 0,810 0,895 1,383 1,109 0,86 0,93 1,339 1,56 1,194 0,338 1,56 0,499 1,671 0,719 0,589 1,336 1,755 1,265 0,461 0,425 0,569 0,234 1,444 0,300 0,800 1,795 0,814 1,1 1,970 1,753 0,666 0,538 0,457 0,890 0,335 0,430 0,419 1,411 0,547 1,957 1,567 0,839 0,239 0,108 0,113 1,656 1,834 1,664 0,490 1,455 0,663 1,902 0,76 0,859 0,342 0,125 0,918 1,895 1,223 1,616 0,366 1,366 0,456 1,605 0,808 0,217 1,715 1,558 1,492 1,340 0,829 1,583 0,169 1,416 0,875 1,345 0,465 1,477 0,737 0,580 0,340 1,333 1,289 0,186 0,388 0,601 1,550 1,788 0,552 1,18 1,135 0,965 1,784 1,716 0,609 0,592 0,589 1,577 0,389 1,364 0,308 1,434 1,364 0,773 1,214 1,981 1,340 0,736 0,276 1,162 0,135 1,137 1,813 0,925 0,476 1,700 0,175 1,106 0,916 0,902 0,783 0,724 0,872 1,492 1,969 1,913 1,718 0,624 0,953 0,281 1,427 0,800 1,349 1,255 1,279 0,542 1,419 0,654 1,385 1,517 1,637 0,223 1,14 0,325 1,176 1,740 0,821 1,897 1,151 0,901 1,374 1,815 1,238 1,771 1,436 1,848 1,870 1,958 0,189 0,723 1,426 1,17 1,296 1,632 0,807 0,622 0,53 0,37 1,609 1,914 1,84 1,506 1,846 0,348 0,333 0,993 0,592 0,204 0,861 0,198 1,564 0,405 1,452 1,903 1,461 0,458 1,579 0,247 0,562 1,489 0,124 1,944 1,704 1,537 0,574 1,266 0,347 0,117 1,43 0,754 1,568 1,860 1,922 0,35 0,617 1,386 1,758 0,488 0,387 0,426 0,925 1,96 1,447 0,60 0,728 1,227 0,869 0,331 1,607 0,836 1,394 0,154 0,565 0,373 0,511 0,622 1,488 1,439 1,286 1,966 0,856 1,447 1,601 0,640 0,690 1,129 1,538 1,173 0,453 0,231 0,839 1,198 0,163 1,820 0,847 1,817 1,119 0,992 0,444 1,341 0,468 0,124 0,267 1,326 0,474 1,884 1,649 1,870 0,59 1,625 0,444 0,537 0,640 0,34 0,732 1,86 0,338 1,681 1,781 1,74 0,284 1,566 1,33 0,264 1,477 1,530 0,951 0,248 1,613 0,797 1,520 0,431 1,951 1,655 0,897 0,46 0,288 1,452 0,515 0,917 0,337 0,874 1,97 0,182 0,658 0,984 1,714 0,704 0,534 0,678 0,437 0,393 1,960 1,173 1,682 1,827 0,597 0,712 1,291 0,282 1,188 0,110 0,984 0,130 0,86 0,650 1,810 1,310 1,517 0,880 0,412 1,311 1,341 0,729 1,692 0,486 1,888 0,958 0,802 1,951 0,889 0,406 1,502 0,365 1,390 1,874 1,594 0,425 0,8 0,396 0,133 0,127 0,355 0,847 0,978 1,663 1,924 0,780 0,743 0,980 0,228 1,226 1,622 0,947 0,466 0,313 1,895 1,143 0,86 1,328 0,499 1,550 0,547 1,53 1,853 1,448 0,147 1,985 0,367 1,395 0,531 1,149 0,804 1,778 1,645 1,854 1,42 1,367 1,968 1,201 0,279 1,490 0,338 0,180 0,90 1,751 0,508 0,752 1,246 0,407 1,439 0,504 1,224 1,619 1,560 1,554 0,469 0,280 0,147 0,503 0,427 1,687 1,223 0,772 0,474 1,469 0,111 1,780 1,449 1,685 1,763 0,414 1,997 0,341 1,579 0,386 1,321 1,349 1,419 1,263 1,453 0,118 1,280 1,282 0,267 1,584 0,920 0,372 0,612 1,460 1,296 0,529 0,517 0,914 0,565 0,909 0,2 1,257 0,688 0,220 1,439 0,202 0,996 0,827 0,797 0,732 1,40 1,537 0,577 0,584 1,305 0,344 1,650 1,658 0,192 0,819 1,960 1,634 1,766 1,650 0,156 0,982 0,456 1,340 0,64 0,482 0,512 0,516 1,955 0,193 0,584 1,17 0,372 1,784 0,299 0,375 1,943 0,127 1,712 1,30 0,729 0,138 0,510 0,443 1,330 0,375 0,470 1,20 1,774 0,185 0,314 0,812 1,246 0,445 1,864 1,424 0,83 1,880 1,625 1,403 1,238 0,609 1,203 0,309 1,721 0,330 0,664 0,737 0,195 1,195 0,571 0,496 0,915 1,671 1,339 0,777 0,383 1,630 1,142 1,288 0,659 1,281 1,965 1,861 0,507 1,485 0,459 1,253 1,598 1,377 0,996 0,461 1,879 0,6 0,805 0,814 1,383 0,159 0,843 1,578 0,211 1,74 1,609 0,246 0,374 1,877 1,742 0,908 0,795 1,339 0,540 1,356 0,443 1,631 1,739 1,554 0,885 0,728 1,316 0,364 1,925 1,748 0,825 0,904 0,372 1,61 1,91 0,767 1,53 0,16 0,909 1,786 1,585 0,791 1,944 1,792 0,903 1,595 0,472 1,173 0,200 1,480 1,77 1,804 0,477 0,495 1,500 1,342 0,97 0,886 1,932 0,928 0,419 0,283 1,450 1,387 1,956 1,946 0,814 0,522 0,757 0,612 0,814 1,974 0,139 0,568 0,183 1,978 1,693 1,511 1,408 0,163 1,184 0,873 1,555 0,889 0,722 1,498 0,588 1,696 1,628 1,847 0,573 1,142 0,766 1,703 0,235 0,883 0,727 1,670 1,395 1,479 1,236 1,93 0,701 1,402 1,853 1,105 1,129 1,169 1,364 1,776 0,375 1,373 1,146 1,125 0,604 1,246 0,821 0,135 1,679 1,626 0,457 1,234 0,316 0,986 1,726 1,120 1,221 1,962 1,752 0,601 1,16 0,457 0,774 1,397 0,917 0,509 0,199 1,211 0,598 1,554 0,276 1,128 1,331 0,327 0,894 1,3 1,664 0,447 1,579 0,720 1,711 1,575 0,291 1,73 1,864 0,960 1,608 0,294 0,528 1,761 1,751 0,361 0,912 1,306 0,655 1,495 1,21 0,308 0,977 0,0 0,685 1,954 1,626 1,784 0,307 0,589 0,258 0,867 1,951 1,740 1,269 1,364 1,598 0,38 0,597 0,781 1,448 1,505 0,207 1,620 1,539 1,144 0,598 0,12 0,195 0,585 0,278 0,20 0,505 0,837 0,462 1,592 0,275 1,83 0,754 1,253 0,55 0,571 1,351 0,725 0,361 0,54 0,388 0,764 1,200 0,714 1,885 0,227 0,533 1,813 0,795 0,162 0,209 0,314 0,21 1,29 0,928 1,174 0,320 0,537 0,420 1,348 1,155 0,761 0,945 1,911 0,752 1,635 0,996 1,596 0,382 1,725 1,778 1,266 0,116 0,713 0,909 1,657 1,57 1,489 0,251 0,211 0,361 1,807 1,422 0,464 1,372 0,148 1,341 0,407 1,695 0,838 1,461 0,859 1,494 0,622 0,954 0,525 0,845 1,274 0,90 1,803 1,637 0,171 0,112 1,697 0,114 1,801 1,610 1,758 1,573 0,229 0,558 1,410 1,110 1,387 0,614 0,296 0,528 0,209 0,947 1,475 0,220 1,905 1,189 1,990 0,906 0,969 1,333 1,541 0,139 0,357 0,661 1,926 0,163 1,789 0,155 1,52 1,615 0,471 1,945 1,696 1,260 1,330 1,950 1,49 1,888 1,142 1,296 1,853 1,337 0,707 1,334 1,294 0,141 1,700 1,811 1,998 1,632 1,865 1,35 1,850 0,686 0,0 0,102 0,991 1,854 0,870 0,925 0,309 1,348 1,276 1,777 1,767 1,406 1,938 0,215 1,11 1,826 0,280 1,738 1,366 1,7 0,614 1,456 1,855 1,179 0,546 0,117 1,527 0,395 1,939 0,389 1,79 0,332 1,724 1,463 1,337 1,731 1,351 0,919 0,494 1,664 0,87 0,149 1,523 1,411 0,553 0,503 1,198 0,94 0,234 0,440 0,474 1,247 1,940 0,13 1,705 0,187 0,536 1,806 1,79 1,654 0,429 1,760 1,825 0,551 1,143 1,183 0,890 1,647 0,532 1,993 0,724 1,280 1,129 1,757 1,931 1,407 1,159 1,196 1,515 1,479 0,649 1,559 1,644 0,551 0,344 0,345 0,988 0,294 1,551 0,180 1,712 1,674 0,132 1,936 0,768 1,579 1,636 1,891 0,499 0,271 0,512 1,313 1,973 0,405 1,957 1,894 0,502 1,868 0,194 0,301 0,33 0,820 1,829 1,619 1,619 1,494 1,756 0,645 0,190 0,209 1,49 0,403 1,47 1,823 1,966 0,746 1,252 0,495 1,331 1,203 0,599 0,378 0,848 1,206 1,852 1,760 1,336 0,930 0,488 0,566 0,85 0,631 1,238 0,230 0,715 1,549 1,806 0,564 0,750 1,747 0,746 0,602 1,696 1,828 1,895 0,162 0,777 0,121 1,168 1,174 1,206 1,778 0,404 1,332 1,425 0,881 0,406 0,302 1,160 0,514 1,864 1,74 1,48 1,624 1,212 1,692 0,895 1,878 0,300 0,132 1,911 0,665 0,880 1,561 1,840 0,62 1,34 0,110 0,549 1,490 0,311 0,944 1,17 1,32 1,817 1,85 0,964 1,466 0,938 1,927 0,573 1,311 1,88 0,297 0,643 1,457 1,330 0,481 1,249 1,896 0,555 1,57 0,488 0,800 1,528 0,914 1,319 1,632 0,5 0,529 0,889 0,525 1,485 1,896 1,595 1,540 1,254 1,290 1,171 0,918 0,399 0,818 1,481 0,866 0,65 1,645 1,678 1,482 1,36 1,675 1,263 1,948 0,974 1,285 0,141 0,647 0,690 1,639 0,888 0,576 1,149 1,33 0,190 1,508 0,113 0,232 0,22 1,47 1,534 1,771 0,331 1,77 1,267 1,143 1,47 0,971 1,193 1,691 1,167 1,647 1,598 0,890 1,25 1,209 1,303 0,304 0,364 0,408 1,921 1,74 1,112 0,172 0,520 0,415 0,455 1,143 0,423 1,132 1,298 0,90 1,315 1,252 1,462 0,867 1,261 1,902 1,863 1,531 1,996 1,157 0,440 1,46 1,541 0,737 0,551 0,696 0,566 0,28 1,712 1,670 1,140 1,724 1,785 0,688 0,229 0,721 1,159 0,772 0,572 0,923 0,152 1,159 1,775 1,78 1,484 1,163 0,821 1,791 1,716 1,353 1,237 1,360 0,998 0,567 0,233 0,101 1,132 1,427 0,188 1,851 1,689 1,650 0,926 1,545 0,718 1,800 1,441 1,968 1,41 0,752 1,127 0,964 0,668 1,984 1,410 0,337 0,437 0,89 0,843 0,562 1,201 0,444 0,794 1,10 1,248 0,661 1,871 0,567 0,843 0,146 1,41 1,861 1,263 0,606 0,952 0,111 0,710 1,893 1,708 0,586 1,46 0,292 1,763 0,390 1,151 1,128 1,986 0,596 0,910 0,824 0,993 0,602 1,15 0,257 1,157 1,690 1,614 0,552 0,127 0,518 0,880 1,25 0,992 0,717 0,685 1,28 1,767 0,233 1,745 0,224 1,982 0,379 0,448 0,654 1,716 0,949 0,209 1,286 0,541 0,330 0,592 1,803 0,777 0,301 1,304 1,216 0,530 0,349 1,793 0,111 0,22 1,97 1,456 0,293 0,232 0,31 1,680 1,155 0,589 1,977 1,28 0,729 1,827 1,277 1,206 1,205 0,573 0,46 1,256 0,40 1,376 1,685 0,665 0,434 1,309 1,898 1,425 0,60 1,97 1,534 1,139 1,319 1,761 0,994 1,577 1,101 1,389 0,973 0,863 0,384 1,608 1,976 1,586 1,42 1,800 1,847 0,980 1,918 1,905 1,617 0,416 1,823 1,350 0,808 1,229 1,254 1,939 1,185 0,934 0,267 0,467 1,431 1,944 0,432 1,83 0,726 1,882 0,227 0,74 1,593 1,429 1,571 0,889 0,743 1,581 0,548 0,155 1,826 1,358 0,282 0,34 1,583 1,281 1,79 0,897 1,867 1,440 0,153 0,499 0,716 1,552 0,16 1,628 1,93 1,326 0,445 1,198 1,214 1,108 0,903 1,197 1,736 0,81 0,965 1,14 0,264 1,582 0,393 1,815 1,710 0,727 1,24 1,792 0,36 1,247 1,971 0,203 0,564 0,157 0,16 1,293 1,68 1,696 1,813 1,690 1,443 1,543 1,861 1,338 1,933 1,197 1,105 0,65 1,875 1,837 1,134 0,297 0,510 1,106 1,964 0,988 1,74 0,891 1,746 1,595 0,771 1,126 0,339 0,189 1,193 1,705 0,731 0,465 1,424 0,100 1,284 0,562 0,39 0,877 0,54 0,210 1,273 0,964 1,365 1,641 0,806 0,319 1,373 1,993 1,223 1,70 1,977 1,794 1,472 1,951 1,964 1,769 0,189 1,579 1,505 1,362 0,561 1,873 0,950 1,776 1,292 0,113 0,595 1,160 0,349 0,358 1,735 0,691 0,883 0,735 0,863 0,763 0,950 1,597 1,626 0,596 1,631 1,418 0,146 1,827 0,608 0,385 0,614 0,575 1,801 1,531 1,803 1,406 1,692 1,421 1,376 0,636 0,706 0,464 0,731 1,768 0,80 1,419 1,198 1,443 1,530 0,469 0,891 0,89 1,338 0,443 0,19 1,333 0,674 0,952 1,529 1,773 1,519 1,498 1,276 0,254 0,612 1,16 1,855 1,156 1,166 1,420 1,726 1,733 0,246 1,574 0,277 0,284 1,66 0,563 0,963 0,867 0,349 0,989 0,711 1,840 0,305 1,409 1,949 0,29 1,144 0,229 1,463 1,312 0,909 1,301 1,421 1,225 1,650 1,436 1,249 1,303 1,831 1,383 0,300 0,110 0,679 0,628 0,654 1,205 1,982 1,660 0,760 1,572 0,904 1,242 1,574 1,631 0,669 0,945 1,494 0,546 0,327 1,602 0,5 0,866 0,921 1,909 1,831 1,690 1,601 1,276 0,943 1,176 0,552 0,765 0,512 1,579 1,123 0,713 1,820 0,94 1,293 1,255 1,262 0,781 1,69 1,441 1,599 0,847 1,897 1,226 1,224 0,2 0,834 0,670 1,502 0,979 1,753 1,873 0,791 1,602 1,928 1,290 1,346 0,387 0,924 0,177 1,321 1,691 1,956 0,522 0,653 0,352 1,259 1,180 1,224 0,531 0,20 0,603 0,184 1,800 0,98 0,968 0,178 1,323 1,268 1,818 1,259 0,299 1,909 0,810 0,521 0,781 1,691 1,2 0,168 1,929 0,887 0,701 1,47 0,986 1,428 1,614 0,316 1,127 0,534 1,46 0,974 0,763 0,617 0,706 0,293 1,149 0,217 1,809 1,271 0,162 0,746 0,356 0,310 1,19 1,475 0,494 1,28 1,744 0,491 1,857 1,671 0,63 1,323 0,22 1,693 0,911 1,815 1,842 0,575 0,947 1,453 1,631 1,337 1,309 0,381 1,266 0,253 1,294 1,793 0,442 0,57 1,507 1,604 1,123 1,526 0,105 0,117 0,86 1,950 1,193 0,187 1,632 0,635 1,901 0,375 1,174 1,161 1,914 0,588 0,810 0,554 0,485 0,728 0,831 0,860 0,386 0,455 0,513 0,341 0,816 0,985 1,733 0,909 0,524 0,982 0,331 1,770 1,469 1,337 1,527 1,355 1,542 1,490 0,527 0,171 1,310 0,998 1,801 1,202 0,755 0,801 1,22 0,321 0,83 0,918 1,734 1,47 1,921 1,782 0,375 0,535 1,137 1,165 1,675 1,750 1,263 0,361 0,579 1,342 0,505 0,896 1,664 1,501 0,352 0,319 1,264 0,132 0,256 0,140 0,94 1,483 1,33 1,855 0,497 1,645 0,389 0,663 0,296 1,577 1,861 0,873 1,555 0,6 0,722 0,849 1,886 1,615 1,693 1,674 0,963 1,880 1,393 0,413 0,52 0,351 0,926 1,672 0,143 1,785 1,361 0,815 1,752 1,848 0,908 0,12 1,158 0,182 0,220 0,386 1,438 1,455 1,722 1,301 0,315 0,970 1,93 0,339 0,597 1,385 0,349 0,245 0,420 0,984 0,703 0,732 1,942 1,161 1,442 1,565 0,777 1,559 0,255 0,780 1,932 1,662 0,478 0,780 1,684 0,675 1,180 0,232 1,501 0,9 0,944 1,295 1,192 0,902 1,341 0,362 0,717 0,730 0,640 0,571 0,953 1,78 0,931 1,930 1,711 1,490 0,142 0,895 0,613 0,255 1,110 0,872 1,160 0,817 0,948 1,908 1,35 1,284 0,838 0,90 1,208 1,994 0,228 1,857 1,67 0,477 1,319 0,12 0,982 0,399 0,542 1,156 0,197 0,713 0,359 1,33 0,268 0,8 0,824 1,374 0,343 1,875 1,541 0,221 1,602 1,452 1,812 0,218 0,151 0,642 1,24 0,43 0,775 1,529 0,413 0,975 1,982 0,37 1,149 0,362 0,394 0,890 1,878 1,880 1,237 1,727 0,328 0,845 0,295 1,57 1,569 1,944 0,723 1,222 0,342 1,541 1,662 1,849 0,48 0,511 1,657 0,310 0,272 0,344 0,147 1,319 0,126 0,680 1,975 0,341 0,163 1,882 1,595 0,169 1,297 0,421 0,101 1,61 1,418 0,991 1,281 0,470 0,91 1,788 0,963 0,71 0,690 0,572 0,426 1,868 0,861 0,815 0,952 1,230 1,651 1,747 0,86 1,183 0,576 0,943 1,902 1,460 1,700 1,505 1,648 0,736 1,580 0,151 1,559 1,477 1,223 1,580 1,520 0,577 1,468 0,345 1,388 0,81 1,20 1,291 0,378 1,251 1,828 1,733 1,632 1,875 0,475 0,351 0,443 0,911 1,734 0,99 1,506 0,956 1,132 1,344 0,655 0,762 1,797 0,417 0,670 0,250 0,748 1,136 0,900 0,59 1,680 0,647 1,901 1,207 1,270 0,134 0,111 0,18 0,442 0,559 0,583 1,397 1,781 1,529 0,108 0,676 0,834 1,41 0,26 1,262 1,602 0,436 1,613 1,22 1,264 0,665 1,765 1,337 0,246 1,542 0,686 0,413 0,854 0,706 1,128 1,929 1,624 1,171 0,514 0,464 1,972 0,552 0,181 1,195 0,534 1,301 1,789 0,102 0,232 1,901 0,173 1,129 1,38 1,777 0,768 0,447 1,44 1,414 1,516 1,675 1,354 0,84 1,107 1,165 0,582 0,462 1,38 1,58 1,269 1,936 0,161 1,535 1,969 0,753 1,445 1,127 0,365 1,908 1,606 1,632 0,370 0,757 0,874 1,839 0,641 1,2 1,139 0,28 0,529 1,117 0,376 1,151 1,673 1,29 1,258 1,846 1,797 1,296 0,539 1,554 1,315 0,374 1,350 1,829 1,300 0,331 1,733 1,97 0,437 1,787 1,129 1,46 1,559 1,45 1,558 1,858 0,475 1,108 0,346 1,443 0,53 1,905 1,341 0,970 1,700 1,243 1,213 0,108 1,178 1,546 0,81 0,832 1,983 0,160 1,328 1,516 0,555 0,786 0,540 0,790 1,32 1,694 0,964 1,13 1,664 0,734 0,964 1,515 0,835 1,515 0,475 0,212 1,216 1,397 0,984 0,65 0,890 0,986 1,478 0,222 0,994 0,643 1,667 1,801 0,813 1,456 0,47 1,124 0,209 0,861 0,452 1,107 0,648 1,265 0,163 0,283 0,358 0,788 0,759 0,607 1,483 0,66 1,798 1,371 1,444 0,357 1,543 0,253 1,930 0,779 1,28 1,68 0,19 1,831 0,374 1,359 0,749 1,723 1,501 0,794 1,200 1,270 0,890 0,169 0,469 0,650 0,694 0,396 1,228 0,396 1,955 0,876 0,660 0,189 1,208 0,803 0,692 1,377 0,712 0,526 0,215 1,798 1,829 1,565 0,396 1,956 1,781 1,902 1,493 0,231 0,485 1,249 0,610 1,501 0,154 1,979 1,255 1,652 0,102 0,757 0,598 0,722 1,832 0,859 1,56 0,999 1,151 1,215 1,586 0,667 0,303 1,475 1,213 0,882 0,597 1,215 0,64 0,505 0,843 0,273 1,344 1,972 1,744 1,938 0,820 1,935 1,81 0,773 1,739 0,807 1,260 0,23 1,75 1,89 0,511 0,57 1,921 0,277 0,967 1,303 1,964 0,61 1,311 1,903 0,737 0,315 0,153 0,642 0,113 0,988 1,747 0,552 0,314 0,661 1,367 1,221 0,771 0,143 0,707 0,910 1,704 1,455 0,70 0,574 1,658 1,680 0,148 1,75 0,337 1,128 1,619 0,406 0,799 1,756 0,10 1,105 1,969 1,857 1,213 1,588 1,570 0,901 1,80 1,474 1,232 0,928 1,883 0,934 1,74 1,720 1,52 1,730 1,595 1,869 1,977 1,515 1,408 0,71 0,177 1,983 0,880 1,339 1,335 0,244 0,739 1,494 1,894 0,397 1,62 1,168 0,439 1,362 0,399 0,658 1,556 1,104 1,229 0,776 0,132 0,868 1,191 0,91 1,66 0,152 1,197 1,439 0,149 0,809 0,18 0,548 0,209 0,88 0,944 0,241 1,257 0,335 1,39 0,896 0,490 1,973 1,595 1,987 1,695 0,496 1,146 1,684 0,439 1,733 0,681 1,426 1,129 1,325 1,961 0,802 1,598 1,835 0,462 1,205 0,347 0,761 1,274 0,851 1,987 0,185 0,674 1,94 1,642 1,185 1,756 0,19 1,252 0,406 1,916 0,262 1,964 1,613 0,587 0,965 1,262 0,322 1,598 1,175 1,416 0,129 1,165 0,452 1,437 1,782 0,915 1,783 0,10 1,948 1,466 1,374 0,590 0,861 1,363 0,155 0,988 1,158 0,57 1,161 1,446 0,502 1,931 1,982 0,682 0,399 1,416 1,89 0,563 1,665 1,394 0,407 0,806 0,452 1,300 1,563 1,750 1,258 0,251 1,716 1,353 0,5 1,159 0,770 0,896 1,719 0,216 0,519 1,198 1,576 0,660 1,515 1,592 1,812 0,156 0,82 0,688 0,151 0,419 0,381 0,338 0,240 0,167 1,865 0,571 1,930 1,839 1,999 1,719 0,51 1,0 1,250 0,919 0,137 0,684 1,559 1,83 0,984 0,30 1,409 1,41 0,46 0,999 0,477 1,306 1,951 0,714 1,480 0,88 1,447 0,698 1,533 1,188 1,134 0,835 0,889 0,765 1,816 0,467 1,462 1,78 0,311 0,454 1,69 1,536 0,836 0,658 0,275 0,302 0,14 1,207 1,350 0,9 0,86 1,306 0,867 1,780 0,313 1,452 0,473 0,682 0,362 0,116 1,405 1,332 0,452 1,857 0,978 1,757 0,625 0,589 1,360 1,112 1,31 0,953 0,391 0,895 0,320 1,44 0,531 1,132 0,92 0,162 1,800 1,527 0,220 0,784 1,775 0,486 0,652 1,809 0,601 0,42 1,277 1,383 1,971 1,195 1,102 1,784 0,808 1,660 0,826 0,173 1,252 1,264 1,867 0,832 0,795 0,71 0,76 1,473 1,574 0,178 0,344 0,951 0,213 1,0 0,478 0,693 0,736 0,658 0,106 1,197 0,408 0,375 1,907 1,285 0,736 1,534 1,663 1,119 0,227 1,388 0,344 0,872 1,253 1,411 0,243 1,598 0,8 1,794 0,721 0,645 0,845 1,711 1,721 0,106 1,106 1,559 0,590 0,925 1,972 1,880 1,384 0,249 0,34 0,561 0,946 0,620 0,39 0,963 0,525 1,200 0,113 0,798 0,374 1,49 0,717 0,2 0,130 0,948 0,114 1,859 0,239 0,486 1,426 1,295 1,532 1,293 0,750 1,883 1,359 0,884 1,743 0,744 1,814 0,907 1,384 1,45 1,781 1,148 1,316 0,570 1,567 0,985 0,491 1,186 1,183 0,193 0,999 0,514 1,689 1,781 0,578 1,549 1,118 0,895 1,149 0,468 0,206 1,455 1,205 1,102 1,729 0,728 1,256 1,643 1,361 1,851 1,822 0,822 1,36 1,237 0,581 0,796 1,157 0,419 1,32 0,210 0,391 1,696 0,26 0,337 0,350 1,187 0,616 0,19 1,201 1,726 1,449 1,275 0,198 0,599 1,76 1,861 0,496 0,288 1,971 1,731 0,907 0,481 1,491 0,263 1,30 0,93 0,230 1,75 0,52 1,992 1,808 1,233 0,607 1,32 1,265 0,954 1,127 1,885 0,729 0,686 0,870 1,484 0,952 0,620 0,738 0,750 0,61 1,209 1,669 0,820 0,977 1,577 1,910 1,207 0,688 0,943 0,487 1,635 0,498 0,817 1,61 1,632 1,169 1,488 1,985 0,428 0,945 0,939 1,626 0,681 0,261 1,434 1,638 1,68 0,447 0,283 1,171 0,677 1,323 0,844 1,287 0,678 1,31 0,533 1,922 1,606 0,739 0,458 1,294 0,51 0,271 0,473 0,61 0,837 0,940 0,148 1,181 0,746 1,998 1,807 0,780 0,854 0,982 1,737 1,934 0,541 1,216 0,967 1,644 1,536 1,40 0,877 0,497 1,647 0,375 0,119 0,146 0,896 1,987 0,919 0,295 1,552 1,783 1,334 0,648 0,601 1,762 0,97 1,364 0,623 0,994 1,81 1,464 1,4 1,100 1,349 0,68 1,865 0,957 0,688 1,770 1,639 0,811 1,304 1,825 0,668 0,113 1,605 1,826 0,650 1,132 0,195 0,98 0,617 0,572 1,426 0,438 0,851 0,967 1,876 1,70 1,50 0,228 1,43 1,178 0,344 0,474 1,464 1,472 0,168 0,824 1,38 0,516 1,804 1,755 1,249 0,504 0,591 0,67 1,636 0,831 0,404 0,919 0,968 1,869 0,15 1,870 0,250 1,246 1,590 1,609 1,738 0,692 1,756 1,879 1,27 1,375 1,625 1,63 0,711 1,76 1,651 0,503 0,3 1,629 0,319 1,335 0,600 1,899 0,673 1,526 1,500 1,509 1,26 0,596 1,551 0,642 0,170 1,269 1,692 0,335 1,814 1,381 0,876 0,937 1,802 1,408 1,992 1,308 1,96 0,406 1,687 0,250 0,554 1,632 0,969 1,57 0,40 1,282 0,263 1,525 0,427 1,507 1,478 0,197 0,233 1,705 1,824 0,286 0,63 0,377 0,467 1,25 0,595 1,79 1,148 1,318 0,193 0,50 1,718 0,574 0,919 1,757 0,3 0,718 0,902 1,885 1,583 0,368 0,655 0,328 0,733 1,678 0,224 0,702 0,16 1,189 1,52 0,191 1,682 0,121 1,416 1,239 1,759 0,724 0,884 0,864 0,193 0,816 0,739 1,302 0,775 1,731 0,790 0,782 1,660 1,330 1,452 1,983 0,741 1,405 1,118 1,542 0,784 1,897 0,96 1,580 0,119 1,428 0,309 0,817 1,367 0,925 1,888 0,604 1,508 1,658 0,930 1,109 0,36 0,232 0,419 0,759 0,468 0,885 0,948 1,349 0,343 1,422 0,602 0,51 1,381 0,449 1,613 0,142 1,783 0,64 1,71 1,699 0,573 1,551 0,552 0,171 0,142 0,244 1,176 0,490 0,288 0,221 0,128 0,206 1,420 0,940 0,493 0,823 1,417 0,932 1,203 1,525 0,214 0,150 0,620 0,596 1,103 0,162 0,432 0,458 0,343 0,405 1,291 1,121 0,442 0,367 1,614 1,120 1,82 0,257 1,119 0,315 1,167 1,748 1,757 0,41 0,41 1,899 1,173 1,925 0,369 0,888 0,688 0,21 1,244 0,323 0,328 0,474 1,255 0,345 0,928 0,468 0,252 1,362 0,842 1,619 0,791 1,986 0,269 1,428 1,624 1,119 0,989 1,757 0,825 0,606 1,927 0,914 1,80 1,360 1,80 1,101 0,713 0,176 1,160 1,599 1,229 1,368 1,534 0,784 1,578 1,60 0,548 1,858 1,51 0,960 0,116 0,345 0,134 0,162 0,842 0,92 0,961 1,714 0,623 0,553 0,304 1,948 0,870 1,849 0,773 1,592 0,670 1,306 0,3 0,692 1,126 0,453 1,667 1,708 1,330 0,441 1,778 1,229 1,715 1,101 0,708 1,936 1,6 0,56 1,386 0,646 0,665 1,637 0,160 1,13 1,465 0,434 1,867 0,395 1,559 0,494 0,886 1,219 1,824 0,547 0,339 1,513 0,158 1,572 0,147 0,715 1,209 0,657 0,120 1,816 1,316 0,248 0,802 1,898 0,648 0,161 0,204 1,666 0,333 0,514 1,642 0,296 0,843 0,332 1,879 1,46 1,0 0,656 0,849 1,765 0,266 1,85 0,374 0,771 1,266 0,730 1,468 1,396 1,544 1,542 1,869 1,771 1,345 0,337 0,938 0,366 0,449 1,320 0,84 0,444 0,285 0,66 1,353 0,692 0,954 0,215 0,378 0,343 0,893 0,122 1,529 1,355 0,832 1,13 1,768 1,252 0,729 1,696 0,889 1,867 0,548 1,814 1,713 1,51 0,620 1,447 0,415 1,373 1,369 0,402 1,490 1,683 0,22 0,65 0,110 0,889 0,597 1,715 1,416 1,8 1,258 1,561 1,129 0,115 1,978 0,582 0,998 1,793 1,990 0,483 1,545 0,915 1,468 1,287 1,726 0,321 0,166 0,352 0,934 1,666 0,75 0,265 0,677 1,254 0,763 0,57 1,491 0,425 0,457 0,855 1,648 1,509 1,524 0,475 1,814 0,775 0,780 0,762 0,478 1,794 1,479 1,436 1,223 0,230 1,580 1,940 1,18 1,254 1,107 0,532 1,342 0,152 1,942 0,670 0,233 0,744 1,957 1,37 0,738 1,45 0,893 0,159 1,760 0,198 1,574 1,849 1,191 1,881 1,677 1,957 1,609 0,937 1,439 0,324 0,895 1,717 0,260 1,143 1,759 1,288 1,215 0,864 0,517 0,498 1,570 0,642 1,924 0,600 0,910 1,98 0,967 0,811 1,130 0,666 1,225 1,857 1,412 1,581 1,180 0,181 1,768 0,102 1,537 0,940 1,680 0,621 0,946 0,95 0,772 1,992 0,237 0,251 0,452 1,887 1,201 0,388 1,223 1,72 0,959 0,371 0,811 0,481 1,246 1,622 0,378 1,811 0,636 1,917 0,193 0,212 1,752 0,939 0,115 1,398 0,292 0,781 0,159 0,589 1,197 0,564 0,732 1,996 0,934 1,571 1,798 0,187 0,415 0,368 1,505 1,971 1,676 1,701 1,152 1,478 1,192 0,557 0,322 1,326 1,344 0,546 1,629 0,729 1,138 1,697 1,187 1,406 0,707 0,905 0,39 0,804 1,626 1,524 0,868 0,667 0,444 1,419 1,827 1,500 1,407 0,463 0,164 0,343 1,94 0,659 0,209 0,277 1,801 1,581 0,183 0,210 1,90 0,448 1,33 0,244 1,709 1,51 0,683 1,147 0,807 1,800 0,71 1,138 0,553 1,645 1,967 0,633 0,347 0,138 1,259 0,296 0,446 0,281 1,538 1,588 0,196 0,573 0,651 1,702 1,145 1,364 0,309 0,794 1,623 0,677 1,139 1,840 1,812 1,994 1,839 0,374 0,255 1,387 0,429 1,616 0,534 0,166 1,482 0,554 0,80 1,137 1,260 0,438 0,185 1,420 1,824 0,668 0,560 0,643 1,68 0,68 0,773 1,210 0,836 0,795 1,594 1,657 1,866 0,244 1,577 1,569 1,143 0,366 0,574 0,982 0,299 1,998 1,820 0,92 1,910 1,806 0,355 1,781 1,182 1,82 0,687 1,146 1,305 1,323 1,431 1,501 0,410 0,994 1,987 0,682 0,157 0,491 1,92 0,379 0,425 1,569 1,481 1,216 0,137 1,715 0,700 0,422 0,109 0,540 1,583 1,666 0,599 0,84 1,549 0,332 0,227 0,498 0,781 0,172 1,76 1,608 1,560 0,781 1,784 1,212 1,808 0,177 1,513 0,303 0,375 0,639 1,74 0,263 1,387 1,851 1,171 0,657 1,662 0,538 0,957 0,523 1,488 0,415 0,394 0,451 1,41 0,876 0,777 1,985 0,772 0,19 1,98 1,18 1,823 0,61 0,874 1,148 0,727 0,573 1,675 1,567 0,832 1,68 0,418 0,101 1,744 1,72 0,381 0,410 1,474 1,858 1,286 0,103 1,479 1,70 0,511 1,187 1,525 1,523 1,722 1,225 0,636 0,195 0,624 1,709 1,765 0,183 1,817 0,487 1,268 0,357 1,471 1,584 1,314 1,964 0,45 0,37 1,116 1,543 1,652 1,725 1,95 0,58 0,292 1,405 0,860 0,304 1,470 1,474 1,847 0,661 1,649 0,373 0,28 0,780 1,970 1,341 1,226 1,297 0,41 1,837 0,256 0,731 1,248 1,629 0,985 1,901 0,423 0,584 0,569 1,676 0,94 1,560 1,640 1,172 0,669 0,643 0,336 1,620 0,474 1,87 1,3 0,344 1,763 0,12 0,16 0,464 0,512 1,761 0,929 1,346 1,906 0,138 1,886 0,902 0,943 1,211 1,326 0,16 1,314 0,931 1,656 0,669 0,205 1,805 0,677 1,250 1,295 0,144 1,866 0,20 0,869 1,644 1,329 1,499 0,890 1,903 1,891 1,380 1,15 1,584 0,958 1,373 1,645 0,336 0,185 1,79 0,112 1,796 1,891 1,512 0,651 0,608 1,336 0,281 0,451 1,126 1,317 1,183 0,945 0,87 0,448 1,483 0,746 1,485 0,194 0,179 1,397 1,625 1,606 1,614 1,893 1,249 1,246 1,22 0,525 1,642 0,365 1,115 1,660 1,615 1,167 1,867 0,215 0,147 1,447 1,148 0,192 1,154 0,404 1,999 0,350 0,296 1,171 0,332 0,65 1,946 1,812 1,167 1,432 1,219 1,429 1,200 0,344 0,32 1,834 0,327 1,322 0,457 1,6 0,84 0,665 0,348 0,279 0,589 1,502 1,978 1,796 0,496 1,130 0,368 0,625 1,256 0,489 0,358 1,498 0,12 1,24 1,523 0,886 0,884 0,43 0,678 1,133 1,175 0,997 0,549 0,784 0,172 0,579 1,944 1,441 1,416 1,817 0,478 1,716 1,7 1,125 1,287 0,952 1,95 1,1 0,190 1,434 0,785 0,73 0,753 0,614 0,797 1,706 0,523 1,60 1,752 1,391 1,150 0,373 1,575 1,955 0,313 0,382 0,481 1,215 1,879 0,393 0,832 1,210 0,680 0,579 0,816 1,868 0,220 1,58 1,152 0,44 1,135 0,757 0,653 0,255 1,476 0,230 1,473 1,542 1,793 0,429 0,669 0,840 0,612 1,721 1,335 1,409 1,831 0,549 1,839 0,37 1,618 0,651 1,392 1,995 0,806 1,664 0,354 0,493 1,165 1,459 1,58 0,245 0,73 1,903 0,225 0,490 1,986 0,314 1,115 0,727 1,18 1,584 0,920 0,504 1,793 0,184 1,842 1,184 0,179 0,68 0,760 0,78 1,64 1,779 1,799 0,847 1,683 1,535 0,548 1,74 0,427 1,27 0,742 1,54 1,39 1,792 0,997 0,494 1,165 0,630 0,709 1,875 0,48 0,391 0,840 0,254 1,430 0,981 1,265 1,696 0,312 1,53 0,218 1,707 0,126 0,313 1,957 0,925 0,432 1,798 0,381 0,525 0,291 0,891 1,322 0,92 0,365 1,765 0,877 0,388 0,721 1,312 0,304 1,209 1,531 0,382 0,134 1,77 1,857 1,998 1,597 1,408 1,851 1,712 0,600 1,286 0,385 0,729 1,382 1,811 1,936 1,315 1,720 0,636 1,76 1,361 0,44 0,329 1,180 1,466 0,782 0,122 1,191 0,815 1,614 1,582 1,968 1,193 1,603 1,972 0,195 0,297 0,900 0,633 1,88 1,931 1,100 0,514 1,16 0,124 1,530 0,504 1,757 0,61 0,605 1,528 1,935 0,651 0,680 0,514 0,207 0,591 1,390 0,734 0,827 1,712 1,626 1,331 1,846 1,34 0,821 1,821 1,271 0,417 0,463 1,195 0,773 1,183 1,103 1,467 0,705 0,718 0,203 1,8 0,680 0,47 1,617 0,985 0,52 0,582 1,605 1,75 1,878 0,587 1,656 1,262 1,679 0,78 1,421 1,319 1,785 0,721 0,257 0,872 1,275 0,500 0,50 0,731 0,199 0,569 1,558 1,50 1,414 1,659 1,894 1,816 1,451 0,744 1,845 0,311 1,18 0,98 0,286 0,195 0,553 1,632 1,594 0,441 0,564 0,519 1,188 0,615 0,933 0,773 0,923 0,232 0,338 0,220 1,592 0,612 1,821 1,158 1,658 0,53 1,719 0,455 0,508 0,484 1,774 1,813 1,975 1,687 0,959 1,744 1,109 1,890 0,410 0,655 1,723 1,357 0,765 0,708 1,937 0,366 1,441 0,673 1,159 1,63 1,889 0,492 0,521 1,714 0,251 1,994 0,552 1,980 0,809 0,823 0,431 1,727 0,597 0,880 1,59 1,886 1,315 1,266 1,603 1,788 0,102 0,617 0,10 1,513 1,780 0,420 1,716 1,212 0,67 1,55 1,342 0,496 1,299 0,61 1,400 0,596 0,102 1,342 1,416 1,93 1,868 1,540 1,391 0,26 0,708 1,995 0,729 0,426 1,771 1,245 1,27 1,696 0,170 1,811 0,88 1,431 0,170 0,748 1,754 0,38 0,389 0,404 1,62 0,936 0,484 0,530 1,709 0,786 0,109 1,139 1,754 1,280 1,868 1,337 1,307 0,662 1,721 1,629 0,162 1,995 1,944 0,548 0,731 1,159 0,416 0,499 1,676 0,242 1,978 1,408 1,931 1,185 1,220 1,681 0,879 0,604 0,780 0,556 1,389 1,716 1,610 1,459 0,112 1,721 1,317 0,24 1,480 0,500 1,612 0,100 1,293 1,503 1,614 1,397 1,380 1,724 1,254 1,385 0,774 1,444 0,207 1,661 0,255 1,555 0,758 1,871 1,662 0,956 1,905 1,444 1,868 0,373 1,865 0,614 1,225 0,222 1,952 1,533 0,606 0,598 0,653 1,91 1,539 1,391 1,401 1,454 1,338 1,227 0,671 0,437 0,809 1,878 0,152 1,734 0,748 0,881 0,840 1,571 1,139 0,400 0,7 1,539 0,531 1,215 0,803 1,66 1,130 0,321 0,766 1,199 1,312 0,278 0,632 1,729 0,359 1,616 0,782 1,753 0,274 0,706 1,811 0,587 1,521 0,633 0,910 1,521 1,610 0,18 0,704 1,630 1,320 0,301 0,114 0,89 1,87 0,885 1,998 1,932 0,293 0,684 0,132 0,382 1,12 1,60 0,62 0,457 1,528 0,198 1,885 0,537 1,886 0,336 1,788 0,254 1,707 1,7 0,789 1,323 0,147 0,453 0,722 1,472 0,15 0,980 1,102 1,894 1,591 1,583 0,161 1,350 0,606 0,738 1,661 0,439 0,720 0,913 1,214 0,386 0,39 1,411 1,362 1,803 1,184 0,202 1,169 1,308 0,894 1,816 0,788 1,719 0,405 1,201 0,274 1,401 1,387 0,962 0,722 1,140 1,356 1,346 1,294 0,391 1,93 0,935 0,651 0,337 0,372 0,175 0,80 0,913 0,283 0,267 1,8 0,715 0,231 1,660 0,107 0,624 1,105 0,768 1,545 1,522 0,395 1,474 1,668 1,907 0,476 1,661 0,495 0,654 0,731 0,127 1,149 0,609 1,341 1,578 0,164 0,271 0,327 0,205 0,993 1,710 1,449 0,874 1,536 0,378 1,643 1,107 1,671 0,273 1,327 1,820 1,451 0,612 0,400 1,533 1,967 1,380 0,587 1,909 0,183 0,947 0,613 0,55 1,298 0,478 0,590 0,728 1,399 1,9 1,672 1,814 0,385 1,741 0,109 0,456 0,3 1,765 0,532 1,563 0,939 0,535 0,38 1,577 1,756 0,233 1,511 1,194 0,991 0,109 0,29 0,61 1,480 1,69 0,717 0,284 0,774 0,54 0,476 0,618 0,352 1,240 1,318 1,593 1,959 1,101 1,192 1,923 0,126 0,348 1,996 0,804 0,542 1,983 1,167 1,549 1,939 0,785 0,558 1,665 0,834 1,375 0,74 1,891 1,220 1,832 0,427 0,897 1,400 1,856 1,860 0,517 0,963 0,555 0,433 0,34 0,902 0,923 0,486 1,324 1,816 0,390 0,875 0,491 1,32 1,465 0,308 1,207 0,233 1,806 0,298 0,584 0,336 1,515 0,662 1,779 1,812 1,931 0,872 1,972 1,181 1,869 0,800 0,663 0,112 0,303 0,232 1,665 1,204 0,885 0,896 1,499 1,527 0,99 0,210 0,982 0,567 0,732 1,126 0,331 0,117 0,406 1,804 0,625 1,515 1,108 1,263 0,529 1,585 1,379 0,375 1,932 1,966 1,524 1,992 0,384 1,345 0,528 1,243 0,827 1,242 0,11 1,990 1,480 1,575 0,351 1,202 1,396 0,189 1,595 1,91 1,182 0,804 1,221 0,116 0,877 0,809 0,702 1,968 0,614 0,936 1,219 1,306 1,380 0,389 0,231 1,229 0,928 0,379 1,515 1,587 0,3 1,911 0,656 0,881 1,164 1,325 1,632 1,270 1,579 0,658 1,712 1,914 1,660 1,122 0,405 1,894 0,195 1,870 1,667 0,421 0,574 0,269 0,205 0,964 0,987 0,829 1,884 1,99 1,294 1,776 0,441 1,894 1,109 1,433 0,946 1,795 0,277 0,557 0,847 0,783 1,959 1,725 1,666 0,765 0,333 1,926 0,538 1,138 1,293 1,469 0,959 1,67 0,706 0,754 0,461 0,464 1,135 0,158 1,218 0,614 1,231 0,9 1,939 0,155 1,999 1,728 0,989 1,472 0,87 1,526 1,455 0,417 0,100 0,980 1,541 0,80 1,873 0,260 0,593 1,328 1,359 1,765 0,851 1,97 1,937 0,703 0,900 0,556 0,14 0,277 0,512 1,110 0,829 1,783 1,572 0,126 0,333 0,42 0,522 0,38 0,656 1,662 0,115 0,390 0,138 1,743 0,870 1,467 1,510 0,803 1,118 1,440 0,248 0,800 1,29 1,417 0,346 0,832 0,276 0,105 0,692 1,526 1,302 0,586 0,79 0,966 1,398 1,861 1,872 0,697 1,576 1,743 0,963 0,501 0,830 1,782 1,913 1,663 1,397 1,560 1,210 1,620 0,598 1,331 0,507 0,998 1,518 1,185 0,917 1,601 0,994 0,322 0,571 0,271 0,578 1,837 1,489 0,213 1,758 0,938 0,291 0,294 1,74 0,569 1,564 0,773 0,247 1,176 0,209 1,858 1,137 0,669 0,343 0,823 0,860 1,831 1,342 0,113 0,722 0,777 1,150 1,882 0,757 1,322 1,867 0,762 1,263 1,176 0,351 1,974 0,499 1,189 0,426 0,905 0,115 0,3 1,945 0,905 1,686 1,605 0,43 0,724 0,716 1,239 0,759 1,220 1,29 1,906 1,848 1,640 0,218 1,380 1,898 0,711 0,942 0,912 1,523 1,306 1,562 0,724 1,682 1,725 1,84 1,74 1,441 1,292 0,410 1,175 0,767 0,370 0,713 1,598 0,932 0,358 0,260 0,273 1,875 1,388 0,615 1,252 0,312 0,53 1,476 0,706 0,405 1,252 1,691 1,732 0,183 1,702 0,539 1,421 1,496 1,493 1,871 0,458 1,689 1,312 0,249 1,101 1,66 0,324 0,852 0,950 1,749 0,458 0,311 1,989 1,904 1,734 0,77 1,976 0,890 0,876 1,359 0,239 1,697 0,206 0,228 1,193 1,55 1,120 1,623 0,605 1,592 1,571 0,967 1,749 0,6 1,155 0,653 0,712 0,711 0,192 0,363 1,286 1,516 0,260 0,922 1,280 0,485 1,480 1,229 0,951 1,167 1,640 0,862 0,904 0,852 0,424 0,829 0,369 0,322 1,162 1,221 0,440 1,96 1,671 0,635 1,944 1,162 0,540 1,669 1,549 1,755 1,826 0,565 1,265 0,640 1,412 0,970 1,725 0,891 0,700 1,172 0,601 0,24 0,976 1,807 0,690 1,315 0,614 1,283 1,610 1,351 1,312 0,490 0,956 1,742 0,650 1,465 0,961 1,228 0,162 0,88 0,232 1,228 1,912 0,500 0,700 0,599 1,348 0,554 0,359 1,458 0,229 0,754 1,870 1,345 0,801 1,873 1,789 0,791 1,726 0,330 1,981 0,369 0,290 1,311 1,301 0,469 1,253 1,375 0,66 1,189 1,999 0,900 1,604 1,794 1,999 1,229 1,601 1,816 0,51 1,200 1,302 0,542 1,476 1,824 0,329 1,745 1,64 1,575 1,927 1,7 1,13 0,994 0,500 0,291 0,360 0,732 0,309 1,685 0,337 0,355 0,562 1,469 1,949 1,694 1,280 1,790 1,608 1,742 1,39 1,101 0,816 0,708 0,390 0,870 0,975 0,403 0,609 1,20 0,802 1,49 0,146 0,667 0,974 1,770 0,829 1,833 1,862 1,762 1,332 1,102 0,707 1,761 0,919 0,79 0,753 0,999 1,6 1,805 0,513 0,578 1,461 1,4 1,496 0,135 1,388 0,434 0,829 0,249 1,974 0,859 1,547 0,56 0,80 0,425 1,272 0,562 1,111 0,950 1,316 0,446 1,279 1,499 0,467 0,233 0,894 0,488 0,752 0,517 1,25 0,525 1,451 1,489 1,375 1,745 1,502 1,97 1,950 0,781 1,329 1,909 0,383 1,533 1,486 1,506 0,234 0,563 1,321 1,30 1,273 0,702 0,949 1,197 1,428 0,115 1,186 0,789 0,837 1,411 1,253 1,327 0,314 0,974 0,19 1,18 1,191 1,835 0,532 1,66 1,943 1,234 1,709 0,592 0,272 0,528 0,111 1,352 0,601 1,629 1,268 1,46 0,77 1,150 1,408 1,7 0,403 0,119 1,885 1,901 0,877 0,809 1,615 1,864 1,793 1,490 1,404 1,654 1,147 1,608 0,739 0,9 0,846 1,767 1,16 1,984 1,508 1,352 0,167 1,728 1,707 0,161 0,728 0,69 0,18 0,701 0,141 1,110 0,793 0,257 1,721 0,88 0,974 0,374 1,210 0,778 1,168 1,253 0,149 0,176 0,888 0,926 1,970 0,346 0,478 1,431 0,576 0,130 1,862 0,600 1,111 0,720 1,663 0,465 0,401 0,1 1,174 1,589 1,813 0,493 1,742 0,938 1,306 0,486 0,528 0,635 0,749 0,837 0,16 1,406 0,298 0,309 0,229 0,306 0,641 0,671 1,57 0,731 1,617 1,763 1,656 1,919 1,421 1,814 1,368 0,909 1,735 0,227 1,571 0,735 1,296 0,383 0,208 0,378 0,415 0,848 1,426 0,94 1,93 1,734 0,334 1,217 0,431 0,273 1,806 0,755 0,668 1,845 1,758 1,729 1,134 1,980 0,548 0,934 1,467 1,191 0,760 1,76 0,410 1,337 1,867 0,346 1,675 0,190 0,487 1,549 1,928 0,763 1,724 1,626 1,803 1,763 1,264 1,259 1,296 1,14 0,655 0,457 1,645 1,98 0,371 0,84 0,487 1,401 0,531 1,398 1,78 0,478 0,179 0,328 1,29 0,259 1,378 0,47 1,943 0,128 0,796 0,658 1,147 1,158 0,990 0,853 0,163 0,815 1,388 1,970 1,604 1,872 0,565 1,706 1,456 0,49 0,806 1,882 1,985 0,309 1,452 0,534 1,700 1,122 1,908 0,728 1,928 1,257 1,472 1,1 1,322 1,390 0,343 1,482 1,921 0,769 1,398 0,58 1,309 1,136 0,933 1,257 0,569 1,514 0,731 1,813 0,628 1,657 1,817 1,956 1,651 0,161 0,183 0,593 1,34 1,437 0,667 1,876 1,215 0,749 1,4 1,675 0,460 0,382 1,38 1,675 0,544 1,749 0,84 0,311 0,784 0,348 0,227 1,143 1,916 1,195 1,498 0,885 0,145 0,939 1,978 0,586 0,908 1,937 0,704 0,4 0,758 1,11 0,742 1,738 0,385 1,273 1,161 0,763 0,127 1,591 0,234 1,726 1,909 1,604 0,124 0,729 1,341 0,576 1,368 0,127 1,127 0,86 1,183 0,197 1,173 1,746 0,49 0,206 0,305 1,156 1,712 1,607 0,960 0,971 0,719 0,146 0,643 1,122 1,662 0,537 1,521 0,321 0,605 1,864 0,555 1,903 0,980 1,489 0,217 0,448 1,803 1,516 0,883 0,328 0,11 1,572 1,796 1,268 0,544 1,252 0,252 1,666 0,401 0,315 0,924 1,85 0,507 1,495 0,973 1,944 0,540 0,883 0,68 1,133 0,756 1,256 1,871 1,224 0,250 1,82 0,187 0,333 0,567 1,432 1,973 0,863 1,673 0,377 1,208 1,606 0,394 1,163 0,316 1,527 0,741 0,423 0,721 0,569 0,903 0,779 1,754 0,719 0,907 0,859 1,140 1,245 0,334 1,944 1,430 1,875 1,693 1,172 1,918 0,108 1,655 1,133 0,283 0,224 0,45 1,902 1,104 0,42 1,382 1,243 1,505 1,844 0,294 0,967 1,941 0,594 1,497 0,462 1,37 1,972 0,89 1,296 0,187 0,495 1,48 0,184 1,971 1,761 0,597 1,284 0,960 0,564 0,201 0,501 0,652 0,847 1,96 1,698 0,993 0,767 0,903 1,19 0,70 1,294 1,39 0,105 1,684 0,593 1,640 1,814 1,268 1,837 0,391 0,791 0,453 0,608 0,525 0,324 0,941 0,838 1,717 0,67 0,257 1,795 0,298 1,264 0,951 0,971 0,108 1,784 0,176 0,712 1,141 1,636 0,653 0,111 0,363 1,444 1,895 0,24 1,765 1,409 0,775 1,740 0,348 0,826 0,953 1,839 1,808 1,745 0,121 1,712 1,498 0,372 1,618 1,379 1,961 1,146 1,344 0,851 1,795 0,229 0,241 1,784 1,701 1,846 0,117 0,740 1,256 1,219 0,268 1,809 0,787 1,815 1,46 1,598 1,458 0,841 1,319 0,846 1,31 1,366 1,610 1,271 1,113 1,41 0,581 0,361 0,889 0,491 1,124 0,867 0,926 0,374 1,958 0,290 0,579 0,648 1,585 0,861 1,823 0,769 1,340 1,622 1,205 0,78 0,878 1,72 0,790 0,984 0,203 1,208 1,426 1,850 1,298 1,502 0,826 0,774 0,287 1,812 1,284 1,423 0,903 0,726 1,943 0,201 0,445 1,849 0,115 0,235 1,447 0,690 1,309 0,494 1,82 0,540 0,122 0,102 0,872 1,991 0,890 1,40 1,535 1,273 1,267 0,7 1,7 1,803 0,609 0,33 0,166 0,732 1,365 0,980 1,608 1,684 1,317 1,737 0,569 0,559 1,976 1,439 1,946 1,965 0,522 0,864 0,347 1,185 1,690 0,929 1,755 0,387 1,831 0,564 0,377 0,456 0,266 0,601 1,986 0,470 0,389 0,272 1,637 0,788 1,156 1,48 0,840 1,320 0,545 1,221 1,458 1,673 1,79 0,475 0,963 1,366 1,862 0,809 1,27 1,397 0,554 1,503 0,774 1,720 0,994 0,492 0,416 1,242 1,206 0,925 1,625 1,291 1,819 1,474 1,552 1,965 0,376 1,775 1,757 1,872 1,936 0,419 1,326 0,231 1,181 1,160 0,490 0,806 0,525 1,567 1,249 0,111 0,661 1,702 0,750 0,544 0,76 0,656 0,983 1,875 1,519 0,322 1,174 0,212 1,34 0,484 1,355 0,777 0,186 0,911 0,294 1,601 1,322 0,598 1,64 1,245 1,614 0,378 0,98 1,683 0,723 0,411 1,924 0,741 0,458 1,657 0,296 0,31 0,957 1,885 0,465 0,137 1,512 0,848 1,692 0,342 1,630 1,264 0,938 1,283 1,308 1,464 0,645 1,76 1,257 1,394 0,684 1,885 1,529 0,972 0,562 1,849 1,785 0,17 0,840 0,161 0,877 1,487 0,14 1,435 0,307 1,568 1,554 0,594 0,532 0,163 1,443 1,253 1,277 1,556 1,231 0,427 1,45 1,314 0,998 0,246 0,625 0,936 1,649 1,655 0,711 0,940 0,717 0,266 0,27 1,40 1,959 1,373 1,666 1,453 1,947 0,369 1,863 1,801 0,207 1,329 1,646 1,14 1,80 0,201 1,742 1,203 0,928 1,774 0,509 0,941 1,334 1,387 0,914 0,790 1,141 0,566 1,298 1,566 1,418 1,382 0,102 1,309 1,9 0,28 1,555 0,135 0,196 1,304 0,417 1,387 0,581 0,421 0,435 0,943 1,459 1,654 0,117 0,445 0,2 0,645 1,86 0,488 1,288 1,851 0,614 1,445 1,828 0,500 1,601 0,972 1,20 1,893 0,554 1,302 1,530 1,391 0,561 1,805 1,543 1,832 0,819 0,963 0,780 1,358 0,347 1,360 0,275 1,648 0,193 0,479 0,544 0,452 1,267 1,883 0,978 0,996 1,394 1,805 0,285 1,245 0,928 0,195 0,595 1,839 0,927 0,940 1,549 1,177 0,707 1,951 0,25 1,487 0,906 0,698 1,746 1,89 0,989 1,227 1,198 1,725 0,470 1,56 1,552 1,165 0,839 1,998 1,265 0,897 1,408 0,893 1,577 1,246 0,675 0,508 1,780 0,718 0,962 0,434 1,894 0,300 1,930 0,893 0,909 1,382 0,871 1,511 0,679 0,339 0,348 0,868 1,335 0,31 0,666 1,829 0,443 1,173 1,160 1,659 1,249 0,885 1,996 1,114 1,734 1,219 1,332 1,36 1,689 1,169 0,300 0,489 1,704 1,655 1,433 0,214 0,492 1,548 0,758 0,203 0,681 0,43 1,371 0,383 0,456 1,877 0,238 0,906 0,202 1,327 1,381 1,590 0,616 1,947 1,332 0,368 1,165 1,850 0,55 0,402 1,786 0,477 1,500 0,345 0,912 1,908 1,392 1,595 1,780 0,729 0,62 0,473 1,894 1,895 1,984 0,820 0,203 1,84 0,971 0,363 1,366 1,777 0,36 1,434 1,395 1,748 0,111 1,57 0,877 1,809 0,63 0,42 1,837 1,578 0,51 1,321 0,592 0,611 1,268 1,467 0,483 1,261 1,293 0,651 0,919 1,348 1,103 0,159 1,711 0,61 0,260 0,106 1,369 1,898 0,229 0,983 1,312 1,794 0,12 0,104 1,480 1,722 1,435 1,242 0,455 1,72 0,353 1,652 1,573 0,980 0,128 1,449 1,300 0,399 1,113 0,231 1,116 0,516 0,924 0,996 0,622 0,387 1,890 0,168 0,448 0,983 1,883 0,550 1,897 0,974 0,465 0,937 0,319 0,185 0,81 1,574 0,875 0,599 1,675 1,908 1,607 1,834 0,826 1,685 0,455 0,371 1,661 1,264 1,25 1,596 1,555 0,688 0,789 1,481 0,246 0,507 0,78 1,358 0,154 0,810 0,33 0,391 0,870 0,917 1,506 1,222 1,223 0,800 0,308 0,967 1,145 0,350 1,6 0,957 0,173 0,417 1,478 0,924 1,21 1,868 0,95 1,271 0,351 1,978 0,903 1,966 0,491 0,364 0,39 1,307 0,254 1,931 1,342 0,656 1,817 1,899 1,951 0,55 1,233 0,221 1,954 0,324 1,76 0,477 1,959 1,435 0,306 0,312 0,182 1,917 0,961 1,386 1,66 1,224 0,687 1,417 1,408 1,615 0,525 0,58 1,134 0,281 0,897 1,293 0,367 0,671 1,13 0,745 1,543 0,160 0,7 0,593 0,385 0,788 1,113 1,287 0,313 1,736 1,972 0,118 1,807 0,796 1,143 0,493 0,682 0,923 1,65 0,606 1,5 1,111 1,712 1,38 1,321 0,654 1,135 1,482 0,745 1,551 1,506 0,704 0,746 0,469 0,330 1,475 1,941 0,219 1,196 1,590 1,183 0,107 0,693 0,63 1,241 1,350 0,186 0,996 0,202 0,560 0,21 0,320 0,541 0,144 0,158 0,44 0,873 0,84 0,411 1,275 0,37 1,47 0,693 0,113 1,736 1,109 1,168 1,928 0,242 0,70 1,820 1,893 0,858 0,902 1,901 0,221 1,843 0,556 0,349 0,173 1,386 0,333 0,823 1,276 0,246 0,655 1,577 1,70 0,416 1,554 1,904 0,717 1,105 0,796 0,508 0,196 1,869 0,734 0,185 1,569 1,932 1,730 1,431 1,478 1,629 1,819 1,856 1,402 0,129 0,404 1,828 1,770 1,360 1,80 0,260 1,666 0,263 1,64 0,633 0,200 1,646 1,322 1,294 0,675 0,84 1,560 0,790 1,281 0,295 1,235 1,985 1,909 1,756 1,606 1,620 0,789 0,482 0,453 1,0 1,54 1,782 1,388 1,892 1,306 0,772 1,257 0,284 0,728 1,468 0,538 0,50 1,23 0,353 1,601 1,101 1,166 0,947 0,653 0,206 0,762 1,928 1,437 1,13 1,628 0,82 1,24 0,316 1,916 1,402 1,438 1,971 0,338 1,330 1,9 0,505 1,747 0,883 1,514 0,712 0,123 1,151 1,301 1,33 0,103 0,50 1,761 0,706 1,192 1,674 1,502 1,690 1,920 1,290 0,508 1,429 0,258 1,708 0,691 0,593 0,814 0,379 0,635 1,895 1,258 1,268 1,459 0,348 1,435 1,103 0,442 0,58 1,688 1,256 0,414 1,221 1,606 1,303 1,842 1,641 1,596 1,395 0,499 0,166 1,2 0,284 0,470 0,79 1,202 0,582 0,380 0,875 1,561 0,453 1,144 0,559 1,319 1,565 1,312 0,639 0,189 0,336 0,604 0,889 0,198 1,360 1,296 1,898 1,94 0,936 0,169 1,191 0,150 1,977 1,403 0,61 1,793 1,483 0,533 1,928 0,649 0,66 0,321 1,106 0,185 0,580 0,183 0,737 0,438 1,61 0,304 1,706 1,378 1,604 0,886 0,529 1,391 0,391 0,351 1,807 0,934 1,40 1,20 0,353 0,697 0,348 0,283 0,697 1,318 0,883 1,210 0,785 0,724 0,467 0,82 0,56 0,546 1,619 0,932 1,18 0,702 0,90 0,40 1,388 1,204 1,154 0,595 0,641 1,276 0,452 0,779 1,76 0,654 1,355 0,599 0,212 0,214 1,675 0,485 1,566 1,481 1,92 1,444 1,716 0,868 1,113 1,620 0,15 1,875 1,159 1,966 0,588 0,782 1,359 0,70 0,879 1,776 1,804 0,45 1,456 1,820 0,152 0,367 0,762 0,599 0,697 0,421 0,417 1,667 1,870 0,802 0,151 0,447 1,988 1,436 0,198 0,327 1,257 1,304 0,117 1,351 0,127 1,891 0,264 0,895 1,30 0,290 0,152 0,554 1,44 1,287 1,264 1,493 0,371 0,437 0,300 1,413 0,368 1,587 0,277 1,409 1,962 1,413 1,810 0,968 1,355 1,312 1,815 1,136 0,849 0,764 0,949 0,426 0,751 1,623 0,591 0,743 0,383 0,127 0,98 1,904 1,755 0,337 1,831 0,7 1,175 1,364 0,850 1,325 1,372 1,32 0,861 0,763 0,615 0,116 1,614 1,366 1,75 0,402 0,625 0,399 0,922 1,157 0,159 1,793 1,417 0,899 0,923 0,69 0,416 1,593 0,529 0,569 1,278 1,828 1,756 0,508 1,799 0,101 0,118 1,877 1,132 1,112 1,943 0,907 1,561 1,722 1,646 0,309 1,536 1,421 1,205 0,137 1,21 1,148 1,614 1,669 0,135 0,517 1,64 1,402 1,815 0,541 0,930 0,390 1,263 1,54 0,644 1,458 1,163 1,776 1,515 1,723 0,910 0,251 1,983 1,511 1,639 0,425 0,662 1,361 0,228 1,456 1,776 1,993 1,595 1,662 0,791 1,492 0,344 1,256 0,39 1,936 1,430 0,853 0,400 1,76 0,266 1,730 1,291 0,252 0,664 0,920 0,288 1,902 0,370 1,908 0,736 1,17 0,132 1,525 1,242 1,98 0,796 1,224 0,982 1,560 0,836 0,500 1,607 0,854 1,406 1,133 0,206 1,902 1,719 1,653 1,877 1,933 1,927 1,852 0,87 1,153 0,44 1,257 0,550 1,930 0,635 1,907 0,716 0,262 1,399 1,392 1,789 1,873 0,564 1,818 1,103 1,348 0,617 0,698 0,710 1,58 0,614 1,726 1,310 0,221 0,500 1,203 1,770 0,897 0,236 1,880 1,128 1,743 1,211 0,431 0,731 1,659 1,148 0,49 1,537 1,884 1,576 1,175 0,884 1,271 1,929 0,645 1,521 0,409 1,518 0,10 1,42 1,0 1,338 0,530 1,183 1,203 0,774 0,418 0,677 1,32 1,115 1,335 0,767 0,514 0,730 0,297 0,91 0,27 0,718 0,636 1,639 0,917 0,324 1,773 1,800 1,606 1,445 1,745 1,286 0,871 1,974 1,637 1,17 0,923 1,454 1,402 1,668 0,176 0,471 0,320 0,419 0,745 1,824 0,929 0,807 0,292 1,138 1,678 0,780 1,983 0,939 1,984 1,937 1,877 1,216 1,624 0,534 1,880 0,135 1,349 0,906 1,75 0,955 1,840 1,723 1,58 0,430 0,299 1,120 1,247 0,976 0,976 1,205 1,929 0,838 0,765 0,269 0,252 0,127 1,359 0,495 1,179 0,432 0,104 0,409 1,33 1,309 1,894 0,991 1,661 0,273 0,564 1,736 0,65 0,914 0,324 0,889 0,792 0,253 1,483 1,385 1,101 0,745 1,909 0,86 1,542 1,924 1,294 1,412 1,876 0,360 1,362 0,703 0,867 0,666 1,445 0,474 0,383 0,349 1,272 0,355 0,123 0,815 1,183 1,872 1,918 1,713 0,895 0,697 1,209 1,609 1,465 0,278 0,207 0,799 0,796 1,459 0,876 1,822 1,541 1,499 0,568 0,604 1,573 1,448 0,602 0,760 1,553 1,467 1,949 0,602 0,474 1,289 0,22 0,218 0,817 1,705 1,157 0,301 1,216 0,695 0,110 1,837 1,805 1,294 1,24 0,156 0,90 0,588 1,447 0,682 0,414 1,313 1,730 1,586 0,968 1,522 0,106 0,632 0,37 1,912 1,76 0,520 0,878 0,351 0,783 0,185 0,917 0,592 1,995 0,699 1,474 1,947 1,947 0,653 1,784 0,512 1,764 1,333 0,372 0,149 0,675 0,359 1,704 1,886 1,524 1,954 1,179 1,258 0,905 0,592 0,689 1,427 1,239 1,624 0,722 1,512 0,576 0,400 1,492 1,990 1,939 0,610 0,571 0,729 1,39 1,710 1,809 1,47 0,325 1,817 0,621 0,484 1,423 0,757 0,149 1,413 0,235 1,39 1,370 1,980 0,654 0,315 0,233 1,656 1,35 0,772 1,549 0,456 0,259 0,222 0,914 1,618 1,903 1,594 0,342 0,541 0,346 0,665 1,382 0,735 0,823 0,251 1,296 0,418 1,223 1,621 0,587 0,566 1,113 0,937 1,353 1,686 0,63 0,24 1,38 0,635 0,311 0,508 0,502 1,233 1,98 0,457 0,194 0,888 0,676 1,372 1,376 0,542 0,161 0,121 0,111 1,292 1,772 1,876 1,586 0,93 0,512 0,109 0,551 0,728 1,197 0,49 1,578 1,217 0,677 0,369 0,355 0,215 1,996 0,206 0,819 1,924 0,642 0,930 1,348 1,134 0,784 0,358 0,983 1,789 0,181 1,147 1,420 0,327 0,0 1,596 1,510 1,211 0,135 1,352 1,633 1,649 1,85 1,486 0,586 1,171 1,153 0,817 1,612 0,122 0,133 0,628 1,571 0,391 0,636 1,766 1,771 0,65 0,959 0,87 1,201 0,739 1,160 0,383 0,186 1,525 0,976 0,489 0,382 1,951 0,304 1,447 1,898 1,915 0,924 1,696 0,431 1,760 0,135 0,159 0,823 1,819 0,729 0,928 0,249 0,572 0,487 1,143 1,95 1,91 1,180 0,971 0,136 0,822 0,48 0,965 0,338 1,516 1,515 0,115 0,720 1,279 1,964 1,32 0,247 1,111 1,120 0,154 0,854 1,591 0,517 1,924 1,876 1,137 0,642 0,911 1,709 0,538 0,283 0,121 1,794 1,4 0,143 1,37 0,312 0,243 1,346 0,50 0,465 1,259 1,689 1,852 1,267 0,195 1,714 1,8 1,853 1,605 1,353 1,377 0,856 0,661 0,314 0,706 1,341 0,315 1,727 0,233 0,145 1,246 1,15 1,232 1,720 0,533 1,348 1,435 1,285 1,820 1,709 0,941 0,282 0,346 0,708 0,483 1,645 0,686 1,37 1,399 0,919 0,759 0,931 0,97 0,69 1,657 0,293 0,302 1,440 1,617 1,520 0,139 1,606 0,68 1,885 0,358 0,721 0,530 0,316 1,969 0,914 0,39 1,588 0,423 0,218 0,706 1,828 0,763 0,968 0,673 0,94 0,862 1,365 1,529 1,261 0,853 1,726 0,715 1,105 0,915 0,96 0,837 1,190 1,14 0,512 1,134 1,533 1,798 1,188 0,735 0,560 1,203 1,487 0,627 1,888 0,940 0,590 0,413 1,593 1,495 1,244 0,968 0,787 1,560 1,425 1,688 1,54 1,146 0,22 0,283 1,587 0,153 0,588 0,225 1,998 0,530 0,495 1,877 1,151 1,659 0,275 0,271 0,309 1,808 1,716 0,85 1,529 1,750 1,525 1,470 0,108 1,37 1,463 1,195 1,247 0,987 0,435 1,352 0,51 1,336 0,171 1,722 1,776 0,404 1,910 1,821 1,529 0,295 1,809 0,207 1,75 1,903 1,495 0,161 0,815 0,197 0,705 1,907 0,288 0,753 0,115 0,926 0,555 0,409 0,827 1,965 0,717 0,395 0,870 1,379 0,245 1,124 1,392 0,573 1,334 0,372 1,672 1,420 1,274 1,131 0,264 0,228 0,715 1,159 0,188 0,982 0,920 0,466 1,414 0,114 1,758 0,401 0,535 0,147 1,356 1,427 0,298 0,1 1,424 0,939 0,665 1,110 0,300 1,2 1,640 1,283 0,376 1,872 1,561 0,641 0,192 1,501 0,942 0,884 1,997 0,709 0,304 1,145 0,123 0,932 0,162 1,6 0,270 1,609 0,587 0,829 0,693 0,662 0,410 1,864 1,911 1,305 0,681 1,595 1,62 1,812 0,548 1,495 1,266 1,185 1,239 1,901 1,316 1,63 1,496 0,413 1,198 1,86 1,909 1,120 0,71 0,300 0,826 1,234 1,574 1,35 1,48 0,7 0,584 0,753 0,858 0,953 0,38 1,384 1,563 0,361 1,907 0,48 1,335 1,757 0,252 0,598 1,991 1,439 1,78 0,123 1,232 1,717 0,865 1,963 0,214 1,742 0,519 0,884 1,563 1,837 1,67 0,745 1,266 1,280 1,990 1,215 0,378 0,412 1,2 0,64 0,426 0,572 1,219 1,79 0,497 0,972 1,35 1,421 0,157 1,794 0,104 0,430 0,295 1,206 0,529 1,167 0,198 1,774 1,369 1,931 1,672 0,817 0,996 1,978 1,740 1,807 0,514 1,768 1,459 1,621 0,949 0,517 1,920 0,424 0,480 0,655 1,408 0,717 0,788 1,707 0,941 0,789 1,228 1,373 1,372 0,569 1,958 1,558 1,113 1,233 1,833 0,22 0,274 0,260 0,528 0,320 0,828 0,771 1,221 1,958 1,126 0,654 1,828 1,287 1,41 0,515 0,291 0,489 1,221 1,81 1,884 1,163 0,396 1,501 0,557 0,351 0,601 1,210 0,827 1,824 1,879 0,824 0,694 0,858 0,211 0,578 0,546 1,619 0,813 0,535 0,112 1,819 0,896 0,395 1,860 0,878 0,897 1,142 1,387 0,698 1,919 1,470 1,4 1,210 0,720 0,300 1,808 0,125 0,525 0,36 1,397 0,374 0,358 0,463 1,696 1,557 0,859 1,122 0,65 0,355 0,2 0,163 1,584 0,164 0,840 0,768 1,353 0,131 0,125 1,357 0,541 1,9 0,301 1,789 0,142 0,469 1,0 1,864 1,337 0,884 0,843 0,278 1,584 0,436 1,647 1,996 1,55 0,644 0,553 1,833 0,20 0,86 1,519 0,59 1,434 0,29 1,614 0,516 1,830 1,714 0,113 0,635 1,79 0,92 0,795 1,675 0,874 1,613 1,533 1,617 1,56 1,434 0,936 0,37 1,580 1,501 1,745 0,739 0,604 1,346 1,540 1,526 0,831 0,639 0,354 1,853 1,84 0,678 1,437 1,474 1,933 1,858 0,670 0,440 1,587 1,333 0,67 1,328 0,498 1,695 1,324 1,349 1,21 1,155 1,120 0,834 1,676 1,108 1,229 0,230 0,315 1,925 0,890 1,780 0,887 1,117 0,299 0,299 1,372 1,886 1,728 1,415 1,680 0,14 0,632 1,725 0,691 0,389 1,200 0,953 1,822 0,839 0,886 0,651 1,107 0,563 0,824 1,100 0,200 0,691 1,982 1,53 1,340 1,803 0,980 1,137 0,68 1,560 1,328 0,50 1,388 0,458 1,825 0,102 1,691 0,773 1,407 1,942 0,492 0,364 0,635 1,805 1,356 1,914 1,818 1,242 0,180 1,561 1,573 0,740 1,319 1,950 1,558 1,900 1,113 1,134 0,180 1,993 0,488 1,700 1,864 0,81 0,716 1,202 1,405 1,56 0,496 0,642 1,803 0,315 0,691 1,166 0,142 0,958 1,949 0,383 1,811 0,410 0,702 1,323 0,70 1,910 1,24 1,603 1,457 0,269 1,277 0,963 0,905 1,390 0,907 1,99 0,411 0,740 0,484 1,97 0,57 0,585 1,600 1,613 0,212 1,363 0,863 0,656 1,917 1,397 1,749 0,245 0,363 1,873 1,548 0,2 0,738 1,723 0,755 0,55 0,244 0,741 1,368 0,137 0,258 1,636 0,870 1,750 1,546 0,382 0,289 0,559 1,22 0,558 0,66 1,195 0,59 1,445 0,363 1,470 1,106 0,977 0,227 0,758 0,550 1,976 0,273 1,936 0,916 0,794 0,647 0,857 0,289 0,576 1,202 0,683 0,909 0,941 0,69 0,539 1,631 0,91 0,541 0,106 0,599 1,667 1,784 0,838 1,86 0,127 0,949 1,550 1,618 1,495 1,787 1,38 1,715 1,138 0,24 1,401 1,655 0,546 0,420 1,965 0,963 1,700 0,731 1,240 0,705 1,448 1,158 1,854 0,713 1,20 1,525 1,382 1,580 0,498 0,343 0,559 0,812 0,932 0,652 1,29 0,744 0,927 0,68 1,240 0,93 1,707 1,981 0,699 1,737 1,497 1,366 1,948 1,575 0,688 1,130 1,579 0,990 1,205 1,782 0,584 1,289 1,139 1,446 0,383 0,328 1,833 0,799 1,562 0,872 0,90 0,637 1,341 1,12 0,433 0,46 0,781 0,563 1,785 1,591 1,109 1,584 1,204 0,722 1,442 0,852 1,805 0,20 0,182 1,511 1,952 0,333 0,353 1,168 1,80 0,70 1,511 1,291 1,977 0,158 0,665 0,721 1,405 0,515 0,158 0,947 0,69 0,125 1,380 1,468 0,744 0,801 1,386 0,368 0,316 1,688 1,861 0,5 1,799 0,337 0,726 0,757 1,971 1,716 0,933 1,773 0,625 0,367 0,736 1,358 0,411 1,512 0,897 1,650 1,638 1,874 1,36 0,993 1,833 1,368 0,982 0,312 0,873 0,644 0,104 0,469 0,979 0,609 1,587 1,898 0,954 1,99 0,645 1,164 0,47 1,841 1,54 0,242 0,501 1,736 0,691 1,616 0,129 1,760 0,772 1,690 1,574 1,34 0,617 1,736 1,871 0,326 1,828 0,22 0,108 0,890 0,31 0,686 1,554 1,626 0,820 0,120 1,554 1,488 0,551 0,786 1,959 1,404 0,260 1,642 1,111 1,951 1,931 0,231 1,391 0,284 0,231 0,669 1,796 1,345 0,86 0,760 1,418 1,509 1,827 1,185 1,834 0,679 1,710 1,274 0,940 1,943 0,692 0,747 0,554 1,117 1,190 0,427 0,721 0,156 0,500 1,457 0,978 1,952 1,815 0,708 0,795 0,241 0,112 0,47 1,50 0,286 0,87 0,687 1,720 0,922 1,87 0,389 0,481 0,726 1,983 1,550 1,308 1,93 0,247 1,31 0,487 0,251 0,97 0,219 0,404 1,879 1,36 1,962 0,298 0,921 1,399 0,46 1,875 0,13 1,663 0,930 0,171 0,627 0,274 0,993 0,719 0,704 1,931 1,492 0,672 0,800 0,820 0,809 1,174 1,353 0,484 0,23 0,222 1,599 1,702 1,971 0,682 0,176 1,348 1,427 1,443 1,183 1,930 1,160 0,743 0,480 0,492 1,254 1,262 0,756 0,782 1,323 0,846 1,365 0,289 1,321 0,434 1,405 0,85 1,258 0,386 0,163 0,47 0,744 0,959 1,624 1,483 1,610 0,552 1,282 1,430 0,943 0,90 0,863 0,959 1,69 1,914 1,441 1,444 1,433 0,151 0,453 0,172 0,252 1,180 0,757 0,107 1,706 1,251 0,197 1,295 1,132 0,712 0,536 1,261 0,460 1,84 1,431 0,253 1,332 1,895 1,485 1,685 1,904 0,482 1,37 1,468 1,867 0,910 1,587 1,967 1,774 0,270 0,219 0,319 0,816 0,179 0,175 1,244 1,836 1,748 1,872 0,193 0,303 0,8 1,284 1,707 1,188 1,962 0,724 1,616 1,809 0,492 0,60 0,223 0,568 0,982 0,205 1,81 1,90 0,699 1,585 1,644 1,576 1,535 0,341 1,841 0,582 0,431 0,799 0,712 1,391 0,664 0,149 0,930 1,994 1,267 1,358 1,112 1,81 1,371 0,916 0,590 0,959 0,293 0,957 0,466 1,307 0,992 0,983 1,80 0,143 0,62 0,644 0,500 1,480 0,440 0,186 1,744 0,159 0,956 1,389 0,232 1,37 0,418 0,414 0,224 0,49 1,879 1,730 1,773 0,617 0,893 0,639 1,489 0,301 1,664 1,262 0,654 1,4 0,2 0,598 1,424 0,35 1,710 1,199 0,689 0,681 1,792 1,344 0,844 1,168 1,702 1,991 1,573 1,190 0,502 0,193 0,962 1,498 0,32 1,752 0,327 0,768 0,754 1,3 0,795 0,89 0,529 0,370 0,661 0,854 0,142 1,301 0,267 0,6 0,173 0,204 0,183 1,821 0,6 1,233 1,855 0,175 0,130 0,720 0,576 1,245 1,368 0,152 0,740 0,241 1,791 1,663 0,672 1,295 1,793 0,781 1,586 1,90 0,545 0,370 1,969 1,265 1,637 1,661 0,519 0,416 0,618 1,395 0,811 0,40 1,997 1,539 1,317 1,928 1,930 1,937 0,926 1,856 0,275 1,718 1,708 0,684 0,679 0,849 1,881 0,621 0,720 1,628 0,906 1,599 1,207 1,838 1,502 1,819 0,492 1,153 1,878 1,301 0,899 1,391 1,31 1,489 0,432 1,186 0,289 0,540 1,58 1,656 1,967 0,936 1,927 1,824 0,726 0,521 0,211 1,934 1,296 0,132 1,578 1,134 0,191 0,190 1,721 0,975 1,58 0,973 1,240 1,447 1,204 0,982 1,343 1,671 1,540 0,469 1,559 1,424 0,292 0,751 0,500 1,233 0,359 0,556 0,829 1,281 1,422 1,786 1,812 0,805 1,894 1,507 1,695 0,309 1,449 1,403 1,934 1,945 0,286 1,284 0,100 0,778 0,735 1,141 0,364 0,555 1,982 0,787 0,330 0,769 1,464 1,826 0,284 1,891 0,195 1,768 0,92 1,213 0,148 1,587 1,558 1,36 1,775 1,656 1,432 1,766 0,490 1,934 0,110 0,954 0,56 1,307 0,659 0,173 1,330 1,563 0,837 1,940 0,103 1,714 1,862 1,53 1,594 1,444 1,816 0,323 0,538 1,129 1,10 1,620 1,379 1,137 0,603 1,664 0,514 1,600 1,367 0,371 0,667 1,816 0,308 0,202 0,16 1,299 0,643 1,974 0,357 0,841 1,939 0,378 0,757 1,684 1,737 0,517 1,247 1,829 0,327 1,614 0,367 1,665 1,577 1,956 0,114 0,724 1,730 0,615 0,922 0,582 1,154 0,168 1,201 1,844 0,947 0,688 1,884 1,877 1,803 1,536 1,958 0,541 1,999 1,10 1,667 0,516 0,181 1,4 1,228 0,529 0,127 1,455 0,404 0,150 1,955 1,939 0,75 0,389 1,793 0,277 1,606 0,695 0,609 0,237 0,667 1,137 0,74 1,300 0,805 0,411 0,435 1,807 0,819 1,373 1,57 0,364 1,334 1,314 1,863 1,657 0,871 0,497 1,935 0,190 0,128 1,729 1,770 0,847 1,738 1,170 1,72 0,678 1,667 1,728 0,158 1,730 1,315 0,394 0,196 1,419 0,853 0,0 1,637 1,669 0,818 1,727 0,67 0,431 0,273 0,11 0,536 1,509 0,462 1,427 0,545 1,150 1,269 0,349 1,677 0,884 1,559 0,441 1,537 0,763 1,238 0,230 0,846 0,976 0,262 0,364 0,505 0,718 1,570 1,938 0,701 1,349 0,293 1,437 1,32 1,965 1,359 1,55 0,816 1,904 0,775 1,285 1,477 0,955 1,791 0,715 0,774 1,355 0,12 1,494 0,933 0,712 1,289 0,426 1,275 0,89 0,481 0,895 0,799 0,82 0,754 1,802 1,311 0,481 1,567 1,40 1,329 1,626 1,111 0,482 0,453 0,20 0,386 1,605 1,999 0,229 1,293 0,758 0,794 1,761 0,531 0,137 1,664 0,858 1,987 0,346 0,409 0,975 1,804 1,687 1,107 1,178 1,900 0,675 0,62 1,306 0,872 1,672 1,712 1,607 1,904 0,494 0,675 0,502 0,809 0,620 0,572 0,678 1,689 0,896 0,223 0,551 0,919 0,864 0,175 1,726 1,240 1,566 1,742 0,933 0,294 1,274 0,776 0,384 1,49 1,705 0,239 0,417 1,414 0,931 0,177 0,89 0,790 0,123 1,473 1,305 1,366 0,686 0,68 1,256 1,636 1,379 0,278 1,456 0,374 1,599 1,785 0,680 1,360 0,125 0,975 0,301 0,550 0,862 0,777 1,359 0,986 0,976 1,6 1,436 0,105 0,995 0,192 1,489 1,955 1,861 1,845 0,547 0,105 1,683 1,860 0,446 1,254 1,562 0,787 0,854 0,983 1,953 0,821 1,646 1,129 0,724 0,96 0,370 1,944 0,679 0,256 1,155 0,845 0,283 1,50 0,476 0,163 1,232 1,524 0,277 1,243 0,610 0,56 0,614 0,106 1,454 0,82 0,595 1,639 1,580 1,543 1,254 1,357 0,517 0,593 0,736 0,205 0,620 0,576 0,456 0,274 0,28 0,92 0,837 0,790 1,70 1,924 0,114 1,665 0,559 0,796 1,102 1,141 0,718 0,242 0,98 1,978 1,142 0,712 0,8 0,637 0,439 0,774 1,339 0,888 0,468 0,878 1,472 1,267 1,357 0,768 1,52 1,378 0,131 0,494 1,449 1,283 0,115 0,888 0,51 0,928 1,7 1,167 0,744 0,582 0,924 1,926 1,170 1,439 0,196 1,112 0,392 0,598 0,342 1,157 1,538 1,322 1,210 1,678 1,436 1,324 0,86 0,203 0,935 1,633 1,761 1,122 1,663 1,824 1,197 0,960 1,343 0,552 1,275 1,562 1,363 1,539 1,270 0,464 0,821 0,144 1,844 0,175 1,152 0,164 0,90 0,701 1,900 0,181 1,897 1,113 1,973 1,198 0,433 0,378 0,663 0,358 0,432 1,385 1,371 0,370 0,715 1,447 0,502 0,901 0,777 0,683 1,764 1,962 1,906 0,681 0,428 1,375 1,592 1,175 0,58 1,61 0,378 0,393 1,147 1,410 1,343 0,672 1,760 1,659 0,208 1,902 1,691 0,177 0,615 1,206 1,78 0,361 0,4 0,600 0,52 0,727 0,512 0,891 1,55 0,339 1,668 0,392 1,344 0,835 1,535 1,439 0,617 0,584 0,692 1,406 1,460 0,745 0,945 1,121 0,322 0,702 1,84 0,731 1,6 0,370 0,804 0,162 1,448 1,183 0,565 1,117 1,813 1,14 1,933 1,215 0,470 1,81 0,677 0,368 1,967 1,680 0,483 1,868 1,651 0,884 0,366 0,100 0,497 0,274 1,606 1,14 1,427 1,184 1,896 0,481 1,984 1,136 1,538 0,776 0,209 1,956 1,209 0,257 0,798 0,182 0,64 0,847 0,763 1,110 1,434 0,795 0,316 0,586 0,96 0,593 0,845 0,306 1,977 0,574 1,594 0,558 0,762 1,546 1,629 0,759 0,4 0,502 1,986 1,293 0,847 0,117 0,713 1,629 1,730 1,765 0,893 1,271 0,258 1,952 0,808 0,325 1,159 1,200 1,681 0,21 0,464 1,998 1,682 1,103 1,102 0,893 0,206 0,293 1,801 0,630 1,440 1,426 0,906 0,484 1,597 1,839 1,794 0,176 1,773 0,326 1,409 1,91 1,34 1,721 0,820 0,117 1,961 1,387 0,203 1,575 0,233 1,277 0,79 1,572 0,407 1,897 1,655 0,298 1,482 0,920 1,412 1,824 0,828 0,915 1,818 0,64 1,224 1,402 1,719 1,356 1,334 0,883 1,949 1,671 1,525 1,306 1,38 1,198 1,446 1,448 1,875 0,167 0,241 0,648 0,684 0,426 0,283 0,794 0,906 1,595 1,305 0,88 0,739 1,542 0,798 1,479 1,792 1,217 1,92 1,760 0,84 0,14 1,915 0,135 0,743 0,582 0,472 1,50 0,268 0,69 1,53 1,34 1,3 1,241 1,534 1,358 0,546 0,919 1,683 0,350 1,265 1,326 0,606 0,321 0,245 0,459 1,289 1,157 1,913 1,44 0,318 1,493 1,433 0,203 1,863 0,848 0,847 1,549 0,132 1,710 1,963 0,751 1,18 1,615 0,172 1,999 1,514 1,261 0,845 0,249 1,227 1,878 1,96 0,219 1,493 1,478 0,51 1,10 1,774 0,170 0,426 1,1 1,574 1,738 0,915 1,451 1,712 0,321 1,124 1,91 1,363 1,790 0,197 0,848 1,191 1,633 1,672 1,644 1,425 1,141 0,334 0,112 1,121 1,38 1,839 0,434 1,253 1,735 1,327 1,140 0,144 0,680 0,809 0,801 0,960 1,388 0,509 0,6 0,267 1,394 0,211 1,505 1,632 0,462 1,37 0,779 0,25 1,657 0,384 0,427 1,720 0,709 1,586 1,651 0,483 1,278 0,902 1,778 0,394 1,208 0,357 1,831 0,883 0,830 1,202 0,966 0,784 1,193 1,951 0,510 1,179 1,793 1,984 0,36 0,569 0,846 1,592 0,361 1,847 0,240 1,869 0,818 1,250 0,367 0,424 0,227 0,719 1,856 0,169 0,859 1,948 0,396 0,462 0,263 1,86 1,29 1,454 0,539 0,406 0,564 1,413 1,962 1,334 0,389 0,218 1,840 1,139 1,849 1,368 0,298 1,940 0,25 1,121 0,172 1,739 0,605 0,638 0,234 0,383 0,962 1,730 0,688 0,611 0,81 1,321 1,581 0,575 0,376 1,167 1,718 1,181 1,412 1,701 1,326 0,126 1,819 1,940 0,153 1,303 1,131 0,216 0,298 1,781 1,245 1,785 1,615 1,356 0,351 0,55 0,335 1,463 0,901 1,175 1,974 1,144 1,703 0,75 0,625 0,573 0,179 1,147 1,103 1,482 0,955 0,962 0,159 1,439 0,13 0,68 0,158 0,613 0,580 0,0 0,605 1,248 0,515 0,518 0,208 1,816 1,80 1,653 0,567 0,483 1,566 0,439 0,313 0,94 0,185 0,62 0,450 0,187 1,245 1,554 1,798 1,921 1,728 0,282 1,326 1,87 0,949 1,885 0,520 1,152 0,913 0,387 0,598 0,724 1,282 1,271 0,275 1,597 1,389 1,84 1,275 1,299 1,605 1,847 0,516 1,656 1,81 0,956 1,385 1,523 1,745 1,446 0,398 1,495 0,295 0,670 0,518 0,2 1,270 1,981 1,821 0,926 0,878 1,437 0,436 0,960 0,481 1,837 1,917 0,17 1,65 1,122 1,528 0,399 0,24 1,768 1,853 0,407 1,577 1,369 1,693 0,789 1,639 0,155 0,443 1,679 0,363 0,236 0,342 1,411 1,268 0,988 0,441 0,132 1,25 1,543 1,39 1,988 1,313 0,403 0,97 1,333 1,593 1,78 0,753 0,14 0,926 0,839 0,19 1,738 1,512 1,516 0,220 1,951 0,241 0,649 1,490 0,768 0,434 1,132 1,606 0,906 1,26 1,21 0,935 0,53 0,923 1,931 1,392 1,45 0,678 0,53 0,418 1,711 1,96 0,223 0,979 1,756 0,915 1,260 1,785 0,938 1,912 1,222 1,716 1,228 0,167 0,206 1,935 1,559 0,300 1,88 0,193 0,295 1,326 1,836 0,555 1,203 1,79 0,404 1,617 1,665 1,288 0,526 1,227 0,10 0,272 0,546 0,363 0,931 1,741 0,879 1,612 0,599 1,421 1,661 1,417 0,393 0,527 0,746 0,959 0,45 0,182 0,355 1,603 1,833 0,972 0,910 0,261 1,423 0,109 0,360 1,722 0,225 1,696 0,938 0,694 0,690 0,469 0,834 1,819 0,848 0,337 0,84 0,465 1,770 1,956 0,843 1,424 1,536 1,648 0,947 0,997 0,839 0,348 0,655 0,577 0,878 1,370 0,791 0,732 0,476 0,614 1,94 1,982 1,734 0,953 1,980 1,140 1,963 0,12 1,123 1,328 0,493 1,512 1,450 1,526 1,769 0,248 0,718 1,403 0,563 1,728 1,124 1,220 1,720 1,668 0,407 1,119 1,328 0,256 1,560 0,677 0,537 1,83 1,594 0,690 1,895 0,116 0,870 1,254 1,28 1,240 1,357 1,533 0,367 0,462 1,793 0,370 1,570 1,555 1,310 0,886 0,493 1,600 0,267 1,39 0,581 1,949 1,944 1,415 1,284 0,183 0,28 0,973 0,141 0,988 0,157 0,798 0,723 0,127 1,829 1,808 1,30 0,913 1,86 1,246 1,775 1,254 1,813 0,47 0,438 1,82 1,914 0,545 1,913 0,772 0,894 0,584 0,84 1,21 1,642 1,120 0,708 1,375 1,171 0,440 0,31 0,248 0,372 1,219 1,92 0,987 0,477 1,292 1,817 1,697 0,285 1,444 1,886 0,670 0,325 0,749 0,366 1,430 1,311 0,342 0,415 0,70 1,866 0,839 1,163 0,781 1,959 1,531 1,459 0,181 1,490 0,281 1,661 0,884 0,329 1,270 1,284 0,433 0,497 0,819 0,341 1,508 0,417 1,750 0,663 0,80 1,477 0,76 1,342 1,352 1,451 1,970 1,540 1,911 1,671 0,528 0,489 1,468 0,49 0,295 0,474 0,161 1,455 1,360 1,900 1,921 1,561 1,392 1,373 0,584 1,647 0,265 0,424 0,830 1,348 1,683 1,106 1,276 1,392 0,416 0,325 0,826 1,490 0,576 0,154 0,949 1,604 0,30 1,870 0,760 0,85 1,685 0,391 0,445 1,739 0,327 0,209 1,308 0,478 1,911 0,610 1,909 0,222 1,373 1,838 1,516 1,702 0,270 1,922 1,454 0,544 1,888 1,702 1,518 0,719 0,201 1,724 1,471 1,132 1,474 0,895 1,791 0,78 0,526 1,238 0,438 0,573 0,660 0,740 1,661 0,929 0,340 0,721 0,550 1,403 0,151 0,642 1,453 1,122 0,730 0,721 1,516 0,867 1,465 1,544 0,911 1,815 1,862 0,929 1,285 1,973 1,291 0,738 1,26 1,603 0,651 0,15 0,329 1,739 1,620 0,475 1,940 0,609 1,124 0,384 1,837 1,122 0,52 1,372 1,941 0,911 1,262 1,607 0,976 0,97 1,335 0,636 1,128 1,138 0,719 1,169 1,181 0,587 1,112 0,892 0,24 0,453 1,880 0,396 0,441 0,927 1,559 1,760 0,845 1,854 1,974 0,794 0,186 0,435 0,81 1,754 0,4 0,633 1,218 1,611 1,128 1,129 1,749 0,784 0,430 0,171 0,719 1,246 0,591 1,336 1,512 1,787 0,549 0,640 1,123 0,603 0,268 0,813 0,832 1,886 1,478 0,363 0,461 0,292 1,524 1,155 0,397 1,178 1,299 1,569 0,120 1,632 0,848 1,523 1,973 1,943 0,683 1,187 0,297 0,339 1,295 0,592 0,587 1,31 0,282 1,805 1,349 0,859 0,425 0,866 0,657 1,953 1,552 1,180 1,654 0,654 0,881 1,964 1,174 1,515 1,668 0,801 0,242 0,831 0,570 1,466 0,539 0,615 0,60 1,755 1,36 1,693 0,596 1,764 1,423 1,358 0,642 1,368 1,665 1,142 1,885 0,586 0,97 1,703 1,265 0,186 0,990 0,424 1,473 0,37 1,260 0,822 0,364 0,23 1,982 0,236 1,306 0,987 1,857 1,261 1,440 0,397 0,780 1,534 1,134 0,757 1,646 1,98 0,195 0,908 0,301 0,854 0,646 0,181 1,369 0,721 0,21 1,323 1,167 1,5 0,839 0,60 1,552 1,812 1,191 1,783 0,548 1,326 1,970 0,924 1,106 1,472 1,552 1,321 0,945 0,599 0,616 0,918 1,311 1,217 1,674 1,242 0,666 0,126 0,51 0,411 0,305 0,573 0,844 1,861 1,461 1,398 1,368 1,120 0,379 1,339 0,842 1,428 0,643 0,540 1,320 0,251 0,940 0,49 1,266 0,521 0,563 0,911 0,981 0,826 0,256 1,516 0,594 1,401 0,846 0,34 0,496 0,341 0,889 0,675 0,78 1,976 0,831 0,305 1,403 1,859 1,910 0,561 0,437 0,517 0,217 0,496 0,630 1,696 0,95 1,38 0,860 1,123 0,414 0,501 0,89 1,450 0,822 0,572 1,103 0,79 0,978 1,906 1,700 0,462 1,889 1,801 0,654 1,180 1,408 1,443 1,843 1,34 0,779 1,969 0,831 0,538 0,802 0,745 1,524 0,608 0,902 0,53 0,740 1,645 0,78 1,412 0,692 0,819 1,915 1,767 1,662 1,584 0,349 1,126 0,772 0,839 0,589 1,754 1,787 1,22 0,397 1,782 1,389 0,411 0,801 0,470 1,356 1,713 1,422 0,156 1,416 0,140 1,558 1,660 0,336 1,239 1,663 0,772 1,903 0,479 1,396 0,290 0,583 0,971 1,712 0,110 1,93 1,738 1,896 0,247 0,515 1,988 1,602 0,94 1,49 1,943 1,530 1,370 1,31 1,624 1,134 1,245 0,215 1,428 1,993 1,472 0,18 1,0 1,843 0,410 0,618 0,785 1,715 0,251 1,27 0,704 1,937 1,941 0,944 1,228 0,882 0,788 0,402 0,65 0,991 1,520 1,226 1,574 1,740 0,954 1,808 1,944 1,157 1,875 1,913 1,216 0,401 0,351 1,896 1,478 1,466 0,870 0,373 1,934 0,395 1,965 1,244 1,449 0,304 0,423 0,562 0,44 0,456 0,652 1,595 0,858 0,193 0,703 0,824 1,643 1,274 1,420 0,456 0,671 0,842 0,928 0,230 0,224 1,457 1,148 0,947 0,661 1,436 0,419 0,273 1,11 0,643 1,746 0,443 0,341 1,341 1,960 1,717 1,790 0,783 0,482 1,30 0,837 1,637 0,53 0,100 0,382 0,69 1,2 0,711 1,776 1,408 1,118 1,942 1,993 1,906 0,633 1,392 0,126 0,891 1,674 1,263 0,958 0,386 0,876 0,687 0,530 1,444 1,43 1,228 0,360 0,765 0,803 1,433 0,298 1,938 1,852 1,381 0,806 1,407 0,986 1,112 1,99 0,568 1,28 1,166 0,680 0,706 0,224 0,637 0,445 1,389 1,675 1,500 0,414 1,754 0,33 1,694 1,787 1,188 1,413 0,755 1,826 1,780 0,37 0,91 1,509 1,945 0,924 1,19 0,765 0,723 0,686 1,794 1,392 0,855 0,124 0,397 1,598 1,412 1,521 0,578 0,34 1,467 1,302 0,865 1,911 1,365 0,854 0,567 1,164 0,619 0,20 1,353 1,301 0,97 0,724 0,902 1,96 1,442 1,966 0,199 1,753 0,174 0,244 1,990 0,864 1,484 1,287 0,242 1,653 0,314 0,779 0,561 1,899 0,739 1,782 0,602 0,663 0,924 0,799 0,512 1,230 1,944 1,260 1,952 0,837 0,870 0,946 0,222 0,959 0,91 1,1 1,480 0,703 1,440 0,14 1,293 0,445 1,285 0,542 0,567 1,541 0,740 1,176 0,462 0,429 1,781 1,505 0,741 0,739 0,207 1,350 1,431 1,606 0,636 0,62 0,28 1,856 0,456 0,217 0,22 0,76 1,276 1,390 0,141 1,382 1,188 0,77 0,998 1,165 0,957 1,2 0,952 0,777 0,929 1,60 0,806 0,546 1,225 0,300 0,65 0,406 1,125 0,352 1,369 1,681 1,326 1,826 1,719 0,957 0,360 1,262 1,465 1,60 1,834 1,389 1,177 1,225 1,74 1,306 0,972 0,285 0,258 0,464 1,623 1,116 1,193 0,798 1,628 0,606 1,272 0,129 1,563 0,217 1,429 1,615 0,800 0,764 0,966 0,747 0,891 0,65 0,87 0,277 0,595 0,80 1,912 0,85 0,300 0,663 1,921 0,850 0,837 0,208 1,892 0,608 1,220 0,21 0,693 0,271 1,717 1,468 0,502 1,204 1,737 0,722 1,913 1,879 1,921 0,885 0,289 1,73 1,901 0,134 0,635 0,410 1,443 1,500 1,93 0,953 0,187 0,768 1,84 0,723 0,740 0,515 1,506 0,545 1,14 1,944 1,435 1,963 1,580 0,839 0,486 0,99 1,752 1,965 0,545 1,11 1,173 1,127 1,806 1,424 0,376 1,372 1,209 1,750 0,478 1,477 1,737 1,615 0,391 1,654 0,630 1,515 0,863 0,572 0,870 0,193 0,600 0,109 0,510 0,64 0,885 1,343 0,691 1,491 0,513 0,835 1,695 1,910 0,633 1,174 1,388 0,73 1,449 0,439 0,337 1,616 1,475 0,111 1,418 0,527 1,249 1,745 1,705 0,143 0,785 0,506 0,54 0,41 1,559 1,275 0,938 0,859 1,657 1,518 0,821 1,654 1,571 1,847 1,712 0,765 1,329 0,128 0,156 0,395 0,108 0,5 1,381 1,879 0,259 0,698 1,660 0,501 0,795 0,979 0,414 0,853 1,25 1,822 1,880 0,115 0,534 0,734 0,261 1,856 1,805 0,960 0,274 0,294 1,125 1,672 1,441 1,777 0,644 0,492 1,679 0,930 1,855 0,48 1,411 0,772 0,314 0,615 0,964 0,563 0,81 0,766 0,509 1,822 1,725 1,113 0,55 0,709 0,612 1,113 1,678 0,420 0,307 0,389 0,183 1,271 1,8 0,825 0,484 1,44 1,309 1,209 0,570 0,892 1,236 0,800 1,118 0,363 0,419 0,988 1,770 0,41 0,179 1,804 0,567 1,645 0,819 1,324 1,328 1,808 0,10 0,498 1,238 0,161 1,579 0,472 1,965 0,638 1,980 1,601 0,226 1,960 0,571 1,3 1,699 1,470 1,17 0,958 0,422 0,643 1,368 1,472 1,511 0,658 1,572 1,183 0,540 1,577 1,28 0,776 1,209 1,902 1,313 0,890 1,487 1,205 0,987 0,309 1,65 0,319 0,17 0,517 0,875 1,697 1,109 1,271 0,993 1,88 0,418 1,178 0,389 1,306 0,45 0,929 0,581 1,774 0,874 0,725 1,804 0,938 1,659 0,466 0,403 0,86 1,593 0,631 1,939 1,545 1,257 0,186 0,583 0,995 1,131 1,406 0,219 0,636 1,355 0,920 1,211 0,889 1,89 0,703 0,67 1,285 1,904 0,809 0,977 1,152 1,294 0,817 1,622 0,339 0,365 0,476 0,769 1,111 1,936 1,69 1,42 0,714 0,250 0,318 0,854 0,104 1,797 1,562 0,918 0,264 0,937 0,574 0,695 0,737 0,877 0,460 1,432 1,30 0,713 0,654 0,682 0,640 0,862 1,152 1,604 0,968 1,782 0,481 1,834 0,635 1,197 0,456 1,924 0,409 0,254 0,110 0,940 0,984 0,806 1,461 1,713 0,118 0,761 0,438 1,530 1,140 1,551 1,815 0,348 1,838 1,510 0,674 0,969 0,305 0,326 0,223 1,441 1,209 1,211 0,686 1,665 1,983 0,132 1,90 1,354 0,690 1,493 0,829 1,507 1,522 0,947 1,310 0,757 0,935 0,882 1,984 0,724 1,905 1,170 1,812 0,191 1,476 1,45 1,626 0,839 1,925 1,644 0,900 1,411 0,249 0,859 1,887 0,50 0,52 0,871 0,827 1,242 0,831 0,745 1,514 1,728 0,928 0,251 0,800 1,575 1,684 1,371 0,295 1,239 0,240 1,886 1,101 0,391 1,308 1,452 0,18 0,682 1,288 1,51 0,822 0,750 1,503 0,837 1,129 1,768 1,105 0,89 0,348 1,835 1,982 0,523 1,936 0,577 0,664 0,466 1,671 0,677 0,462 0,154 0,428 1,704 0,685 1,192 1,72 0,49 1,821 0,956 1,23 1,802 1,772 1,533 0,775 1,602 0,173 0,224 1,806 1,690 0,80 1,966 1,798 1,500 0,809 1,945 0,713 1,222 0,838 1,755 1,167 1,281 1,308 0,696 0,440 0,236 1,293 1,541 0,349 1,743 0,292 0,443 1,405 0,804 0,608 1,477 1,396 1,68 1,846 0,339 0,87 0,616 0,844 1,980 0,966 0,532 0,358 1,242 1,715 1,571 1,666 0,426 1,593 0,754 0,905 1,468 1,279 0,863 0,745 0,875 0,844 0,448 1,92 0,243 0,927 1,404 0,637 1,575 0,30 0,257 1,297 1,618 0,434 1,548 0,248 1,8 1,182 0,450 1,729 0,355 0,527 0,917 0,469 0,117 0,665 0,103 1,468 1,200 0,871 0,718 1,838 1,794 1,892 0,612 1,601 1,545 0,251 1,38 0,863 0,201 0,927 1,712 0,288 0,116 0,702 0,917 0,175 1,514 1,416 1,542 1,758 0,195 1,739 0,126 1,538 0,127 1,783 0,285 1,751 0,539 1,439 1,327 1,195 1,363 0,606 1,865 0,920 1,178 1,81 1,821 0,967 0,750 0,691 0,745 1,604 1,865 1,468 0,940 0,963 1,809 1,473 1,493 1,967 0,629 1,321 0,846 1,458 1,271 0,444 1,293 1,653 0,713 1,685 0,213 1,557 0,568 0,168 1,455 1,869 0,941 0,98 1,11 1,363 0,577 0,658 1,20 0,97 1,294 1,785 0,766 0,998 0,179 1,600 1,55 1,507 0,630 1,432 1,371 1,845 1,853 0,192 0,693 1,794 1,990 1,181 0,5 1,354 1,828 1,728 0,988 1,89 1,830 1,940 0,792 1,368 0,16 1,733 0,566 1,173 0,961 1,686 1,461 1,720 0,515 0,161 0,579 0,45 0,458 1,891 0,828 1,236 1,630 1,811 1,460 0,224 0,467 0,315 1,38 0,447 0,273 0,152 1,10 1,579 1,718 0,387 0,901 1,926 0,561 1,13 1,643 0,201 0,934 0,212 0,972 1,830 0,737 1,136 0,269 0,375 1,223 0,39 1,849 1,723 0,313 0,159 0,216 0,701 1,472 0,141 1,365 0,119 1,998 1,582 1,924 0,539 1,765 1,226 0,622 0,704 1,299 1,139 0,399 0,554 1,187 1,934 0,340 0,959 1,927 0,295 1,75 0,979 1,404 0,323 0,803 0,858 0,150 1,654 1,29 0,854 1,463 1,55 0,213 1,455 1,27 0,335 1,274 0,438 0,589 1,262 1,457 1,944 0,42 0,12 1,458 1,87 0,982 1,565 0,617 0,332 0,325 1,633 0,394 0,135 0,478 1,867 0,794 0,757 0,93 1,838 1,743 1,125 0,766 0,30 1,437 1,456 1,674 0,48 1,651 0,784 1,891 1,872 0,652 1,276 1,530 1,552 1,322 0,650 1,231 0,97 0,574 1,149 0,824 1,829 1,397 1,345 0,357 0,656 1,612 1,46 1,917 1,690 1,124 0,300 0,880 0,889 0,618 1,747 1,53 0,573 0,132 0,345 1,844 0,851 1,604 0,766 0,891 1,218 0,371 1,655 0,130 0,541 0,398 1,477 1,936 0,710 1,920 1,414 1,190 0,480 1,691 1,884 1,158 1,51 0,622 1,236 0,734 1,772 1,556 0,490 1,127 0,441 1,587 1,762 1,6 1,185 0,554 1,507 1,520 1,675 1,549 0,328 0,723 0,129 1,152 1,201 1,940 1,156 0,316 0,574 1,323 1,538 0,879 1,24 0,962 1,533 1,523 1,176 0,134 0,248 1,625 1,288 1,948 1,195 0,633 1,897 1,628 1,822 1,634 1,635 1,67 0,476 0,405 0,622 1,518 1,341 1,630 1,838 1,826 1,203 0,420 0,989 0,717 0,85 1,206 0,216 0,506 1,645 1,746 1,372 0,635 1,903 1,985 1,752 0,122 0,502 0,90 1,11 1,470 0,276 1,318 1,178 0,299 1,425 1,533 0,380 0,513 1,538 1,89 0,970 1,638 1,557 0,357 0,559 1,503 1,270 0,517 1,528 0,70 1,985 1,190 0,440 0,634 1,819 0,33 0,612 0,134 1,444 1,680 0,987 1,729 1,371 0,117 1,84 1,54 0,519 0,197 0,311 1,894 1,85 0,446 1,920 0,461 0,90 1,773 0,778 1,979 0,922 1,802 1,199 0,490 0,669 1,380 1,941 0,478 1,671 1,514 1,797 0,499 1,498 1,546 1,79 1,991 0,62 0,145 0,25 1,963 1,471 1,973 0,238 0,451 1,897 0,662 0,142 1,797 0,34 1,183 0,705 0,123 0,719 1,242 0,303 1,660 1,192 1,652 1,189 0,630 0,767 0,971 0,589 1,526 1,973 1,548 1,49 1,895 1,0 1,436 1,503 1,140 1,747 1,340 0,108 1,836 0,49 0,712 0,216 0,890 0,892 1,441 1,580 0,382 0,254 0,197 1,100 1,174 0,184 1,780 0,22 0,796 0,384 1,154 0,142 0,639 0,452 0,137 0,761 0,236 1,154 1,740 1,123 1,550 0,142 1,680 1,220 1,867 1,530 0,533 1,147 1,52 1,751 0,970 1,480 0,499 0,544 0,666 1,586 1,85 0,249 0,926 0,568 0,664 1,836 1,509 0,198 1,727 0,896 0,395 1,900 1,309 1,436 1,313 0,847 1,885 1,720 0,31 0,716 1,71 0,140 1,712 1,627 1,606 0,291 0,120 0,18 1,391 1,133 0,52 1,421 1,590 0,982 0,559 0,302 0,459 1,599 0,771 1,381 0,564 1,851 0,488 0,710 1,95 0,368 0,67 0,312 1,663 0,549 0,409 0,403 0,945 1,486 0,626 0,714 0,69 0,635 0,719 1,983 1,412 0,561 0,280 1,228 0,1 0,634 0,305 0,880 0,397 0,65 1,382 0,584 1,873 0,766 1,239 1,315 0,722 0,505 0,472 0,171 0,881 1,197 0,74 0,598 0,57 0,464 0,900 1,433 1,97 1,68 1,709 1,743 0,73 0,829 0,728 0,695 1,649 1,687 0,145 0,389 1,729 1,746 0,481 1,538 0,680 0,70 0,308 0,74 0,320 0,680 0,981 0,678 1,351 1,161 0,788 1,526 1,234 0,200 1,38 0,217 1,822 1,676 1,22 1,699 1,383 1,58 1,593 1,709 1,720 1,959 0,431 0,652 0,703 1,366 1,2 0,45 1,14 1,528 1,99 1,767 0,606 1,161 1,176 1,637 1,222 1,435 1,731 1,677 0,922 1,364 1,370 1,60 1,976 1,9 1,251 0,914 1,545 0,342 0,57 0,654 1,230 1,796 1,826 1,184 0,484 0,556 1,652 1,569 1,854 1,375 1,956 1,367 1,43 0,242 1,490 1,30 0,212 1,873 1,265 0,718 1,794 0,909 1,599 0,490 1,584 0,992 1,607 0,130 1,977 1,142 1,848 0,846 0,677 0,507 1,882 1,6 1,813 0,511 1,813 1,24 1,6 0,927 0,923 0,27 0,611 1,702 0,588 0,919 0,261 0,647 1,559 1,147 1,609 0,108 1,573 1,256 1,67 1,560 1,615 1,184 0,114 1,621 0,405 1,400 0,922 1,483 0,499 0,333 0,140 0,598 0,606 0,354 1,113 0,307 1,274 1,168 1,118 0,599 1,189 0,867 1,346 1,186 1,722 1,567 0,234 1,197 0,678 1,292 0,556 1,680 1,580 0,713 0,420 1,286 1,481 0,629 1,531 0,509 1,813 1,651 0,620 0,230 1,623 0,155 1,865 1,282 1,454 1,685 1,447 0,509 1,214 0,994 0,731 0,596 1,644 1,796 1,994 1,527 1,913 0,574 0,859 1,798 0,721 0,248 1,848 0,273 1,265 0,321 0,450 0,623 0,461 0,870 1,416 0,838 1,935 1,448 0,266 1,436 0,500 1,823 0,660 0,630 1,961 1,740 0,710 1,867 1,488 1,13 1,579 0,969 1,320 0,857 1,860 1,633 0,718 0,551 0,902 1,880 0,248 0,89 0,125 1,0 1,740 0,205 1,949 1,831 0,529 1,173 0,223 1,110 0,519 1,938 0,348 0,940 1,83 1,4 0,408 1,856 0,184 1,798 1,641 1,872 0,523 0,310 1,746 1,665 0,873 1,832 0,280 1,382 1,284 0,688 0,213 0,706 0,942 0,465 0,823 0,504 1,502 1,708 1,637 0,525 1,65 1,383 0,849 1,824 1,273 1,256 1,509 0,147 1,61 1,392 0,881 1,269 1,62 1,376 0,570 0,338 1,494 1,229 0,519 0,898 1,861 0,319 0,134 1,625 0,846 0,974 0,442 1,495 0,268 0,526 1,290 0,609 1,472 1,950 0,262 1,389 0,695 1,899 1,869 1,759 1,523 0,18 0,250 0,717 0,667 1,847 0,606 0,402 1,16 0,171 0,897 1,959 0,197 0,776 0,506 0,355 1,237 0,91 0,102 1,208 0,133 0,918 1,708 1,245 0,619 0,674 1,823 1,593 0,396 1,773 1,587 0,490 0,415 1,10 1,878 0,907 0,80 0,356 0,0 1,773 0,921 1,867 1,133 0,980 1,533 1,393 0,835 1,752 1,740 0,54 0,946 1,4 1,640 0,120 1,468 1,469 0,65 0,567 1,902 1,927 1,97 1,107 0,188 1,498 1,834 0,137 0,680 1,522 1,291 0,646 1,465 1,328 1,605 0,383 0,569 1,328 0,126 1,126 1,89 1,506 1,768 1,318 1,509 0,435 0,207 1,685 1,733 1,550 0,422 0,207 1,152 0,556 0,661 0,240 1,58 0,447 0,938 0,713 0,300 0,413 0,432 1,580 1,447 0,157 0,401 1,368 1,941 0,107 0,915 1,848 1,987 0,612 1,958 0,183 0,389 1,112 1,643 0,873 0,610 1,849 0,260 1,473 1,242 1,847 1,111 0,14 1,1 0,346 1,520 1,104 0,47 1,646 1,180 1,164 0,545 1,776 0,631 0,61 1,214 0,535 0,775 0,953 1,899 1,877 0,106 0,127 0,556 1,679 0,783 0,149 0,955 0,434 0,425 0,375 0,421 0,742 1,559 1,191 1,418 1,496 1,129 1,753 0,390 1,950 1,946 0,948 0,486 0,979 0,583 0,540 0,189 1,646 0,439 0,930 0,657 1,252 1,792 0,818 1,314 0,111 0,168 1,290 1,459 1,572 0,561 0,13 0,255 1,724 0,98 1,271 0,536 1,825 0,429 0,787 1,827 1,519 1,799 0,242 0,738 0,842 0,597 1,741 1,155 1,481 1,537 1,11 1,314 1,437 0,42 1,935 0,779 1,338 0,51 1,663 1,984 0,592 1,97 0,324 1,562 0,765 1,132 1,855 1,579 0,580 0,342 0,978 1,912 1,524 1,532 1,152 1,58 1,477 0,13 0,471 0,143 1,628 0,577 0,263 1,252 0,275 0,647 0,928 1,215 1,410 1,884 1,520 0,455 1,58 0,518 1,885 0,872 0,155 0,710 0,743 1,707 0,369 0,315 1,513 1,42 0,14 0,372 0,138 1,63 0,681 0,297 1,607 1,144 0,740 1,534 1,405 0,761 0,142 0,189 1,962 1,377 0,831 0,680 1,627 1,10 0,570 0,514 1,578 1,146 0,469 1,192 0,720 0,438 1,293 1,819 0,73 1,101 1,575 0,659 0,195 0,401 0,789 1,969 1,347 1,800 1,12 0,240 0,61 0,896 1,649 1,492 0,867 1,356 0,730 0,228 1,101 1,242 1,206 1,840 1,539 0,888 0,873 1,486 1,737 0,977 1,220 1,778 1,823 0,8 1,884 0,105 0,161 0,49 1,737 1,795 1,245 1,811 0,230 1,212 0,771 0,952 0,196 1,856 1,889 0,528 1,296 1,842 1,490 1,652 0,652 0,424 0,942 1,406 1,741 1,662 1,767 1,635 0,440 1,325 1,498 0,545 1,345 1,992 0,715 1,946 0,338 0,648 1,379 1,801 0,225 1,621 0,943 0,187 0,458 1,23 1,3 1,319 0,539 1,501 0,648 1,772 0,822 1,598 0,747 0,597 1,528 0,537 1,100 0,918 1,584 1,782 1,474 1,6 1,106 1,876 1,957 0,997 1,687 0,569 1,521 1,615 0,451 0,50 0,909 0,500 0,286 1,485 0,452 0,18 1,566 0,569 1,52 1,815 0,115 0,279 0,27 1,231 1,47 0,635 0,493 1,797 1,839 1,366 1,878 1,978 0,630 1,363 0,193 0,683 0,526 1,664 0,193 1,584 0,170 1,31 0,240 0,960 0,841 1,478 1,170 1,876 0,127 0,696 1,859 1,699 0,631 1,899 0,443 1,993 0,813 0,200 1,185 1,682 1,536 1,557 0,722 1,516 0,617 0,503 0,629 1,335 0,456 0,112 1,517 1,505 0,857 1,325 0,818 0,902 0,545 1,420 0,102 0,16 0,342 0,171 1,562 0,52 0,877 0,358 0,769 1,712 0,537 0,988 1,333 1,313 1,210 0,428 0,771 1,433 0,653 0,331 0,402 1,484 1,914 0,674 1,110 1,841 1,189 1,71 0,175 1,767 0,683 0,287 0,245 1,679 0,56 0,223 0,366 1,317 0,259 0,673 1,301 0,815 1,502 0,6 0,637 1,414 1,101 1,204 0,697 0,741 0,896 1,224 1,350 1,332 0,891 1,851 0,115 1,489 1,990 0,364 1,206 1,914 1,252 1,420 0,681 0,353 0,36 0,863 1,780 0,69 0,829 0,200 0,776 0,502 0,406 1,961 1,457 0,861 0,964 0,96 1,271 0,69 0,116 1,324 0,179 0,620 0,165 0,24 1,513 0,444 0,359 0,797 1,505 1,703 0,432 0,983 0,941 1,414 1,651 1,211 1,678 1,74 1,513 0,917 0,731 0,323 1,718 0,333 1,431 0,300 0,214 1,305 0,201 1,270 0,194 0,361 0,408 1,943 1,196 0,0 1,671 0,254 0,814 0,463 1,624 1,979 0,46 1,102 1,27 1,515 0,761 1,881 0,483 1,312 1,219 0,298 0,564 1,416 0,760 0,22 1,683 0,207 0,140 0,86 1,891 0,338 0,192 1,375 1,14 0,262 0,526 0,408 1,14 0,236 1,531 0,207 1,101 0,395 1,369 1,7 1,807 0,569 0,832 0,164 1,948 1,737 0,921 1,576 0,751 0,332 1,939 0,948 1,398 1,614 0,640 1,367 0,441 1,574 1,66 0,322 0,228 1,403 0,568 1,127 1,955 0,285 0,160 0,80 1,52 1,173 1,195 1,889 0,728 0,309 0,529 1,843 1,407 1,641 1,576 0,643 0,485 0,626 0,465 0,976 0,248 0,229 0,993 1,804 0,812 0,966 1,622 1,423 0,267 1,589 0,323 0,145 1,410 1,138 1,530 0,295 1,766 1,262 1,387 1,899 1,350 0,409 0,201 1,736 1,639 0,511 0,197 1,717 0,899 1,538 1,627 1,860 1,70 1,814 1,613 0,501 0,347 1,576 1,494 1,482 0,11 0,242 1,90 0,83 1,118 1,658 1,275 0,726 1,148 0,523 0,134 0,452 1,159 0,831 0,451 1,228 1,610 0,356 1,885 0,919 0,853 1,920 1,472 0,191 1,68 0,672 0,704 0,292 1,867 1,701 0,925 0,340 1,840 1,835 1,336 1,628 0,11 0,18 1,652 1,299 0,794 1,752 1,185 0,722 0,767 1,933 1,668 0,480 0,114 0,792 1,703 1,902 1,447 0,869 0,873 1,295 0,165 0,475 0,76 0,732 0,169 0,170 0,86 1,781 0,605 1,955 0,646 0,216 1,345 1,708 1,847 0,821 0,348 1,547 1,863 0,824 1,756 1,326 0,744 1,863 0,571 0,897 0,479 1,260 0,104 0,281 1,630 1,951 1,35 0,629 1,726 1,689 0,712 1,154 0,306 1,226 0,191 0,350 0,640 0,322 1,714 1,601 0,469 0,123 1,277 1,791 1,253 0,448 0,722 1,509 1,880 0,207 0,510 1,959 0,942 1,757 0,592 0,644 0,141 1,967 1,348 0,432 1,771 0,605 0,938 1,401 1,553 0,609 1,441 0,861 0,14 0,10 1,670 1,99 0,385 0,825 1,395 0,337 1,529 1,406 1,280 1,427 1,601 1,987 0,508 0,118 0,492 0,166 0,333 0,488 0,224 1,988 1,910 1,713 0,51 0,593 1,236 1,563 1,285 1,314 0,777 1,438 1,644 0,489 1,839 0,762 0,911 1,262 1,854 0,577 0,365 1,605 0,55 1,413 1,349 0,574 1,460 0,302 0,554 0,924 0,350 1,869 1,368 1,962 1,904 0,416 0,478 1,371 1,497 0,97 0,943 1,382 1,906 0,894 0,149 0,815 1,286 0,148 1,960 1,345 1,25 1,858 1,214 1,928 1,616 1,948 0,229 1,848 0,936 0,413 0,16 1,20 1,263 1,400 1,783 1,264 0,215 1,594 0,485 1,71 0,471 1,0 0,498 0,835 0,948 0,31 1,509 0,396 0,297 0,426 0,303 0,36 1,365 0,432 0,308 0,933 1,287 1,91 0,517 1,477 1,703 0,452 1,273 1,675 0,3 1,985 1,297 1,98 1,688 0,576 1,723 0,941 0,26 0,26 1,736 1,349 0,448 0,160 1,16 1,474 1,233 0,183 1,585 0,1 0,961 0,373 1,201 0,370 1,78 1,594 1,242 1,770 0,521 1,261 1,474 1,383 0,929 0,136 0,781 0,25 1,355 1,30 0,988 0,361 1,838 1,154 1,426 0,60 0,940 0,703 0,846 1,372 1,770 0,189 0,625 1,805 0,105 0,856 1,239 0,344 0,476 0,432 1,411 0,997 1,370 1,259 0,248 1,678 1,615 1,302 0,395 0,574 0,969 1,872 0,214 1,769 0,396 1,257 0,277 0,141 0,766 0,655 0,901 0,805 0,893 1,704 1,775 1,229 1,575 0,984 1,97 1,900 0,442 1,834 0,92 0,695 1,200 0,4 0,839 1,967 0,696 0,187 0,294 0,136 1,238 0,114 0,510 0,753 0,370 0,551 0,244 1,788 1,753 1,171 1,803 0,364 0,85 0,452 0,623 1,481 1,103 1,55 1,598 1,333 0,208 0,774 0,558 1,469 0,781 1,298 0,693 1,850 1,268 0,104 1,427 1,799 0,597 0,997 1,60 1,632 0,497 1,298 0,865 1,444 0,500 1,476 0,729 0,536 1,760 0,105 1,127 0,708 1,901 0,667 0,872 1,303 0,960 0,599 0,260 0,576 1,776 0,511 1,226 0,276 1,154 1,810 1,702 1,222 1,478 0,732 1,541 0,574 0,319 0,509 1,65 1,599 1,909 1,277 0,97 1,759 0,61 0,144 0,607 1,255 1,183 0,537 1,584 0,520 1,98 1,702 1,737 0,138 0,877 1,941 1,218 0,127 1,390 0,751 1,65 1,38 1,850 0,8 1,208 0,508 0,967 0,489 0,639 0,686 0,546 0,24 1,299 0,325 0,40 0,724 0,626 1,34 0,237 1,105 1,171 0,963 0,548 1,341 1,253 1,61 1,946 0,468 1,573 0,168 0,225 1,111 0,685 1,424 1,381 0,262 0,187 1,25 0,574 1,552 1,319 0,940 1,85 0,340 1,136 0,484 0,513 1,295 1,552 0,721 1,177 0,198 0,807 1,670 0,184 0,675 1,981 0,925 0,183 1,822 1,186 1,242 1,856 1,103 1,677 1,254 0,807 1,769 0,516 0,984 0,963 0,897 0,866 1,880 0,141 0,324 0,938 1,936 0,130 0,538 1,34 1,829 1,990 0,908 1,895 1,756 1,71 1,774 1,156 0,940 1,158 0,981 0,541 1,498 1,935 1,187 0,96 1,142 0,351 0,654 0,726 1,991 0,78 0,700 0,559 0,773 0,432 0,24 0,248 1,31 1,465 1,460 1,331 0,952 1,923 0,823 0,156 1,935 0,208 0,8 0,985 1,472 0,399 1,733 0,890 1,247 0,580 1,742 1,329 0,444 0,268 0,526 0,393 0,778 1,5 0,650 1,979 0,33 1,82 0,73 0,521 0,470 1,433 0,353 1,504 0,840 0,557 1,594 0,461 0,602 0,845 1,573 1,147 0,894 0,826 1,741 1,829 0,691 1,168 1,703 0,486 0,826 1,952 1,65 0,479 0,127 1,508 0,166 1,844 1,332 1,350 1,151 1,875 0,225 1,88 0,149 0,966 1,646 0,933 1,681 1,28 1,222 1,990 1,264 0,868 1,482 1,9 1,472 0,104 1,103 0,632 0,104 0,235 1,809 1,319 1,949 1,350 0,448 1,959 1,784 1,387 0,144 1,706 0,185 1,708 1,914 1,805 1,521 1,215 1,270 0,128 1,961 0,456 0,573 1,445 1,318 0,697 0,861 1,371 1,96 0,701 0,23 1,403 1,987 0,749 0,667 1,784 0,744 0,122 0,539 1,773 1,603 1,43 1,292 1,616 1,639 0,697 0,438 0,912 1,241 1,19 1,358 0,528 1,598 0,456 1,354 0,258 0,388 1,672 1,538 1,577 0,894 0,688 1,437 0,447 1,97 0,272 0,342 1,486 1,934 0,963 0,598 0,494 1,475 1,804 0,635 1,412 0,320 0,370 0,377 1,589 1,752 0,415 0,587 0,224 1,979 1,757 1,402 0,228 1,825 0,964 1,256 0,709 0,549 0,462 1,319 1,999 1,195 1,248 0,96 1,699 0,417 0,755 0,559 1,609 1,184 1,905 1,9 1,486 1,730 0,662 1,285 1,573 1,180 0,113 0,780 1,207 1,487 1,517 0,529 0,36 0,485 1,35 1,32 1,998 1,27 0,640 0,360 0,117 0,309 0,838 0,98 0,319 1,893 1,650 1,655 0,725 1,230 0,596 1,571 0,515 1,500 1,858 1,178 0,947 1,141 1,254 0,274 0,938 1,886 1,110 0,210 0,494 1,40 1,115 1,304 1,11 1,276 0,973 1,500 1,250 0,953 1,693 1,220 1,105 1,867 1,239 1,474 0,376 0,623 1,756 0,378 1,393 1,475 1,345 0,494 0,769 0,148 1,705 0,411 1,267 0,866 1,322 0,394 0,181 1,870 0,754 1,799 1,733 1,147 1,221 1,129 0,131 1,197 0,455 0,787 0,252 1,713 0,705 1,404 0,809 1,268 0,802 1,55 1,658 0,948 1,932 0,499 1,947 1,138 0,653 0,552 0,781 1,304 0,912 0,586 0,531 1,495 0,885 0,480 1,147 0,821 1,536 0,673 0,448 0,103 0,286 1,743 0,940 1,539 0,83 1,711 1,103 0,574 1,138 1,163 1,125 1,622 1,737 1,104 1,775 0,511 1,531 0,725 0,10 1,739 0,576 0,517 1,206 1,604 1,124 1,145 0,647 1,193 0,656 0,526 1,648 1,553 1,157 0,48 1,770 1,674 1,345 0,870 0,809 1,217 1,929 1,877 0,661 1,418 1,891 0,153 0,741 1,644 1,120 1,816 1,616 1,199 0,441 1,517 1,36 0,595 1,209 1,311 0,872 1,832 0,944 0,727 1,69 1,658 1,791 0,418 1,665 0,728 1,707 1,526 0,562 0,313 1,269 0,610 0,225 0,374 1,516 1,639 0,801 1,233 1,532 0,527 1,722 1,527 0,722 0,754 0,791 1,776 0,6 0,810 0,263 1,51 1,343 1,67 0,257 0,56 1,815 1,369 0,459 0,392 0,285 1,404 0,869 0,284 1,245 0,437 0,746 0,549 1,754 1,639 0,736 0,972 1,244 0,608 0,771 1,752 0,350 1,225 0,947 1,778 0,490 1,926 1,340 1,119 0,623 1,974 0,991 0,769 1,23 0,911 1,153 1,389 1,460 0,669 1,336 0,14 0,932 1,426 0,932 1,438 0,455 1,445 0,193 1,848 0,580 1,94 1,266 1,95 0,206 0,563 0,375 1,60 1,124 0,275 0,114 0,765 0,147 0,373 0,458 0,280 1,65 0,363 0,29 0,248 1,103 0,131 0,258 0,511 0,797 0,330 0,424 0,990 0,414 1,609 0,900 1,576 0,563 1,515 0,993 1,91 0,73 1,484 1,47 1,870 1,343 0,831 1,120 0,569 0,291 1,206 0,298 0,50 0,923 1,123 1,687 1,153 0,850 0,880 1,229 0,784 1,365 1,307 1,899 0,377 0,363 0,809 0,557 0,109 0,723 1,248 0,345 1,925 0,39 0,995 1,136 1,191 1,508 1,379 1,188 1,218 1,345 1,820 1,525 1,79 1,52 0,728 0,87 0,178 1,661 1,781 1,557 0,248 0,945 1,732 0,102 0,848 1,898 0,234 1,667 1,25 1,666 1,356 0,432 1,603 0,139 1,993 0,238 0,765 1,396 0,673 0,799 0,38 0,435 0,220 1,501 1,185 0,537 1,45 1,611 1,577 0,36 0,809 1,364 1,232 1,766 1,448 0,566 1,339 0,23 0,79 0,81 0,259 1,630 1,409 0,158 0,329 1,377 0,734 1,84 0,159 0,411 0,686 1,469 0,535 1,752 1,221 1,280 1,176 1,966 1,153 1,261 1,729 1,825 1,410 1,100 0,623 0,560 0,84 0,322 1,206 0,739 1,982 1,795 0,631 0,139 1,946 0,995 1,83 0,886 0,544 0,934 0,242 0,539 0,224 0,713 1,90 1,882 0,295 0,819 0,143 1,604 0,464 1,668 0,823 0,654 0,370 1,18 1,80 1,560 1,556 0,152 1,367 1,690 0,789 1,208 1,896 0,921 1,412 0,714 1,533 1,314 0,978 1,391 0,33 0,590 1,644 0,695 0,842 1,719 0,700 0,146 0,101 0,604 1,331 1,976 1,555 0,426 0,292 1,167 0,622 1,971 0,609 1,542 0,616 1,498 0,205 1,557 0,362 1,638 1,708 0,777 0,745 0,616 1,980 0,874 1,644 0,505 1,48 1,472 0,585 0,132 0,39 1,849 0,571 0,352 0,671 1,49 0,72 0,244 1,650 0,252 0,983 1,686 1,372 1,998 0,486 0,383 0,570 0,210 0,3 0,901 0,456 1,394 1,258 1,406 1,868 0,954 1,781 1,138 0,582 1,379 1,413 1,929 1,372 0,51 1,536 1,473 0,180 1,509 1,285 1,499 0,330 0,766 1,400 0,11 0,902 0,479 1,773 0,733 1,288 0,30 1,821 1,114 1,259 1,960 1,481 1,379 0,60 0,416 1,490 0,679 0,565 1,810 1,895 1,299 1,989 0,265 0,25 1,252 0,728 1,52 1,905 1,165 0,128 0,512 1,577 1,920 0,911 1,875 0,322 0,100 0,619 1,724 0,923 0,239 1,791 0,749 0,184 1,571 0,835 0,356 0,653 0,708 0,875 1,103 1,413 1,92 0,808 0,680 1,945 1,415 1,881 1,45 0,642 0,621 1,12 1,366 0,34 0,794 1,980 1,694 1,953 0,91 1,305 1,778 0,533 0,982 0,235 1,313 0,622 1,949 0,208 0,757 1,613 1,214 0,484 1,794 0,551 1,328 0,774 1,373 1,189 0,14 0,559 1,53 0,183 0,348 1,495 1,530 0,5 1,106 1,319 1,971 0,416 0,366 1,243 0,297 0,13 0,176 1,448 0,163 1,824 1,826 0,666 0,68 1,945 0,519 0,868 0,347 1,381 1,763 0,434 0,123 0,695 0,675 0,963 0,769 0,848 0,664 1,737 1,182 1,842 0,729 0,727 0,704 0,671 0,82 0,325 1,616 0,247 1,272 1,438 1,405 1,175 0,74 0,307 0,320 0,97 0,823 0,820 0,243 1,322 0,609 0,913 0,852 0,335 0,339 1,597 1,877 1,281 1,103 1,938 1,256 1,137 1,163 0,92 1,58 1,837 1,355 1,661 1,888 0,945 1,648 0,290 0,350 0,816 0,408 1,286 1,488 1,168 1,44 1,434 0,790 1,889 1,953 0,754 0,762 1,223 0,587 1,874 0,856 0,483 1,425 1,989 0,57 1,386 0,676 1,97 1,957 0,616 1,150 0,463 1,9 0,122 0,217 0,332 0,15 1,43 0,358 0,398 1,494 1,745 1,968 1,569 0,882 1,857 1,195 1,260 0,570 1,988 1,844 1,377 1,533 1,934 0,695 1,101 1,16 0,725 1,673 1,368 1,861 1,216 0,521 0,567 0,308 0,704 0,756 1,726 0,303 0,280 0,633 0,626 0,1 0,325 1,104 1,720 1,84 0,301 0,240 1,368 1,48 0,202 0,16 0,736 0,741 1,151 1,638 0,141 0,221 0,627 1,429 1,780 0,173 0,159 0,35 0,70 1,787 1,468 1,209 1,246 1,658 1,13 0,614 0,84 0,814 0,847 1,636 0,70 0,176 1,681 0,266 0,960 0,73 1,14 0,76 1,174 0,490 1,925 0,284 0,111 0,860 0,886 1,892 1,244 0,783 1,575 0,995 1,856 0,187 0,532 0,514 1,748 1,771 1,176 0,987 1,260 0,524 0,706 1,788 1,272 1,906 0,661 1,652 1,969 0,941 0,346 1,307 0,265 0,826 1,975 0,36 1,224 1,144 0,255 0,711 1,886 1,8 0,429 0,335 0,79 1,877 1,98 0,823 0,483 1,756 0,137 0,323 1,290 1,63 1,482 1,223 0,189 0,434 0,192 0,989 0,996 0,326 0,5 1,17 0,477 0,844 0,856 1,487 0,491 0,489 1,52 1,491 0,520 0,257 0,627 0,142 1,890 0,836 1,356 0,237 0,1 1,393 0,868 1,105 1,500 0,714 0,428 1,749 1,592 0,194 1,76 0,828 1,601 1,980 0,171 0,783 0,138 1,39 1,247 0,405 0,170 1,269 1,510 1,882 1,540 0,504 1,554 0,776 0,986 0,996 1,840 1,659 1,821 1,538 0,683 1,811 1,584 1,331 0,185 0,926 1,316 1,397 1,547 1,685 0,261 1,614 1,340 0,52 1,457 1,614 1,390 0,723 0,124 1,681 0,623 0,336 0,190 0,810 1,362 0,73 0,366 1,200 0,434 1,672 1,288 0,15 1,900 1,485 1,439 1,159 0,725 0,461 1,27 1,853 1,768 0,760 1,221 1,84 1,929 1,366 0,417 1,530 1,934 1,443 1,701 0,283 0,602 1,198 1,483 0,46 1,128 0,78 0,508 1,551 1,76 1,343 0,581 0,176 1,378 1,168 0,192 1,32 0,576 0,281 1,954 1,440 0,224 0,522 0,392 0,16 1,483 1,469 1,5 0,517 0,190 1,712 1,49 1,174 1,605 0,12 1,875 1,978 0,342 1,687 0,365 1,308 0,720 1,352 0,295 1,722 0,598 1,367 0,246 1,57 1,150 0,484 0,502 0,902 0,29 0,458 1,717 1,148 1,684 1,110 0,737 0,642 0,217 1,187 1,615 0,968 0,980 1,551 1,432 1,418 0,725 1,341 0,569 0,483 1,901 1,748 1,701 1,210 1,863 1,139 1,282 0,273 1,543 1,5 0,948 1,785 1,963 1,260 1,229 0,585 0,754 0,364 0,177 0,447 1,621 1,741 0,268 0,358 0,517 1,513 0,428 0,14 1,380 0,243 1,860 1,164 0,306 1,466 0,943 0,967 1,221 1,785 0,731 1,707 1,109 0,989 0,995 1,72 0,963 1,648 1,24 0,497 1,225 0,367 1,371 0,934 0,64 1,695 1,595 1,181 0,56 0,976 0,15 0,568 0,992 1,732 0,534 1,965 0,389 0,153 0,694 1,362 1,934 0,414 0,522 0,64 1,656 1,221 0,988 0,759 1,203 1,361 0,616 0,480 0,329 0,661 0,869 1,237 1,987 0,530 1,765 0,523 1,318 1,34 0,635 1,664 0,504 0,650 0,295 1,851 0,779 1,706 0,550 1,891 1,706 1,175 0,117 1,209 0,319 1,496 1,403 1,481 0,875 1,172 1,739 1,734 0,565 0,789 0,188 1,172 1,792 0,649 0,897 0,449 1,232 1,477 0,994 0,650 1,438 1,117 1,53 1,570 1,63 1,252 1,375 1,87 1,678 0,529 1,338 0,667 1,954 0,779 1,795 0,723 1,817 0,105 1,11 1,668 1,918 0,772 0,146 0,540 1,297 0,90 0,17 0,938 1,239 0,679 1,777 1,229 0,402 0,472 0,774 1,500 1,953 0,137 0,510 1,305 1,518 1,783 1,921 1,539 1,5 1,493 0,955 1,577 1,793 1,766 1,432 1,703 0,173 1,558 0,342 0,522 0,34 1,171 0,508 1,371 0,906 1,815 1,980 0,465 1,597 0,241 0,282 0,215 0,660 1,355 1,670 1,299 1,424 0,838 1,490 1,497 0,536 0,327 1,246 1,196 1,364 1,835 1,777 0,628 0,351 1,529 1,306 0,348 0,695 1,458 0,120 0,410 1,530 0,684 1,187 1,225 0,579 1,229 0,12 1,40 0,54 0,850 1,419 1,344 1,617 1,506 0,324 0,382 1,394 1,393 1,139 0,907 1,242 1,33 0,342 0,692 0,215 0,91 1,356 1,648 1,389 1,560 1,486 0,842 1,474 1,890 1,213 1,557 0,236 0,41 0,983 1,493 0,114 0,421 1,192 1,127 1,788 0,834 1,372 1,437 0,376 0,504 1,649 0,715 0,437 0,511 1,438 1,475 0,230 1,111 1,998 0,50 0,594 1,843 1,681 1,46 1,14 1,467 0,316 0,352 0,672 1,342 0,622 0,133 1,10 0,291 1,269 1,313 1,664 1,29 1,142 0,327 1,20 0,5 0,25 0,716 0,760 0,155 0,858 1,387 0,382 0,505 0,784 1,238 1,123 1,578 0,478 0,505 0,765 1,854 1,197 0,994 1,313 0,858 0,477 1,96 1,329 0,508 1,304 0,798 1,719 1,118 0,922 0,503 0,319 1,585 1,679 1,897 1,942 0,512 1,86 1,452 0,284 1,151 0,628 0,466 1,79 1,926 0,72 1,254 0,67 0,486 1,26 1,178 1,299 1,948 0,26 1,108 1,844 1,202 1,376 1,590 1,389 0,297 1,492 0,485 0,370 0,121 1,397 0,730 1,505 0,56 0,831 1,580 1,835 1,53 0,515 1,272 0,784 1,327 1,756 0,458 1,959 0,955 0,793 1,387 0,585 0,124 1,97 1,393 0,754 0,229 1,172 1,950 0,960 1,801 0,311 0,989 0,204 0,444 0,394 1,393 1,740 0,184 0,511 0,100 0,441 1,706 1,181 0,12 0,161 1,630 0,756 1,915 1,171 1,250 0,434 0,374 1,960 1,336 0,179 1,929 0,794 1,427 1,238 1,791 1,978 0,208 0,644 1,502 0,883 0,354 1,942 0,606 0,6 0,125 0,732 0,716 1,182 0,886 1,904 1,556 0,947 0,397 0,763 0,644 0,833 1,376 1,856 0,707 0,800 1,314 0,871 1,687 0,26 0,889 0,850 0,781 1,258 0,406 0,696 0,107 0,334 0,580 1,716 1,657 0,538 1,34 1,111 1,522 0,385 1,530 0,955 1,650 1,169 1,785 0,180 1,330 1,50 1,713 0,70 1,88 0,766 1,624 1,998 1,737 0,212 1,206 0,763 1,488 0,143 1,576 1,190 1,3 1,491 0,933 1,176 0,659 1,743 0,935 1,388 0,420 0,909 0,167 0,847 1,300 1,405 0,263 0,608 1,934 1,850 1,243 0,593 1,930 1,201 1,790 1,982 1,257 1,30 1,650 0,85 1,480 0,245 0,550 0,306 1,525 1,667 0,826 1,483 1,161 0,644 0,223 0,360 1,959 1,849 0,952 0,574 1,809 1,931 0,494 0,764 1,381 0,897 1,977 1,4 0,912 1,559 1,649 1,296 1,638 1,810 1,156 0,798 1,692 1,24 0,836 1,59 0,78 0,811 0,898 1,637 1,590 0,515 0,716 1,505 0,3 0,68 0,323 1,149 0,818 1,902 1,301 0,662 0,241 0,819 1,752 1,440 1,895 1,112 0,185 1,493 0,266 1,353 0,292 0,171 0,534 0,593 1,933 0,363 0,966 0,179 1,164 0,752 0,364 0,399 0,916 0,747 0,391 0,38 1,130 0,57 0,333 1,141 1,982 0,635 1,256 0,313 1,83 1,729 1,368 1,331 0,429 0,191 0,418 0,563 1,324 0,624 1,999 1,545 1,598 1,417 1,22 0,311 0,485 0,308 0,738 1,859 0,223 1,551 0,875 0,286 1,5 0,316 0,282 0,879 0,493 1,220 1,792 1,24 1,214 1,181 0,80 1,175 0,747 0,553 0,837 1,824 1,785 1,866 0,481 1,138 0,890 1,294 1,476 1,853 0,718 1,994 1,509 1,425 0,878 1,213 0,199 1,525 1,494 1,242 1,39 0,509 1,719 0,64 0,151 0,130 0,624 0,556 0,428 0,65 1,639 0,501 1,17 1,394 1,627 0,51 0,282 0,912 1,103 0,380 1,150 0,920 1,696 0,778 1,288 0,514 1,50 1,142 0,899 1,892 1,796 1,796 0,692 0,147 1,179 1,698 0,654 1,468 1,65 0,404 1,53 0,185 1,675 0,426 1,90 0,880 0,94 1,428 1,41 1,959 1,438 0,187 1,33 0,307 0,163 1,559 0,535 0,773 0,578 0,763 1,882 0,505 1,853 1,399 0,52 1,480 1,192 0,99 1,196 1,311 1,263 0,826 1,134 1,456 0,354 1,593 1,182 1,167 1,829 0,20 1,417 1,54 0,171 0,170 0,614 1,930 1,208 0,710 1,717 1,144 0,288 0,250 1,279 0,393 1,418 1,846 0,401 0,557 1,582 0,173 1,974 1,693 0,988 0,475 1,526 0,921 1,755 0,985 1,51 0,252 0,295 1,184 0,215 1,672 1,56 0,975 0,222 0,349 0,459 1,429 0,24 1,559 1,191 0,639 0,370 0,302 0,697 1,578 0,180 1,602 1,652 1,326 1,429 1,382 1,756 1,750 0,965 0,885 0,873 1,424 0,344 1,760 0,467 1,751 1,459 0,362 1,238 1,7 0,166 1,732 0,603 1,556 0,265 0,20 0,49 0,144 1,268 0,791 0,732 1,796 0,620 1,26 1,393 0,497 1,728 0,623 0,810 1,821 1,822 0,445 1,835 1,730 1,594 1,318 1,433 1,161 1,907 1,156 0,948 0,410 1,610 1,574 1,724 0,725 1,60 1,604 1,68 1,95 1,244 1,53 0,915 0,163 0,661 0,719 1,105 1,191 0,49 1,149 1,600 0,551 0,925 1,363 0,502 0,403 1,570 1,284 0,292 1,846 0,410 0,884 0,469 1,237 1,148 0,479 1,439 1,959 0,631 0,478 0,268 1,783 0,416 1,900 1,18 0,790 0,985 1,794 1,209 1,740 1,159 1,245 1,760 1,242 0,248 0,596 0,294 0,103 1,833 1,539 1,772 0,104 0,295 0,91 0,146 0,396 0,637 0,273 0,462 1,841 0,596 1,926 0,957 1,200 0,300 1,795 1,740 1,869 1,222 0,945 0,867 0,744 1,887 0,458 0,816 1,943 0,636 1,7 0,111 0,707 0,254 1,394 1,401 0,997 0,280 0,996 0,358 0,702 1,927 0,261 0,497 0,272 1,452 1,808 0,679 0,364 1,359 0,220 0,214 1,985 1,344 0,316 0,918 0,675 0,314 1,48 1,599 0,749 0,742 0,80 0,994 0,123 1,389 1,787 0,626 0,56 0,212 0,982 0,88 1,737 0,43 0,826 0,475 0,296 1,232 1,29 1,358 0,137 1,377 0,643 0,701 1,79 0,742 0,193 0,719 1,194 0,127 0,665 0,119 0,852 1,882 0,61 1,247 1,441 0,379 1,439 1,179 0,431 1,892 1,237 0,928 0,897 1,14 0,834 1,56 1,309 0,554 0,476 0,923 1,780 0,573 0,232 0,205 0,427 1,326 0,977 1,887 1,131 1,578 1,276 1,302 0,630 0,443 0,593 0,894 0,649 1,681 1,770 1,313 0,419 0,905 0,111 1,739 1,284 1,545 0,110 0,247 1,413 0,852 1,991 0,972 0,446 1,386 1,268 0,676 1,662 0,572 0,627 0,999 1,140 0,349 0,429 0,205 0,415 1,580 1,135 1,332 1,417 0,61 0,588 1,475 1,800 1,664 0,305 0,451 1,567 0,116 1,638 1,110 1,46 1,205 0,813 0,335 0,541 0,99 0,275 1,144 0,833 1,433 1,300 0,390 1,28 0,85 0,880 1,78 0,128 0,989 1,520 1,150 1,728 0,12 1,886 1,695 1,913 0,663 1,103 1,677 1,880 0,740 1,65 0,493 0,306 1,347 1,129 0,624 0,735 0,296 0,374 1,118 0,199 0,128 1,302 1,99 1,942 0,998 1,346 0,127 1,110 1,845 0,379 0,146 0,438 1,130 1,439 1,588 1,305 0,688 0,945 1,829 0,267 1,657 1,364 1,928 0,706 0,636 0,148 0,786 0,914 1,264 1,164 0,340 0,967 0,589 1,980 0,352 1,55 0,20 0,942 1,377 1,914 1,307 0,903 0,357 0,985 1,717 0,539 1,377 1,182 0,239 0,720 1,362 0,519 0,669 0,773 0,941 0,502 1,214 1,629 0,191 0,849 0,917 1,483 0,919 0,243 0,627 1,861 0,463 0,927 1,933 1,77 0,724 0,399 0,493 1,243 0,467 1,268 0,486 1,806 0,736 0,398 0,250 1,797 0,589 1,38 1,187 1,190 0,752 1,226 1,498 1,20 1,624 0,203 0,47 1,474 0,133 0,742 1,248 1,459 1,847 1,290 1,471 1,869 1,286 1,938 1,35 1,932 1,464 0,753 0,724 0,733 1,115 1,425 0,284 0,118 1,468 0,570 1,432 0,758 1,455 1,846 0,322 1,876 1,641 1,227 0,324 0,863 1,617 1,408 1,569 1,476 0,654 1,785 1,155 1,779 1,646 0,409 0,95 0,57 0,411 1,562 1,646 0,331 1,420 1,998 0,454 1,24 1,938 0,188 1,289 1,780 0,828 1,926 1,222 1,517 0,118 1,794 0,775 0,826 0,6 1,212 0,938 0,502 1,323 0,893 1,919 1,275 1,258 0,160 1,541 1,211 1,930 0,464 0,747 1,702 1,436 0,291 0,164 0,991 1,495 0,284 1,244 1,86 0,237 0,269 0,172 0,284 1,298 1,529 1,344 0,480 0,119 1,811 1,474 1,516 1,519 0,903 0,245 1,531 0,804 0,346 0,773 1,649 0,707 1,891 1,680 0,143 1,280 0,342 1,940 0,672 0,703 0,305 1,983 0,16 1,379 0,674 1,863 0,100 1,21 0,48 0,594 0,758 1,860 0,364 1,482 0,754 1,697 1,206 0,249 0,162 0,361 1,804 1,445 1,743 1,699 0,15 0,689 0,827 1,220 1,948 1,432 1,161 1,736 0,756 0,263 0,236 0,423 0,886 0,69 0,871 0,208 0,975 0,599 0,52 1,70 0,336 0,96 1,965 0,500 0,419 0,433 1,870 1,401 1,71 1,494 1,800 1,184 0,81 1,844 0,819 1,609 1,878 1,481 1,801 1,341 1,401 0,9 1,602 1,557 0,827 0,414 0,21 1,206 0,220 1,723 1,629 1,920 1,972 0,745 0,206 1,965 0,209 0,132 0,693 0,366 1,710 1,368 0,827 0,453 1,662 1,762 0,88 1,217 0,745 0,146 0,238 0,56 0,146 1,893 0,614 1,605 0,506 1,123 0,95 1,543 0,719 1,32 1,132 0,926 0,54 1,677 1,459 0,416 0,324 0,607 0,61 0,304 1,979 0,85 1,227 0,216 1,284 0,197 1,372 0,110 1,149 0,862 0,231 0,470 0,985 1,1 0,475 1,566 0,992 1,8 0,869 0,168 1,326 0,831 1,658 0,575 0,10 1,4 1,895 0,482 1,640 1,255 1,681 0,71 0,221 0,105 0,928 0,957 0,115 0,282 0,913 1,521 1,724 1,36 1,51 0,994 1,241 1,894 0,800 0,886 0,947 1,266 1,911 1,67 0,864 0,56 1,307 1,286 0,635 0,733 0,468 0,876 1,815 0,643 1,368 0,951 0,658 1,476 0,931 0,234 1,313 1,703 0,77 1,892 1,709 1,263 1,343 1,247 1,727 1,610 1,167 0,613 0,261 1,906 1,652 1,390 1,472 0,232 1,500 0,60 0,603 1,274 0,66 0,126 1,985 0,376 1,582 1,818 1,995 0,613 0,542 1,932 1,484 0,21 1,12 0,515 1,604 0,607 0,908 1,805 1,75 0,99 1,870 0,783 1,815 0,224 0,178 1,126 1,174 1,386 1,7 0,294 0,705 0,2 1,193 1,311 0,392 0,947 0,426 1,716 0,294 1,79 0,761 0,419 0,575 0,335 1,51 1,746 1,448 0,419 1,212 1,883 1,391 0,611 0,401 1,213 0,322 1,907 1,445 0,196 1,547 1,927 1,618 0,329 0,906 0,502 1,172 1,934 1,802 1,406 0,534 1,328 0,331 1,270 0,255 0,618 1,144 0,530 1,671 0,777 1,462 1,733 1,669 0,739 1,114 1,227 1,624 1,14 0,108 1,887 0,444 1,153 1,480 0,820 0,401 0,57 0,697 0,616 1,509 1,580 0,852 0,709 1,530 0,339 1,15 0,333 1,614 1,403 1,671 0,473 0,996 1,680 1,780 0,149 0,932 1,203 1,156 0,978 1,983 1,903 0,90 0,558 0,737 0,293 1,504 0,997 0,963 0,407 1,764 1,280 0,697 1,574 0,901 1,984 1,34 0,952 1,988 1,236 0,983 1,80 1,761 1,673 0,67 0,46 1,333 0,626 0,654 0,760 0,593 0,201 1,321 1,678 0,445 1,16 1,360 0,29 1,476 1,248 0,816 0,214 0,174 0,587 0,3 1,793 0,971 0,673 1,690 0,287 1,944 1,934 0,27 0,52 1,637 1,888 1,992 1,54 0,795 0,955 1,741 1,928 0,96 1,941 0,106 0,732 1,781 0,690 0,418 0,315 1,650 1,850 0,570 1,794 0,558 1,239 0,88 0,387 1,102 0,531 1,884 1,163 1,699 0,52 1,489 0,355 0,869 0,607 0,945 1,494 1,153 0,653 0,754 1,760 1,901 1,45 1,496 0,330 1,35 1,671 1,627 1,233 1,382 0,475 0,389 1,840 0,156 1,603 1,392 0,226 1,148 1,544 1,911 0,331 1,230 0,84 1,321 0,304 0,395 1,372 1,820 1,600 0,930 0,434 0,0 1,734 0,995 0,481 0,807 0,648 0,598 1,604 0,881 0,879 1,917 0,295 1,299 1,749 1,128 1,813 0,268 0,374 0,916 0,535 0,694 1,712 0,706 0,72 0,49 0,746 1,93 0,509 1,794 1,450 1,787 0,244 0,663 1,226 0,616 1,726 0,584 0,301 0,695 0,913 1,872 1,802 0,820 1,531 1,247 1,909 1,906 0,52 1,407 1,919 1,144 0,337 0,728 1,380 1,631 1,710 0,996 1,967 1,358 0,279 0,411 1,899 1,937 0,3 1,866 1,178 0,536 1,301 0,718 1,170 0,983 0,367 1,189 0,977 1,219 0,703 1,43 1,897 1,90 1,364 0,432 1,609 0,716 0,398 1,787 0,950 1,502 1,25 1,148 0,473 1,193 1,722 0,928 1,180 0,796 1,529 0,335 0,800 0,367 0,351 0,204 0,18 1,364 1,555 0,660 1,585 0,395 0,185 0,11 0,845 1,379 1,839 1,568 0,698 1,932 1,488 0,583 0,357 1,559 0,749 0,633 1,553 1,576 0,888 0,912 1,530 0,557 1,58 1,907 0,484 0,830 1,306 1,967 1,653 1,885 1,12 1,774 0,812 1,380 1,661 1,659 1,7 0,950 1,858 0,230 0,103 0,384 1,956 0,961 0,725 1,476 0,165 1,721 0,969 0,809 0,789 1,935 0,430 0,594 1,322 1,159 0,489 0,287 1,383 0,977 0,338 0,790 1,259 0,898 0,136 1,10 1,512 1,327 1,38 1,549 0,393 1,890 0,35 1,989 0,515 1,422 0,910 0,893 0,418 0,106 0,574 0,878 0,228 1,508 0,342 0,872 0,854 0,333 0,244 1,353 1,25 0,802 0,754 1,916 1,420 1,628 0,254 0,975 1,793 0,475 0,671 0,494 1,42 1,244 1,27 0,684 1,361 1,719 1,208 0,436 0,149 0,395 0,815 1,662 0,724 1,553 1,72 1,258 1,513 1,641 1,447 1,802 0,830 0,807 0,5 0,691 1,78 1,647 0,684 1,216 1,450 0,437 1,703 1,182 0,222 0,846 0,858 0,395 0,969 0,867 0,51 1,299 1,757 1,253 1,408 1,834 1,398 1,199 1,913 0,984 1,562 1,902 0,989 0,270 0,617 1,738 1,429 1,620 0,35 1,677 0,262 1,645 1,396 0,424 0,236 1,643 0,283 1,416 1,265 0,983 0,820 1,482 0,694 1,9 1,348 0,864 0,996 1,658 1,97 0,359 1,183 1,181 1,487 1,523 0,913 0,39 1,384 1,610 0,153 1,820 1,513 0,611 1,888 1,986 0,522 1,850 0,188 1,838 0,701 0,307 1,814 0,824 0,947 1,495 1,519 0,670 0,626 0,87 0,100 0,749 0,42 1,724 0,777 1,479 1,427 1,226 0,587 1,527 0,644 1,503 1,842 1,527 1,456 0,525 1,361 0,179 1,634 0,339 0,655 0,314 0,230 1,551 0,547 0,50 0,307 1,739 1,173 0,666 0,519 1,966 1,727 1,220 0,52 0,591 0,460 0,403 1,817 0,36 1,923 0,876 1,84 1,996 0,533 0,263 1,681 1,127 0,559 1,649 0,745 1,868 0,459 1,47 1,436 0,710 0,779 0,803 1,78 0,211 0,13 0,277 1,170 0,323 1,354 0,504 1,994 1,265 0,81 1,720 0,19 1,185 0,626 0,155 1,208 1,117 0,338 1,403 0,569 0,404 0,638 0,145 0,350 1,656 0,296 1,283 0,209 0,700 0,10 0,874 1,175 0,266 0,251 1,745 0,9 1,644 1,223 1,655 1,523 1,752 1,473 1,202 0,856 1,700 1,793 1,183 1,440 1,653 1,317 0,650 0,832 0,851 1,425 0,546 1,868 1,294 0,856 1,746 1,641 0,252 0,921 0,390 0,623 0,56 1,566 0,822 0,969 0,583 0,864 1,361 0,998 1,256 1,437 1,23 0,781 1,247 1,117 0,450 0,908 1,833 1,381 1,407 0,965 1,109 0,686 0,580 0,56 0,27 1,983 1,725 1,878 1,886 0,382 1,493 0,255 0,659 0,718 1,15 0,371 1,854 0,104 1,663 0,777 0,347 1,210 1,562 1,957 0,696 0,358 0,350 1,435 1,111 0,981 0,283 1,336 0,960 1,443 0,806 0,653 1,941 1,431 0,616 0,81 1,784 0,829 1,148 1,100 1,957 1,946 0,469 0,387 0,595 0,470 0,486 0,238 0,285 1,464 0,648 0,626 1,713 0,746 0,499 1,477 1,86 0,811 0,599 1,740 1,355 0,652 1,793 0,800 0,891 0,980 1,857 1,743 1,652 0,173 0,401 1,109 0,114 1,747 0,308 1,707 1,111 0,267 1,237 0,45 1,211 1,312 0,922 1,199 1,884 1,35 1,701 0,813 0,575 1,659 0,847 0,810 0,153 1,616 1,416 0,911 1,762 0,689 1,894 1,120 1,845 0,733 0,302 0,700 0,724 1,722 1,25 1,778 0,35 1,801 1,156 1,897 0,304 1,663 1,668 0,970 1,716 1,538 1,621 1,348 0,949 1,899 1,443 0,718 1,673 0,29 0,48 0,756 0,509 0,35 1,37 0,906 0,449 0,666 1,453 0,369 0,198 0,298 0,821 0,198 0,431 1,251 0,365 0,118 1,247 1,299 0,725 1,186 1,114 0,524 0,402 1,882 1,962 0,118 1,877 1,686 1,885 0,306 0,705 0,902 0,999 0,893 0,62 0,861 0,756 0,806 0,920 1,380 0,166 0,741 1,703 1,374 0,798 0,62 1,480 1,199 1,151 1,517 0,889 1,904 0,271 0,539 0,809 1,30 1,683 1,601 1,620 1,940 1,814 1,902 0,958 1,826 0,342 1,292 0,64 1,129 1,434 1,312 1,993 0,686 0,400 1,247 1,918 1,131 1,524 0,9 0,874 0,568 0,217 1,309 0,388 1,658 1,35 0,661 1,448 0,197 1,925 1,8 1,39 0,731 1,31 0,581 0,71 0,341 1,507 1,297 1,141 1,853 1,325 1,938 0,212 1,119 1,493 1,333 1,16 1,275 0,463 1,375 0,41 1,413 1,228 1,301 1,627 1,504 0,148 0,849 0,268 0,739 1,194 0,895 1,112 0,401 0,739 1,862 1,369 1,365 0,160 0,729 1,999 0,641 1,912 0,877 0,628 1,933 1,689 0,111 0,845 0,367 0,918 1,217 1,653 0,613 0,511 1,596 1,35 0,362 0,291 1,494 0,13 1,210 1,961 1,273 0,759 1,898 1,504 0,863 1,119 0,218 0,481 0,688 1,114 0,398 1,588 1,633 1,518 0,452 0,962 1,494 1,457 1,211 0,110 1,648 0,639 1,383 0,793 1,573 0,864 0,393 1,973 0,499 0,924 1,100 0,469 0,763 1,186 1,532 0,330 0,52 1,82 1,780 1,459 0,182 0,543 0,282 1,947 0,510 0,54 0,83 1,922 1,254 0,740 0,693 0,92 1,785 1,337 0,176 0,488 1,831 1,362 1,258 1,947 0,477 0,201 1,46 0,953 1,302 0,967 1,162 1,86 1,349 1,723 1,222 0,492 0,86 0,614 0,211 1,225 1,980 0,757 0,409 0,905 0,232 0,600 1,986 1,996 1,421 1,357 1,739 1,923 1,50 1,715 0,378 0,80 1,276 1,557 0,516 1,723 1,361 0,719 0,444 1,567 0,977 1,57 1,649 1,837 1,694 1,80 0,534 0,118 1,911 0,170 0,239 1,608 1,251 0,117 0,762 1,881 1,798 0,146 1,832 1,327 1,480 1,251 1,957 0,302 1,923 0,729 0,632 0,91 1,884 0,178 0,132 1,22 0,624 0,133 0,389 0,469 1,375 0,580 1,80 0,970 0,826 1,418 1,232 0,442 0,199 1,218 0,7 0,530 1,483 0,9 0,586 1,457 0,296 0,196 1,868 0,49 1,521 0,351 0,347 0,940 0,954 0,761 1,777 0,36 0,253 1,276 0,192 1,706 1,131 1,955 1,644 0,779 0,656 0,167 0,916 1,596 1,479 1,57 1,975 1,785 0,633 1,349 0,907 0,178 0,136 0,990 1,315 0,694 1,200 0,923 0,506 0,412 0,846 1,641 0,957 1,414 0,715 1,646 1,119 1,843 1,776 1,181 1,417 1,748 0,378 0,293 0,369 0,942 1,873 1,528 1,612 1,591 0,96 1,688 0,968 0,679 0,885 0,666 0,363 0,192 0,210 0,314 1,162 1,788 0,69 0,434 0,335 1,492 0,811 1,275 0,481 1,805 0,122 1,59 1,456 0,858 0,543 1,108 0,274 0,528 1,449 0,613 0,188 1,228 1,19 0,260 0,613 0,654 1,550 0,795 1,805 1,575 1,358 1,919 0,624 0,149 1,176 0,173 1,546 1,48 0,882 0,229 1,415 0,359 1,942 1,561 1,645 1,796 0,825 0,686 1,129 0,422 0,738 0,972 0,696 1,906 1,588 1,334 0,271 0,294 1,185 1,783 0,252 0,243 1,182 1,523 0,36 0,140 0,389 0,754 1,178 0,239 0,342 0,128 1,728 0,989 1,489 1,208 0,570 0,882 0,923 0,207 1,922 1,512 0,244 0,536 1,847 1,625 1,91 1,206 0,455 1,847 0,423 1,774 0,163 0,522 0,363 1,181 1,397 0,816 0,469 1,47 1,275 1,245 0,286 1,739 1,478 1,844 0,753 1,822 1,350 1,590 1,270 1,45 0,260 1,420 0,709 1,508 0,334 1,161 0,533 0,449 0,483 0,152 0,962 0,637 0,896 0,198 1,732 0,685 1,874 1,779 0,478 0,49 0,524 1,1 0,132 0,412 0,671 0,675 0,386 1,320 1,762 0,609 1,305 1,182 1,5 0,248 1,162 1,876 1,983 1,689 1,727 1,817 0,737 0,317 1,205 1,400 1,962 1,47 1,483 0,883 1,293 0,417 1,154 1,626 1,532 1,417 0,255 1,376 0,630 0,152 1,557 1,608 0,807 1,762 0,511 1,115 1,374 0,836 1,355 0,198 0,651 0,754 1,664 0,910 1,529 0,443 1,885 1,152 1,973 0,298 1,749 1,730 0,429 1,915 0,451 1,361 0,7 1,153 0,701 1,355 1,990 0,359 0,926 0,163 0,128 0,789 1,490 1,880 0,304 0,795 0,215 0,156 1,226 0,865 0,885 1,880 1,354 1,835 1,627 0,324 0,171 0,896 0,832 0,947 0,258 0,129 0,768 1,426 0,233 1,534 0,453 1,130 0,762 0,572 1,556 0,13 1,566 1,555 0,149 1,551 1,241 1,392 0,732 0,478 0,212 1,860 1,131 1,386 1,797 0,710 1,799 0,41 1,713 1,602 0,606 1,129 0,873 0,758 1,240 0,646 1,330 0,814 1,668 1,459 1,854 0,411 1,306 0,579 0,162 0,706 0,388 1,619 0,537 0,936 0,860 0,872 0,454 0,645 0,279 1,767 1,871 0,610 1,3 0,665 0,359 0,521 0,70 1,374 0,589 1,564 0,26 0,323 1,203 1,246 1,148 0,713 0,478 0,421 0,536 0,881 0,905 1,814 0,114 0,111 0,501 1,71 1,520 1,738 0,807 1,421 0,532 1,918 0,182 0,171 1,174 1,744 1,542 1,145 0,903 0,136 1,966 0,164 0,851 0,335 0,776 1,217 1,504 1,624 1,25 0,137 0,291 1,979 1,985 0,657 0,406 1,948 0,386 0,377 0,469 1,871 1,486 0,863 1,480 0,918 0,973 0,213 0,386 1,703 1,327 0,817 1,103 1,635 0,533 1,459 1,727 0,511 1,140 1,3 0,352 1,998 0,887 1,125 1,598 0,854 0,406 0,101 0,470 0,668 0,749 1,422 1,311 1,934 1,405 0,943 1,293 0,307 0,650 1,660 0,492 1,342 1,725 0,269 1,462 0,738 1,447 1,651 0,145 0,869 0,6 0,108 1,298 1,791 1,764 1,364 1,577 0,180 1,529 0,453 0,687 0,883 0,815 1,76 1,213 0,22 1,790 0,913 0,393 0,66 1,570 1,774 0,586 0,415 1,561 1,442 1,316 0,402 1,243 0,159 1,45 0,410 1,41 0,885 1,233 0,151 0,366 1,859 0,959 1,249 1,573 1,358 0,294 0,944 1,447 1,611 1,632 1,884 0,797 1,55 1,528 0,116 1,893 1,391 0,357 0,388 0,579 1,973 1,939 0,461 1,735 1,640 1,104 1,341 1,550 0,421 0,918 0,455 0,304 1,729 0,660 0,675 0,810 0,411 1,446 1,735 1,566 0,323 1,969 0,732 1,673 0,283 1,580 1,983 0,880 0,691 0,780 0,652 0,688 0,557 1,815 1,170 1,787 0,441 1,104 0,487 0,70 1,499 0,925 1,963 1,328 1,246 0,744 0,811 1,539 1,598 1,244 0,446 1,802 0,803 1,323 1,450 0,868 0,871 1,397 1,36 0,882 1,227 1,658 0,375 0,24 1,322 0,673 0,391 1,100 0,105 1,903 1,635 1,641 0,657 1,193 0,287 1,52 0,197 0,483 0,304 0,58 0,847 1,555 1,956 1,539 0,120 1,527 1,148 0,855 1,211 0,903 1,323 0,710 0,840 1,604 0,973 1,532 1,527 1,686 0,567 0,869 1,884 1,556 1,987 1,962 1,240 1,815 1,266 1,698 1,195 1,335 0,17 1,817 0,691 1,108 0,238 1,938 0,77 1,898 1,629 1,253 0,937 1,412 1,779 1,343 1,877 1,720 0,302 1,977 0,461 0,706 0,149 1,571 0,51 0,838 0,822 1,29 0,742 0,383 0,368 1,476 0,161 1,29 0,697 0,812 0,4 1,949 0,610 0,257 0,178 1,808 1,970 1,555 0,319 1,874 1,943 1,593 1,448 0,167 1,260 0,940 0,734 1,539 1,26 1,997 0,112 0,630 0,726 0,34 0,175 1,514 1,896 1,474 0,825 0,40 0,944 0,568 1,379 0,606 0,551 0,927 1,222 0,547 0,925 0,38 1,108 1,819 0,130 1,864 1,973 1,176 0,479 1,148 0,776 1,128 0,805 0,150 0,997 1,801 1,536 0,295 1,571 0,211 0,174 0,980 0,536 1,4 0,154 0,943 1,986 0,430 0,56 1,222 0,208 0,934 1,195 1,469 0,387 0,813 0,168 1,306 1,914 0,37 1,738 0,243 0,132 1,586 0,336 0,253 1,405 1,296 0,769 1,100 0,947 1,693 0,577 1,891 1,197 1,801 1,114 0,426 0,557 0,263 0,364 0,861 1,612 1,295 0,678 0,518 0,41 1,9 1,652 1,98 1,362 0,466 1,767 0,136 0,271 1,582 1,962 0,174 0,445 1,623 0,419 0,512 0,849 1,924 1,949 0,387 0,872 1,287 0,453 1,240 1,492 0,281 1,438 1,977 1,665 1,507 0,473 1,573 1,479 1,363 1,502 1,490 1,747 1,771 1,555 1,984 0,35 0,746 0,450 1,823 1,590 0,149 0,961 0,898 0,139 1,980 0,827 1,14 1,762 1,404 1,371 0,877 0,190 1,156 0,361 0,137 0,996 0,371 0,576 0,614 1,792 1,827 0,640 1,566 1,567 1,407 1,730 0,116 1,811 1,842 0,489 0,265 1,934 0,718 1,518 0,190 1,143 0,764 1,276 0,313 0,899 1,480 1,912 0,463 1,966 1,101 1,238 1,879 0,939 1,132 0,17 1,44 0,645 1,478 0,697 1,799 0,561 0,509 0,832 1,588 0,871 1,635 1,686 1,350 0,243 1,30 0,299 1,819 0,112 0,858 1,582 0,533 1,186 0,330 0,305 0,279 0,614 0,347 0,957 1,16 0,757 0,378 0,468 0,7 1,739 0,749 1,999 1,349 0,684 0,867 0,949 1,279 0,219 1,939 1,653 0,601 0,140 1,361 0,846 0,574 1,317 0,146 1,646 1,686 0,757 1,21 1,36 0,142 0,305 0,503 0,433 0,35 0,557 1,703 0,586 1,509 1,749 0,466 1,689 0,917 0,947 1,645 1,619 0,439 0,893 0,304 0,830 1,806 1,568 0,95 0,988 1,636 0,646 0,719 1,969 0,954 1,474 0,997 0,804 0,914 0,144 0,115 1,310 1,102 0,994 1,700 1,863 0,234 1,612 1,526 1,661 1,978 1,110 0,52 1,82 0,270 1,832 0,941 1,458 1,75 0,764 1,219 1,806 0,793 0,713 1,67 0,120 1,35 1,752 0,146 0,900 1,344 1,885 1,400 0,948 0,990 1,54 1,648 1,265 1,687 0,295 1,472 0,87 0,624 1,742 0,842 0,874 1,793 1,197 0,677 1,325 1,230 0,623 1,737 0,38 1,196 1,524 0,204 1,82 0,263 1,909 0,892 1,923 0,485 1,95 0,580 0,750 1,183 1,530 1,945 0,886 1,385 0,928 0,565 1,153 0,48 0,191 0,411 1,751 1,919 1,548 0,931 0,725 1,986 1,687 1,443 1,101 1,503 1,918 1,855 1,513 1,857 1,640 0,327 0,873 1,931 1,380 0,98 0,330 1,158 0,222 0,940 1,680 0,133 1,213 1,882 0,414 1,52 1,649 0,681 1,42 0,0 0,536 0,566 0,613 0,838 1,218 0,910 0,254 0,568 1,958 0,191 1,957 0,448 1,925 0,78 0,263 0,867 1,603 0,448 0,824 1,566 0,496 0,757 0,248 0,181 0,215 0,971 1,554 1,449 1,704 1,559 1,315 1,576 1,734 1,63 0,489 0,177 0,858 1,170 0,512 1,790 0,561 0,195 0,767 0,367 1,234 0,785 0,625 1,905 1,754 0,855 1,529 0,288 0,498 1,506 1,388 1,807 1,541 0,691 0,413 1,664 1,247 1,548 1,865 1,307 0,457 1,161 0,631 0,531 0,636 0,715 1,275 1,279 1,425 0,666 0,588 1,634 1,609 0,37 0,522 0,25 1,252 0,597 1,813 1,442 0,266 0,682 0,13 0,232 1,304 1,703 1,55 1,928 0,13 0,793 0,795 1,806 0,237 0,559 1,187 1,64 0,898 1,959 1,110 1,206 1,256 1,916 1,543 0,746 1,214 0,717 0,150 1,840 1,3 1,996 1,79 1,774 0,105 0,666 1,59 0,555 0,941 1,231 1,405 0,84 1,509 0,79 1,937 1,262 0,413 1,787 0,462 0,321 0,686 1,499 1,925 1,447 1,168 1,916 0,304 0,961 1,329 0,899 1,414 0,504 0,430 1,220 0,149 1,851 1,411 1,353 0,189 0,824 1,434 0,838 0,707 1,214 0,801 0,399 1,73 0,709 1,4 1,265 1,236 1,321 1,756 1,611 1,273 0,937 1,739 0,792 1,453 0,515 0,349 1,847 0,169 0,624 1,42 0,150 0,589 1,572 1,821 1,993 0,936 1,418 1,981 0,690 1,312 0,350 0,640 1,706 0,46 1,160 0,34 1,348 1,331 0,382 0,410 1,390 1,476 1,818 0,155 1,680 0,727 1,227 1,999 1,535 1,512 1,813 1,31 1,165 1,568 0,636 1,877 0,759 0,459 0,646 1,44 1,153 0,128 1,765 0,4 0,958 0,221 1,391 0,368 1,353 0,132 0,192 1,963 1,569 1,510 0,340 0,186 0,380 1,220 1,90 0,672 1,752 1,367 0,923 1,169 0,761 1,606 0,250 1,9 1,983 0,851 0,387 0,418 1,670 0,443 0,539 1,224 1,531 0,76 0,640 0,988 0,256 1,119 0,100 1,333 0,122 1,551 1,810 0,926 0,786 1,482 0,687 1,276 0,265 0,913 1,42 0,613 1,795 1,491 0,871 1,495 1,501 0,631 1,553 0,525 0,5 1,156 1,426 1,119 0,739 0,718 1,249 0,441 1,936 1,697 1,308 1,378 0,310 0,633 0,183 0,838 0,152 1,83 1,174 1,69 1,928 0,509 1,242 0,690 1,735 0,655 1,640 1,71 0,532 1,280 1,433 0,937 0,824 0,659 0,94 0,674 0,895 0,251 0,520 1,484 1,860 0,24 0,588 1,565 0,264 0,331 1,124 0,373 0,74 0,755 1,195 0,644 0,453 0,867 0,179 0,517 1,300 0,813 0,958 0,963 1,919 0,157 0,296 1,12 1,163 1,130 1,192 0,287 0,603 1,339 0,201 1,903 0,189 1,15 0,179 0,799 1,872 1,734 1,193 0,336 0,285 1,968 1,503 0,498 0,385 1,906 1,65 0,257 1,600 1,223 1,784 1,272 1,864 1,675 0,193 1,132 0,604 0,485 1,276 0,400 0,771 1,966 1,706 1,83 1,604 0,778 1,596 1,664 0,111 1,408 0,230 1,214 0,731 1,749 0,23 1,330 1,17 0,19 0,28 1,924 0,486 0,326 0,202 1,189 1,879 0,46 0,762 1,727 0,728 1,518 0,189 1,197 1,753 1,690 1,997 0,328 1,179 0,949 0,164 1,162 0,718 1,271 1,221 1,444 0,271 0,89 0,563 1,341 1,44 0,391 0,306 1,187 0,78 1,109 0,803 0,55 0,939 0,703 1,717 1,464 0,576 1,720 1,519 0,172 0,471 0,987 0,582 0,832 0,431 1,371 0,331 1,137 0,881 1,799 1,989 0,475 1,227 1,730 1,554 0,366 0,895 0,697 0,419 0,800 0,115 1,843 1,492 1,198 1,819 1,882 1,81 0,105 0,676 1,928 1,733 0,293 0,312 0,352 0,689 0,241 0,958 1,211 0,223 1,551 1,788 1,998 1,620 0,579 1,781 1,871 1,883 0,111 0,878 0,707 1,981 0,941 0,766 0,987 0,160 0,387 1,628 1,239 1,441 0,134 0,522 0,973 1,96 0,120 0,889 1,572 1,462 1,68 0,314 0,70 1,992 1,688 0,594 0,525 1,968 1,663 0,734 1,685 1,135 0,922 0,193 1,255 0,131 1,2 0,49 0,709 0,735 0,478 0,538 1,401 0,164 0,814 1,837 0,138 1,57 1,679 1,421 1,984 0,876 0,937 0,123 1,465 0,960 0,37 0,673 1,702 1,187 1,467 0,952 1,482 0,966 0,585 0,433 0,48 1,628 1,65 0,358 0,635 0,454 0,976 0,127 1,276 1,143 0,928 0,271 1,182 0,622 0,662 0,171 1,51 0,657 0,511 0,600 0,329 0,618 0,95 1,970 0,216 1,559 1,502 1,595 1,685 1,1 1,596 1,606 1,910 0,631 0,543 0,548 1,785 1,380 0,837 0,396 1,544 1,315 1,367 0,317 0,952 0,752 0,93 1,910 1,705 1,631 1,928 0,69 0,749 1,415 0,272 1,615 0,530 0,68 1,276 1,974 1,495 1,200 1,970 1,889 1,695 0,483 1,963 1,812 0,732 0,564 1,922 0,164 1,850 1,975 0,586 0,938 1,750 0,141 0,588 0,299 0,265 0,630 1,144 0,460 1,584 0,889 1,954 0,208 1,735 1,216 1,805 0,287 0,108 0,343 0,168 0,897 1,299 1,14 1,707 1,624 1,206 1,159 1,168 0,26 0,715 0,85 1,660 1,39 1,365 0,382 0,851 1,391 1,49 0,862 0,266 0,669 1,809 1,848 0,967 1,320 1,246 1,671 0,120 0,542 1,928 0,980 0,597 0,390 0,482 0,421 1,431 0,835 1,213 0,998 0,999 1,943 1,807 0,856 1,605 1,363 1,495 0,7 1,727 0,382 1,657 0,953 0,423 0,300 0,812 1,734 1,667 0,48 0,973 1,749 1,916 1,599 0,10 1,29 0,931 1,29 1,926 1,449 1,981 0,977 1,518 1,385 0,942 0,381 0,225 0,944 0,807 0,459 0,764 0,209 0,94 1,981 0,482 1,830 0,588 0,272 0,307 0,582 1,835 1,333 1,639 0,888 1,82 0,232 1,950 0,243 1,126 0,772 1,729 0,354 1,489 0,743 1,127 1,163 0,277 1,236 1,819 0,177 0,484 1,359 0,102 0,203 1,117 0,418 0,602 1,960 1,992 0,680 1,950 1,838 0,412 0,272 0,654 1,40 1,270 1,243 1,369 1,944 1,573 0,480 1,533 1,911 0,989 1,690 0,499 1,628 0,265 1,536 1,43 1,261 1,169 1,707 1,818 1,37 1,793 0,416 1,175 0,381 1,855 0,256 1,72 1,831 1,828 0,724 1,874 1,743 0,713 0,95 0,864 0,707 0,490 1,625 1,938 1,755 0,10 1,715 1,82 1,600 1,224 1,95 0,605 1,216 1,211 1,861 0,274 1,383 0,957 1,735 0,994 0,878 1,115 1,13 0,401 0,997 1,742 0,226 0,211 0,223 1,725 0,120 1,719 1,964 1,104 1,494 1,540 0,450 1,225 0,279 1,591 0,422 1,701 1,807 1,90 0,981 1,590 0,657 0,737 1,942 1,562 1,461 1,789 1,45 1,480 1,909 0,180 1,301 1,891 1,829 1,693 1,436 1,181 0,711 0,927 1,188 1,479 1,238 1,64 0,808 1,356 0,628 1,85 0,987 1,316 1,199 0,496 0,572 0,442 1,295 0,847 1,982 0,920 0,450 0,441 1,819 0,723 1,821 0,934 0,571 1,663 0,404 1,447 1,231 1,80 0,253 0,503 0,509 0,819 0,89 1,659 0,470 1,280 1,505 0,662 0,474 1,361 1,211 1,51 0,405 0,228 0,732 0,508 0,285 1,398 1,701 0,766 0,728 1,385 1,999 1,956 1,242 0,352 0,256 0,917 1,51 1,395 1,300 0,210 0,33 1,666 0,329 0,401 0,451 1,486 0,828 1,926 0,317 1,8 1,256 0,114 1,888 0,325 1,584 1,228 1,554 0,666 0,477 0,995 0,675 1,938 0,618 0,89 0,637 0,67 0,825 0,703 1,109 0,498 0,24 0,341 1,817 1,349 0,767 0,306 1,596 1,534 0,321 0,714 1,904 0,661 0,538 0,563 0,562 1,582 0,353 0,863 1,35 1,579 1,949 0,196 0,89 0,46 1,665 1,243 0,467 1,20 1,569 1,60 0,400 1,510 1,625 0,753 1,709 1,884 0,73 0,914 1,46 1,173 0,810 1,191 1,93 0,491 0,577 0,903 1,33 1,343 1,505 1,785 1,89 1,893 1,111 1,905 0,880 0,242 0,982 0,982 1,997 1,779 1,114 1,334 0,879 0,887 0,671 0,549 1,557 0,626 1,6 0,519 0,124 1,478 0,441 0,93 0,214 1,317 1,287 1,103 1,733 1,529 0,185 1,965 0,75 1,314 1,145 0,123 1,855 1,248 0,14 0,717 1,743 0,802 0,548 0,621 1,837 1,121 0,228 1,430 0,18 0,482 1,525 1,887 1,278 0,373 1,279 1,601 0,258 1,531 0,662 1,689 0,592 0,412 0,915 1,349 1,289 1,631 1,964 1,138 0,966 0,676 1,499 1,211 1,376 1,430 0,721 0,18 0,918 0,117 0,332 1,861 1,410 1,950 1,69 1,147 0,458 1,668 0,182 1,607 0,609 1,681 0,117 1,119 1,422 1,630 1,389 0,540 0,536 0,789 0,318 1,153 0,305 0,358 0,85 1,481 0,593 0,563 1,360 1,93 0,597 1,281 0,649 0,924 1,9 1,839 1,407 1,263 0,210 0,658 1,620 1,987 0,673 1,0 0,762 0,661 1,119 1,119 0,273 1,97 0,779 1,649 1,913 0,766 1,853 1,117 1,77 0,314 1,886 0,580 0,781 1,943 1,567 0,840 0,233 0,3 0,556 0,652 0,83 0,233 0,747 1,143 0,629 0,806 1,33 0,944 1,902 0,864 1,37 0,31 1,510 0,842 1,36 1,704 0,243 1,377 0,299 1,588 0,778 0,728 1,949 1,51 1,825 1,581 0,965 0,332 0,753 1,693 0,136 0,151 1,837 0,324 0,16 0,598 1,593 0,936 0,905 0,226 0,86 0,129 0,909 0,58 0,764 1,306 1,333 0,471 1,620 1,621 0,467 0,118 0,75 0,686 0,792 1,785 0,500 1,392 1,918 0,546 1,905 0,136 0,160 0,327 0,139 0,399 1,834 0,529 1,438 0,525 0,791 0,94 1,102 1,856 0,403 0,110 0,113 0,663 0,296 0,125 1,768 0,498 1,289 0,835 1,661 0,644 0,435 0,446 0,351 0,337 0,231 0,222 0,825 0,565 1,936 1,86 0,422 1,879 0,688 0,489 0,328 1,78 0,883 0,113 1,557 0,643 1,180 0,374 1,605 1,514 0,303 1,937 1,738 0,62 0,979 1,870 1,281 0,449 1,440 0,622 0,78 1,34 0,276 1,732 1,535 1,372 1,579 0,585 0,342 1,65 1,651 1,247 1,475 0,457 1,570 1,769 1,214 1,607 0,32 1,907 1,905 0,392 1,232 1,74 1,646 0,404 0,518 1,788 0,417 0,734 1,194 1,222 1,472 0,517 1,989 1,101 1,150 1,90 1,928 0,89 0,343 1,939 1,602 0,70 1,63 1,447 1,977 1,317 0,280 0,728 1,770 1,95 0,529 0,658 1,372 0,327 1,773 0,251 1,642 1,767 1,539 1,221 0,808 0,936 1,702 1,221 1,417 1,737 1,374 0,188 1,129 0,136 0,135 0,124 1,143 1,519 1,621 1,32 1,411 0,760 0,5 0,135 1,664 1,400 1,571 1,153 0,817 1,886 1,418 0,569 1,946 1,985 0,572 1,145 0,790 0,918 0,698 0,17 0,997 0,991 1,361 1,243 0,454 1,738 0,188 1,514 1,272 1,350 1,378 0,875 1,559 1,518 0,913 0,964 0,316 1,534 1,42 0,570 1,697 0,264 1,370 1,195 0,42 1,815 1,132 0,486 0,154 1,802 1,761 0,469 0,920 1,635 0,683 0,762 1,748 0,431 0,370 0,796 0,253 0,85 1,259 0,109 1,314 0,845 1,789 1,741 1,38 1,873 1,150 0,808 0,600 1,36 1,785 1,785 1,206 1,133 1,148 0,836 0,671 0,732 0,5 1,305 1,386 0,210 0,76 0,562 1,133 1,865 0,752 1,203 1,23 1,970 1,893 0,123 0,311 0,895 0,716 1,970 1,283 1,812 1,36 0,364 1,955 1,508 0,227 0,553 0,914 0,756 0,475 0,993 1,294 0,339 0,638 1,265 1,911 0,929 0,923 0,195 1,565 0,558 1,463 0,340 1,149 1,508 1,278 0,698 0,857 0,324 0,924 0,24 0,249 1,64 0,775 1,675 0,896 0,215 0,219 1,257 1,149 1,250 0,271 1,575 1,579 0,160 1,944 1,165 0,550 1,107 0,525 0,178 0,704 0,450 1,462 0,674 1,667 0,620 0,33 1,539 0,289 0,262 0,777 0,737 1,202 1,669 0,82 0,533 1,68 1,851 0,649 0,891 0,454 1,626 0,814 0,647 1,603 1,393 0,874 1,255 0,660 0,170 1,5 0,557 1,752 0,700 0,278 0,134 0,745 1,420 1,280 0,32 1,234 0,628 0,428 1,626 1,674 1,280 1,836 0,756 0,929 1,886 1,653 1,908 1,903 1,186 0,340 0,666 1,599 1,779 0,640 0,202 0,230 1,726 0,193 0,401 0,793 1,0 1,608 0,917 0,881 1,784 1,402 1,868 0,658 0,384 0,965 0,920 1,862 1,579 1,824 0,166 0,147 1,391 1,199 0,376 0,121 0,653 0,484 1,241 0,284 1,203 0,599 0,852 1,86 1,683 1,458 1,555 0,313 0,38 1,102 0,252 0,204 0,221 0,684 0,670 1,137 0,794 1,381 1,196 0,516 1,803 0,430 1,272 0,981 0,343 0,398 0,881 1,650 0,143 1,63 0,359 1,435 1,533 0,770 0,532 0,759 1,885 0,488 0,705 0,943 1,630 0,283 1,505 1,218 1,560 1,312 1,102 1,480 0,374 1,967 1,379 1,186 0,123 0,459 0,160 1,495 0,523 1,566 0,180 0,952 1,732 0,584 0,746 0,349 0,115 1,474 0,901 0,106 1,955 1,545 1,816 0,635 0,763 0,413 0,66 0,80 0,895 0,996 0,774 0,611 1,420 1,733 1,703 0,164 0,859 1,13 1,614 1,617 0,308 1,624 1,67 1,287 1,901 0,300 1,470 0,766 0,562 0,60 1,819 0,939 1,52 0,744 0,421 0,877 0,248 1,299 1,586 0,815 0,842 1,303 0,139 0,478 0,368 1,34 0,904 1,246 1,221 1,313 0,214 0,585 1,401 1,528 1,830 1,818 0,277 0,874 0,584 0,900 0,843 0,25 1,149 1,908 1,483 1,393 0,16 0,48 1,606 0,552 1,4 1,628 1,405 1,906 1,638 0,811 0,908 0,645 0,565 0,775 0,466 1,861 0,523 0,585 1,628 0,974 0,408 1,438 0,134 1,826 0,402 0,487 0,521 1,660 1,372 0,422 1,328 1,897 0,930 0,987 0,318 0,715 1,776 1,294 1,673 0,468 0,325 1,617 0,193 0,275 0,188 1,834 1,299 0,733 1,357 0,500 1,171 0,937 1,205 1,449 1,272 0,801 1,360 1,850 1,93 0,465 1,344 1,708 0,697 1,958 1,986 0,210 1,779 1,186 1,640 1,172 1,201 1,57 1,730 0,508 1,167 1,415 0,275 0,263 0,100 0,324 1,981 1,610 1,562 0,423 1,484 0,304 1,154 1,828 1,912 0,431 1,562 0,749 1,728 1,70 0,634 1,525 1,450 0,203 0,810 1,816 0,768 0,774 1,466 0,501 1,623 1,508 1,287 1,80 1,138 0,749 1,327 1,712 1,167 1,188 1,871 0,373 0,892 0,945 1,166 1,147 0,714 0,515 0,871 0,519 1,492 1,353 1,564 1,366 0,685 1,234 1,612 0,201 0,74 0,646 0,549 1,736 0,688 0,907 1,916 0,472 0,351 1,992 1,310 0,846 1,788 0,539 1,869 1,464 1,397 0,599 1,906 1,526 1,600 1,264 0,300 1,37 0,768 1,678 1,992 1,963 0,312 1,833 0,466 1,546 1,343 1,399 1,104 1,728 1,66 0,744 0,19 0,947 0,261 0,324 0,528 0,284 0,413 1,720 1,620 0,310 1,685 1,173 1,494 1,494 0,248 1,808 0,126 0,875 0,990 1,132 0,57 1,827 0,135 1,316 0,840 1,312 1,922 1,102 1,495 1,434 1,616 0,208 0,118 1,987 0,111 1,0 0,489 0,861 1,421 0,655 1,82 0,606 0,738 1,637 0,539 0,520 0,514 1,119 0,918 1,566 1,851 1,479 0,484 0,467 1,574 1,34 1,629 1,692 0,241 0,342 1,599 0,449 1,481 0,719 0,827 1,699 0,860 1,129 0,115 0,890 1,855 1,220 1,787 0,919 0,813 0,597 1,79 0,350 0,561 0,915 1,283 0,646 0,4 1,90 1,800 0,729 0,889 1,773 0,735 0,477 0,758 0,883 0,462 0,176 0,947 1,666 0,291 0,466 1,215 0,194 0,27 1,86 1,128 0,184 0,681 0,434 1,351 0,633 1,567 1,630 0,863 1,732 0,277 0,651 1,401 0,316 0,379 1,737 1,50 0,623 1,429 0,706 0,124 0,923 0,559 0,598 1,473 1,245 0,411 0,366 1,156 1,193 0,230 1,161 1,333 1,536 0,235 1,303 1,627 0,433 0,514 1,132 0,954 0,37 1,51 0,57 0,870 0,159 0,408 1,581 0,912 0,499 0,545 0,489 0,705 0,560 0,949 0,313 1,362 1,922 1,309 0,213 1,863 0,163 0,87 1,541 0,336 0,718 0,856 1,225 1,222 0,266 0,475 0,228 1,115 0,309 1,759 1,702 0,42 0,641 0,308 1,624 1,94 0,205 1,526 0,189 1,332 1,718 0,886 1,123 0,359 1,578 0,755 1,425 1,63 0,697 0,15 1,830 1,266 0,70 0,709 1,369 1,826 1,584 0,661 1,486 1,714 0,759 1,34 1,982 0,818 1,110 0,769 1,203 1,966 0,544 1,844 1,716 1,151 1,194 1,50 1,85 1,18 1,915 1,614 1,209 1,208 1,89 1,238 1,402 1,727 0,125 1,711 0,328 1,295 0,580 1,621 0,37 0,239 0,541 1,984 1,827 0,658 1,503 1,35 1,179 0,581 0,151 1,258 1,777 0,75 1,663 0,674 0,269 0,542 1,456 1,199 1,688 1,726 1,390 1,421 0,497 1,765 1,353 0,876 1,482 1,451 0,50 1,58 0,262 0,713 1,294 0,949 0,30 1,538 0,597 1,680 1,742 1,706 0,387 1,203 0,830 1,27 1,371 1,213 1,141 0,903 1,508 1,382 1,699 0,907 0,149 0,578 0,877 0,482 1,205 0,911 0,241 1,235 1,771 1,947 1,885 0,579 0,498 0,797 0,402 0,978 0,89 1,511 1,686 1,178 1,398 1,497 1,237 1,891 0,684 1,272 0,29 1,725 1,13 1,548 0,343 1,549 0,553 0,159 0,64 1,225 1,323 0,971 0,146 1,736 0,857 1,154 1,578 0,210 0,789 1,400 1,788 0,91 1,795 0,76 1,640 1,647 1,207 1,893 \ No newline at end of file diff --git a/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt b/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt new file mode 100644 index 00000000000..3c72fcd4772 --- /dev/null +++ b/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt @@ -0,0 +1 @@ +6440 83916 58118 73171 96210 99187 60941 77067 67540 74483 56385 44103 14249 3621 77468 45805 28846 61704 3778 19999 25605 8390 11903 9676 32848 68796 57758 83283 31574 46562 34396 31813 92079 96868 24560 87814 25060 12651 50776 87012 27963 1745 58558 54954 95637 99434 55304 79323 10470 28553 73657 42508 48983 74191 44279 39591 56340 6440 4778 47539 42540 42284 31888 52594 11824 8082 21597 54920 29116 47633 54038 40946 92453 86127 56273 56891 83591 61077 24009 11729 23255 95003 2789 21457 55551 19552 77911 8325 74174 13760 24497 82273 89291 44652 14389 56653 35818 72010 91380 21357 83166 82859 60805 14278 32750 88190 63779 53622 69542 14122 70437 24773 86451 49744 45499 85293 75373 69486 67918 65242 45166 54186 95268 14784 71578 52834 62186 43042 39846 97586 45617 63213 20449 30401 32361 76282 65566 60707 14565 62902 54598 65662 70891 52140 93979 87216 42018 73799 35034 33819 1520 31488 91662 79229 7081 57597 67181 2473 83114 5646 4259 19753 24663 81828 24840 6313 3492 14951 42097 94157 94149 37423 60870 87639 70811 13968 47064 35648 75660 91983 43519 96851 14094 13547 13834 22641 84026 39133 71339 46668 74769 99837 20088 51680 12703 3606 79247 48141 25866 50896 65782 93643 89466 60922 37064 93651 71066 80510 86518 57426 18394 89021 43484 62969 29900 37400 39432 4403 20027 73257 16824 61671 45451 2966 89596 73449 8666 37 23389 3344 49166 53040 36659 67307 63454 98697 49317 12485 2800 50065 65790 77931 86074 94824 78322 30360 35949 99486 31426 77939 190 25916 44944 46986 54647 88757 55345 86082 24390 72037 99274 96070 82434 5515 19979 88921 68761 65287 87715 45426 83849 4975 24879 77139 52264 56390 70927 35904 84032 13845 70349 77503 8877 45180 13253 86686 70111 96651 69945 59221 64934 48052 3621 33863 54453 50934 14813 89542 29898 43422 2127 81714 58140 19864 50188 85271 24253 20793 77881 21356 99240 32912 98613 52479 12446 44377 61909 78225 25018 8680 26284 1616 35711 84629 95162 54614 77694 36891 79350 10264 38028 53351 24787 21204 13041 81571 68522 71213 85404 8023 78695 52572 61231 58019 21533 303 59429 77751 15474 92049 38369 18915 73823 33249 7855 2394 95536 78452 62965 36083 8405 56170 10168 24944 84620 29188 99653 71016 70233 80211 30540 74749 99237 71708 63088 54280 65813 2495 46887 38657 68164 18689 75583 75606 95986 54812 58662 93270 30086 54922 27677 16421 75866 40602 71611 6548 1070 12037 15461 1831 64545 33752 84404 80581 51225 8082 66991 88093 68584 95676 18148 26331 72491 59526 75357 44590 17122 63198 16251 43218 22122 30409 22754 62929 1454 14020 3599 79898 41691 3827 2778 60130 38760 25662 45536 55642 48636 40774 86100 16011 39499 7 40064 81489 22572 42166 11104 72938 69346 19889 11742 27837 86516 65283 5416 60009 88058 81395 97462 89748 80343 27268 72266 97218 34931 93399 53839 72554 30067 6368 16237 28670 49647 30544 97692 4848 88869 26742 36970 99626 18109 58172 48142 79285 49591 17985 58320 98657 20128 57543 16102 21195 41689 5364 53310 24363 78562 46868 81696 19519 90672 80289 52645 19126 9317 60411 40726 20192 16933 58872 77776 95023 43920 44025 74520 38462 98693 32813 87652 56967 81842 47430 46903 68051 34824 56988 11603 92116 36309 94350 95550 49186 95163 85216 92317 28730 67597 1593 72666 5882 83151 99433 18358 52340 35580 98427 16613 38935 70751 88525 15282 16582 86045 37855 27453 84735 58818 83821 78912 38030 22026 53829 94630 44402 89088 12859 75457 16155 68199 72477 90075 76914 54512 94442 25545 24811 8096 60266 4282 15761 27882 70041 3752 24049 44553 77307 55245 619 14450 65477 80975 6726 84342 5102 61825 59959 73851 74207 50621 6018 38041 63492 75227 65861 52937 11378 27958 35833 81840 42993 44385 97967 23563 55988 29640 45123 32503 94270 44406 73678 81372 92942 37854 67216 96971 77601 69600 49812 27137 7440 4480 95814 4879 82515 39454 41743 87859 19016 77353 8084 67885 11145 84378 71952 87401 50013 58720 91051 72378 24372 57450 58876 38091 23468 14365 2406 20512 69643 97320 7646 83813 90717 10866 370 22861 80644 4494 30504 65184 49310 80984 27286 9913 2494 5024 13521 28214 21615 49029 75797 90573 41210 68920 1739 3760 12852 87607 14965 39593 19911 84655 71803 79122 34 38734 96928 76689 25951 12645 92890 24551 72433 76832 54948 32140 46493 47020 45055 54853 9441 71404 35202 20553 19631 53630 9606 11963 93009 83292 13907 22683 31198 21534 87116 97324 54435 46877 66934 14196 99679 96180 85393 42583 42530 86662 28464 11157 96901 56613 90640 19307 78780 99003 81804 89377 64213 93446 52291 86863 59761 95234 47302 3194 60128 31971 99528 29457 51112 65826 66334 94239 27210 31492 99635 23669 24803 27362 73921 60468 19577 45049 6537 25833 94164 11593 11094 29635 19818 14186 1597 24183 4096 89192 41192 30671 19580 36009 18039 35314 12787 62712 17838 79609 82220 2915 34594 31071 25946 56826 31692 43797 49012 97957 74797 97327 37381 52465 74452 22373 57064 62516 9730 99533 54217 79943 21228 9766 53974 3801 60098 1905 652 25926 54715 20986 6073 11113 44278 46407 20621 86787 68509 27832 30692 53866 34371 47746 54606 65900 28469 99815 31654 71174 55744 89400 73665 44508 7687 19095 94171 55091 82604 62474 80183 69527 51531 18341 61096 19576 190 47606 85449 54235 36320 9161 8732 93492 77451 56029 72637 79722 70600 94940 23155 28780 79295 27639 53139 16354 69189 12973 34104 99383 15312 33871 7595 86186 16341 22627 41381 55803 74012 76717 51484 5946 24152 42790 61906 42005 89574 28 97942 74726 19293 85356 51130 25938 57933 23458 46000 55232 27542 9708 8490 31938 78611 93308 2994 99277 73686 59882 1177 85101 35712 37102 79462 48501 40649 4022 56223 70625 8834 78613 61507 27609 90216 26987 77242 33995 29281 51708 94578 47234 49507 98500 11669 10517 83720 82446 5797 79635 70562 92452 50003 18962 84394 93788 42130 31768 61430 64566 63801 38658 80192 50255 42087 52469 47139 69450 3210 67248 97265 38430 40557 59802 47066 35159 79032 21242 46257 16763 34293 80074 53569 37698 83793 58288 21794 28296 87547 68615 79278 85072 97697 40820 67972 58391 41354 70856 22744 23889 2159 22579 13602 97431 58044 44361 67560 78568 95561 77482 24190 98723 2650 66873 67588 93833 23878 90544 82290 11817 78573 4351 28709 10501 35797 19698 70446 29938 52212 30393 67280 96706 97839 7109 73184 3463 42799 1286 66123 99584 22357 75219 10037 63663 28285 26528 51093 11102 69700 17659 36759 60116 51640 77035 74891 77433 42414 63770 38877 99675 57083 89148 50771 99234 77072 71969 83258 78214 19690 39516 8109 25092 13988 37918 28024 97518 13564 66051 2112 4996 20066 78265 51386 52824 52856 70196 98053 79401 57908 49612 69744 94436 76577 99309 46055 28610 53440 27269 69154 49643 16069 61336 81702 31729 9529 66564 44539 31013 16744 73732 91569 5232 40541 68199 88107 14616 83528 68689 4780 68065 9967 32633 53784 31770 66479 37151 67721 15313 81596 29148 70753 57179 75561 77566 6896 30671 21063 74270 88655 62668 64699 1367 47320 72194 83958 29037 28284 95080 45997 5479 51934 83969 45938 73040 70135 3838 49154 40530 72231 54197 66433 26117 30973 74185 55630 89515 27972 77981 37420 6308 31093 18785 91838 14097 42926 91771 91784 28044 96802 52375 65355 70811 63329 4160 13990 40603 59820 48652 13413 43396 60183 50994 31480 18148 15903 1189 18077 2541 32843 62103 82199 32535 44476 83042 29235 67526 82003 53709 2214 43103 86932 57012 96018 30412 29074 8852 59237 9637 34080 56060 28438 50921 12139 81185 36858 23681 16687 93689 47995 9346 28740 39032 34166 29966 82147 3984 54735 37902 73701 46007 25257 44954 55108 16282 15784 64994 44039 58211 35889 67337 24996 12623 75484 41697 68804 94293 11501 42670 12732 45262 59013 6305 89128 48922 98744 21696 24634 58690 69257 41724 10069 3388 28383 32664 87763 79264 94768 1683 70627 70054 83593 25435 22049 45806 47753 78448 73798 8798 62753 41969 10657 95143 99425 93538 60297 64973 11509 7600 98820 86832 68673 65492 7083 9584 9352 7081 48013 61969 80296 97748 49779 28968 62956 67412 46425 35830 51420 63488 47082 87076 64732 95659 29623 29768 43408 81899 99718 22442 15320 29530 61095 9665 46553 40369 3344 66802 11766 78818 14552 42140 81229 48154 50673 5011 81705 4629 73578 23156 96522 11720 71780 66325 98443 88709 81480 27499 48059 158 89742 74533 11372 57437 79069 20857 76614 59264 9901 73265 60103 53076 67290 93958 77006 89991 21805 1279 93130 32418 92397 55056 42607 27839 19719 11388 19419 58491 8735 92204 20161 41073 99360 40713 4074 28007 84300 82923 94483 84318 16761 49942 34580 16190 60220 96699 30357 7254 37715 72260 28628 12213 71597 26036 31485 39151 40093 20737 51062 10041 28571 98854 91376 67226 15246 22261 87770 6037 91298 70787 46116 20221 77868 78305 65311 10711 88876 51595 60973 24139 17347 75017 48200 88144 70407 85700 84686 20307 13912 90932 79297 47528 17444 70468 13236 89395 6258 73852 75603 12947 19430 40004 34531 81296 78089 21279 86923 48022 65754 1353 71777 70749 28054 57928 55595 76003 88565 33465 62765 99835 62466 5158 98842 97861 87095 56424 73367 65208 59737 63179 8236 74549 59860 41675 42247 44780 89901 69873 36988 27222 399 68171 99635 88500 52136 6998 81037 40092 46346 80089 78711 90381 39610 24831 71945 83190 84764 1108 87866 59678 74702 52529 6502 30852 60645 27990 11901 49171 1579 69981 79395 47213 79772 8546 10115 99663 26680 61703 19547 7579 309 86141 2652 59474 68712 87311 53735 74017 725 82341 26498 60627 16535 98649 96923 83665 7882 44199 25442 34667 70900 23801 44113 63500 35758 52787 26911 77207 58104 40451 3631 8053 74208 57260 39302 32164 34943 10293 93184 6618 61864 4922 21771 43714 49259 28811 50211 25566 84176 80719 32257 4989 17539 30875 61716 2878 40307 33523 98384 22687 66971 13768 63263 99645 87922 2449 96873 85649 64259 21856 89381 41721 76249 88949 2854 65141 43075 46235 82494 22248 19705 76229 6382 80462 5554 95658 73222 80734 16879 60053 31851 76768 27011 25540 10583 81818 3727 31282 51250 43894 55970 7740 96727 49639 56540 10090 11596 59481 5927 44162 92851 3167 12434 20683 81584 10052 38941 5772 29048 56178 18607 60375 69240 95053 18507 58126 4484 59304 44034 57735 72072 61606 99905 10185 21911 34190 10757 79910 36215 54819 32346 68056 26531 73005 26083 1514 92088 33597 44148 76512 99002 18800 62866 81427 69050 5781 46627 89507 71873 57515 16825 84227 86449 92171 48832 31309 21926 50817 76706 4946 12931 59027 72220 43229 41582 9091 19157 87151 75785 92608 41891 70806 59131 67405 78497 17325 39158 34368 57460 32578 22838 45739 44718 85909 77156 2253 96609 59685 50427 57676 21553 57668 95272 78674 1421 49118 32198 44251 55024 53264 34833 26437 88382 62777 34196 43478 92942 33093 61904 41653 49177 5676 52572 94723 94740 9015 98557 30831 98977 72686 95990 67962 1408 72382 74039 43960 82834 5908 13307 28483 58878 38185 94187 37989 98504 47747 52875 68748 51918 66471 88271 51196 63838 5045 9485 66323 79505 90410 87738 88436 16518 49412 88129 11651 64754 14201 27440 42959 78118 8879 94238 80375 6974 45896 53668 84656 29536 87278 46767 57020 47381 96743 29106 71925 69480 75772 8286 47356 39726 36320 11473 53934 15853 24110 63835 60800 73541 106 78486 72003 16239 8622 86769 2130 12038 45295 95162 13003 94524 60200 71600 48292 39252 77649 69916 65810 18277 85149 96149 75207 38190 59661 22416 1061 15635 29040 73545 90800 64493 99321 39828 3864 87961 65716 35464 4249 74687 73531 60526 6943 89037 8994 54521 93497 37337 87370 90632 89833 51548 26669 71670 20895 68055 83810 71029 80181 5613 84659 37628 83879 92229 16304 53591 8501 41741 38520 49493 9404 85003 72400 96774 74686 69830 65323 31174 61092 11609 53188 7838 61375 88450 68884 27927 39276 55350 63854 524 45018 97128 20025 1431 75023 2298 78367 42275 85311 17496 50179 4009 81932 68969 6939 98105 91769 28408 28023 89003 36464 76331 91986 44416 3240 59830 30404 72686 110 96162 38957 29587 80221 16275 58944 65221 80483 46299 10925 97938 75144 80315 74352 63296 50943 94724 93439 43491 96350 81788 25288 84386 89216 61127 70064 11290 51088 60307 65351 92000 50331 64147 61764 14990 11856 3996 61310 45650 67867 75962 98659 58398 38518 75082 77887 60585 23491 28663 55554 70227 10269 68548 31189 92227 26999 70727 51911 64922 49340 50935 30050 61332 20148 86639 86509 8768 91279 47174 55796 72818 79119 42333 670 319 77150 80267 53300 28065 60222 81863 40856 45589 76426 25488 80640 99749 1219 19557 78734 12873 7739 35330 31034 10404 66 30536 59917 74844 24452 28922 77802 11627 86672 39217 65154 72219 10882 41401 39442 17993 28915 22680 30389 71706 65828 9715 3442 97435 70236 13005 36532 84951 55495 887 33714 1235 99155 13841 69739 62084 32487 89814 44730 85140 33862 13444 37101 61952 88843 35105 94069 66674 84498 5384 98325 81100 1374 23807 69193 67685 64235 45323 73177 4958 30622 79001 13629 48715 33116 82220 7102 52028 51125 54186 66470 59743 72716 65753 27119 53679 60971 34692 24780 16002 73146 19549 66443 25185 40348 71043 11383 92605 69669 15699 74608 15336 66508 81920 23861 89648 52125 6743 77340 33006 29238 10606 42772 99364 71598 75839 63836 47998 7167 88482 48472 10335 79552 65095 9850 34830 58281 3983 66078 64511 89739 53019 19140 9863 28048 65304 61725 46574 23701 66665 24305 20917 48070 1332 63015 88293 93039 66015 49733 13254 57098 90187 38662 94450 12104 36348 87607 85196 18063 75893 75196 22904 41739 8547 17034 17407 25599 15432 97111 49824 8283 734 65842 92588 78716 13626 34142 69744 81605 41765 95537 57128 87492 49475 58676 52653 8743 92691 18897 19420 51493 75879 89498 56030 82100 56889 94503 4046 24949 685 7251 72522 67024 98577 62327 53686 99462 20442 94868 74927 40926 99307 72724 45898 17218 11603 53158 84870 4434 71015 62084 71817 70284 17629 49396 33841 80609 44594 61555 10064 93257 12997 79567 37191 20971 81226 17087 33512 70132 20454 90459 81732 2985 23261 23294 6730 13621 17396 77466 19407 87415 21663 95079 20107 52917 67791 62529 74596 52037 76899 27735 6452 63298 83009 52140 10815 44890 44450 68312 49379 94421 7736 47415 31458 76456 22518 48841 37762 85094 50968 76912 40126 29562 3570 6321 52525 33088 95259 4406 61560 90125 3997 94727 99523 46031 43460 64239 85182 79064 14824 16188 48994 68316 41865 76494 61118 18989 27904 8277 15206 2724 20965 33086 94317 72611 68772 65027 12528 50466 11616 40363 4561 94434 83307 75348 95107 27481 94519 4029 88793 77766 93514 3942 29157 80078 74060 36150 53508 85446 52970 35715 33490 87792 52883 8660 42113 7594 87653 92760 45091 66885 15224 7062 51571 11662 55540 70376 94667 33870 66016 60529 7456 11859 63557 23640 54880 41704 89761 59173 23034 57360 223 43139 17568 93455 91740 92510 20588 96622 98022 76763 78348 78894 16517 73726 99793 46746 68342 21297 35761 17478 99292 13121 95828 66413 32225 76569 80136 67035 98744 67819 43230 38053 6197 31505 7495 26103 36671 52074 63856 74965 85349 78180 53660 15442 24622 89284 27660 27545 87040 52283 31896 31532 58599 2688 30221 48993 88117 75116 88669 832 63456 65791 82349 87397 57412 48375 20899 5368 7667 50896 17713 40864 81815 61260 89137 81625 87710 84499 27639 7082 68975 32397 85371 25029 30513 37498 79572 37060 92554 63721 99078 25033 36152 29860 89729 48259 14756 26833 19356 45788 79822 50178 16656 57526 55776 11704 53365 23517 81137 67969 55960 2889 10467 23497 34099 50701 86102 39531 8787 40221 84641 87891 7102 85297 90310 35412 24420 52491 58977 3221 63219 43913 59789 17007 61499 22513 27911 79332 25208 78273 98156 9458 43275 58063 11324 84029 26535 82912 60377 6434 93928 31922 26373 51250 75300 52735 11861 33013 43012 37248 79314 74837 2880 3119 54002 46305 19436 21892 38330 4294 81014 47520 19710 92123 52984 92586 62872 33808 99930 14421 66798 42968 27244 20626 91327 34913 34530 72873 94399 40909 88625 37523 5136 78747 33246 90958 23918 20397 87731 30197 50970 1404 26458 17542 15857 34093 3494 48197 87252 92827 73969 82493 49127 82298 53235 80957 59121 77541 56306 59703 6262 787 19482 46894 95105 99979 68851 75374 80782 22585 80604 70656 18623 55455 80369 54645 60718 90162 37370 89417 64180 65844 43454 68820 35179 63288 25079 72649 83863 78850 17212 90366 44688 26852 93734 7140 93169 27759 81258 36068 89664 362 89444 46488 58600 51615 89468 64596 45146 3548 29409 36039 93893 1527 14986 34807 43522 92976 91666 50782 82713 60106 25602 68162 39977 1657 23941 13238 199 46646 10140 87218 11548 65967 97619 1213 64739 13326 94295 3943 36229 66429 48406 88554 27582 11155 78480 2849 56085 1738 85190 62428 82067 37378 45658 7847 98264 84366 62000 93413 71016 40719 29606 82940 72664 74601 92186 66123 15436 13303 31817 59712 12215 56061 51813 98048 62006 72749 14884 19171 45581 13668 3022 24456 18204 31174 90499 16096 40750 3427 72449 19830 87270 16973 55917 90206 73501 57200 33403 61019 63262 27385 22002 3062 7464 17016 19599 42748 83751 24620 54881 84661 23865 42748 90232 99673 98183 76694 76126 54697 2884 85782 77360 38327 52622 40073 96947 40639 5715 96905 51635 61487 44180 69838 22307 99347 76845 13214 93503 32967 69526 76384 45172 78651 41024 9613 25291 62233 4707 20500 58048 38835 12415 84253 91063 85041 69953 84805 71522 92363 41588 29382 15178 86620 50819 62145 19096 67532 58585 84243 31466 99901 31752 83360 99787 19917 10371 26525 13330 34365 571 92016 20108 17809 42388 10990 49402 52291 65310 8771 51026 11533 67580 51668 63755 14611 82710 79496 5197 68511 22269 44592 21518 33219 15430 15201 72005 62596 86878 77462 41369 68958 63637 84482 71537 63265 8002 97590 53430 43107 71492 10334 22800 91366 76312 68535 86065 91250 92389 95581 46810 24098 22233 97211 52824 96382 90981 1557 63278 51860 43866 7949 67476 41406 55320 84354 49877 88105 18413 8529 66618 59888 38258 18294 50938 10775 45732 91658 9203 28649 95322 36388 11938 28824 94772 22600 16977 14084 1868 9044 73791 41071 16660 74847 95778 20621 62677 7595 19932 31334 57442 22616 22537 73666 60669 25451 4252 35952 84245 37840 65588 76791 15809 11594 26924 37219 24878 44538 12444 7248 4939 95306 90480 21012 7600 16693 43734 7486 5155 93896 9757 84708 56446 89737 51875 1376 40621 61072 15300 31364 27861 98198 9333 89195 55189 43511 4263 60883 14909 49836 12380 33893 80142 65684 90941 95623 43469 80120 2813 72095 89763 86669 55007 21578 35238 35249 13422 52036 57539 95926 66205 6625 11681 66170 26643 17396 1219 96552 10068 7017 89823 40544 66525 40792 41127 39384 34452 55210 3327 85682 23614 65629 53941 43398 94421 20746 87233 50694 55069 42148 74254 91247 67076 7904 36735 87721 17713 75581 94206 51844 8489 748 43982 30545 29680 64400 6963 870 71606 55547 59679 35936 62250 39752 36604 32850 87022 85763 65139 59194 16964 50916 97694 6777 98104 52840 29548 58737 50478 7107 39846 93845 2125 62111 39686 56406 7688 60039 33791 71677 9834 18388 29862 37512 66443 24085 50341 49898 3531 71601 97393 48315 32502 46223 26735 67461 45501 95961 86448 43379 404 85459 62421 53937 31886 1246 85779 98349 13879 76221 92140 15762 59472 53481 67416 92719 8241 48397 2503 6165 64793 95508 3923 46689 97084 96847 52793 78650 59114 31794 79603 64729 99975 85632 64642 88846 79108 86045 72723 51278 72006 64754 27137 14380 93246 83178 97801 70241 16809 28791 58853 67690 48316 75023 73463 81598 34459 32139 71219 93862 4677 33633 14518 79655 46194 22158 45700 52687 31874 85939 96581 71397 1115 45843 10049 54540 32715 34088 70095 89329 12284 531 63563 46204 9261 22690 69733 99749 76475 22114 92831 51466 3 14061 77820 20744 21396 46749 22026 5487 25138 52141 81779 77289 70814 9790 82845 34638 7945 53379 34602 53833 35149 41061 70684 88468 71298 49539 38007 44360 91433 7550 7228 73790 70680 71262 59950 88204 3702 78709 62941 1876 64894 70476 74871 10168 79155 32407 67221 84076 29892 66831 11005 52941 87200 9548 68045 72402 33877 65303 87735 24996 56976 5549 78004 77753 18105 80606 12338 49266 42961 61414 23711 79232 67617 16505 25127 80999 87641 7315 69915 88878 47086 9727 15954 19700 16953 9356 93855 26822 81067 66398 25027 13764 70134 66724 3790 51268 26603 62353 21177 17299 3247 88353 78955 64588 47342 47003 3528 51992 55601 82651 69656 69086 10448 23161 9643 38278 50150 16132 12959 56216 59691 6753 1467 22334 40311 696 21472 36570 83810 44271 46500 51866 2060 77687 69407 68422 8915 99238 41825 33304 57243 78971 23665 12579 25948 43686 66508 48922 99546 85034 38281 30346 9942 59228 81890 12136 76237 94958 77012 89708 61883 99688 75588 63265 14884 29706 67823 88696 69312 10995 88617 33344 7416 16253 87195 97968 52122 69366 38709 76680 59405 98587 62153 38548 24987 34771 87029 63139 53028 82853 16295 89088 56401 65187 66738 19051 54207 52422 18235 41162 5512 41639 25074 97427 96008 82963 73232 97965 57301 16491 57759 4894 26963 4683 6133 39042 44033 91523 70299 80139 52672 58311 65911 15011 6743 51187 12561 29332 56861 67629 90936 77949 51575 23253 59266 4930 97852 82563 15500 87358 13779 15049 76511 44200 14021 46765 14175 65809 70497 74263 13281 45983 23688 82777 70967 77428 66074 16647 85757 33851 20292 11347 96379 68760 14715 47883 67283 7603 32969 91906 53667 81188 14062 86881 7876 35542 89702 85034 47584 9915 68752 49493 66084 35210 20396 58659 20124 5646 42357 84257 38266 20668 61435 27538 52994 17494 28919 9884 46750 7183 40743 98767 25459 86858 25651 30899 95563 43778 45963 75374 63063 29170 89982 83307 95357 57842 86409 33765 50702 34511 11203 91977 58489 20208 81590 98874 45937 79984 17578 45002 87903 21065 55433 58845 14594 51795 18824 34766 55453 8117 69887 5638 85361 59996 26918 25060 55473 64017 54665 64662 32767 90519 24830 31332 63919 97894 96491 49115 37303 20780 80393 5945 10937 37471 37365 75074 78382 94270 88632 50555 79046 81263 24785 24755 82250 88712 65649 3789 36741 55403 53369 96222 43724 1309 451 38310 37712 35328 27473 21691 75995 33575 33451 90953 72942 13847 72579 20453 82732 12413 32194 27279 40744 49314 23962 91823 80708 24414 6353 44153 50840 71599 1692 31134 91250 6222 35557 37630 26209 9503 66319 17118 81587 64702 53715 16117 16124 48415 69280 17365 34343 27817 61270 81562 85371 99203 28894 85177 73105 62244 25794 72897 61689 43687 91845 98022 23853 54272 5872 88858 73439 72775 68613 53611 66784 86994 39297 85321 65316 33061 43892 59991 83723 4253 44635 16080 17476 28989 75156 90812 85272 21610 26925 87004 17186 42039 48343 93703 89740 87689 50805 86849 91580 24590 28353 83161 14881 24610 1154 71 64282 94339 40178 86463 29988 75746 49751 52958 88700 79004 53418 23555 33880 94467 49550 65320 71568 91768 67505 40939 50024 77353 99672 227 58428 76915 29659 76443 17055 46061 83408 72628 88964 27160 38557 32181 98539 46792 47262 33491 9367 10351 98600 39147 17512 77525 80620 18639 10925 80720 20803 85762 70556 29134 29335 93666 51610 7852 21175 70696 90232 26692 12313 57828 17123 54442 99965 63885 89391 13371 88824 40158 54499 55076 49459 20288 11581 17662 92517 2382 91482 81210 39323 28315 92629 46489 20492 40170 10554 41758 65937 91664 28062 11290 31461 2915 54704 12824 20038 96458 88306 99769 68600 81299 97659 87126 37091 61505 45079 71551 2675 87311 44752 80731 59531 64616 26664 30949 82209 96645 55510 93202 54866 8266 27478 26852 12138 8697 2349 42640 73807 37511 99301 57064 60251 59433 85165 99069 38955 67175 37583 8602 86671 34752 50306 18722 61373 60945 65216 49062 81885 48582 55112 99100 59973 4078 26631 47145 75540 40521 30309 32248 87811 36942 96651 89969 68140 27808 7603 41264 44877 11871 10445 9873 98878 21186 32302 29918 19396 50584 98207 837 93020 60884 76546 83705 27080 64026 87408 48326 57272 47832 25152 54833 78915 71608 20012 38337 15950 51511 57331 43162 35212 60175 60292 79006 47452 78743 46234 86259 31491 89422 84985 77298 90170 98800 15213 88619 3908 24202 49766 36800 25600 29323 24890 17291 42691 61502 67109 34301 77433 31867 43488 39941 88907 97925 26179 42828 93748 40764 24007 21979 33758 81716 96562 67534 48524 91136 67946 35888 23028 44564 75587 10540 20848 85103 20308 90781 37362 88360 22635 15689 31375 33666 35772 7479 25976 82986 13508 41176 83839 83044 12208 40520 89298 33542 76171 8223 75434 25340 16223 19936 25063 21241 12251 19545 5463 54520 15017 95336 24374 54701 38907 2147 46719 38381 93259 34186 91854 91813 14991 42042 97185 86789 52119 41992 26365 54852 21269 71292 28331 14000 68894 63575 79126 33188 80310 23025 35517 22047 32418 6933 63683 10388 79 44209 61977 3446 14912 94926 28887 27308 9739 34265 34568 98964 37967 69795 29105 65877 78897 96135 80911 1386 69491 27712 39058 56748 14589 63156 40916 68135 23664 89334 80126 71175 32026 22761 77890 13355 84370 40927 4875 96465 73826 14911 1905 44742 48899 94745 95602 91005 73887 70732 16417 70710 31502 17951 98473 33471 46618 59794 19434 86310 11267 94073 72637 13199 62157 40726 34183 90085 48539 61564 94589 18802 34533 12583 76345 94574 60260 39097 1299 10284 43158 41658 41322 98517 96453 62953 42960 8302 1022 66502 18486 33422 41936 7401 17490 3185 64155 7220 96314 77449 92361 1076 74475 39389 49600 32966 46805 70897 14715 16718 66013 54865 71454 7854 81947 42982 48017 5546 40139 7166 91856 28023 71227 3134 58604 4087 5116 50343 18731 33383 51956 64826 69002 62786 5695 12442 24022 30678 89738 12745 11850 96170 19495 28034 62128 61195 86021 79870 49405 70175 92277 72516 97307 40686 84952 58389 32566 83599 77685 9874 83693 73618 44206 85569 80064 56351 13374 2511 29267 15416 56941 78693 1079 78259 29865 2767 62811 83912 88963 31498 67196 87314 61633 64903 31500 59631 50133 66690 54611 96587 42433 16490 94130 45108 94813 5620 40450 8261 27712 20089 85330 81940 54693 88040 56702 22001 21916 44965 94583 58192 69672 73937 43170 18444 24597 23916 64041 67223 57293 99964 47812 97840 57885 39993 77712 53794 6138 77157 40895 61470 5114 14520 99838 54398 92443 83504 14559 6863 9711 64579 8973 61132 7358 36511 11156 61873 87797 74990 78808 70640 43715 78853 19851 12364 1984 10089 59105 65905 87373 45808 59342 60403 70740 80751 54798 23588 10029 52703 80439 97113 16989 69776 98077 50885 47411 53558 44388 43266 78087 60121 68271 27245 34337 98850 93303 94497 56258 59094 57975 28649 49093 69611 96743 39261 51267 72938 51216 80492 28476 28837 59904 91170 46625 30151 48528 31698 39340 52671 90270 30382 33722 21930 42527 57234 3892 90869 88254 3808 26926 10493 37797 21149 4223 25785 97414 46455 57099 15986 10997 27239 94786 30359 83383 32071 96188 59545 58686 91492 50745 2146 14289 69714 48612 84821 45494 46846 70411 93469 65368 60225 2983 90691 40008 31982 98169 12173 69946 62265 83241 54541 25965 9891 85237 26320 30985 81260 52068 26471 49675 999 63742 6869 35559 58298 92740 2538 67032 81061 35882 1510 83722 51105 13177 70672 54635 56406 5505 79606 30108 1461 67052 1570 95069 10883 31687 32331 83865 18135 68704 48132 81292 941 46380 7466 74380 40220 71052 49526 39294 52512 55728 8439 55386 85657 33682 18797 85098 22379 20628 87539 95291 4713 53702 46213 81119 83979 75439 51611 93464 10292 60896 59254 75289 81930 38553 99319 84670 56389 60429 56358 66009 4808 72289 87250 10941 88891 31553 26516 9088 47304 11720 96567 72762 28425 99186 72078 91129 46730 97170 82436 12961 90360 47171 56992 17328 3502 5993 87712 5677 83883 72287 31472 68415 42603 29773 96198 42873 51969 9961 41929 83302 26885 26055 27003 17099 78382 78619 84889 2923 11493 62318 3528 2575 95656 87263 59222 33643 67882 52594 21352 82129 44804 2025 54880 67672 25438 83721 33685 17148 57250 84717 76820 98881 2759 93000 67198 76453 16416 59798 30329 17818 88909 27756 67722 833 53609 94080 98546 93975 23982 73393 71672 81697 82370 4967 46327 91291 91999 8691 46689 43485 17802 85473 55976 85465 45805 904 83184 61037 70365 26253 7947 1297 52110 26641 62897 39371 98954 28040 48782 83670 49762 70341 81669 5899 38158 60502 79870 92483 73031 35012 43493 24174 630 62223 85533 48651 11600 86432 32600 86857 98477 22196 65066 80324 56064 44024 72364 32862 14620 58661 48196 81786 66129 51502 89470 5713 76540 79644 37410 55363 46514 65639 27204 53645 63418 42459 96268 3755 20663 11291 65657 13424 59529 13375 58352 19087 96415 45811 24731 79616 94474 42454 98232 3178 20919 49721 67660 56238 9004 19013 14183 17697 83234 78027 8902 32007 11111 52435 447 65584 43917 14355 64465 64148 40434 54763 82918 33802 68938 42156 1133 12607 95676 42111 3417 41404 71053 27117 53360 13910 44934 34932 13671 61365 14504 97194 42954 26405 72855 47687 30862 88859 31828 7378 94920 85382 68969 59023 21937 62109 31992 47086 15988 47537 73037 12294 93192 76807 42773 37249 33434 50492 38252 13015 54725 29068 15957 17402 17376 59012 27306 29830 25052 66474 71603 75731 30914 16747 59145 95779 26382 89324 12814 92058 3356 3969 93307 60109 3007 34371 7388 6638 76089 18465 12902 35591 91042 16454 78708 49865 55774 2876 34429 57055 65903 90376 69284 84680 74995 66850 11373 96325 96309 62151 74506 71701 41623 92369 62369 96356 88442 19476 69323 6756 91813 13531 25728 67251 75927 41928 87875 86235 94885 3479 96612 5530 87309 32841 94201 69804 32658 94055 11855 63668 31293 86649 40965 4131 84801 11944 47013 76370 63881 77084 4197 34257 76548 54513 54438 97672 7650 89960 67952 71952 98719 64310 59618 86452 4562 60007 5141 29602 13844 79933 25762 2460 6650 44619 45984 80039 81746 85042 81754 62767 96409 14168 55138 19742 60426 17980 1987 94281 6646 75171 74943 68426 90806 77090 13608 55299 5753 2577 6160 43272 27589 59492 11014 52678 60341 34269 1278 72888 49571 50357 33885 65210 87548 63017 56009 48111 13941 10825 91006 73468 17356 52058 24040 34917 80227 84728 51199 41061 17767 30453 69160 28290 10844 49873 92805 57291 34442 60998 81745 15570 46078 40669 86986 33194 56888 63505 51167 90574 55680 8923 91512 19650 81646 1949 69776 95266 39115 19229 35875 69920 16781 26881 52272 6882 46464 6922 7505 81942 86581 94676 46174 12071 54283 16424 78632 41263 17316 32145 4925 96588 20977 94847 97871 62365 13642 40153 37694 59807 30411 4942 39534 6170 96261 40918 61979 13191 24531 99816 71010 77273 62281 57262 56151 61215 80577 91377 38945 32401 6373 38707 39604 33974 21668 64635 33995 53791 20870 68784 48872 41813 12454 92565 66873 63729 52302 41527 91329 71298 7195 95699 33734 92793 55333 80968 14305 58630 48387 14988 27433 53778 79505 26052 30216 94824 11290 5565 9488 78036 62811 7444 41558 30676 92132 54462 33498 63979 14744 59414 56229 3346 36747 77550 85517 14472 30660 1926 48141 11040 50083 73910 71581 82562 38348 66844 53108 93103 59061 31567 54254 77251 35620 48822 17879 82946 27087 43633 42403 97518 97806 27173 40105 73215 18399 96526 67233 71942 48370 59906 33719 55838 79700 21530 64375 55866 97171 85592 71394 93521 70826 89048 54485 68213 29677 52453 58938 28216 31425 90044 43497 73530 85459 11185 5156 17116 23372 4973 53699 42868 5154 54381 87131 66741 84913 5432 11482 16611 20132 8065 66000 92853 51631 97727 68573 57234 87227 49253 74393 42182 39265 50068 95004 40035 62422 23295 56576 98494 36841 55161 96600 3228 13557 18140 53842 78471 87354 86462 64354 72831 88942 89988 55171 27513 25401 72394 61518 82246 36703 2132 34342 34963 90036 23174 35074 27058 80443 90308 72369 23208 40916 30358 86976 21336 91287 22617 79814 91762 35750 33837 95161 66098 35037 58101 3847 40832 9987 9567 71192 39132 48582 26441 86042 28435 59055 63881 26143 42962 18660 2693 64409 50703 51612 26046 51528 15811 37299 50108 28591 49026 50989 54421 47270 1010 53591 82895 13575 50294 12782 974 83777 3728 90778 59612 29688 6020 52598 54914 94999 80517 6266 28916 34929 53878 83446 19454 33396 79289 42690 86058 529 26411 9022 61383 63603 4082 87957 62391 92421 51450 35256 17424 95812 24772 32637 25147 22902 99707 31213 43996 50188 35213 52508 27037 49999 5261 47398 81198 94362 8699 89230 17285 30948 73603 21430 23281 51371 65105 83260 73222 7600 75342 27536 32551 14712 64269 10491 38567 58995 39560 90638 75851 83775 27881 19363 39775 40407 11033 58973 51725 76680 8759 28190 31749 65952 42692 43284 87809 96574 36915 5383 2150 2883 9590 17256 98034 28835 15540 92189 61517 15473 7447 53797 39290 21598 18795 71421 86528 90575 64381 94838 28031 90591 76192 55712 83964 12260 50219 46835 125 55927 51947 90678 40579 64422 89891 23707 56651 25894 38311 1010 80680 93660 51095 31203 23359 51361 89427 38693 68775 61324 18661 10268 29972 1179 80867 73890 42491 60863 78647 48537 41084 50153 62856 24254 92847 17824 81246 35581 36274 40565 44742 59322 83862 20598 59257 69618 35695 62487 77385 23339 5769 9098 57348 59703 79357 10405 81234 35737 78085 37306 32368 50903 1635 46424 83447 59511 48098 46403 48389 12025 78922 22412 88879 45157 17784 4375 48728 81950 318 8344 68015 22024 83144 76144 4182 21568 37256 52414 9163 68895 77668 22615 19143 63260 37050 99544 98956 4142 84641 40918 48918 94073 95354 44382 94627 27649 75592 94569 78534 56073 95369 36641 99723 26105 40178 4055 34044 37118 52101 28379 77843 104 10919 22833 67788 13478 40809 43000 22553 73483 90150 22436 41188 68684 69951 36680 58175 7335 12242 18535 19397 26117 80949 9720 69375 3587 59751 26168 41164 9786 23796 53653 60470 54349 8409 32500 95612 56377 86483 20125 82548 59887 62275 93379 48870 81055 74452 9275 50752 31348 83082 20976 80757 11139 76574 2445 97723 50888 73469 7033 19617 17675 28062 74715 30255 85771 30734 40717 68981 40377 18199 77190 23429 15071 3675 17612 5680 30537 55262 17200 76008 10649 75359 28903 86416 9185 73658 93201 68152 91987 51884 80105 54065 98616 51394 30846 17645 58945 73017 50638 13279 72337 85622 33466 1092 76378 40641 36364 77736 5881 73197 30443 90323 48912 65884 7909 96363 63986 82818 10331 9187 77834 67363 31693 43767 25759 88267 75307 7468 72803 64693 39084 96051 15177 92743 71691 27972 57503 27377 20502 12244 29016 28834 98174 11461 61329 4585 25777 41067 42784 13709 80206 8230 93475 82007 28230 87970 85590 73793 65797 97579 19279 31668 39311 58400 82436 58637 77137 47218 31832 85261 79952 72614 24226 36109 24360 80343 4663 15449 73168 52996 33110 98388 21415 81250 38623 80483 96193 70923 75148 37451 67099 87578 63020 7464 94903 87034 33168 57551 18258 17188 60582 76448 98873 68159 88915 13958 56608 62377 30056 69968 77383 64393 92353 93307 15505 44231 26972 99617 21313 64803 61268 95611 67216 87135 65138 78876 73623 428 86611 62677 72537 5516 53607 9770 93017 46971 9583 92405 21822 82092 61616 44186 5884 70393 25540 23109 26932 85003 26493 9299 1757 64290 53891 47773 54575 49536 49008 14780 42333 95835 83966 83907 28332 56774 24141 8458 49384 55687 48123 22043 25688 31958 43448 74497 53102 62098 17857 24834 80844 5423 45848 46411 88644 75629 96349 81636 73180 50237 92667 61178 90714 22241 5860 6823 53780 98663 75715 75111 3616 51569 29911 87657 22395 6708 78954 88692 43372 38888 18787 7804 29894 16921 72856 32256 1835 34296 95581 68032 12141 16914 89722 11162 74845 73079 30946 6300 42672 53834 55824 54736 30654 33373 29184 88868 83547 23298 73049 88165 59169 16125 49214 82873 77514 12202 26122 95833 70731 85576 48078 98418 71074 54500 26117 55592 24386 96244 40837 56455 74668 46793 51048 5562 2178 92888 71392 82393 71475 37976 77258 4986 79038 19589 84546 67337 82492 39733 46360 11308 94519 19810 97191 39277 52487 89485 76381 65793 92296 33284 74098 43942 3253 14641 87930 86607 35165 66950 61192 21571 13039 24296 32616 95705 80752 70297 56399 65365 44308 24200 71361 23854 48990 84253 9794 39731 53885 3757 28746 92277 94541 47208 64348 94155 78814 79818 81453 1076 57350 55767 20521 16655 38063 92640 52498 94627 71228 46301 34251 66212 2870 12853 86994 88591 96295 55278 40692 906 44868 52551 28444 70615 74493 16452 57157 46036 35304 43151 83401 41507 6426 13450 85310 20336 52991 75641 49095 78493 14912 48701 26324 71118 76347 6031 3716 67510 40829 93711 12543 79506 6502 12321 46342 58669 20188 24219 80871 48281 61607 6184 1163 91395 5860 45021 37806 3514 50274 84578 84167 58799 46324 4827 23787 37972 68457 11231 90644 31283 28431 19638 93295 27115 89625 95713 24583 22480 22833 70774 27829 75693 2318 51187 94203 60670 6897 53691 16917 10809 8867 13898 49751 69607 90390 11951 31531 62806 77645 51671 62543 33239 65806 9088 35044 58529 3278 77778 10044 80755 23218 20358 80085 80633 24633 56708 69873 8603 94801 17021 70904 35113 70516 34689 68022 97043 26258 89016 22560 14204 74838 92156 98160 51860 79294 41537 5178 82325 96820 7643 84377 1570 24188 6632 75938 18332 23112 34036 1562 30126 81174 95909 46002 39963 62659 39790 92506 84292 6049 47270 84800 67587 22376 85130 94013 59879 5132 84125 85474 73915 83064 83651 50570 12234 55953 88999 73287 92215 21778 67349 2353 64911 37021 39415 55528 1110 72945 78410 66743 61005 74990 50503 77194 50558 42182 94608 22896 77216 46727 28317 33037 41397 59216 25825 46385 26111 599 51767 6072 21023 49885 42379 2009 56092 60179 91927 10615 22818 6382 83835 72567 13710 46551 19787 9858 92896 41666 54141 4462 94964 6225 69193 11364 35907 68179 15162 55030 89686 81038 21130 4849 49942 5146 59379 44228 5325 45887 95880 39515 26995 15740 64535 79116 74001 78694 3726 37218 57550 62308 87154 31702 77910 71493 55966 23513 67720 34301 97009 82010 19489 7907 59918 48276 67718 83656 79249 53494 23358 66328 2177 28475 71135 7582 10132 34788 36353 83330 38480 15622 83841 55224 29491 26867 15095 1626 32185 18070 60617 27805 25795 40334 88180 90897 30089 57796 98016 15442 50202 49760 24219 79754 86198 38897 27069 7320 67832 33258 72120 99737 62972 37928 77429 72864 71434 4544 45745 95717 91451 35167 3604 1094 85905 16247 39830 17199 45112 79817 17277 1543 289 96492 70597 3898 69009 55747 17656 7458 10640 27514 14104 91828 72295 69163 49245 56943 40941 31670 90397 47946 98398 91142 369 98387 71887 21362 44655 56641 23559 69503 66141 79994 54580 44471 61417 17751 71074 39398 91653 37258 49687 47747 87428 61501 15572 30018 95591 41216 51606 56326 91186 73406 72421 28984 38120 22249 35654 29918 57998 13823 12869 68046 95098 36073 44798 18279 91177 15183 5230 50295 27001 64417 74789 51829 6614 88315 49728 46094 19223 19687 65958 59688 19671 94417 35066 60034 577 5852 11667 54571 34840 96480 6368 47530 13133 24361 46517 93589 43397 31919 53256 6169 27515 5874 26512 74501 20025 74333 90163 30186 70484 13990 83978 13761 90997 56893 49722 21052 45060 67700 65612 39588 73809 99599 97202 57326 47326 65574 34250 69719 26513 45138 92991 15974 55432 48106 38171 30870 89758 81352 35738 75923 35967 37944 39094 74636 20197 90388 55760 59959 22756 39733 96104 11350 63765 86789 93849 42440 52531 46730 13842 25230 35117 28337 51761 58062 11263 75558 41228 3327 30552 5812 30253 20356 82363 62144 31269 55212 14959 90902 10317 94299 10590 97847 58210 20127 3502 70246 35883 76533 11886 20695 29709 9069 50233 70798 59237 54712 94988 5697 27911 18348 61048 39078 8805 27255 30768 7451 30017 6909 64366 59520 79872 57807 97314 76973 316 75926 43436 69637 73781 82423 35227 14834 43155 16098 62033 98397 62818 53757 47461 84914 76773 37852 37439 74830 63648 11807 2104 38569 86804 90409 98751 8472 89549 25929 92783 30826 52418 60110 27800 35680 80437 73480 33095 73599 70669 72510 61894 1512 32591 24458 73230 12597 53592 80533 3828 86132 67802 11325 82688 30131 88537 5232 9140 25875 60943 86268 16967 86559 35903 97608 2007 46908 65338 18332 20819 92397 56696 98800 54448 20200 33256 35086 60486 76010 47482 66733 52626 9708 82966 94678 62202 88525 83711 22090 1653 71480 30294 66933 45901 20327 83600 8357 84287 27999 58724 80205 91625 1776 42379 59354 78539 52559 11213 75935 79258 22699 6416 10805 76242 62825 53963 67194 36503 22681 26718 76082 46710 27167 8478 32345 39920 39066 5353 13621 91015 77388 16145 41774 48412 70410 13891 42980 82937 89715 27687 45875 68901 14476 55604 97626 61314 63143 28012 69174 41528 81156 3257 96109 75748 68838 66964 28137 9447 5293 32270 23620 97719 40383 70771 70796 71172 27193 83863 92582 8562 24370 27797 84836 7921 79749 35722 79008 65846 79177 22160 27759 48517 39483 63734 86110 65366 84403 50316 75316 29147 63076 32484 39741 55058 12743 73643 70422 15388 86355 39506 69221 72162 32458 52743 12076 22618 62547 48591 96749 17399 27388 71589 77788 64817 37661 57096 90074 30362 19242 30164 25677 57478 11267 26129 69815 75633 19552 95451 22468 42135 49713 64497 12533 8586 70727 62782 11428 67244 5352 80943 35567 11470 70358 47440 86442 35068 22846 59944 23278 72149 78551 3835 635 13929 20814 52083 70374 35299 29168 45151 29828 93492 89500 25812 2461 67089 56293 86488 19043 64189 81149 65893 30378 38829 63223 79496 66677 40441 89369 19297 38119 38675 31226 26114 15078 21936 78976 56296 66460 35243 58570 79679 13590 16953 77836 99149 8482 36296 94631 64340 32896 58022 73670 26295 26420 55156 47878 89250 54340 52140 20249 5302 48579 60356 34213 41378 311 58759 86592 94607 73082 4792 63746 50760 51873 94981 61937 99977 28248 87913 11318 70702 48431 10202 43451 21347 88608 80938 40488 62531 49180 73556 66662 19264 88143 427 86634 38270 93873 79243 2634 32696 52014 73982 99067 83856 97335 10120 12404 38285 24282 18197 4203 26729 73715 74253 30085 53586 72072 24446 63611 82280 52505 69422 63060 22672 88125 71424 69990 87726 31169 93381 65164 55299 46247 28005 21355 42643 71240 68737 53145 19174 97315 96332 32072 82915 26134 39804 66912 66580 35809 5450 63311 62903 65390 15070 89354 18433 20640 90926 97688 80858 28057 60614 57141 52190 259 70620 84873 32646 10506 49168 96109 41980 81026 13860 46822 24588 74970 81748 59135 69392 36050 1473 29643 70401 66363 28365 22778 99871 75049 65988 97335 53355 82270 23437 43575 9667 93720 92831 95068 47224 33610 63833 93315 57762 71264 1293 70596 34698 77317 39734 3936 31258 39098 44409 21139 75190 92866 6462 97698 62395 75530 79991 23581 8845 53321 17928 50638 33471 83431 93229 23767 91270 1075 18502 11334 31251 16846 21993 99971 25411 57680 47903 343 85768 46154 60500 82148 66090 65936 36458 26114 79826 71847 69426 58308 8131 7842 79128 32857 44940 71104 35166 8672 27684 11119 52016 69318 92566 2810 68416 65140 10878 75620 48741 52100 51684 58193 51726 71038 21444 97470 67773 32587 64136 62361 92849 39201 32604 9244 58776 76646 92500 29658 17167 49030 5664 48379 29760 70635 30951 54415 66457 90437 5641 37378 90637 79517 98656 86849 88625 65496 95278 78395 92087 64575 49401 30461 15737 46614 5282 74930 30744 16232 10150 46150 13018 91040 24412 1137 75036 81850 29319 55536 35730 19593 88925 36367 22361 3023 4941 72008 28512 13597 7353 81528 59324 41350 68847 80696 87769 67785 85477 68017 25404 26871 19378 64783 74598 54720 39263 97759 80366 16711 99113 97649 86541 63246 56743 2083 95139 56017 56839 70136 26844 37631 27226 85961 52256 13277 2813 53388 49106 24099 46485 35865 20894 26671 90195 97738 24105 4884 37091 15375 26105 40091 51923 84566 5989 20367 10801 60957 43767 63326 62049 59127 48731 12159 49033 25331 17723 73626 58378 44667 39542 57276 34693 16234 12694 92059 64520 68588 82718 87141 81643 75 74140 88118 88154 72882 40840 24182 49127 57696 20419 24449 40227 21169 26320 69938 95904 11200 99031 49231 34552 43633 68859 60506 13756 70490 95297 81701 67493 53679 64050 60198 9849 62027 84491 70745 98485 78410 43961 93554 89623 32613 4970 46206 54381 2262 72898 46794 62262 83727 56292 90551 85749 20825 98432 26123 2941 90970 16383 61440 17558 22274 32838 60554 79265 23602 63277 20353 40679 61685 65245 24030 85809 77746 84149 73533 48265 94785 62927 37594 9294 32799 82231 21760 7444 6999 85684 5309 44461 34859 62973 96526 23963 99840 2890 31272 69297 37262 9481 20321 35048 88389 12569 13575 59497 16052 3944 31203 11865 27139 50282 73678 36431 76201 50603 32103 71780 57932 87213 35085 40457 40025 55049 81225 8194 23094 70937 25369 41460 53184 178 7280 18160 36299 11172 61945 51277 55223 88144 7367 89748 9900 73398 5109 26819 70481 2393 86857 77061 61314 90009 12973 32466 79471 64998 33119 49332 74290 6401 84077 87964 53765 44460 56585 93460 57987 17540 78519 74394 72527 96634 28039 68151 21620 32861 60459 93834 59480 40335 34980 21480 1468 50439 55918 36580 19207 73533 74444 73397 40241 1176 73664 43426 846 71028 1489 43812 52288 53341 74257 86828 66019 49166 91297 57249 74559 44359 70446 37750 96057 3480 99664 54830 37789 45228 29008 78325 78679 81060 51436 22478 92082 45058 22876 51221 60532 80150 51406 21759 55462 48020 13446 56995 20859 25887 47190 69444 61703 45616 13355 75576 89232 35625 31407 5023 28631 35132 47368 20679 85913 47550 54726 52760 66841 39225 99491 64033 60366 3621 34053 1875 46291 72337 26597 22059 61247 59686 50393 80667 45459 25428 61601 52673 55200 66737 9756 76262 4934 75868 95462 34408 28371 60331 69468 76464 14209 36589 10177 67273 22377 23605 37316 73514 52749 64189 95588 34222 88009 575 65236 70126 8291 17291 46331 12703 79271 41440 7163 75222 86261 64753 31687 41076 99357 14129 60269 92453 98739 39970 31893 34387 31000 53225 91818 4219 82082 69249 97699 68610 42745 57883 99316 97250 39360 1937 12094 42297 94335 29309 98063 57346 31605 75726 62456 68439 46424 51240 52691 73793 35237 96011 11926 48100 58463 45260 24212 2786 21305 70173 10273 80279 26210 10347 32366 32577 41789 29484 73292 97635 97662 76393 9353 11723 1037 2076 65924 95846 82934 75980 13612 41276 21049 22208 13858 7289 264 85869 24718 19719 73073 28529 21912 12403 96543 43962 86307 36676 52603 60586 62479 34588 64274 50883 78926 99162 14504 59880 20017 31618 69206 5861 21615 7151 82978 50019 64669 6184 45322 98168 49996 20055 99055 77637 31773 70653 96722 79815 50767 95625 9832 72791 88844 66365 40775 96231 24287 44784 62886 32648 25671 2714 53523 21737 41574 99144 3318 46645 60545 71579 45851 28798 32902 54143 89897 66609 61318 47890 85322 87659 41569 32637 41476 11279 38973 87486 11199 49785 51517 80203 53013 7842 85946 99853 61957 42665 58019 37308 90769 23590 66375 18826 67511 83044 34432 70431 37640 28227 67221 82738 85314 74549 79716 54638 27455 57579 56688 17624 43309 41460 29551 98219 37497 2076 53303 4728 4092 53444 52390 9693 82463 52744 86381 30844 50612 87435 81297 3295 57105 64577 40410 87 43040 88936 68843 26735 81273 96791 46578 97372 39709 1717 88820 14749 5996 30429 57814 94614 47463 88119 95416 15177 55266 16532 81044 75713 33039 88263 65851 23758 79156 68180 32612 97546 45900 84389 17170 55956 19661 51841 63816 12491 35882 52006 42771 90705 22620 76848 27026 47805 55117 31033 7123 62974 94231 29527 54383 78059 38808 35715 93363 8082 97564 6629 75554 8997 1195 79036 92531 35748 57537 27251 89003 73576 52111 20979 11603 36486 60114 55710 20294 30286 97656 34805 22403 67904 96320 26501 83581 28521 32790 82454 33816 87297 14538 72140 24853 20152 88859 70873 91467 90213 16465 5176 91915 23127 47132 7695 79813 84197 71645 98164 83929 97115 91929 7911 94192 2398 12979 1987 39333 51671 82126 84759 91231 87764 72036 10943 91700 14890 816 45493 46714 32227 14174 67844 84470 13938 63581 7168 71618 61895 18151 15549 31470 12882 45139 57984 77872 24933 85867 13158 39851 86082 97866 980 91562 40612 64408 38535 62042 14115 32119 42739 70478 32753 70078 36924 59314 13821 4768 83434 21964 56995 5740 20716 22099 82985 49979 93248 20130 50607 98515 41104 58440 26106 75846 77448 31280 29028 57354 76462 92145 58076 6659 84727 33603 85066 49427 76997 99428 53099 38534 94307 579 90835 41356 24983 23418 17696 2939 82900 95853 64664 19927 83198 69091 92157 4017 34831 67946 4168 91375 14959 59119 41169 87998 85677 48765 84271 27059 12230 50410 36856 21219 70720 60106 12098 60441 66965 77045 46693 47736 34785 40233 70358 61039 27884 61896 10454 78437 16246 34921 87539 15760 32368 76471 42183 1363 32131 21596 74010 42554 59274 46578 62213 71180 1945 8916 99118 53735 13023 1157 59617 73620 33836 78686 9084 44264 71289 40217 63776 84753 57551 61881 57461 84443 80253 33610 23833 60133 55113 61720 86328 75888 25713 54283 93453 79193 32704 59446 19993 68986 8164 62857 27644 78606 13689 23219 9252 17366 58889 3063 40701 92641 3641 73766 98233 55078 52419 33844 47886 73693 2308 16829 68827 24943 23938 5104 59967 30036 86568 57090 21995 43214 61284 43660 61468 72899 55922 31797 75725 74973 73381 91284 49370 77197 2516 22854 74247 8823 86711 77769 81542 89972 48050 31824 84215 9685 57922 79174 55133 95259 42546 75069 38750 63166 10001 22900 53225 80484 74679 47337 5677 26802 26642 88802 30644 63084 800 97561 32562 64738 83586 32387 88291 14043 73517 78038 2483 36041 75662 31043 34957 9463 5642 76334 77492 52517 20076 52019 91057 45372 77437 54299 61805 72198 46067 18584 31823 67164 71752 11387 53700 47496 32203 84160 90568 87118 44455 71586 17232 26871 97961 71808 50539 15391 12281 58326 39520 32690 26864 6975 8466 65356 84811 32884 5716 28765 82005 85025 34129 59082 45332 86221 59621 43521 32365 17348 48309 93332 98698 42177 61919 96444 88160 55092 56103 2500 1552 89172 48669 31105 39631 72308 91431 50408 36623 42355 54652 7274 79638 84287 99693 55320 79604 14026 90005 35716 63269 53738 19962 78010 5599 19576 48457 25251 82407 17786 33366 43041 82943 33994 98802 80401 11392 62929 44678 63383 25966 9180 31743 50643 8081 18415 12084 51376 71215 90776 75513 89736 92214 37619 73768 26508 98504 30411 50066 43149 66235 10601 2671 6685 2724 77383 97807 20187 41431 51435 46346 63920 59289 47628 24915 37007 19452 47206 8782 91012 20434 56088 98813 82079 31406 60914 86480 47277 15190 82826 20162 36715 83237 75128 67769 13562 52622 37754 14895 65303 70389 96459 11919 82851 18152 8773 68841 9918 42264 60891 38146 76498 29978 3135 97086 64572 36208 90903 64524 2193 74674 68327 50675 66832 74055 76514 62724 82161 22193 1860 27615 16662 24733 57148 54288 1330 26083 28354 15644 37923 11324 71135 72069 47402 11334 89864 14195 2924 99976 61371 3053 50404 58094 82657 29422 72516 37904 87105 47345 67440 82370 33618 28850 21028 66638 81325 25233 86013 1741 79579 87091 747 90729 33635 12214 59749 77463 82656 54225 23283 95778 23899 85153 37614 14006 30636 83351 73870 6970 55485 24684 82651 5856 91506 36534 9411 91765 31392 87504 35677 21167 52844 88621 32011 90424 53722 69851 87024 2827 13724 73898 84121 44424 29073 69566 86445 37903 13185 11836 89056 77554 19643 87047 52802 78767 8238 71502 25763 67791 62489 81817 41726 9522 28711 67906 43734 26804 42835 4598 32993 65560 99579 72864 28012 57544 63369 12186 79339 56823 91376 65702 27244 37033 5132 48509 58176 38895 45881 74219 57785 27451 29481 9277 72573 87524 1096 55157 80259 52130 56183 53570 57215 93731 89316 61208 59613 39157 54521 89476 36924 13475 85203 56529 38849 8097 93577 64220 23214 26939 57852 48530 21907 83539 92504 15229 94298 55882 30533 21795 77968 81454 84841 32004 34346 50955 39907 14754 37337 42443 41860 95049 3769 33862 58720 79259 85020 9957 21902 39535 95519 73341 44563 12868 96230 28187 91552 3951 83368 62991 65463 45279 7523 1590 92342 86448 96472 37965 48105 51089 90622 28684 15860 74446 47426 13875 44355 13407 18264 36436 88903 2093 3089 7943 27621 13797 40149 5497 65454 63854 69381 59483 96207 2754 3360 22159 75395 79145 2600 97860 56791 45481 46206 40775 27910 84457 97596 31617 18775 35833 81072 76681 79120 98462 25219 52957 42076 79928 64052 41618 82902 54992 99309 42426 45033 45719 94911 45080 84509 54951 95323 17082 26084 17620 76336 9485 66917 74863 2772 22926 16446 70908 91271 854 73538 58667 90366 56318 26804 59952 27219 4082 59594 51344 36890 45442 67179 43273 17064 29086 1680 88211 4897 29322 39831 18061 25455 54638 6824 41459 96698 47733 88069 55468 29156 71382 84182 29181 61270 19195 13808 51643 78742 2492 23422 2505 98842 60833 23258 64355 42241 93285 47671 48127 28325 72769 20442 46584 88953 59608 39651 21438 6165 52506 6347 77787 12560 53467 66744 83759 72772 44115 41174 73291 51535 44134 5892 18324 25489 18243 13628 4511 1570 96693 43277 13193 89563 92008 69367 33413 74565 3532 49334 68813 29867 73551 10089 38397 42181 39261 98290 54446 63497 8062 4310 62745 82866 67265 10769 60205 3218 43029 78852 18178 8697 16046 67678 84569 49310 55737 87810 10620 57806 90172 32095 43525 99147 50611 6360 37138 47172 11302 87087 65647 59212 33550 87576 58186 97637 27984 46567 99893 83266 91223 75280 57401 18948 57433 39920 60741 35973 34602 96992 5349 80733 6067 74107 79622 76036 82236 73115 73756 36109 49776 31993 16585 44286 9025 6827 72598 57971 60664 50909 7216 96212 53006 55930 3837 77616 86397 60439 97336 84867 98092 32963 88085 45234 13578 76497 84884 2649 25546 24911 78259 49791 62615 74046 11195 88001 1996 2987 95562 90720 79282 20116 82620 11070 89781 84880 51630 93046 18903 52482 44224 81698 33468 14300 63337 93419 10239 41812 43464 69454 5309 7940 37810 1513 37499 55525 34788 78441 36595 96662 53544 73226 9136 85453 91294 26775 6776 79712 69419 43914 99487 88765 46849 86030 32348 28470 48165 88495 18024 15386 25762 69848 46072 8844 4535 11955 5513 64103 7547 74027 72707 31242 62623 75404 27771 72184 47189 34153 50401 64194 69806 6712 38478 9637 80902 79102 62807 71484 8883 7710 88134 90562 23223 66535 36069 65605 3895 26511 8545 96855 31594 40833 59003 60569 21487 88450 21914 66558 89676 75963 5472 16575 14340 97732 6400 25881 77033 75511 80758 32634 78381 4204 69645 67016 41967 93532 83522 27861 6443 38410 35107 56010 46184 86672 30186 385 53456 87670 62454 30249 40982 63873 34519 84503 60623 89112 47 25536 14147 11445 78988 58310 60450 73268 12907 47187 72649 76419 24498 64017 2497 42415 88676 44039 10717 11916 23182 20858 90396 16404 55891 45117 35347 86906 9447 76994 10837 29126 92168 67828 74391 31910 46154 78640 43945 74876 23117 52418 64735 74211 50206 42733 52723 25149 64183 57083 50329 90425 57168 92272 13574 18737 5550 77677 67772 81044 51991 55584 38372 43915 894 97506 90464 84827 72937 23800 71697 2610 26710 97551 33013 54149 96451 19412 80689 51307 54944 44480 9074 55861 60563 22456 22755 73140 59575 87885 32186 5496 4748 50143 16850 16913 78944 54375 64987 82145 43865 81889 51192 46494 21341 89203 46601 80392 87589 18709 83175 40871 99101 34906 18615 36231 30749 72311 90748 31889 91068 39208 65422 88761 20098 97055 5170 30389 5975 32537 31261 4927 93669 79557 78952 51072 45242 85111 73393 96096 96273 62662 23730 59727 86623 83124 75696 71164 55902 81999 36207 46876 85923 25469 36110 99347 16339 43457 2260 19265 2706 69504 6223 85744 34700 67702 50352 95988 15086 20537 5575 41508 85495 2388 64733 95728 82168 39022 636 20191 30594 45217 41053 76353 80047 21975 15107 88753 51664 7704 16368 22478 20650 8314 60576 61864 38485 23797 38217 91307 91264 2218 19610 77417 78509 80161 82458 22594 95616 56923 29079 5197 6723 76605 11634 14381 89517 68340 76295 82706 79094 38823 85318 42406 18258 87691 99456 29455 13976 4485 6776 62262 32713 22724 80506 37645 94739 41305 97647 7373 93204 4863 51100 29990 33940 60687 13039 19999 3458 40990 82678 746 38566 45593 39124 51523 39271 6410 51334 79619 29108 64159 93073 62942 70939 37317 49374 80261 80319 21780 75775 61477 79002 58715 15903 70466 38249 55293 1882 64532 49214 80604 430 18091 59951 67772 97434 31584 44096 76887 48936 56391 68099 41243 62665 45035 23228 13702 41816 99433 74976 81267 22503 98572 29661 21350 29098 93587 62070 14149 57382 46438 14651 25623 67612 25569 97470 14562 84974 46008 89172 12310 1761 39451 27219 3753 97577 89534 58389 43340 83843 36631 87392 29779 68144 8191 10659 1005 70278 34579 42353 62580 75336 15831 24855 61387 39392 67608 84129 17315 20982 77382 94585 84490 50396 17434 25228 93977 13373 26288 38826 28388 34105 5737 15586 59498 92324 72463 31555 33391 17872 89401 2808 22963 81108 18457 57263 81547 92421 89631 59244 22119 27406 15574 24901 83670 568 90705 40394 57722 46469 80921 63984 34593 38355 32385 95122 73588 44227 32824 33829 71623 91750 81163 92503 68207 77194 6937 97639 61487 68377 64617 71377 70027 97963 97516 15999 47364 35449 26650 68157 61251 79084 58627 37336 81897 17858 18347 88814 6447 79566 52037 51258 19292 13790 31600 8924 23650 25673 27369 63249 22303 16750 58581 13790 5685 96356 51130 56167 79505 40242 98729 53154 35032 6803 70983 69202 64932 40977 40653 30016 29924 57495 95335 12876 31845 12667 62573 64852 34047 14517 33851 27776 73885 29640 25744 62297 46491 73207 1362 75076 39841 38621 62547 35850 70061 70024 38225 72545 32982 54146 97957 6999 16905 56689 22817 14283 53047 82484 52403 42470 67521 77711 32264 64068 24007 65852 3635 17358 80510 85219 13486 12490 40054 90893 83449 74813 82106 49183 31063 51738 45573 10838 28751 9591 59453 57548 44638 15627 46818 80438 40393 10862 91732 39564 20845 35127 18129 87823 99775 44455 66477 28492 14254 87467 59904 37817 78711 41951 63485 79355 6368 71447 88789 63491 36499 49 80198 10635 5979 28440 75690 34073 24291 16961 89587 46046 932 27945 29256 6018 47288 16009 5668 78999 28903 24453 6694 96862 9857 26222 40452 18354 43380 29807 56619 35507 67487 69782 20265 52972 36433 18371 35357 15335 31947 56189 80676 7776 60032 19784 15723 7214 34714 21135 48015 35243 68192 33728 41159 28802 49267 9242 2804 35848 4595 79371 97504 54158 80243 47978 86627 59730 9658 37773 21654 96406 73578 38293 15625 10381 69265 71406 90526 15295 59904 99083 42775 4976 84493 40291 5947 81653 80378 68042 46754 97839 4225 87228 21998 72111 42628 48716 28320 63621 91589 72657 91583 90448 57737 99329 53968 76459 68372 74951 48108 31990 90346 87970 49846 33855 61474 30330 24294 72802 81039 4964 80553 87170 20257 20840 47004 15637 81181 84029 68418 21335 52908 58348 41425 3226 96013 65458 7264 50744 38660 62864 69522 3746 22607 84843 22016 39712 58511 22764 32598 17790 74007 29842 60117 70145 47015 16572 9851 2695 20691 93642 13318 46557 95458 2388 91135 32089 70992 87392 43903 83273 97872 26543 34624 70045 77427 54061 51539 76665 63470 27873 44138 53125 22689 42130 43804 85119 76112 18963 1169 69981 42266 88406 2380 22570 76182 839 67125 11006 94524 60898 88410 33290 72299 63695 8656 48681 86838 41415 25611 36170 59396 92322 36859 55144 17185 20105 92822 79861 92798 52802 85995 81867 51348 22376 82091 76239 83740 68861 13127 60245 37840 28666 15866 74811 29231 69391 66845 34327 62502 52807 42311 81931 78199 69304 76203 25035 18923 64190 65431 99392 30829 23063 71090 95546 66332 96289 26914 89072 24109 71177 16546 81454 44541 71668 69937 77866 68873 74709 64094 71496 42586 91675 85367 22604 28894 10022 27897 21498 48049 28777 40089 68202 78759 40009 24804 66902 17110 88594 30008 13041 72204 33766 75554 36685 30803 86896 39261 76803 38165 84370 12486 57365 74073 13080 8907 71789 8595 42844 69705 14977 60795 12459 49263 59971 78716 13701 85511 12336 3886 56494 96910 25844 14246 26907 65984 11625 67052 20912 11124 67336 85063 85199 79801 31948 30461 48016 45412 18211 20178 2898 3062 94398 64506 14822 17310 77012 49962 99319 29574 89960 6624 41172 37938 41348 34637 22196 85742 99535 43724 94528 41776 9544 12491 46200 54841 63920 69581 57190 50042 90681 52793 20252 74419 31035 17668 77843 96907 59855 51200 11713 75146 84739 35734 36478 56115 58038 60867 78831 609 75225 15155 43620 96652 12736 12025 81644 76585 30964 99766 98496 21364 45801 32272 53750 8745 58860 44807 89990 78832 49905 89571 67048 96970 91611 19332 98457 93519 94480 71268 8254 56989 21999 36950 13323 27302 94926 27408 44914 25346 15834 78292 42811 37401 28892 71458 95480 2957 98193 72483 28715 50214 41176 63609 48817 81251 67010 65168 51016 14640 84363 10495 51445 35975 41653 45629 90896 19325 67093 99110 37600 3693 82138 57710 12293 2722 99896 54922 23547 36289 10127 95515 72128 39983 47038 86979 70549 81553 43355 37496 93285 37333 94001 3781 95040 22464 68460 80421 40836 45779 36543 43111 31454 85904 60929 12010 57034 18733 49573 48759 63245 9104 28208 13400 72872 51822 3202 44014 10515 26721 23851 80696 92427 10303 53998 94808 10044 98324 55129 89887 69323 78708 99283 43520 21066 52703 96834 74196 29176 69496 44628 64149 63950 26151 95445 17305 30955 41693 8163 66764 1855 45207 62052 94847 75844 48364 24537 36887 98294 92507 96652 63968 31923 6488 88706 48623 19119 5861 28439 84260 97620 90166 59847 25532 12259 41012 75356 11498 29261 36079 86677 28542 83738 95208 57589 66956 33527 70629 69897 3768 71766 39634 10098 42191 52528 52023 70108 11450 96846 1525 85286 56792 70364 84987 27516 13311 90502 81077 44695 69929 46889 268 17492 38784 73853 48691 32653 31997 10659 58651 67744 46652 12221 53323 27354 27711 70446 77220 18243 21309 79219 5338 76863 22321 12292 20171 66842 82154 41332 35410 95599 42109 84176 12882 85156 39352 85462 81708 38680 50054 6199 26515 86476 9052 4503 51939 22718 87225 14697 85619 73291 95991 83276 35280 9860 5875 101 71721 40659 91767 2026 7634 66229 84467 17959 95821 17600 8460 25882 52155 56829 78943 86394 74579 31023 83575 70278 50157 82324 13505 60432 16724 64449 37663 80098 82344 73226 7047 78460 3433 94365 34771 93567 89907 37367 6394 83704 11162 31528 82154 65759 17025 67975 64551 22964 6432 45343 17902 63042 85929 60694 97522 42803 21526 18587 16127 11718 80808 87950 54980 15014 1223 76115 31046 51141 24247 17015 46618 42534 78067 39583 4286 62467 69589 84875 82520 42481 49980 55630 74597 96347 43125 48654 75585 2779 22872 78643 9903 56501 74295 20513 44550 59513 32998 12197 16766 36379 12830 12824 98303 40538 27333 9146 53116 69315 50501 6595 54572 25329 19943 30543 68314 39894 95334 96449 15601 73655 72498 49694 29746 94990 80561 4148 77912 48587 69092 73773 98223 55298 63170 21013 97260 50043 70794 71172 18247 61041 7400 94969 1593 76577 51747 45678 45468 47626 74957 37027 72696 22067 80597 30344 88210 91772 50536 32046 19819 36811 12014 24113 16859 73746 53438 26044 33110 73142 11785 15639 57860 34618 17339 57209 67168 82703 73860 73906 45061 38660 88857 78852 20374 43717 4073 81662 72420 11740 34417 74063 10397 25619 31811 65180 55554 96324 96802 74910 90641 7623 68272 99204 9523 82541 8744 67858 97928 58 59437 9096 15046 46209 71957 25619 32716 39866 24756 69141 8554 19230 61701 7630 24329 17365 49615 3898 77767 90901 33719 17725 56131 67801 38648 55980 58252 46113 46121 22448 81809 68682 25334 8728 16960 57709 94369 82565 98848 97723 77332 67399 69526 51838 42827 72134 51760 75313 94341 35111 45672 95110 35832 12735 35178 36488 91020 64336 86993 81502 95613 39166 66355 19137 25946 2320 83919 57751 13652 91038 87141 95592 1648 97236 2800 88262 57830 82616 21197 40707 4606 94758 50273 86637 64677 31015 13498 30816 53796 66687 63769 67237 86410 5321 14399 44986 19808 38071 16561 20514 93102 49984 31895 64522 9601 40005 94080 53347 46031 2959 41517 14593 13645 59226 81947 8674 94078 19949 38537 10782 2659 72727 46653 91274 96458 32894 24428 27447 89404 62091 71298 48008 26367 39403 76987 59623 71623 6445 37579 80871 93432 85708 99723 3884 88283 98605 25733 83342 71560 87933 38645 70769 69036 12444 62044 23225 64465 9979 18970 38070 53993 20619 20451 90107 74731 66133 26147 25605 35465 71093 54061 78462 85149 45146 49406 91973 70574 7754 26023 71271 54439 67818 41516 82168 31184 36108 98512 27349 62001 62081 71517 92553 49303 95940 25219 34199 96952 25151 28262 16586 33489 69905 8773 21940 43179 81463 23615 4315 92825 6669 85578 98110 57753 86824 25507 12679 69106 67576 894 86116 35128 4384 41683 73505 30812 32148 35505 55676 42206 33859 83232 78890 9686 71248 63445 43765 95547 29349 30595 85901 48628 22619 53504 54166 83221 18609 68077 72196 63271 48087 8530 99141 35591 15113 82842 20700 79618 26619 91577 53878 51454 3041 44233 85701 6906 28144 20271 78579 95727 82941 97034 67424 44687 39938 89399 57550 41655 5614 72926 74409 64381 7873 87348 53762 97635 72901 83474 89046 41902 86503 2193 86626 87554 14039 49648 78972 56507 87271 75099 64114 74553 46734 36421 88327 28529 67219 37339 57972 49417 22219 11827 51279 84400 22226 51138 81087 9428 26136 22013 38742 45211 98110 6877 85307 21561 78024 81615 87201 22948 2471 77534 88519 83141 52405 25883 67695 46654 39258 92822 64997 18798 83581 37928 3762 13085 56784 71089 67844 20916 89828 9628 79848 8452 67610 23331 3597 13991 92388 1461 90718 65411 75106 45907 85459 44100 8731 17613 50923 28180 1962 95755 68324 32465 51564 50512 61130 49679 48954 25468 92141 92028 96086 9159 68033 17422 39050 80081 33887 31392 97655 34646 40509 84074 98651 56004 81933 17486 18685 32996 96150 84459 81172 64117 95284 14237 27187 57363 72808 77224 80083 32327 89734 54052 88727 88454 74303 34908 88211 68884 75890 82018 1417 64717 55370 35219 6700 8894 60741 12804 87865 88440 38756 32436 5117 14186 26711 43826 97477 49838 37310 73486 33971 53205 87438 62789 66053 68111 55839 37316 88477 87148 28305 31966 51962 26974 63043 17108 47311 64178 95672 15299 33333 77095 89092 85020 80350 63434 49467 67567 59922 84037 6330 72521 37775 32373 94714 23167 25699 23945 89948 76298 67422 19369 45055 46764 49691 5955 48380 7675 95361 40430 75189 22981 79679 63897 91034 85702 63930 68527 48336 68501 56862 90750 10565 4389 79154 40563 13947 66713 50120 71031 66891 32796 16943 78866 48839 66967 15175 92264 40576 21891 93018 59430 5443 19642 29856 80052 51935 10826 77842 98270 71099 74892 48714 53085 15028 56874 41505 89185 27672 89900 97321 75870 65134 29455 6507 3916 80708 10929 30703 27682 53523 21106 52591 99645 54237 2160 33806 9712 11841 13159 75114 76602 55631 12670 37016 87182 77785 94611 14368 21890 8790 37676 58073 45789 26634 78586 73591 5879 26196 53177 69791 82289 32253 32942 73173 33966 85555 55151 36177 51756 53825 3922 765 10787 20031 60217 12202 1999 66239 86406 32471 1364 2631 72729 81069 68815 1672 93143 78053 85468 81132 6968 51515 39799 79037 72903 37991 76015 49143 91826 61597 23545 59244 99 2571 58512 73403 21322 86475 76541 56462 63050 40016 21220 8381 24260 42262 77413 59300 59651 45141 76024 6477 50890 31313 49287 27844 28530 35558 17144 68296 80627 60140 87752 3500 19496 63094 34160 15913 62974 82923 34143 25666 4846 71067 96506 67969 21100 83842 37823 75294 98470 68379 55275 94122 2675 17731 39131 59012 70045 96376 84697 6509 88703 75249 29435 9413 85413 1534 91200 49913 23258 11869 48585 34866 38410 15340 73937 95304 66062 27203 32851 8248 76827 65350 25631 23641 76696 75722 62000 59439 93668 83919 87319 99237 90398 4307 96998 81757 31417 19263 97151 97579 48254 86856 62745 45985 52123 63762 54937 60637 28567 39727 77133 66500 51183 56318 28358 89801 5223 44554 35898 55660 29127 10100 34999 42157 80422 22891 29584 28580 4905 2031 67117 90929 32989 1762 20461 74992 77176 69719 34938 21248 1521 58303 34101 34243 28005 22506 54037 59611 14469 94649 98820 42567 81659 58450 73969 42065 59093 32471 58445 41605 74645 75172 4566 33305 33009 8484 97344 23448 93036 36755 36719 37783 67525 7511 22409 29356 64228 36046 28394 11574 12603 3155 81652 2926 1286 81999 79798 32365 25488 29867 1636 68696 71803 15033 20969 78022 67532 36437 59735 13140 21100 22000 55059 34774 58622 60195 7311 56877 43989 48665 30791 35161 5843 12255 15907 13221 65386 58056 74486 33790 38140 84624 43974 10770 65605 32090 44109 5453 44806 21255 41988 45560 80340 57825 88666 85910 17021 37994 73230 47726 96797 11852 54644 78593 23073 4450 14255 25257 94383 23029 11050 97092 1925 86116 48494 29855 57962 82543 35603 43475 86747 79894 42276 4181 3262 60252 91753 73650 38396 75301 50644 81800 45747 2292 89198 62418 87442 87323 65447 49401 60086 54502 4130 12126 71467 60681 30589 22295 26519 96991 3113 25441 45049 79070 82593 50039 50070 96021 32998 42925 10784 2641 98882 32963 17963 42725 48401 72073 10290 44589 86476 73444 44490 98732 75938 79648 36727 46984 67889 830 14552 7645 49728 90426 94055 56956 21493 68569 15796 57487 80572 21965 12689 98331 71450 54239 77023 8745 21483 21544 58653 41072 2587 13997 26324 669 800 34390 4036 83676 94478 61031 26563 88935 33674 74448 75206 27208 53205 28031 24746 19137 92412 95699 11675 99558 88692 14421 20807 11498 69503 44896 95235 99252 27365 63171 60988 24479 45696 72424 95674 77915 52229 61593 64024 31653 67955 58948 33340 62586 33715 13122 52096 77806 99835 5404 30884 68877 97194 80844 16791 90141 77922 49136 8698 14058 73485 55680 81692 86071 30461 47743 9113 89961 97059 66538 39360 42192 49218 97864 57088 80659 48312 88658 76539 34865 45889 53111 8118 27204 88311 12113 46434 78145 3477 36532 1620 59983 87879 14452 87112 57215 48780 52019 71117 60973 96604 23781 9706 27662 1865 95961 87849 96641 25837 54173 40702 50011 76713 2965 74417 49433 19815 77934 52470 91172 60089 84086 27713 89840 84818 67410 24853 77883 13542 78040 30805 38871 51230 9365 20807 28869 13353 15645 92023 96594 22821 20296 92375 5950 88356 57551 83910 9171 67867 93123 27713 44471 97282 27429 20628 53423 78183 63879 54930 56123 63269 24661 15767 97074 6281 84057 25569 11419 82951 45889 27307 92510 35830 71129 66763 99921 67054 951 10765 77795 51250 94301 36363 61879 63252 46533 74179 30781 49770 63382 73124 2908 14490 10265 7457 64461 87142 63803 56915 22350 74276 18221 99435 90977 65109 76410 80122 98328 63293 58158 70199 33609 87316 25528 12239 48751 39540 72643 11584 27338 11323 31888 23212 84037 81577 88149 79103 62541 72818 2805 50081 28750 62757 24970 84275 28641 10829 89234 5916 45855 1363 48668 60717 5543 48656 61033 78883 16372 88777 56697 41941 33855 52346 10749 6560 8164 22982 72399 83563 36234 57576 71550 56811 51888 82148 42806 8019 35195 97535 81990 70365 63896 9051 41412 76652 97802 59328 54130 22525 18939 11764 84565 22114 40845 71404 98088 63589 41891 3785 73039 55840 99428 19411 24540 93867 71320 43764 32477 44171 45731 24919 48563 50591 57494 92449 83148 72619 49849 53209 67052 46324 35652 25775 70631 76680 72472 37950 3760 9499 36793 84315 4890 80653 60319 78290 82608 82379 24344 40054 17532 61005 57284 65277 11895 87372 71982 81375 96523 91298 41965 14562 90239 48240 81343 82186 42266 89938 35440 56451 41981 72577 80211 50454 52639 57418 21469 25314 36080 47729 43585 75725 51731 39741 80717 22320 40003 94566 42050 34204 93699 8099 64602 27474 47788 96452 54417 33816 7089 40516 23748 68654 54964 80626 92053 92168 57136 25856 4001 78014 24721 79966 19588 98453 9430 85849 48174 64833 61995 53482 59792 99131 15063 36344 81259 73114 95588 31872 91197 88480 44175 10295 7195 55513 20833 95098 82697 64367 47449 90023 58707 67128 58864 68295 32280 94129 30291 35914 17972 93224 71219 16986 91650 93906 30794 32173 54528 26430 86862 36455 53963 22994 95555 49045 25312 95437 53469 53129 46561 48639 92542 47171 12402 62843 39405 25686 73684 39901 47137 82971 50266 43922 87223 1735 44536 54034 89464 70461 14768 21338 47484 28821 4058 57511 80120 30476 31694 4198 30495 50819 54608 32118 17785 59134 30526 44379 69273 73971 72700 54205 84346 93899 53573 72899 8016 89714 28231 86300 98598 42495 52333 10267 85968 14657 21078 7344 2182 45773 14883 53899 21565 28616 33319 59299 19640 51136 25564 35398 22971 59929 30839 76200 91876 52944 569 54388 55609 87354 98314 81815 61379 64052 34207 34387 19676 73842 46010 14306 69590 30130 89818 84597 57621 36493 74496 92374 96870 18446 23593 36450 85848 67473 41990 60732 56814 79987 4708 7237 21475 94109 96588 84816 14570 92032 54723 48744 37374 67266 58621 43394 70406 77067 30647 19175 85761 47296 26530 37142 29176 46485 40509 50680 84114 74697 96053 26078 19831 43940 14525 41042 70099 77599 90072 58759 13357 3222 52104 30911 55314 91353 3862 45889 15060 58605 79945 87633 91601 12565 88172 6372 1764 96551 85776 19349 57477 79175 81915 55792 73744 85826 59496 63472 97549 77470 81341 75910 62130 52198 62463 35890 62473 63930 17232 4833 70960 50984 83458 43889 83309 80834 56615 30827 29878 71614 76681 26196 7851 95794 64408 13365 83509 86027 90584 36870 20750 49122 96813 97457 23053 42365 39403 1198 11158 14862 74240 68724 7152 39841 75687 20207 62878 64418 83943 23887 8245 5025 33404 64149 12475 37781 97442 5413 61921 41187 74959 95853 32052 72050 57340 35808 72486 91006 78334 70821 13563 37010 88124 85165 95750 6627 40145 48777 96689 44786 65766 60021 81375 91767 28778 78310 52488 30876 38321 49924 10843 8548 74882 95837 52492 3480 29842 65878 23056 41976 66987 6689 15314 46930 2127 57771 67615 36978 84141 17677 68776 65106 31629 22577 84839 28335 38044 40486 42146 76353 42174 19704 65886 93066 69650 68342 12926 52797 39175 12159 32381 43309 27001 40002 88445 13745 71253 130 91185 54122 72082 33804 27830 64139 69335 52893 18419 12025 65129 36391 52188 97387 13961 66968 51003 11045 81604 86013 37014 21486 95670 50622 70065 13788 2387 19865 88113 84850 23182 77372 66731 39010 2714 80304 43865 58517 18196 8580 5624 25841 51195 14363 7654 58378 48119 80583 30093 76920 11591 66229 81071 72302 16138 49007 78926 30479 22114 65474 48689 50952 7014 14634 72317 84152 91310 52949 46802 21864 92038 1089 63919 93463 81203 73881 98826 82477 46376 83592 44022 99047 4248 36234 41443 47968 65012 91550 97860 51071 11493 17894 45915 16839 79826 52750 84372 70038 96286 25275 47603 78263 582 78823 42322 65944 77150 40006 87722 23554 89853 63021 70260 50256 72493 61936 57999 55498 37968 45937 99376 15918 55880 56259 41874 76417 90940 82832 77907 14590 57231 57584 43414 39440 81255 54380 48103 77910 71609 87004 46110 50146 24047 49710 49005 33043 92255 48374 94481 40804 73283 39764 3446 79728 24824 43733 72520 43503 21570 51392 6397 55981 40330 10696 62722 73896 49790 1704 94463 99724 20656 22011 99929 35503 71269 8125 46081 64436 21470 87582 7580 51213 62146 96819 15186 66562 35939 23643 12390 10170 36319 56061 95352 97362 13990 16940 84782 28615 22504 9980 9579 10308 32672 30766 18551 73500 12735 39856 41258 92820 28947 96501 43344 55091 78991 59466 68120 45978 35593 92918 74763 23257 64692 61762 6792 50943 77202 46199 76464 60897 12001 3331 91735 43410 36342 8060 3007 51254 45384 27910 59973 32142 18164 46137 71159 44920 64238 63045 75273 66098 1450 33632 88710 49695 81186 2702 46351 73081 48579 6835 79793 95623 17165 2977 2182 91088 10437 85866 8297 37077 24226 17897 52396 9158 47346 59654 50791 35832 58045 129 85066 72948 54807 51991 72847 22504 36734 9517 16013 92668 6483 35073 55494 99927 86477 88212 97794 98820 76632 58145 45552 61798 41104 86671 76696 16998 82932 89938 82039 88402 12552 7660 79041 36744 47179 15031 43184 28064 32241 44591 11060 78140 97470 58438 33400 35680 4421 33924 8142 9453 91211 91388 39445 21389 21509 7712 8533 42242 57051 99326 59015 75033 8165 6989 59937 32605 20083 67171 4890 36257 6173 48558 29365 58905 69298 42165 64850 74762 7268 84652 42687 39776 86470 34464 77823 76755 93039 88055 22046 84942 40779 68247 12630 62269 76431 61962 42875 76530 331 96826 30201 50084 54213 75795 9503 60079 51205 46466 14781 32496 47873 34839 64383 39322 52368 58081 23732 83864 53502 47754 62819 98967 21827 78706 68045 56354 40609 12177 62765 19179 43007 25335 70623 4731 93540 94453 43129 96004 52218 94369 95671 37363 77946 18812 2601 60307 46760 8939 34782 68574 69582 57753 95332 20923 13376 10429 88296 69146 32655 63148 4927 62920 30636 92322 29716 20144 97744 86329 66731 42378 52070 13072 16500 12105 73937 63910 9723 963 2263 93417 19171 10657 65962 10064 41887 54848 77186 11540 10762 66124 20878 987 78530 72042 46199 11498 94697 52136 78334 83695 7192 41068 36709 12257 77335 99959 85919 25221 31080 44801 88526 59796 5065 55524 36623 48201 47790 13728 4166 14305 95322 73277 58355 35054 45697 31139 44608 47501 22923 93600 36331 68888 67751 35660 68701 97394 26907 71455 68808 88835 84422 25030 30248 30278 71413 86153 55109 43107 43889 21310 85287 96984 78885 37133 47784 6004 10335 96869 78262 24001 22876 96422 22242 36329 24524 92542 74288 44035 4441 16558 95854 39421 8248 30196 67610 23648 82868 27520 88780 63930 18065 7247 88505 75327 54905 1689 65806 9534 46541 81133 21861 8299 5075 64031 35922 25071 23650 60837 51027 68060 71396 30021 27331 13744 35996 33437 77231 5678 7736 1711 64121 98128 54009 97062 46483 26748 27588 72576 64192 36802 77233 51496 49455 79656 21485 10699 21519 12695 61834 96968 76047 1234 63465 28350 27926 42936 19014 25067 68997 88624 80911 18852 3225 73529 75956 27321 44919 6312 80003 60521 41737 93686 14793 61426 75994 74583 35666 1006 70479 76123 27065 24761 81598 84645 26765 30827 87201 82055 4155 10422 7245 24337 71557 94673 30668 84306 52715 37446 42660 32277 18769 30919 97855 26799 81671 86498 21148 1057 25385 47098 98499 42171 48258 54129 68268 68629 16923 76436 57444 20881 21992 43968 93060 58185 24164 16999 88323 53914 93500 63729 32239 10014 42338 77035 45266 92159 69884 30446 78202 92676 42130 30960 37677 70306 27792 7971 62551 60979 5045 5231 64321 91330 29092 98430 2927 34710 15877 19569 83165 68005 52937 39224 20666 26421 68570 54469 74394 89665 79828 81955 57222 44137 86186 26463 9841 60221 41007 99118 28784 20601 67333 92017 20504 68982 95250 66491 75399 8661 77997 18744 1846 43691 16738 15800 89443 47542 69413 66971 56075 89366 4950 72257 4887 61047 3300 26635 23268 43021 20535 4472 24425 83240 88201 45112 74982 69171 51131 82705 69502 60889 99356 10544 1726 82412 49677 89221 39595 92633 48607 89053 84626 83995 71749 20362 48674 58990 40448 24085 42214 50462 56821 74232 54603 70070 25600 17742 63466 57363 16312 59360 43987 96813 97501 35611 52630 79852 29149 69824 89043 17632 99127 99784 92376 63975 1752 60194 39467 58726 46906 63913 89283 95573 14873 11620 53447 81322 36225 66907 5020 73894 69296 99235 89194 24869 59554 66811 91942 95895 57214 58166 23327 38538 29333 51986 93067 2605 96195 53799 67070 65942 55373 88922 4413 10472 87561 18941 3898 26276 38724 61106 91905 82727 44243 87671 37362 74105 16733 45712 28596 73916 38206 9181 72974 26838 37067 21511 44831 29345 15885 90173 93411 58196 11128 17645 44721 35590 66197 82439 78746 16765 22592 66481 65086 55884 8858 91426 35923 21546 52734 76771 87677 63527 9473 26349 31051 63638 89399 55669 54839 82595 35151 67687 25454 50429 52402 3798 39264 58081 78799 66538 12237 76061 84310 23191 81196 22349 52605 89234 94238 60594 22447 46895 54811 23385 84414 5310 69808 27284 60015 41440 83425 17381 53486 42076 11139 14302 94442 30337 81910 34041 12820 79151 22246 13442 1129 65958 78040 4305 31936 56354 81262 56026 85783 35169 19015 59724 55248 84924 92570 75181 39600 97828 83546 7197 84213 45776 95257 33129 84795 86442 95365 9020 44437 4143 85691 35448 75270 32795 5660 38778 45340 48548 40036 97307 44354 39689 90569 72660 75541 79186 94177 13182 76955 7943 20547 99621 88161 38257 91826 46022 13393 71537 29549 61327 39696 85309 26521 1466 1258 47360 85784 75028 49674 51711 97548 56021 45973 89234 740 34953 80296 33912 31254 29074 52386 37209 24612 96262 32091 98119 91380 99219 81033 97100 96873 68457 41143 18099 18646 56096 21091 38897 69315 94349 1310 74012 13779 84197 69647 22763 34150 454 21662 34008 85773 65366 36194 46478 35081 41996 45758 78864 70859 38684 48950 92188 35659 76175 20921 65859 98433 82731 33944 85046 25006 39809 25129 90278 19991 44532 30602 83529 28467 53426 25654 35392 42277 65121 70952 10933 18313 67069 5033 13904 63088 28469 18470 12237 35700 76045 47908 42268 55561 66586 9994 98142 40822 77490 43987 42795 429 54835 68447 10483 48382 64655 48135 72849 15925 99135 79762 86508 67110 2241 95948 22505 28024 60238 86880 55327 82041 69472 82810 12494 69568 77415 59748 32921 77284 53080 59542 99847 13822 76279 2069 99908 97927 79138 42838 71055 74119 33652 18455 88399 92368 27979 37017 30720 16077 40999 85578 5008 1843 27679 40717 60500 10762 2448 10012 33082 85765 7188 16033 12383 73365 33190 68493 16523 18278 9626 71495 25282 93286 39826 64989 64854 76345 74781 73845 18413 68890 15723 87645 60319 59591 32998 20353 60461 6544 33273 55634 87942 84377 18791 42449 38835 47052 8403 33154 97668 3867 57866 61363 74091 85464 2250 90315 42867 75996 92833 82084 7930 47290 16929 73384 72615 27644 62433 837 36847 69569 19335 22361 26802 96235 87701 17620 91458 70114 66814 27802 31816 70749 64433 46467 87098 52773 4460 82720 73125 75947 3684 41628 66489 49606 81774 60758 59205 98213 38828 43142 43810 64168 27452 5971 50752 28824 42540 43590 97573 96443 32343 63021 18069 41804 39402 41409 88288 95324 68859 78501 46524 55871 72020 5474 27876 60739 39678 3333 2 59541 28533 42924 42848 94891 52651 34306 33060 28540 53705 94692 35019 32830 23609 35140 80551 65106 66163 24033 61810 44521 3568 26917 95485 28563 65496 76305 74884 34501 99966 10661 79489 99337 75157 15925 66799 70961 53106 73735 73051 31813 46684 8487 21157 87525 48856 73780 67148 35880 68750 8327 10365 29753 67846 49702 6371 90764 46308 12066 46368 51326 39643 87519 53871 42902 49003 57898 50080 18632 46831 26585 45934 80104 85797 66127 90887 74794 62445 39014 36548 11001 1729 81619 87503 34902 20521 55640 94620 98489 28923 97852 92661 1786 57230 78921 10662 77297 5204 38887 19091 59271 57877 33230 43228 43872 99775 77953 39935 35671 41878 12202 39511 51532 27542 55648 88205 46479 68695 93551 52125 13249 79036 29627 15361 58574 60728 56544 91352 11834 12801 7111 12637 52377 26696 16911 23475 78645 61664 27215 26212 55590 91984 7810 93872 904 75409 85846 80397 99039 40095 54435 3079 52040 81961 60988 83103 78325 45090 38465 72907 26996 22192 28567 75393 67911 32011 932 56894 63934 36941 62452 11208 8871 1976 61436 32593 50587 97797 42179 93327 87300 59963 71136 16818 17287 16614 8965 52212 97032 58180 42400 94228 33258 86416 56658 40110 90857 24409 85084 55401 98751 84855 612 18007 45828 16376 10053 77414 52495 37118 88783 31889 16838 75320 91357 77200 9397 31387 71740 59426 41888 22517 86964 83418 89897 49548 30971 58704 79511 53435 44003 80268 95475 28552 92918 52218 43435 88405 28670 15570 82590 82627 44139 29303 87446 24183 68893 89396 84486 82311 968 71081 11875 90886 62681 44931 52110 54625 18554 63655 18082 96454 51170 72894 67182 4159 44727 86737 255 62875 91200 38701 76078 31141 29809 35494 34382 50844 50480 34204 40257 12753 15834 6301 84500 17626 10051 46858 89571 9543 86659 9724 97258 19791 36363 48930 50615 90255 14045 32694 88543 95207 56008 1230 68502 74878 54343 10102 96334 64269 61049 94283 72673 36326 1293 16666 83532 17270 36746 19682 44828 91929 54060 34849 11831 31012 31558 51839 81400 56790 14371 25967 18741 13651 28878 88260 88824 39551 82915 5504 95507 87371 392 51198 58548 28548 83917 57507 6458 72180 59546 52240 5006 52145 19829 265 43599 55993 75770 24956 5322 16151 2935 27984 93211 71433 79993 13071 27468 15312 55564 41013 809 95465 20708 84664 85225 89630 78639 65165 94972 973 34066 47861 32038 72453 93402 54081 70936 80424 70093 34909 91934 54730 69315 49618 59894 89176 60610 85012 61130 37488 64545 49389 94110 39681 37630 35225 6897 31 91921 90304 54682 90412 4221 59863 69917 65164 17677 57759 46984 25385 54590 11609 96921 96027 24757 77770 37939 63125 55574 86848 53914 91484 95041 60845 90881 64025 18519 84395 75487 76234 75799 6829 87798 95751 73655 59829 2065 33397 10292 11748 87094 47048 92885 64773 86251 93182 16462 22787 28492 15453 58089 40328 73366 92449 57471 30777 90114 9312 29160 77452 99733 35769 15575 16689 75359 47759 21385 11896 5094 93213 13970 15897 34338 43202 8403 77123 84389 25430 43770 45105 8969 10382 39041 5243 93044 61393 91182 40610 14883 44501 34642 36701 55050 85673 46966 78161 67942 31080 48724 4613 48568 19295 59441 67036 62577 59433 12126 53271 24540 43287 21147 48020 79484 61382 4906 82900 19150 13473 45069 20370 82258 8397 55673 27167 65727 49267 28441 8369 20019 73639 51843 35197 1135 76916 84341 15731 71182 56743 77646 63068 21905 74230 4212 85948 89468 12302 49443 97153 11292 52391 12993 39433 30537 96330 92917 12311 52614 8595 56744 63651 30821 49302 54413 70573 40059 27997 35041 34864 53907 2305 31373 89873 53175 32615 33899 97737 49010 62906 35339 94903 59084 2589 96569 65736 20061 6685 93889 3782 23859 3227 1019 75111 65645 22075 30006 4771 33146 48101 82399 94158 58156 29813 20431 28933 21256 69593 79920 16748 81116 41599 52731 41004 18404 29039 13955 64350 3834 86869 51478 4333 90453 97979 91036 53789 27951 3872 67274 79330 4580 43393 82111 42878 94119 74544 84869 52521 31348 10065 27140 82517 30374 85105 84474 86299 16611 40366 99170 39508 38280 36991 86796 77210 81218 20352 1099 36993 89261 63766 59400 19864 50367 35381 3920 83178 12572 20696 85615 34086 37374 73024 90918 83068 69101 10048 26961 15223 61593 51537 52668 68932 78280 2624 36358 53079 40532 94773 5041 26543 35000 16860 90139 49223 8404 9154 10575 70665 35074 40463 23606 78743 84171 53441 16170 27822 48707 1 51614 25188 87372 57713 37954 56772 96796 96998 15525 6135 43810 15878 8543 91388 99868 1389 6806 86275 69481 80478 5087 26200 57474 7026 49140 7806 55469 43300 78333 71166 25025 99522 41631 38641 3658 7292 73610 29361 55993 5125 46281 60443 75436 61730 8534 38448 6548 63787 99641 82389 27436 37964 53139 75445 72621 15816 1395 98576 89994 33855 96881 72189 80137 45730 63673 15663 2411 7100 28975 63093 27000 50066 76252 45765 86238 49858 95499 34531 22553 21885 55610 54516 69316 75808 53583 52279 44780 83694 80151 70777 12022 82692 83627 90622 24746 30072 67406 4338 88667 6270 25158 17531 91051 78891 50296 87044 94455 9998 33657 62453 24817 76433 12073 55692 63053 58616 27447 92505 9221 90893 76598 4744 57215 61885 60814 180 68409 59491 64872 37004 69553 551 57300 22488 59048 22801 8054 15859 88536 70495 3697 62127 4975 11691 24189 87450 65881 77638 66245 14651 78543 71232 79579 61072 7941 84939 6317 1274 54339 73198 60777 6085 35913 62955 99947 33134 45002 24989 64018 60689 58039 53442 92246 64420 34374 7520 88458 42303 50547 21415 55116 27565 493 56550 35800 18446 94589 4498 47026 79580 48997 311 21455 52129 70561 75276 69848 78445 7147 71521 85180 73894 88848 32642 26732 10673 99788 67120 4787 29302 94580 87023 75216 27354 86872 69839 88830 84548 34001 95590 46319 36193 66997 34491 52026 48224 59416 49634 40886 99427 70550 12965 88247 60516 78987 21292 46883 97377 55248 28647 78640 48319 83720 97774 51803 12620 24925 54050 43933 41681 69231 59820 22758 98252 14328 31346 46283 17608 64535 80321 50167 27736 66131 55783 25127 8832 68374 79163 18607 27494 28795 99126 64214 83652 13993 97126 28147 8891 53548 66889 31908 7460 60866 29477 81849 84790 96917 60478 2753 84288 23764 34027 46219 99198 65882 11456 58603 72390 7837 47390 49956 49367 77930 29247 59385 27901 3806 37688 65051 20594 54747 10583 16734 43203 72823 193 55852 46473 1573 78474 50635 69908 90225 38830 98809 60372 39471 7493 16563 93235 74811 65793 40684 1032 67940 53792 21912 82091 65413 29056 35201 16070 26494 36237 14839 62503 38947 2700 88998 40343 49984 80642 62213 45329 46966 43140 6585 43698 77943 73940 77499 79884 6546 27598 15733 58986 54733 46132 58321 72500 97552 2638 22167 75848 60733 15807 72367 34685 12475 54578 55103 58978 89187 63858 98028 5118 33437 54235 99227 42331 37006 43864 43946 31649 90417 20534 40046 28756 47612 17134 64105 29793 72652 28994 83939 36229 81921 87526 92424 32594 59146 26035 18516 99534 23510 26233 94591 43340 923 10950 98009 64856 39400 34217 98399 61659 26691 52735 93309 26337 91266 6182 8396 78264 99354 36606 78686 87482 89877 39001 88057 2275 34920 78603 39391 81714 93075 170 4673 16102 94694 79893 60849 2006 23895 9762 85213 88414 27042 67686 84502 77847 28882 3410 70884 61514 80126 5603 10140 23755 97102 38521 77747 54707 6509 39099 46731 75742 73579 51480 78376 80224 48244 7501 69112 11630 34040 80761 89973 29042 87776 51372 87053 28317 58268 97003 39263 1524 12677 58168 60145 15395 29241 90982 58250 25856 30109 58907 38040 44352 55329 7980 69651 13396 93710 97675 78476 71989 99489 92756 74736 50738 56012 76619 13610 67061 18477 97955 27028 50863 61002 62999 23199 60639 18633 67352 24657 56476 35249 11866 61680 48981 28951 13270 1777 80826 40371 13164 52986 66428 62234 56696 60452 13126 27812 78317 82822 71111 48556 56317 80343 53120 36022 27684 17206 75144 36246 67850 61895 95978 45414 15108 46811 19058 59416 4891 60822 37330 79438 56474 6620 59684 39571 7457 80015 57267 54329 77865 43088 90852 79685 8064 27698 75391 79062 17153 9495 33884 836 63078 2589 735 46677 7333 93616 5744 1033 19554 95333 25636 39805 3367 2181 48349 29420 42020 58725 75001 94734 47491 10441 88866 41732 73142 66918 48047 42160 68405 69341 80412 67076 43436 98099 32968 23184 1232 15116 40759 58508 12716 84774 61979 96144 87514 48352 78161 80974 7015 92603 47803 12768 37361 9378 61652 22765 5181 62600 41816 59434 2928 46033 88201 74499 54754 58793 33941 65504 3651 92723 20183 90839 27638 49150 82868 53246 1627 8439 10939 86295 58173 33292 78662 98805 54646 76919 41069 37771 95473 80750 80260 19148 70859 79581 59141 48551 19551 4994 10422 60703 10318 43357 18055 56409 58672 10521 97064 84534 24512 54137 86090 66374 40173 26045 82513 12274 84929 57409 64209 74499 5148 72214 67727 91071 38494 72398 86422 9251 16846 14507 42429 66651 87856 84218 52912 53690 10106 56767 40438 78418 53778 28091 85540 62908 5157 54381 66284 25203 26558 1227 51707 3785 97769 38930 44257 57863 14489 38504 47655 82862 29971 58186 97915 30333 94093 68999 41393 1516 35398 19441 91528 64480 12625 91302 21499 40384 9611 68024 16289 79299 38708 23328 88766 51873 43788 66636 36551 34812 48827 25240 78970 28539 51641 11202 85678 71589 3039 56199 62607 83725 39932 50085 13942 26140 30850 41445 46699 32160 3813 41247 29857 5470 61981 22539 77820 32454 33739 58924 12709 84202 31939 40518 39859 52619 51064 41245 94453 25068 90484 14510 49819 5603 16934 20894 56111 19107 93260 27112 87757 88109 40846 44718 40883 17270 92298 12486 2060 47705 54408 53210 44542 51457 47593 85446 47897 30087 99856 65490 65598 17088 93439 76118 57417 38550 25968 25672 92234 55712 29230 44889 91978 6020 25964 88262 87315 79898 81637 51180 38764 21222 65248 19559 9326 22763 67952 28611 72082 31591 61329 65832 35943 20830 76583 48633 7359 49345 44485 73783 7034 82075 88379 83955 51575 8220 25680 15826 22289 96711 73141 50600 93047 86359 34245 40618 4932 99667 77548 8109 3099 5096 97708 66570 65427 47997 68620 83834 27304 40127 89753 75089 52826 80646 58243 63301 85504 12046 91063 47990 49956 16087 22540 4684 60663 90328 12751 9729 14563 28277 4799 34049 34934 63529 1135 37029 72240 97715 87803 94990 43255 38064 16337 40802 44069 23400 25734 30909 57525 49434 26050 99436 27893 54373 76437 69304 98090 80540 14731 47524 24425 16054 6702 361 14871 1898 55093 91211 54872 53444 83730 60259 46597 44420 88880 26618 22004 38315 6650 80825 25756 46638 53198 48496 21277 62963 6698 32965 48183 86398 17795 56251 29419 85784 62357 90008 26365 73067 55333 54103 29879 8672 60071 42444 24737 94293 14729 41109 56206 55208 72224 95698 77904 98224 81262 7983 84877 67002 23642 36782 60266 63928 26264 13662 18796 28135 59519 108 97531 60500 28441 28239 19296 11245 35043 20423 16403 5288 72898 76243 77683 16444 12013 67186 65488 88733 13752 56408 54596 69716 56091 97238 1433 82370 70962 17799 30211 19545 52632 70958 54899 85220 9908 74873 64108 68957 52202 37723 96119 45653 10831 30315 7168 20210 53314 43569 74559 36973 42593 96458 45373 45826 12122 18802 89131 63175 61218 34811 33572 14280 79252 8655 72705 95091 46360 66703 74828 99790 71662 53002 67815 26393 33709 67805 46921 58569 29934 19583 1288 57006 21175 50595 21503 42217 47050 76853 94595 55373 81931 76832 86674 55233 78912 89869 63523 59331 30677 98792 23511 99182 57931 46970 80132 68386 39351 62426 76283 22491 92102 50733 90845 28778 92043 90808 9996 14260 3653 47694 13542 80029 46040 13958 9805 34719 99281 63523 20202 19425 86467 38011 61241 37397 43523 92199 26368 14291 47181 87928 20056 77759 6287 37232 64033 10557 1463 54976 37252 43292 7438 75825 79872 35368 84177 81496 54152 69979 6494 8480 80489 64934 14635 8150 11020 57290 26056 28284 22070 96570 85661 54740 65887 17931 30458 53149 63470 93306 22254 76137 31763 36083 24509 76670 36722 32750 21334 64384 99314 12474 9094 22499 97986 961 47534 75371 22621 45477 69489 17197 44164 27159 81666 93062 43669 11940 64406 1215 81335 38239 10853 2897 20331 92234 79175 61734 99377 51778 23372 30697 86053 24933 84348 11203 71676 80510 5431 3492 30352 41281 72038 43233 40157 75821 35549 17016 25850 47398 94224 89648 76303 84539 6359 50348 52909 84526 85591 1141 80216 36562 75044 94989 60258 43417 34789 47718 60601 97456 19571 8036 68687 95195 10359 58050 9544 35290 74449 23328 98197 59660 82273 45610 82834 37705 2898 27306 56865 4225 68655 15604 11275 37904 77503 40626 83666 16177 5194 98435 76437 82335 19593 72533 23060 42453 53935 25355 50929 11539 55489 27752 96217 83990 63989 38173 33867 60056 55954 92544 76 90667 70185 20261 57861 10240 935 55088 88178 38010 58715 17322 26432 20883 95159 40508 26792 55819 14826 69868 20319 40881 82617 69098 99292 46197 5582 17104 49039 98669 63116 79002 62354 29889 39471 76781 4876 27956 93029 86612 2618 12101 162 8904 64474 52929 68208 6854 22184 40895 14768 9657 60579 34527 21810 95874 19324 33877 13030 25611 38382 83688 16930 60535 20614 1844 21722 52104 95777 30223 43662 52061 62981 8222 63009 31961 3353 58262 86376 2849 15877 481 45123 69825 69649 46828 17275 2152 49418 19418 78183 3390 67985 61049 64144 27350 71633 69891 66235 45583 56257 80777 83854 8075 39385 67650 63206 70537 83671 63890 76048 67061 88416 47044 19411 64856 14376 5841 61041 92764 37209 99734 31221 98473 30852 6453 69549 18303 4401 9009 24822 81172 65859 3670 32481 94988 77503 50191 62894 2540 84740 24800 59138 58197 96968 8346 1069 48220 42367 97560 6533 28369 46459 4046 96429 60669 30719 40018 16788 86175 72083 12114 96077 78889 3556 78410 67777 86599 92730 37569 36402 83857 6312 95396 21162 76281 50895 88951 92421 6479 18213 5482 43219 92551 11344 60649 4586 86205 23250 62739 44852 55387 35931 52892 62317 32702 33561 69260 1903 66465 45294 30649 81448 45510 53178 93791 37920 94951 64415 40713 79516 28847 55558 12905 93717 19113 18390 2345 90678 8667 97153 3004 13245 63816 62409 45124 80852 24802 54942 24275 82815 15227 55735 21252 60022 97144 63860 37928 45268 57771 97102 56947 80837 31604 60024 155 64523 15860 95293 90316 13355 30951 45073 29636 47183 57222 35497 29758 75418 2118 11754 85077 84143 1261 13536 74926 90350 90354 11240 5552 14160 85833 17568 60148 33112 11274 30906 72590 67701 73493 26648 76755 50060 52972 15547 24046 28866 96388 23862 66786 92601 91444 33796 46583 54544 17765 97233 75163 62657 3114 77604 93833 43260 82412 6600 97397 50312 72227 82070 94126 20551 70712 3057 14631 79287 65650 13990 78226 9338 39156 76570 94627 38133 32553 8380 47893 68678 52022 15420 88309 4835 84282 27184 64642 58562 81039 29493 89168 55920 34193 97757 22960 60539 50831 72272 16295 68144 53921 68934 45946 54720 31750 48676 44352 19284 50606 49352 47220 81640 50847 32784 45022 28074 67241 98482 30697 44104 55325 43493 43296 22894 34051 88328 89966 48583 47127 66645 69319 3585 64494 27543 4847 14485 13904 25631 35467 23897 30358 36581 96307 82673 64068 64350 32474 5956 94744 74157 77882 21598 9412 17485 62332 72332 4955 49559 5211 66097 8764 33831 69605 925 53740 47671 93136 15608 85487 88810 16973 72393 14611 28249 88408 27871 32691 79440 62093 83922 32984 96446 17187 94381 94381 23573 15606 4314 22446 9532 67089 82691 4186 64358 15369 91154 435 10767 24128 66724 58414 19574 58333 64776 72293 58200 2391 86979 13723 62531 17839 85481 85076 67492 54238 87242 72225 29030 78340 97529 84387 45624 35575 24000 67790 749 92104 150 44918 38817 75432 86280 86035 47387 88925 17384 32590 61597 93244 77290 93032 31587 27484 41196 5016 25981 61039 78375 35866 9293 48251 78674 25017 68314 36608 33690 42837 27168 20141 22467 57709 35384 74714 40825 65599 50005 76277 18682 80215 99735 99749 30853 66928 40793 79642 17872 85031 27813 66857 49480 77535 98503 7535 72991 15575 77940 63656 2093 9993 48659 24113 64575 83006 91245 33951 55900 83422 86712 3159 77642 29680 47837 5950 67580 27960 16911 77790 24474 10661 50428 35119 32082 97553 7936 50574 41188 2146 26735 24597 86264 69094 77426 95858 20774 61844 57171 96855 21139 126 2990 91607 13002 77940 10131 75925 34753 4217 3242 24603 13067 55725 64338 49935 50760 32398 81609 46857 19983 89063 60445 80221 75645 64895 52364 95761 26482 55055 44339 99490 57186 58638 88059 16030 25781 13781 60785 98692 94401 48718 5194 47339 33618 94577 74804 80445 2807 97864 3921 79854 14220 55955 91488 31970 16319 97284 13397 38367 62126 77113 65911 61598 26051 6459 85214 82965 20078 44122 78359 81357 76579 51201 75581 66807 94576 68059 33776 12103 28053 53021 54799 56751 61476 828 81238 58392 92182 18350 14297 77381 74437 60690 78433 7439 38417 42212 11257 55559 30789 68109 35179 93222 57272 45430 33339 96841 17918 4278 12002 34048 95986 47837 85968 33933 3150 58395 61440 45525 6912 93641 52109 73976 65484 51907 94033 76943 60890 66015 80625 89808 77542 60571 3224 9738 75165 22077 88189 43917 85907 74825 80677 3167 91190 55141 5458 55337 39541 95505 78134 35593 95156 24189 13458 10104 32664 16334 7882 37041 88893 91643 81234 53853 59512 63388 88057 94458 46915 17856 17982 63644 10997 1243 86070 14307 33630 83248 10734 48456 37145 76103 30151 84483 37965 40102 97079 44369 4506 71915 61387 59121 47215 66811 64333 5371 43492 82230 19780 36091 39559 26420 80406 79923 3999 45131 69457 56546 50995 8072 70903 58779 99108 36896 85697 42955 92300 58549 27578 61732 65847 57474 64123 72666 38596 85412 46780 60821 56590 22905 84836 79844 46473 63739 56995 30107 69623 17753 76797 45517 45254 36668 24135 66228 48358 49552 42586 57906 41415 2322 46657 7589 2809 21628 30017 63673 15287 10844 42678 88789 75376 96130 42773 25903 39825 42000 90346 53800 93003 987 16124 18792 54626 18242 57074 83828 23212 89378 94448 91006 91786 30046 10886 16597 55555 74493 1713 11522 7158 2610 78508 73380 48975 48090 74140 44589 81483 89182 39822 42714 2467 54075 60607 74852 1676 74755 35139 37523 27305 56688 86371 28487 72186 21049 41826 71606 311 65518 34975 3670 76833 26669 81003 12875 87650 48016 18891 62385 49735 29905 72448 92406 57834 56083 87298 9171 44011 32651 3857 51879 71544 18500 38249 15263 68422 95988 65833 71221 48810 24919 62638 59466 51260 20976 64001 9285 89305 86209 48722 49917 26766 23954 23012 35485 61663 17974 25333 36995 88479 84263 47733 83529 89753 28546 91069 95718 60405 45896 61726 1484 67114 3204 50225 83075 82150 61014 92189 21605 892 52503 21967 43081 12724 39697 38664 96535 41218 80319 98957 38138 48620 4383 9311 3519 40212 65870 66889 51808 56866 58163 7748 91774 24763 13450 79673 95163 17072 33173 6960 45708 2048 92475 8164 77160 16014 59506 73015 35766 4849 23173 94216 45832 46454 6379 53976 53566 75160 25423 84951 70507 19574 80746 3813 35260 17384 58668 94380 1679 16146 38792 16573 83128 79420 81747 80864 74374 17174 17406 2196 97353 77353 953 5454 55768 53503 70788 82418 68647 27818 53024 49320 2471 40278 20270 41790 71560 96710 31118 69260 36182 31313 86922 82758 52032 31072 57308 55640 89029 56679 71183 92740 13059 46809 33340 11713 51366 44891 83978 63752 44310 61560 18566 74832 25188 63986 47222 72196 57146 91378 24793 9538 63078 31553 92642 41945 57871 98819 26308 42796 36228 50939 73003 46159 87853 28958 28791 82667 24745 15892 24581 59095 21353 30158 43690 49090 92816 85092 83345 78493 12814 11509 44739 75701 56950 743 20984 10871 75756 80395 22947 89127 41653 57935 48931 26040 28759 17981 83368 57702 51194 5410 22013 26498 67852 5308 43268 28098 92586 70934 46494 29647 20749 25720 10064 4379 16508 58274 88388 94266 29133 67204 91494 62638 70303 56447 5449 15192 66300 98620 67092 16989 67058 17576 84770 77244 22191 31373 14386 88966 5155 31480 43513 25764 9574 54251 31358 52938 8438 25650 47001 24259 33211 46171 98027 10778 89354 19480 88016 20592 95252 43298 29501 74854 29751 91865 32870 67833 337 14700 49751 66431 40719 31165 79131 36067 49495 90540 22355 17465 82520 11986 92890 16645 73515 97310 50538 43591 85470 60788 81951 9587 11697 12413 58270 83650 69384 80940 86071 40869 6410 77560 8733 84473 38286 40366 26121 94416 17142 90001 23481 41917 25009 60949 33835 3701 45741 63993 34924 63580 59820 3009 48799 41323 18677 76186 58540 25554 24079 64536 88223 89925 68437 62889 83287 67219 16699 11862 30311 53239 47452 96457 99635 30613 48986 4397 74063 61938 1117 38027 10339 34998 42224 15937 69266 93939 21732 55890 45739 9068 90366 49516 2115 72726 19724 57875 40861 43624 87776 68016 85636 7734 84032 49261 87739 77896 82696 21784 34128 76949 76312 45571 47395 62763 40838 40900 44819 64187 44049 62738 77388 34286 76802 7676 53123 58611 38651 63889 9207 9254 67148 13218 86489 81288 41757 45745 71043 86734 16958 43009 42478 54183 39617 24051 49618 29376 21747 43045 98321 27218 83193 58389 77449 55452 59520 62678 42288 57450 95443 33975 45322 10791 80762 76331 64370 33724 50665 76120 90345 87912 46234 91557 96577 29953 2587 19935 49939 96134 88650 19236 81851 79620 98845 74298 97319 17638 43049 81374 71647 79763 9980 29005 82445 65902 81428 203 1865 10664 8240 86234 82104 72387 96298 52958 52190 99187 99440 7110 6496 39464 90923 56325 27929 92954 15623 38503 18870 55582 49634 69529 98762 70969 58977 78685 26142 65753 4054 88978 19456 29391 18002 4280 28638 47118 32277 77902 76607 89911 68972 34673 90499 56641 45143 63507 21826 97314 35119 10562 70375 50150 23917 43033 82724 47182 47471 22590 40620 89033 2564 35752 30367 4614 92499 86097 45012 41023 98364 48713 86725 22603 33161 48427 5192 94340 47914 46254 87339 38197 38535 19922 51148 13560 35494 73627 63477 30413 60302 30203 34 79026 32129 22872 2614 53558 9899 77215 47389 61941 60484 96054 61943 15299 44871 70778 96451 71119 50561 68191 29167 18528 52251 73226 77300 49005 2280 57661 64807 2066 91597 80594 18595 50188 32002 55809 85194 20021 90133 43596 38825 18007 76815 45055 28538 13861 95582 70999 27963 49556 10338 57005 34875 65797 31255 65427 21791 52982 35444 49601 52130 57592 68044 29664 45449 22410 11755 4472 94015 87102 77405 24937 22467 28814 91219 51380 58058 40073 89971 72228 96194 294 72701 43826 6680 82172 65305 64351 64570 25877 26516 3432 47448 54000 33514 42182 28813 99203 35981 99001 48649 36594 88951 57499 58764 88903 67710 9975 14637 15184 77012 47478 72054 2719 25268 43007 2585 22256 86137 64931 39962 16876 49540 23337 13338 12396 29344 65041 9003 36477 48951 18846 46858 33582 60955 58214 69121 95480 71599 98313 79252 41768 36871 77191 14840 36049 44318 14521 94879 78075 11078 51618 97927 46114 57768 33375 90327 65225 74123 71964 85608 68763 52765 8920 77378 69764 76331 29964 43558 33501 65303 29971 80484 23679 10836 83641 997 71474 22582 52611 9296 31276 73825 41581 14299 49822 17279 80277 16710 12853 60747 18006 81943 31685 19803 54498 28522 1428 84332 40416 64393 98300 3488 58628 11474 56634 86140 85285 21482 62371 16692 93409 35138 23661 58349 63145 3300 53384 79362 56858 53349 97960 20070 29015 83764 83315 41733 44841 42001 50992 25175 96211 86263 43678 50894 44901 53905 74833 43929 86771 11718 98626 63321 67848 78145 75478 22595 92219 43799 12558 62785 20378 75211 54578 27912 92781 92878 97778 81627 94408 75902 8089 80270 64952 40653 84009 88187 40235 84245 92611 97618 51796 92842 86690 51825 53931 54417 85267 52179 93343 94205 30799 95870 61606 73298 487 46672 59258 76096 17959 2599 94075 93955 38742 89132 99961 42517 85739 74655 65634 98272 98019 64590 89624 91372 24301 86020 36371 92753 96799 63319 35987 34803 84929 26224 26038 29241 28876 28561 45334 37430 68683 80722 5821 93073 722 72690 69962 99245 20663 64969 28335 66246 25646 71254 34935 89608 47272 83199 93707 14060 5712 99516 35675 8587 91851 26107 93643 8601 64468 33985 98704 73054 34481 57012 98823 38243 23823 72174 88787 22621 89580 3179 76704 23486 93189 34345 68438 5763 88634 18565 84781 40325 67327 29132 63844 99406 82844 53440 55934 16252 99427 94426 60299 31050 5105 14582 71932 12598 50038 35066 2124 11896 88948 21274 23603 1628 14456 50424 39684 16391 67579 68767 18217 12785 68591 5824 6694 67283 59488 61348 90067 87392 15329 15995 94084 37167 18968 4066 46306 38608 95264 27337 98818 45866 80624 67899 91896 10324 66639 27148 37871 51079 26447 9663 97584 89460 37779 32503 53719 64250 20360 41178 34779 4935 35488 87568 36395 92896 84498 1045 9072 33899 51054 77022 61221 44667 31257 92832 76109 34461 72680 36904 26718 47382 20043 74730 18124 81717 28067 59817 61781 12221 23914 60979 45776 16078 6918 6962 31463 12428 18597 38703 97414 27134 85803 99782 48695 51058 49535 61293 75940 3163 72289 34248 86361 25150 88766 18904 39936 24462 40846 25290 22495 13849 45665 41404 38893 24665 80237 20190 26375 52674 9498 98829 81538 78512 86385 61712 21052 64607 40098 65516 31328 19565 74652 78040 34408 48597 153 70456 9557 87363 91526 46593 15493 36887 90287 53538 86949 11204 43223 71296 23761 17550 13249 75668 87634 83322 72419 89548 80817 40461 26482 25053 72478 51517 58623 35161 37172 32251 21239 81467 40777 7741 45427 49304 59507 50111 66434 28772 73732 29169 62095 36961 21562 78658 93937 76406 32740 40985 1604 82896 24684 93362 33168 33642 29302 48641 62403 93656 33754 56026 85344 14938 68062 44019 47961 20644 23839 83523 51091 10643 44799 58748 26549 55850 71289 30313 38090 70145 85813 47266 40311 99280 63257 13969 85423 87906 4402 25294 53473 51598 46788 69823 39967 19024 86180 33704 6846 13401 49474 37792 41254 63593 67986 95122 15083 73784 52941 34978 54117 60795 20111 21154 46811 27276 67958 98203 92574 44252 98330 69795 11835 70426 22389 26207 41389 62111 65809 54082 60205 31818 73209 55107 47341 38549 73404 97548 69672 60473 95874 62094 28146 72052 62782 5320 90190 93528 28292 51861 74854 27221 19407 30140 77363 87557 12189 94379 9869 79146 27098 44831 3913 59616 91908 41048 72383 409 15347 62249 60190 25174 55961 37074 87061 41801 58313 50530 94763 11631 7619 58975 31961 46687 72167 49963 81947 82967 75974 55467 34516 27505 3630 47680 36389 15985 16985 78098 94207 54595 22523 88506 80158 54073 45907 63196 12686 38889 21589 68059 42672 86282 28939 73614 83820 22228 56583 3896 52950 461 38154 83865 73098 49470 49934 75490 54565 53774 14061 11162 8826 82910 51620 57517 93639 44254 19857 55124 77915 14039 96282 9701 793 70810 85201 55997 95505 91989 21889 78924 51853 63879 37540 14202 91608 78462 24478 12815 68690 27966 76011 70875 98239 36728 98579 44181 96665 9010 59983 66094 68400 54652 15330 10985 88048 48295 27338 13793 40375 48915 65013 43584 97344 44860 9131 2484 50594 23010 10807 11295 58024 51916 92849 50553 36175 99692 94755 86790 71588 87639 35277 20487 90337 66081 23468 8122 32917 41639 51919 19538 17358 27688 99029 64917 73099 48867 65032 20188 58331 78137 54551 8218 79933 93824 43048 37242 56148 53547 8854 232 76554 71537 2886 31533 37653 96105 7192 76151 40642 12913 61298 24200 71710 91326 94889 37410 72719 94697 65044 57979 98491 92021 40914 71081 15797 33655 9837 10408 9839 47030 77556 4890 71967 29083 27695 64748 88774 34042 29332 24922 39188 6807 72271 51566 28800 23241 61865 82357 59713 4708 81509 93594 20999 6841 53548 33263 84474 99150 62079 52887 40780 34964 73494 27207 14709 73521 7856 7425 6930 47210 71536 66532 26772 30168 31063 93873 36429 92028 21147 15403 85155 20960 66433 43146 61645 21854 2465 84847 21189 25866 18468 20144 77968 84850 35822 83875 64125 92419 76963 63023 67744 25044 33741 67274 17005 92700 98589 97840 44227 65439 23895 24889 96595 28629 47289 60948 71991 88194 98478 41993 79663 58607 11038 90464 12415 4248 79292 87946 44390 58598 30369 97432 70050 6449 40104 70021 99098 54866 44233 22285 15724 34163 72576 53332 55283 36194 70464 13487 48315 47255 68026 6532 73883 65718 69917 4779 34962 90240 25972 88265 19892 63386 37629 57123 4283 35757 88079 45198 19179 53383 15993 11038 79061 17075 9586 34611 1018 31510 11686 62656 81140 91852 3499 34062 58556 70865 51413 21913 13527 30284 96924 57378 82845 4601 73127 22038 31600 17032 28714 75532 89838 29911 72634 23621 80060 90701 87542 71260 97438 23384 56393 80026 40825 2877 36369 43398 87154 66182 60516 51253 91125 2503 81346 88284 50546 45159 73640 44080 57539 64361 66497 12962 27430 23072 19327 58737 28198 28467 2856 67045 21039 74302 29141 40396 39391 30273 6895 80667 33413 55412 55489 13937 60943 56251 52354 79267 85107 15826 17893 74530 19018 39870 56650 40937 86283 17337 74783 99498 19111 87477 89465 99859 78273 27314 20135 3040 18341 47210 99821 98718 39096 56135 66152 67938 77739 75012 18883 54464 8707 12832 28599 94645 33648 86125 75344 29685 76982 96615 90468 36319 35462 82317 98548 36179 56403 9139 61335 48934 62567 45904 41784 74760 90578 98696 73271 38706 96690 2532 52410 40313 19319 81275 83367 91798 70000 53739 64179 71504 32511 9158 92513 11600 2882 70082 99958 52249 37215 48463 29548 9553 38715 91344 7582 35850 3528 66964 30043 82155 20868 81228 67129 47696 71253 56444 51273 4523 29644 63811 85473 42777 479 798 56188 3892 24402 82371 10144 89296 35474 10914 78594 16930 82048 53924 75100 40588 97027 82832 46845 56196 61433 28566 60562 38979 15025 65888 79959 1407 21743 92103 9348 61641 85621 63973 73163 19964 51068 27098 90395 91128 74724 83132 84806 71758 59543 35860 60531 16662 1839 72308 9974 91575 77186 5481 31711 58601 13805 67509 14640 98611 1891 11098 68928 58217 16413 18527 17579 61124 32000 54370 36732 3335 75822 48434 74117 63850 52622 31382 80588 91992 21166 27439 10783 88377 11447 10760 79324 69776 59785 56980 84937 98747 4458 55806 42383 98263 62046 66176 29840 99727 68601 2046 96600 95223 56697 58820 60069 7644 65896 35230 81065 65104 7869 6823 24362 83193 94569 2583 79208 67700 25333 43345 2933 85431 35268 78766 70936 95393 46033 32446 39796 60689 89833 47709 6400 50648 88745 5053 45377 63091 93551 7474 56203 21062 83089 41492 34235 44505 41647 79818 85985 96495 27651 57248 45778 7057 54436 52026 56326 7689 51478 34966 27761 67346 5926 82832 35176 20560 59498 22648 46381 25853 52993 43496 52685 41692 68904 78646 88213 89375 36227 17691 48010 86131 8869 38257 93409 4888 57692 33672 74547 87869 63365 66435 69929 6527 33206 59105 34948 32579 15676 34190 15316 95546 17656 60807 97421 33369 81602 69832 63520 42996 52364 46569 69108 86488 23819 8033 45978 68024 31367 86036 54026 91053 16400 58850 18234 6982 66394 95720 67450 41861 5532 703 1464 58051 69936 24552 78310 44112 81680 75559 62752 48661 65147 11737 70315 76512 40139 40101 96349 83554 22011 45908 83847 64576 65115 81179 91164 18106 89834 32109 18638 39848 58746 52649 30775 49764 96545 70558 34893 1270 92784 10317 27559 66028 67274 1958 87261 98463 34732 79522 92015 67369 67325 27732 95198 14289 63748 69299 34398 75646 79438 39651 10924 36627 43687 96534 59482 959 24600 91643 25366 93822 60791 64053 16860 13943 54929 40914 70657 5105 81002 98933 90557 8088 35321 47432 18744 87304 46830 32902 82533 46403 99955 98334 20884 33218 79561 96590 10656 76285 20465 4356 12298 90117 436 34882 38671 11900 62327 84175 54598 858 12107 69137 11457 41861 5247 86364 15008 13946 84693 36108 41631 55011 70169 35343 35650 92271 14757 7075 34280 56928 3294 19288 14152 22046 73222 73084 72992 58607 20327 47894 6345 40990 53822 3418 56252 46471 14093 22352 21603 80163 36322 52103 45460 56438 17357 67145 72267 50843 3895 95797 73184 51249 32687 3294 85299 61172 77464 32303 37226 74759 50929 15806 52326 42644 14035 42727 62176 49660 98849 81630 69024 91735 74446 99082 52530 29648 61581 67586 14430 87003 44358 39338 19905 26919 15261 57935 2441 14883 94906 19244 56879 57641 97630 2850 56563 34373 25162 32626 61038 77981 59237 81707 35381 42523 71604 57890 2268 84625 80076 23769 5303 45448 25867 81615 34636 50586 7766 94061 93423 78054 54051 23882 34040 61054 9031 14553 63741 1847 94091 89317 78271 72856 59907 84154 73770 41203 72793 930 28569 91719 71464 55221 13615 90148 60790 52960 48429 1722 98298 38263 66703 3791 46466 88241 72787 79177 79585 35101 91339 68909 74040 23425 75234 36835 97921 26586 65489 64593 77802 56464 75058 81863 52491 70964 11596 48589 20179 60274 43141 14691 30765 21832 60978 39296 20712 31268 49117 85256 93118 8717 86573 43697 52680 87346 47295 80775 52242 71889 244 28227 14237 99604 62989 79373 48345 65823 38138 60016 23954 12420 28456 54582 13273 25461 2493 2128 29337 53371 24357 47441 94531 48200 79964 46596 17903 43406 88333 45835 71051 88779 63148 74230 19333 53305 75654 19742 84025 54888 93164 7872 69921 10551 8487 29273 15784 59256 83282 22272 54268 76874 31409 3401 27631 67085 46031 8489 6014 47186 1114 56653 40097 5938 63485 12778 7268 56624 55126 79426 94271 88823 89824 6425 28125 90702 86068 93505 12137 80348 26952 28388 96779 47891 92305 3065 83578 47083 74322 50675 58160 94717 24834 56024 65383 12643 80930 99773 86019 46386 9401 24060 85706 10722 55374 81070 24122 54981 75177 32147 98683 74625 39014 23443 52472 46296 60030 22647 61919 65421 25746 86073 18353 18535 10760 91428 78700 1616 77864 1713 96052 11396 9780 85578 45481 87661 35493 28148 7430 88153 22929 14798 97395 79437 73623 90191 19174 1582 3811 80330 40409 57258 37363 74216 60336 29295 54437 40716 82726 57644 72551 64487 81059 24747 18971 25481 76039 94231 98074 29612 93193 85231 40271 26935 29564 40302 63163 43250 65698 95966 41084 96837 3560 75636 7822 46988 5146 28800 67046 69904 29353 95054 5807 39456 10501 10200 923 23874 25115 31783 65202 52487 6900 39468 52409 71822 96682 32462 5216 73211 82695 7029 45436 86568 87889 57449 68068 57619 63749 6569 76370 2866 70101 54625 62304 34534 38758 90580 55587 78713 78362 31114 55661 50838 7909 36773 65631 44580 96684 66958 68279 50746 43240 82367 62191 27638 99555 16853 73448 20465 3198 13588 65162 17186 48364 21665 9701 24344 22083 6993 37795 82323 44362 28067 57648 2250 77888 84349 82893 64952 57934 66016 21610 98199 98059 77758 42601 15354 25943 33806 52311 78309 68049 69933 80444 76176 56970 83652 16515 35245 77330 47571 60354 90752 43162 78333 6414 85497 47299 35720 1458 58269 70388 49362 49244 61181 49790 82085 18319 40801 47831 70693 47752 33303 47974 66270 94105 30498 91533 99632 23832 48398 3452 45433 11333 37595 35511 62622 6933 21130 61139 36790 13303 97659 43563 91342 23561 56269 23096 34828 39004 22401 81384 97884 87287 24557 85947 36492 74423 53394 37590 23688 94637 7052 97687 47530 39889 88095 59068 10854 83711 34361 47210 33994 12029 49434 64019 7102 22818 41722 63827 19232 60329 80683 64975 72715 32180 36377 80003 31032 79422 26851 79580 17427 78281 85845 72697 35005 9052 59986 20982 99155 98639 21659 99015 29010 11173 31226 12136 90998 8086 94653 36595 34724 23296 88459 69754 79 4872 47601 97286 18887 46366 28177 17514 12404 9736 74669 26300 77158 56593 73981 15673 92411 49530 68473 97643 94616 86224 57732 60004 13230 79691 45774 53924 54051 99707 39298 89727 82384 51573 68249 33031 65798 31702 70716 49288 67520 20466 99486 11706 11159 52405 74793 63982 75828 26480 28069 42786 94683 96402 99734 89551 14652 72579 83272 88112 64079 1311 51852 56032 77441 90780 95589 11566 85429 89129 51258 69805 65707 24198 96015 72294 51578 53030 23369 54155 18672 60805 28779 96957 83654 49841 91631 77154 88894 85860 73563 53778 95637 48221 69570 97916 51277 65487 80823 83465 68948 31107 24869 26288 32065 9481 56236 24988 45508 66992 42268 67759 4858 62302 99315 18432 54187 80526 89280 27600 41787 99719 80105 64578 56124 84400 63179 13229 24456 29915 51740 7919 72511 73558 59152 65458 48057 7220 10756 99129 42980 85829 30417 86927 39558 40306 29454 47622 16479 88008 95095 30792 40026 87443 79333 39838 66469 43100 39939 25853 34501 63595 80454 25275 38886 88907 15731 1207 52230 13955 27046 17645 41200 63949 87162 28118 75779 35634 67932 9930 7467 80961 41222 68682 52512 55281 60897 79666 52569 47042 4452 46312 22084 6319 75966 8477 24786 33917 65546 85900 60160 19570 37799 72226 2437 14202 87350 88224 35875 57900 11241 36977 30756 91074 87697 78856 40083 25816 40326 60424 22044 94971 50772 36296 55385 73802 17967 77768 35543 79830 83081 38214 43520 81332 58503 11379 39481 55327 60707 35571 74491 80587 2101 63280 4842 76657 95207 86107 62862 39700 48902 79072 4107 38407 36683 27071 83366 23996 4654 68845 129 2015 51270 53517 75622 3777 31707 67630 8074 14150 17568 77282 75930 6504 49349 54655 99707 28745 58504 8526 44467 57144 22867 38912 34957 80224 56523 21064 89995 43087 24042 63191 22477 66992 67959 70375 57287 13004 82063 8512 63326 94464 1765 66373 61453 79807 92048 39583 24560 97945 30385 61694 88302 90963 89291 62135 71736 33806 30676 40214 6080 78253 44834 62957 69899 86112 75102 17702 67416 32718 68632 94668 14244 66010 46793 55866 91526 21942 98211 83742 34496 227 86525 47048 59781 55899 59725 97597 76106 31908 93439 80628 7870 92889 37494 58074 51395 40723 48919 65628 49705 53382 66919 14663 89101 37470 40612 80153 12343 97241 58681 82406 23736 95884 77023 65171 5804 91668 91532 52203 82424 42345 70553 17905 75582 51847 83801 28116 85479 40444 26231 84335 33407 97596 26573 6657 65502 96351 9515 11315 78431 76944 36816 91208 60329 60686 70241 7967 70675 21268 6268 17785 44565 45901 90071 87867 71348 97178 4055 52376 23992 19345 58109 66072 93096 74560 42553 69913 95426 45183 37815 71053 97867 1692 71505 17228 90906 83466 85591 82689 85839 19328 73548 9413 79837 68839 79856 4016 1566 12597 38833 97872 91445 41006 45677 79239 94735 93209 77365 67890 17462 51245 91404 48872 3961 40687 560 60315 7722 6091 91852 31562 23863 58359 33981 31909 14827 17791 40146 21157 56888 14185 25950 97527 33774 6932 64397 87701 95371 91681 3146 4250 37130 29148 54485 29106 42710 57235 37454 70040 95578 10164 66570 48795 87458 4701 31122 40588 29698 96635 3158 31400 99676 24147 44914 84723 73738 51692 68191 11192 26292 27613 59504 10687 90406 41703 73918 15657 34005 4496 1487 57796 82149 14141 45579 58965 77079 48244 43225 66535 6635 30476 11887 15452 80922 488 8804 85841 29078 53101 66335 98544 19515 67872 54831 86749 78802 23052 55644 83730 21128 24357 6649 74389 91572 71912 47364 75062 9545 99009 93183 1295 68841 44009 59439 74480 14905 14183 76162 23080 19005 51411 93878 64165 39976 74148 63660 24296 71187 31723 54789 46183 38854 27740 77269 90196 74130 17477 98248 66100 25789 62069 85666 19708 17965 2703 35764 63225 60738 55479 35581 33450 46461 62674 17742 15281 73136 50802 98551 36883 70542 66439 63933 85851 46510 76343 6884 48462 60650 25479 17079 62150 61671 11831 23659 37323 14370 28399 43550 22997 48079 80593 30272 40831 14109 94453 61524 44543 1990 21642 19726 19043 20960 57218 22680 46381 44385 3651 10985 78485 24518 1566 29995 40884 56915 33031 2060 90652 7969 17354 52384 3309 18448 74020 4862 22245 96690 27939 30492 35948 32165 83042 93563 74198 70815 84287 57 8743 65025 127 10487 4062 2217 31976 12851 63356 51933 66660 44690 54543 34486 81349 59451 5444 36541 95043 85158 56805 48335 32127 26106 34009 8523 42075 44316 19056 92757 38457 36599 1067 82670 882 31030 93020 94751 80725 68606 87465 95311 91193 57788 33489 68642 96993 64474 32213 95139 72053 48607 89423 33874 83227 71549 39909 38782 34620 57488 93314 34090 45245 4719 96057 61464 86342 78160 58781 1887 28869 37725 4424 91497 74705 31519 85968 63703 77070 71354 5339 92701 39517 82510 76341 58947 11309 11372 35152 79103 14504 22347 88675 20890 16861 24753 95290 67034 83960 50429 68734 65685 31312 36727 37056 66473 87189 7279 12074 57730 94858 4367 12355 21977 26323 12974 26274 91714 64047 41 5739 94887 31230 20470 45662 97089 93986 35052 47763 89705 92163 53817 39560 93268 16776 31958 22770 24361 59842 44648 93228 81721 29574 59348 11030 73885 39096 18477 58544 70571 74653 26478 90885 56394 68048 26740 32665 72974 29548 95932 50283 55068 17007 77463 97357 99738 14213 53871 73093 3865 88878 69573 84120 79056 24222 75147 54507 86532 43508 96586 77399 92212 41753 70995 90223 86229 57496 29699 93740 50211 51885 79986 11695 86082 26440 10134 35288 62196 21318 87552 78561 72425 96004 89714 33508 33460 43418 87504 70280 45291 86904 55886 29362 31807 28610 95854 74366 91506 43734 20343 3337 31787 16136 11125 5003 2804 40128 20176 71185 55129 60847 56147 49827 35506 7633 83777 91462 52850 67988 97433 28761 24635 5571 82960 97236 80949 15972 19269 13764 89954 25547 44052 16305 73014 71114 89573 60262 41604 16659 74115 39359 38825 83215 91340 6625 8199 8242 96991 86021 6106 80607 64915 24279 17984 18871 58202 78234 44785 67489 13956 62556 16739 35205 81323 33792 92014 75099 77855 71795 2685 11575 77449 25246 52689 25358 13651 31910 42029 16408 10630 26741 77754 63310 72715 28533 69266 66472 45351 16509 49816 20983 65061 18366 1414 21884 97851 23290 46193 78158 8309 59257 25509 48308 63262 8936 50110 20467 91194 58576 77530 45050 68757 18405 39216 24161 49464 92419 9587 81871 55070 18504 94479 59996 94941 47173 78984 34050 60971 43298 31151 1518 64476 69221 7032 80558 73097 89769 76159 44539 10351 97211 54480 52651 40270 18536 57514 17098 37229 26320 7888 22220 98185 41875 9740 11363 10552 31463 30265 43838 71431 41093 79671 65486 60082 24681 69394 38889 2991 37049 32365 99918 95610 40257 80450 28869 63139 51650 79806 53587 31495 8293 78300 15655 6043 87591 62845 57666 20829 43682 14452 43859 80445 81779 22377 52559 75032 18411 68265 11340 25488 14056 68709 53493 39801 13937 95586 3869 24989 56439 29300 76133 77301 11961 74430 16334 18082 67553 49050 84282 25405 80163 706 63370 99566 59979 42175 69327 39880 25504 59004 88020 17657 95087 91540 54143 17071 64426 38467 29454 40384 52686 40569 62098 67337 60908 28535 94886 12281 39213 4742 28531 19863 23420 62786 43609 82754 84869 56560 5456 43501 24985 94638 68292 61293 47459 17626 79172 3673 21751 89728 84689 61382 5537 63699 7578 6968 26030 19187 95319 63209 55534 38949 49007 25012 12476 40327 99167 31930 54965 39538 23991 14172 52639 56501 1905 95768 57091 14714 95550 82073 61410 92185 75620 45256 78369 19008 25130 34339 66664 15742 93537 50110 36929 18386 72919 14848 64477 57427 23103 61478 83886 52460 70040 82811 27777 623 24869 67334 35204 3713 78217 29639 75628 90740 56750 72528 12046 78543 35564 14449 25934 68740 49310 68758 91559 36052 30565 86254 15642 69905 61755 35452 47942 312 435 26044 17100 48537 96603 44957 86221 34427 79812 78220 21977 51398 3474 28889 22172 56997 98577 97820 26757 49630 30372 86905 59953 53228 96954 72768 39712 41618 71093 19249 55925 2692 55625 32905 30841 92546 20074 85934 57288 50481 7330 39705 63227 30186 40027 88594 95458 40999 6955 6962 17370 64264 8542 37363 37785 38249 2266 94900 18875 39286 15149 71708 33027 73346 10465 18165 19900 43554 38918 31593 92936 22601 78347 15180 5864 22656 88054 93680 40074 38087 60498 97699 55222 96734 18001 28313 36865 20276 20267 56180 50745 74877 37939 22184 13581 13247 877 65996 9311 10988 72813 90789 7930 32813 570 62090 12326 34739 45971 29828 44148 75201 93731 57899 84086 74280 67128 4033 84436 41718 80081 88646 18905 44793 34286 92545 45879 89652 70406 71424 44336 95921 24860 85068 83768 98866 85777 21339 91878 83704 24337 91796 73648 71190 46542 98034 61857 51828 71978 30646 9371 59135 7934 76235 11958 12008 58937 91469 53826 46672 75622 26214 57043 69424 98705 34446 21902 72341 53052 51302 60576 99614 7434 36838 80827 14443 32261 16486 17386 80433 29468 26837 48217 38465 44105 70057 76419 89800 16468 17355 34356 51437 59632 57862 78389 59389 13002 92677 79300 69747 92820 22100 45230 17706 9901 99639 27339 95540 14685 78499 17057 96148 69439 95657 4332 15197 2062 85773 84655 35258 38237 69361 95467 30254 15346 36826 20421 93104 25877 45178 28658 58247 22290 67833 3626 76833 47142 56611 94938 27304 39965 56673 44454 14682 72862 70584 99141 63815 15920 52584 21145 19362 27435 60088 20667 44534 89669 40568 33606 66063 5268 20496 23888 68758 8552 80392 21760 23276 53683 69570 88302 15931 97745 45548 50680 39024 31497 93840 42259 2194 53696 9929 68174 63876 95450 19779 1903 93287 46570 75192 55777 59768 23746 86084 25731 59520 60920 3580 80968 47955 54962 57115 41707 36533 83960 79905 50890 49713 67100 14661 47071 24777 64437 69887 56444 98432 27861 73819 80609 86910 18854 95102 26000 45297 41714 33789 6405 79496 51901 50529 36929 35284 66268 58243 65815 12690 46062 38999 3784 86372 88905 71780 81410 13920 4014 96713 16594 44101 20508 27696 39873 34585 62749 19756 23209 3530 76349 43290 94776 73684 11471 78843 33711 12147 59987 72696 61400 37743 62711 93296 75584 91183 66058 91318 85268 84279 44107 64096 57427 46377 95711 5622 77331 45666 56030 79304 81283 80996 47378 73249 80193 45327 52070 77292 59479 71557 1415 69175 66411 79330 26882 13078 95664 66556 10458 66736 94809 11483 9418 33593 51605 52068 55136 65431 87820 39135 31272 87576 67429 17099 54819 25379 79364 9290 54746 64544 56685 40827 91499 10588 71275 23766 94520 83915 20866 89050 91819 86297 97291 95920 93171 96734 82106 4436 80059 5566 75567 33387 5828 67024 1103 28727 5921 59876 95546 1711 78266 74394 65644 96641 14789 89260 63007 26842 28738 87945 32293 94724 77472 80556 58079 63582 11504 89335 39159 24953 33624 69881 41814 96666 35420 56541 37503 5077 13806 64527 3550 58476 32268 66488 24240 14109 91995 90924 9930 57565 97592 92606 81714 15307 21737 81443 49920 57189 78111 62062 46972 47236 75940 32011 29524 395 70806 81302 67663 12746 92328 36673 88210 78314 83840 37671 58664 22187 97096 32972 23788 28246 51502 32052 76107 28925 45624 60802 59445 72896 93771 21028 20720 37279 71559 25000 9108 28646 46456 50385 18402 68938 34793 4941 8555 37809 619 31459 2115 43795 9132 73819 55140 27034 27626 29586 90934 49314 26230 60675 20822 15003 74369 25397 61008 4071 9434 38848 25928 28099 35696 11737 96880 92111 17047 2305 18374 25289 70768 56417 76979 29546 87313 39907 68715 32526 1359 61141 63842 75311 14056 49625 86193 36971 65750 83896 20434 98685 70736 14211 58555 82602 38735 79514 87536 388 47682 75127 6207 77574 92140 42344 64463 36421 89342 59032 1045 54803 48758 5858 69386 53581 90436 52654 96518 91730 46452 51183 36814 21157 31396 15913 75389 33378 70648 9905 85843 12010 93469 23347 58151 92599 23984 7334 94282 97455 34972 7522 46622 639 68981 73400 43569 6178 18360 1515 11499 98811 40228 87297 32930 74628 59617 50795 42522 76877 13848 46782 56633 34582 98149 3394 5689 89508 43016 31635 56249 56011 45748 11562 19871 48318 36835 86495 84310 85431 45412 8037 45656 99631 81823 21843 55774 94624 79363 5081 23835 88505 89763 20183 94903 93680 79840 99749 35462 14213 58011 5497 31474 44430 50616 24208 80768 85650 7638 6266 41304 84584 32760 90357 1594 91979 90357 13524 35540 16282 5538 77020 95265 49765 14618 89426 218 73506 79793 77593 79422 60209 84921 19734 89391 63496 33101 7273 57423 88357 11052 13630 8370 25834 52076 39529 40015 27836 34711 44227 94849 83178 21931 45821 53949 1905 131 70987 78383 80419 66106 43640 53785 2973 24843 55039 61677 70758 426 56767 62807 61785 33234 88193 61735 97131 39584 40545 75138 48668 64723 76612 27471 75133 55894 31453 762 99772 2460 29892 91441 89110 21602 41277 46901 68617 11349 55354 55086 46974 36919 15250 20540 40145 7146 63513 62886 87841 80947 39126 20138 10617 23583 85275 66718 40352 13157 4600 4074 70486 80212 48048 22690 33164 94493 54888 35275 43113 99188 94348 18583 8151 77034 20725 66153 65893 84772 80150 42772 69742 62695 32541 66121 34294 77873 8266 92132 4415 5987 7980 793 58384 89446 34965 43557 85463 95807 37944 18384 94745 33716 87511 23525 90469 84607 75391 30143 56300 28151 5768 18539 35386 10580 89942 93674 76256 87504 60315 6221 59914 56201 91245 52319 63968 29995 4535 65626 9485 56017 32519 28527 57438 95311 27421 15490 91777 94624 89456 68798 75702 13283 37890 75970 52944 43023 40541 64476 38940 78838 96780 22118 38496 87627 31933 10784 65476 95741 94982 35049 1635 39967 41118 81011 63401 49341 77158 38933 99199 32872 25433 63486 88584 15876 17632 49507 45539 6362 79559 17315 26056 86221 59610 75736 93109 59 1974 46148 98972 84426 60044 6377 77012 45073 56540 81722 73479 78475 15534 33991 15761 83673 57116 42824 2899 81801 81699 11366 77314 84998 81460 381 95921 94661 31791 68226 43538 50052 44299 86226 53150 64955 5274 87263 5034 57834 29305 35685 70016 85624 66189 85081 36956 85457 71950 56350 38499 62844 10283 72335 10677 56594 90954 89077 5193 93802 52878 71803 60879 45280 20922 46408 88696 75933 78482 30729 79541 51214 31441 65172 11113 92381 7749 1407 86348 10118 9558 85939 48469 85820 20998 60038 70202 83068 79749 39560 63308 16059 23301 75827 58089 76324 75993 6507 68636 28810 34338 33272 67657 57007 51482 42256 69757 23062 30367 73254 58612 80745 18901 74648 45853 4070 34754 40969 68914 69666 96033 4859 58406 74305 66860 80307 88711 33207 78733 11916 72313 96687 61002 84943 93044 68721 84497 13954 52407 93457 64175 15736 58713 31277 39440 78518 18018 79445 78279 79707 44428 30959 60765 28429 13387 5176 62513 41584 94523 56515 6558 21120 53311 47195 52436 92897 35100 6415 50906 27207 66313 71966 52889 30453 89229 53740 46127 52579 52296 6788 65554 80243 5533 46266 71605 27192 28508 32125 70789 41142 95084 15470 93716 4038 92951 23038 62905 74706 69684 40327 19136 44103 94294 7715 89619 4111 31705 50085 27693 34632 79946 93555 37282 70202 8209 86512 76840 63874 19248 99188 72671 47844 24924 30625 20553 33908 4494 3791 26515 30001 93018 55250 61398 65830 51597 95358 55818 26327 25783 78403 56342 80811 12143 42970 59584 86646 49105 51261 42513 78089 63114 47297 86127 92195 26022 13411 91427 24388 2222 68099 19764 34372 97190 37019 28922 73486 9455 68400 50685 96032 81430 52142 1105 56777 11258 4830 70165 29977 96814 29666 41591 48271 28175 24662 15290 53043 81739 48026 75415 90494 29824 12804 67645 10061 21188 661 43149 74059 24365 3255 88943 45598 19550 67412 13026 44939 71560 26550 8524 11452 5623 6550 85685 28117 75914 57314 18554 40818 44698 87751 44331 65179 86550 1633 19478 44866 70484 14072 78635 80463 32093 36828 66404 8890 33904 72442 65152 85593 83243 91543 76596 80265 92494 3931 13222 47424 46995 32956 64507 18726 97123 91429 50306 98063 92054 68653 97076 14679 56230 86304 34883 10314 13924 85157 63886 67732 26443 98649 73445 61644 30497 74903 70114 35274 66032 58014 74954 43462 48966 83281 74955 63002 78792 35633 92363 85019 89266 35280 4289 24612 37114 46508 65441 56243 6133 78140 63696 30357 13409 95984 90630 67090 39321 85943 21409 86836 37019 32574 8736 12334 65018 96634 88306 63706 37667 20886 85227 41345 95002 5358 57828 75051 45813 4420 84744 80091 69625 84778 83205 33271 58363 67643 19322 68344 70706 57856 25283 65919 32481 75645 97906 15926 91931 42926 15790 28023 86347 37034 54502 45900 55040 98541 98230 71017 1845 46810 93167 56575 54893 83862 87212 17763 78062 10334 52362 50214 27536 56464 85924 50747 55654 8810 60466 46105 84038 79569 90786 46324 33620 52658 70512 14532 31197 27994 16246 86798 64695 63098 21788 36305 63063 13597 34123 80822 24878 45650 13131 2798 35032 84645 46564 96257 9049 41911 37723 95693 60055 59863 87490 76585 32315 50116 95549 31079 70258 63461 86527 45048 98805 87050 85997 53610 11806 50625 70822 76597 76803 17765 59906 64080 68891 7990 2990 55150 44953 3484 47045 5179 60990 86122 90973 26951 68585 89028 4462 81426 3125 15794 86809 96002 2347 29333 58193 15382 59935 71948 40153 7067 64020 71196 16825 51117 20129 84066 88775 36942 53085 61729 81490 82090 69250 37498 41726 43792 33225 67836 78196 94855 40680 82895 61600 26511 67772 46741 87562 49087 93810 69424 12600 34106 90042 86484 51238 32916 97408 4183 51141 81369 67581 66396 47739 4691 83037 8056 28917 59513 25302 35878 34942 64124 86371 59639 97921 53608 25338 49035 2005 86701 25900 78882 66947 80269 2400 54239 50606 53205 79865 90105 96372 74390 16029 37691 86685 30804 4976 62067 53384 7424 34136 87095 84876 26595 26599 52123 78518 75149 93108 11690 10052 44971 45438 15155 84714 11911 48460 67094 8336 30862 47864 99550 7615 14966 81206 28069 51937 70634 50045 59012 66296 24576 91989 50683 42393 95678 59376 17803 33025 17303 54402 84813 14098 36201 21067 72768 14394 91007 73825 63578 6826 50048 43518 16001 81748 28031 58120 41915 60333 64405 96150 92927 60605 41294 9596 34356 90502 4311 13506 21396 91128 92940 22927 315 34253 84016 38989 38650 16180 74403 25121 21601 11682 16707 66322 6146 55862 83497 57035 97879 68022 67925 30880 21753 80522 92276 50970 56795 38603 24675 63253 88538 41407 78968 35820 56503 39117 29226 39563 48214 11657 65851 77601 89413 47528 4883 93978 9476 24054 77027 17758 41813 7880 51527 57833 25576 55181 28646 53689 47789 83122 66442 66346 87468 77542 81664 19025 76308 69119 4037 84012 12945 6669 76719 46677 94794 2696 64198 37868 52165 43704 92061 7666 86763 30595 14506 85736 82595 91768 82259 8419 86871 21804 52857 87118 31797 99049 72675 11830 64089 47587 57691 29247 50609 6585 22679 93389 96279 23660 760 74981 40407 85996 95019 82515 79187 93207 77442 30922 32606 36144 81430 54045 84573 21456 21320 54909 46645 30420 98146 8544 30835 43911 33088 77448 41420 80178 67510 38547 5920 48648 6692 37826 16617 69541 27058 77773 10285 41183 50676 12736 96290 77972 38622 63870 97821 10872 69812 17367 17173 38555 64683 34740 12855 39882 73546 89443 50074 20440 10347 28008 26407 9008 46201 85307 56537 5969 11087 99626 83281 13659 30181 11364 57542 48278 48687 28295 77816 61086 80430 82362 63863 167 33912 91404 59178 28103 27402 50412 10272 7665 89287 91188 47163 32034 72788 69498 51475 79928 84142 81700 78794 39807 2318 18637 80345 72325 45765 16015 92791 32274 94887 48848 83695 60768 94452 45390 59841 24884 382 23815 16449 84254 200 97019 57141 36277 41988 87221 52324 98068 41871 21234 67625 32238 77445 69157 85575 7847 11517 82437 24131 61642 15232 35054 56946 47032 37979 30687 93003 52230 90609 49563 60037 77011 3401 39943 99393 12352 37352 95567 83621 56671 73513 67307 97881 60129 83219 67297 13944 1653 11324 10036 33836 6621 949 51988 70611 30758 92977 30005 22476 70474 74156 62418 94577 68463 10283 88260 41601 40238 94276 26971 84663 91727 94779 27894 60909 85190 20488 25861 44222 11921 49325 86738 79180 47111 42530 10241 51806 84527 86220 97799 18855 21904 77505 36306 41038 75200 13544 12641 73012 16134 70181 40651 91629 53388 26376 73661 33284 92234 61050 46963 99802 57320 93017 13754 17532 40694 74088 79091 36098 23182 61901 38872 17186 43338 85489 14720 89300 16711 86182 8946 77134 53437 65692 25674 89394 98896 71055 97838 72296 26546 47978 37260 20033 96934 56558 75337 63901 27163 71209 45107 7580 27206 67044 74469 20656 15492 72623 91039 65045 29781 77375 43432 67176 3724 56489 5965 17825 59255 39470 56410 62619 42881 20319 9304 81291 18006 28845 17627 34097 36999 35259 70154 24354 78018 84699 30460 80123 63895 69341 17653 99911 89239 71128 56388 23776 61901 79764 94155 36710 88676 99860 84519 98138 34387 873 59668 80417 77026 89026 99375 48822 24186 12133 78347 72117 68891 8317 56772 57820 98664 73594 18481 67870 80962 12298 36235 45121 45286 81216 64497 87490 17157 69604 95641 89247 41268 97354 64901 92138 54064 57243 57124 69385 42018 89067 55684 31098 94339 47700 47236 85614 44233 49970 88095 31238 14452 78719 32249 67253 42059 46034 15422 99989 81416 44701 60848 9481 64533 7922 8438 52732 92315 25908 36079 48313 37618 22649 17980 1494 5933 23406 90907 23240 70679 58138 72680 52457 82196 10247 65757 33043 93689 57860 5337 31427 47688 63061 92023 17372 40159 57206 52676 19299 83813 27500 90496 41171 25989 55304 89476 74085 75123 66131 85203 93711 59204 5568 14727 68364 42207 66465 94771 18372 64698 70838 16852 59603 1929 28317 37398 98295 9463 20520 49521 28714 19153 60453 60967 47005 53631 15967 12080 59996 18319 62482 79592 58345 97110 6258 93256 56607 51581 2098 29256 9916 29229 90629 58531 91989 96190 58999 28135 58133 83260 18055 70433 96912 98685 84796 64253 3144 77324 29641 91578 31780 921 92667 26691 7915 17300 47576 99701 79496 90557 98794 30970 41789 34240 7016 96587 34954 35948 96570 76133 6895 85111 74218 7243 95242 81576 67682 42978 63901 1809 16691 70583 67082 84715 34717 27559 53307 1523 82461 96192 48918 85885 90887 45971 39217 68784 21141 97037 69162 959 61316 8858 82349 72183 89678 14551 62573 10044 83471 25165 42907 49971 70070 43958 83508 15842 78280 15778 25529 66657 43245 49616 15973 58011 81036 23640 36675 98295 91219 87424 30210 16415 85684 71462 61056 94629 65984 68289 25774 82171 97658 58000 94545 76634 65372 27914 93205 9385 16858 80301 32107 55829 63635 67835 66254 19448 70416 1657 39388 49583 99168 68893 80696 83488 67759 3856 66332 51614 45938 22552 70978 65653 35007 13224 57692 66441 16547 44213 94918 1988 25701 16792 55905 38687 35069 38774 61432 76001 92612 63163 22124 76078 79845 22037 41577 74508 72113 74338 4771 52642 56970 46260 66068 86213 78322 76541 59474 53418 75779 79560 53977 81025 32509 57557 17083 83027 70328 57997 28879 79432 36675 68668 45088 10867 39277 95538 40301 12582 5560 6688 20918 69773 82044 32033 20958 17496 10492 4415 43195 29826 76051 38075 21856 65339 55445 22240 36478 61127 90571 83831 76694 24939 75002 74663 38883 13960 11839 23132 69404 58228 73175 61277 41363 96030 77841 85913 3404 14129 58222 75609 43398 29611 29899 70885 93972 70408 63672 88159 62283 98677 33278 53321 50127 45267 32524 33946 13596 88994 40853 68292 26500 1868 39553 46459 11830 35791 82620 8185 17455 68124 34019 26230 90898 25148 80636 63927 21916 46321 26877 68354 80366 48742 49175 94444 63796 16665 54980 542 92958 56848 72891 29614 48249 5294 68228 6900 3173 7486 950 77872 1354 90820 594 32630 53944 42739 86228 93980 27346 44283 65954 19047 46608 97404 63882 68795 92058 21878 86189 25635 99568 58178 45133 17707 78662 78213 12610 95626 12749 73795 43736 43035 24465 14596 88975 86420 93082 81748 95378 89300 97916 55382 98758 80171 47114 59855 24842 30716 89751 92470 39038 56483 57651 13088 90622 78051 13698 26236 89503 27917 8013 2931 29972 43047 23289 25342 88118 98568 80686 27116 77387 27318 26959 92112 71640 19222 14800 46545 25701 71637 85001 11956 7469 91144 11438 76590 2309 54766 26810 64260 23575 89155 6848 80085 9441 20 81121 39164 11137 50669 28820 10597 11042 23100 12451 47740 74310 88415 35701 38955 59005 10984 52334 48353 9086 18535 16948 30533 88758 34373 3468 50740 58398 88321 59673 92576 64344 16360 73133 90277 9980 26709 16514 57383 62584 20551 55 26277 4969 68588 54585 74113 86648 73234 51851 37197 58953 18451 54794 55591 47574 12489 68236 70189 65544 30647 91944 34308 42696 50746 51375 34257 78808 1850 92854 73573 89214 27747 80782 48618 85707 79732 21704 94512 4607 66267 12995 49608 70083 71965 49701 40763 10676 79893 55233 91461 77333 44692 79860 33832 26029 82710 37895 33937 82064 60739 33034 44302 85996 59289 17472 33457 21816 75142 5511 91334 84126 25432 16717 82124 62731 41828 17669 89433 45033 46241 68654 38674 31256 73153 52692 70205 7116 86011 61989 76036 97698 97190 87917 52597 23326 58700 90577 49907 9127 62794 17138 86320 8471 35077 90381 27703 50234 83415 19920 65111 30486 12063 11615 84977 54696 60602 8514 53924 17771 84827 94610 91296 7162 12504 26532 64614 46324 46977 21090 30732 6850 78292 82457 8741 38566 69314 20080 84172 1944 35319 13681 41134 75943 62183 12262 55663 81681 80601 28510 83480 63277 8949 87949 52511 82846 33195 79266 15536 86610 5129 36026 41144 57806 87308 9089 35359 67052 343 94617 26564 43852 98891 32002 63 56632 87367 1842 56916 83518 56892 63431 65170 46640 38106 45753 90900 73075 42182 50160 3644 4666 38755 78973 22718 52928 13770 45217 55589 2633 25820 49957 22083 72665 29907 32808 4055 23704 28693 63266 23529 84598 94888 20686 89019 70140 37296 53213 52683 71287 87722 9306 44481 47173 85743 39482 61528 8172 76883 7030 89309 92629 74712 2258 88361 85136 50355 35806 66314 7634 80204 24059 73768 72950 56660 76951 25379 69859 37931 37381 52157 89871 51993 84858 3371 90844 72267 50215 98101 66519 279 71331 2919 42192 96670 90724 70521 13917 63008 25005 7733 61160 92794 53319 2092 97739 66025 63217 20570 42403 12422 71757 65862 11549 69851 11438 20166 83505 57131 38227 1447 50491 14003 2829 71932 2144 25303 86546 38143 52266 34548 67785 92023 42467 86262 27588 48541 68827 52039 47699 67643 33417 75585 31920 64354 62833 27776 54759 82601 84976 21947 81882 80935 24606 49590 99732 31281 88959 87461 75064 30507 13452 20540 36817 29231 66339 27574 33798 5937 7795 26587 27785 78833 53946 66546 11645 95039 75017 38558 95246 9800 68993 15363 27266 17516 12583 64692 56181 23021 44555 52347 65443 15016 44705 59138 8057 27195 64488 63627 87634 96138 72947 46398 48920 15535 18354 45781 68925 17226 48471 93430 29593 12323 3187 52343 93755 67060 23570 91175 21729 99086 54107 94466 9767 69612 82312 46045 42816 54723 4098 32080 21557 94266 92010 48213 74323 60299 11007 41970 99796 94655 59781 45342 64931 43580 28372 69915 32592 75934 60174 43965 79279 78141 53154 29756 54216 36826 86458 96443 38541 36471 180 13111 23350 75580 3956 85988 23099 91709 43545 860 84820 44988 69147 51103 40392 12638 63991 78548 33689 45072 41623 80236 21986 28959 75967 10437 54772 17086 91443 82386 15382 23051 49611 69268 44049 63554 45970 72453 37609 62519 33462 20340 32993 50551 71066 42962 88998 35389 98036 67402 72976 45573 58929 47558 8644 84569 57815 62198 59175 55755 94670 87207 62253 54439 62189 25836 80308 68445 73507 20921 50679 67118 32345 17095 89308 7585 74781 94571 32104 31184 71966 21903 35587 22818 53939 20065 32868 1793 72448 59610 30401 76419 24992 47522 65082 75481 97775 4543 14855 56071 26018 74532 3211 17655 62319 65482 22012 95194 69090 94604 90125 18745 51404 88119 1259 24959 21314 12540 28923 88918 8559 27404 40731 79226 57006 39339 46441 13747 91285 10316 37744 61803 90603 73691 10440 12271 88304 99617 64468 97684 88813 97439 45145 78828 15123 54919 34003 1898 36957 4528 90995 23057 94980 85751 23938 41006 99242 96133 58309 43518 16975 88445 72688 40280 85609 84280 42167 64076 63994 78445 94090 85045 73569 29967 77924 99402 49782 82747 53851 38768 74525 62381 17053 30785 68419 5798 20096 86951 29842 29812 94149 59172 38641 53582 85553 9162 48164 20880 99001 15230 90119 95930 86605 60604 16867 37560 32712 13624 71294 45270 28598 73341 92222 11004 40910 53527 98154 47701 95390 39009 89192 14525 34193 40270 92217 58420 60157 87440 28105 19782 41360 64110 98149 92003 44755 8183 30352 15346 6700 28813 14013 21571 65538 59726 85760 13874 55900 74801 2083 27724 40447 62528 36483 77450 34775 26317 89821 97026 8589 80201 65439 29218 30523 2747 64152 31495 22145 51914 34017 90727 65393 78219 95171 90757 79370 48829 54726 21095 72309 47922 43138 33927 28563 35054 28403 89067 16214 87231 41054 91958 79878 88385 22026 50137 50513 56110 13786 70577 18086 61934 29590 34327 47001 61706 86380 70702 1 50899 41847 28428 81195 76175 48186 69895 4512 34938 2222 84747 84096 64941 13925 10298 23065 54658 26174 3791 31038 71921 93518 62117 31062 42022 88950 73936 19096 48863 75921 98215 83333 54466 55753 98948 56281 34083 83814 26962 43659 49512 6477 6784 94635 5088 34904 90161 78820 35458 96062 33723 1587 79508 56252 34554 66863 19829 34954 75152 51167 69749 30588 76749 43613 84135 86194 87588 46474 22197 43476 12276 3918 11030 41453 18251 98660 13829 47439 31023 28001 85857 19847 42001 17912 15415 26215 78125 96439 28370 83301 66313 83821 9966 15842 47120 88196 45916 38686 13986 63001 3070 86983 90562 87978 65956 64392 40972 63753 69132 59033 55534 45039 23305 58559 30260 29908 77206 60988 48713 37909 13809 9352 84101 99505 49154 32137 83543 86546 39207 56912 73292 3551 712 60746 54718 48966 8008 64406 14927 37701 55437 8549 3101 84839 67943 21146 54094 99303 59456 43208 40177 71660 47997 56802 41280 66331 94006 32655 76869 55029 52396 23166 67345 92812 17497 56885 93017 50426 38871 95152 84652 45935 91531 99886 98883 93812 13777 25381 56274 95000 74328 29112 30074 28408 58223 83137 43951 97127 19170 78569 74099 55708 84763 53383 44189 51064 46672 67924 89565 38863 58208 28724 83522 60930 73039 56387 47812 43860 73333 86313 29897 9059 9846 57700 30080 39116 15464 1086 48082 3602 20121 41680 33440 14542 21173 61245 21621 68591 91430 55095 45948 62863 66850 11053 58426 34274 29882 41847 99158 3248 52317 95227 3646 52982 7015 93704 28416 51153 93387 28485 74349 17743 35754 55926 51161 75371 3448 95912 60108 65576 20973 50577 23553 56542 399 24332 72407 89050 51100 93787 24290 81320 8363 15465 32522 5079 92202 18146 47982 64550 64293 80281 83505 28328 12469 34857 62869 2584 99592 14975 21805 81933 544 18030 47734 15295 33872 54818 27491 10408 19276 61974 15623 73795 87290 40455 95204 7010 16178 34943 68634 71951 12806 16968 63222 19423 74978 83637 68594 35234 36795 32046 34401 62682 93906 6298 11024 51640 58306 2216 50336 14556 779 55369 74637 56655 89584 56589 23803 50108 96288 58889 76470 79180 74984 53992 46803 53640 13048 81231 20416 76596 86930 33415 44843 42922 92079 93966 1743 33880 91109 15578 45554 20752 62362 52155 96151 15565 78056 83190 89365 97456 11698 36518 92643 98148 62760 60528 12848 88509 52072 91036 10972 78353 98979 43370 7779 48405 18708 53169 91390 25133 62901 77684 74434 75934 97280 70049 46992 39615 28916 30092 76476 90948 65870 66194 99111 66264 34408 18965 54531 33673 89105 43557 9808 44413 22506 80484 50069 59365 24214 3212 292 35755 45019 3689 19595 71380 73969 20674 70400 38672 61984 26088 33607 79616 81117 17115 37705 90701 55592 36311 56650 79833 77410 46451 50241 781 47121 18306 32728 79331 89980 76432 69403 87433 44002 54357 57799 3821 30455 23968 51257 30732 41317 85395 11903 24617 31411 25117 62015 90963 28785 88175 23001 99276 27065 55786 10238 86580 34737 52066 3367 60683 77479 31997 77796 17031 68958 61629 11588 7255 42244 73703 27233 42602 79058 42053 60247 53203 58711 3713 58052 22905 79731 20762 60117 72051 5569 75152 86797 88852 11822 63322 52490 73526 51136 71069 89094 67425 56697 7912 79030 30971 9961 94175 16083 38602 19828 39583 37273 24219 80077 12977 95333 31736 86894 32097 47384 1220 3064 89023 75678 84016 67081 38590 71365 23676 7162 36279 74945 10511 1034 58095 31408 9823 34502 25847 59798 43070 68024 28810 61850 24553 62438 32039 40175 16001 65664 24599 20211 5711 21932 10034 57697 64452 44874 24737 29117 43127 66501 66457 79859 3689 16706 18226 33183 50453 60477 66937 79867 73331 73612 3284 98912 90512 92821 64484 24384 89311 97583 8985 49217 28395 937 87833 71722 41889 14855 34364 37785 99979 98294 66183 79753 27761 97755 59731 38089 11745 83917 23346 20649 91426 19123 90722 80806 16765 36441 54196 25028 43765 22831 81398 34674 10770 13026 60578 51841 66970 87671 77004 55823 39801 81271 55013 96529 42156 20977 69545 71515 17937 23414 42234 86208 62782 90767 49055 61310 3640 4657 53262 20032 11219 60087 54406 45204 60535 6523 90381 30014 31128 25809 32856 92918 98673 50777 94499 86464 26493 24070 52659 17625 68361 97464 20128 71559 95665 80613 80859 14748 65484 1933 2340 99652 28663 19056 11333 3863 45914 87222 21089 25223 32668 93710 75970 89089 53030 82951 88032 21445 3662 71445 96564 16417 42134 38866 32945 7842 73547 75693 93733 64566 9935 40319 38694 30693 99565 13137 44455 70214 6953 35959 13998 26866 6816 3996 14610 88946 12615 1715 86130 90435 86375 55369 43713 25876 78438 7825 46518 56977 78182 7726 88832 67722 79175 20244 17657 20010 42146 90185 10239 44313 21947 39911 19113 45084 79630 65769 46404 51218 94096 26441 68080 54139 13276 34409 80735 83049 51649 71937 4452 85317 574 14233 68096 58322 43171 2638 97563 15829 63669 50563 17001 21391 14553 9540 412 49883 17199 8338 64018 90648 83945 8495 69642 68192 53541 76813 86768 93028 20424 73959 50876 32883 65363 86682 96978 64002 81212 81376 37242 75395 90410 27949 49116 78050 51530 67855 67942 40390 97324 4315 5997 97402 53281 96000 8386 94903 23378 43406 24091 58874 66445 63910 37443 29634 10015 76032 64873 94072 52484 53183 94917 33963 73247 48526 7407 52321 85289 6380 11639 42309 83316 69244 33721 68860 19885 57441 16374 21026 41135 29995 69990 98036 16669 38535 25227 69154 80086 57354 47377 39443 46339 34477 62629 30734 60398 24394 97613 4412 66910 62954 46787 42282 20241 6402 57281 89007 81351 34795 527 85844 58900 14935 66674 8159 53951 72745 28649 50725 55483 7425 82875 84015 72002 74745 49906 43316 25688 75477 552 5320 46494 17926 8360 93820 23003 11181 25169 28089 55217 9938 15682 4903 94690 13436 26776 68963 62336 32256 97513 63389 35951 87718 65964 82964 89207 73914 27248 73680 61448 80486 70873 22652 30673 65944 34233 13077 74842 45433 25457 40134 6828 75802 3676 36220 7113 44242 64611 48438 66226 98069 99726 53007 3279 15859 99428 73427 17140 89548 75595 2532 84206 80542 84294 85073 68490 33170 66190 27650 23174 33270 83472 42139 99627 79456 48062 80180 9947 77250 87572 20390 83847 93354 82952 69965 55782 53260 80472 21910 71169 95162 826 68746 24844 5340 9900 93738 14982 21996 17634 90932 95836 75018 74147 64549 91937 82388 20536 53412 65029 45836 26132 81620 18282 42622 26663 74138 7069 22071 15728 26020 48687 27563 64146 75134 9732 65939 91060 60568 76811 14452 22179 21586 48790 35233 63314 63780 64655 19603 88394 84996 59832 34378 8362 20823 51846 38521 77149 47497 7553 18946 20241 88635 22059 25414 86862 76958 13329 58069 99814 86045 36445 92563 84198 53809 83972 17430 6401 74014 41482 29561 75525 70625 44354 68391 55890 72723 20294 41585 11441 10545 19449 39378 80915 59201 72466 56994 70404 47644 90986 44140 3524 96142 64444 57950 67059 26295 41457 89689 28522 38399 45208 39906 42277 62343 90660 97512 27557 38379 62282 79465 38815 66593 62138 39251 22869 74292 65290 49214 52774 64463 24273 73233 77380 87563 17453 5692 14213 25596 52587 35028 28719 71053 46605 82619 9422 89492 48120 34846 55803 7436 38915 92758 39105 6209 10307 65404 68253 98186 51446 29743 91322 32880 51508 99852 51499 88738 46443 80337 46794 93102 95600 80220 52894 42355 50516 38460 54605 53595 76375 60266 8660 51005 65798 29783 97794 81692 67433 26288 10094 32033 20911 36054 50675 21345 2487 3676 79005 55172 86252 50169 3978 39402 78244 70315 41855 82295 97611 55391 70019 71361 87787 21180 34928 98971 6569 70372 2946 25659 32200 81917 26240 58574 76575 77377 831 63793 75403 4864 78917 91733 61464 26804 49910 66696 40678 80284 88035 39229 71051 88506 2430 34103 23510 70103 52058 18583 8488 27077 35730 28973 34770 72229 94581 87113 46797 52890 99489 75633 56620 64354 38806 64459 43265 90580 39721 30432 12127 88639 46179 95498 72687 65669 81626 43672 92816 93856 39318 94613 7555 35110 9907 29652 87897 93047 6118 55984 66482 94277 98392 65552 88821 76363 49699 72046 85194 55724 67845 59332 67848 99392 27513 68758 8972 36720 49009 67808 46580 22648 82357 49546 15786 9757 22295 12079 97279 42524 16152 89601 20451 19701 66649 87959 14486 4288 20945 31667 73134 82197 81740 83966 51494 22163 7893 32857 90351 24541 15850 73238 32327 73395 83945 78252 93871 82964 65893 78733 61535 89156 1504 85577 94258 75491 49571 17671 60810 54593 29056 77837 28986 55760 86670 7424 92666 82205 6948 75011 85642 40547 27486 83681 82054 88074 43542 55174 84432 85249 59620 83986 56880 25906 64364 71693 57390 42772 55735 5947 29258 24014 18903 58463 64272 79047 45701 72366 74932 42508 46251 6815 82787 51285 33759 22668 41276 11042 66262 14157 61523 68329 54927 79577 12278 65918 99030 26284 52430 200 58014 16185 17442 13588 12896 23685 91643 23209 49501 36429 26877 16304 62807 37886 94187 59833 24647 14700 76287 75600 75079 17945 69000 83886 9972 56809 85486 60473 74292 5379 95538 45434 72423 44204 2122 83783 12588 93950 73737 93789 88444 97462 67288 1154 11501 46331 89475 53637 57080 91811 51341 88829 18065 95674 93708 27831 35676 417 13109 41974 9759 88195 16773 60240 2477 30418 95755 85400 41945 18191 78277 53356 29007 23187 32062 5834 70925 23943 93369 93455 63221 76083 78729 42078 13023 49220 80830 6465 77530 5393 90474 39906 80651 76389 30326 20957 31823 28118 9581 24062 75424 14904 52862 33200 20897 58734 40155 59939 319 66150 13403 78831 74146 94180 5421 83396 33317 1142 96716 57131 53970 75672 18150 78091 87956 51738 69412 16287 55701 43075 24159 63690 16508 69992 76910 41788 48188 1101 75085 13354 33182 12947 31787 86510 46850 21858 9794 69785 41153 15925 86468 84920 8732 71125 91543 83629 89172 44654 74658 919 39252 49568 48351 47227 24074 67199 82717 81977 53759 45470 45349 80752 84664 15779 69058 68514 99881 76596 81561 18410 41030 4792 41374 72438 52163 53875 37227 31967 60426 95415 28019 7529 9640 57046 94931 58580 67133 98927 16600 11505 90248 61729 70103 35683 53706 9794 84318 31147 45256 14601 63507 86943 52566 63691 20459 55649 29898 93739 29495 8394 24578 55636 18927 66140 15681 53570 65972 89466 64995 20354 69627 22964 6835 8410 19220 20195 89686 45371 7649 45784 44121 15567 56678 37621 61189 87102 96985 41422 26544 53254 91313 11526 31143 84662 32229 86002 28243 47089 80737 57442 97087 19059 6720 62074 22139 38900 13359 18680 39932 56864 55845 44307 70826 18720 929 73332 86312 49543 81623 3085 50107 85302 92160 7792 91858 35273 94880 80944 23829 31986 75193 96163 71743 39524 75896 36060 10181 51429 62851 14009 98872 35853 61637 37816 24129 79436 58068 46031 38515 74870 89767 28905 76735 66934 36714 90158 75447 76791 11898 42249 18101 97705 65045 45073 92745 35518 17719 7921 53428 88264 35445 41828 13027 72717 87166 42548 66084 50597 65265 51559 9189 97212 10949 17383 75918 39314 88592 16667 4020 22371 53686 1044 26335 14730 92288 30229 47168 59467 29558 27625 99568 73130 1966 43369 82261 31168 31201 84931 90905 24551 88385 34593 91420 57575 44687 67009 14892 4683 1374 66601 8325 21230 2205 64079 16529 92985 14208 84427 54921 35921 67664 17046 96533 48566 45988 76357 60387 1919 23482 39432 87649 43489 80608 75440 79013 39430 56716 79856 67448 96741 83018 1326 71928 26109 90324 48970 50304 22253 26723 5063 61518 2617 59082 52730 67969 65763 67762 79578 22858 60223 33160 93441 35023 79115 19672 16761 65811 48915 10628 18448 27303 88530 17699 56282 84217 47814 68670 68547 60500 72854 57302 36720 82869 84296 11725 81096 2870 64290 58866 99759 43817 33113 61084 25998 51202 2456 50903 34249 53193 91041 50390 42860 41157 78145 71129 76222 91668 36540 20423 82710 96699 34077 7316 29559 75342 37760 19416 4657 31334 14099 9484 57008 93505 33550 92356 67937 33949 62262 92571 64681 39013 80782 46463 8274 35043 4115 67287 35372 9796 10138 65073 90650 50095 58608 36207 77624 85641 34323 69300 95077 25388 94390 91770 15558 44383 37846 9204 64432 59241 60570 20484 64682 35695 7311 2024 72613 57159 27 28057 14864 20610 50543 32355 5417 61979 47988 89666 29986 67522 31650 5304 68293 10509 42773 67508 99109 99156 93852 92172 38719 80977 22235 20060 18735 51337 72527 41125 35516 36399 67062 77335 27215 2055 56262 94978 17373 60115 53388 3450 76972 4495 2787 70269 51820 1272 68020 9442 99317 13326 74587 28076 62010 81269 75729 8748 47659 59231 54268 20200 88999 3851 82059 54182 34475 84464 81810 89269 45180 75085 83527 80538 94514 91597 832 20422 60450 53813 2795 71971 67752 54553 14560 98556 78333 15744 18975 6682 67368 48174 32521 43353 39530 84434 85055 33603 92610 65100 77800 92954 19977 45488 93027 8334 973 44896 81509 67716 33132 64471 98533 5838 12875 29285 97800 74868 75637 72242 67870 81815 32274 96453 48629 63753 3341 17464 25994 90540 72099 64552 93513 78511 25959 86203 83404 28890 22554 93631 57291 9139 85723 3044 99510 2575 70295 25269 81650 19565 69897 25054 29034 80684 39746 54284 42349 9083 36612 74476 74795 5669 48413 55968 53185 81181 81336 21559 60956 19529 11449 31665 35025 82115 47878 93534 83810 64821 34370 22433 97621 42998 42816 79079 65921 56250 6972 37863 95938 41427 82047 56768 37373 58734 63174 51498 29582 49401 16786 69074 91714 82499 83763 20594 30104 32830 14930 43961 16243 7852 4311 5521 39807 23051 67274 20921 22845 3862 48469 61837 78162 39033 44500 92134 22877 97728 98657 18014 84516 50821 47961 17506 98652 25841 16646 48233 38083 79112 16575 53745 96745 1220 51951 51152 78855 95004 81306 83599 48009 77018 77238 98768 27016 6761 21464 48952 78968 26358 52196 10973 88754 61728 35196 37831 70524 19150 3970 79962 68648 18816 8624 50695 43615 52063 98116 41011 4928 81690 93963 36370 41839 51284 42232 93677 87981 3652 51555 78385 56356 67520 92086 71098 18083 59993 7854 8641 46949 11701 1408 55441 37316 95491 47961 60279 55619 3609 83373 88938 26057 18398 39099 49292 16794 7361 64639 80427 17832 44209 96814 21427 8937 79546 56938 83980 61908 60589 56320 9099 78358 33172 22187 66719 65951 32695 42339 44252 17210 739 10467 7692 56662 26117 76447 80252 92366 73211 66850 44058 73220 20270 86882 45753 45973 26940 80181 3026 65786 95504 22385 26671 90938 15138 94777 23244 40423 84933 69887 7311 63637 15416 48828 44816 26738 31969 17084 99413 3556 55411 63201 92195 34664 10414 44452 67 17537 5044 97637 89667 57536 56402 9321 73343 53640 29296 90912 67792 30525 72903 17181 91605 69261 84175 19920 61168 68071 52413 44596 92909 67820 51316 38079 19767 20933 74675 85057 41390 7989 32529 22948 92666 78956 82694 62252 84627 33694 75615 44205 15565 194 69974 25003 26095 92794 34516 87931 95934 3522 54468 72086 5887 26297 95550 10949 85376 92705 2035 13360 52561 94909 66763 73147 71953 40990 78053 59527 35801 93288 66701 27524 42893 21879 29337 54313 30614 19108 13731 35474 65094 91332 69014 37050 76628 49682 83506 61513 23193 77310 9961 278 28023 43332 33921 79780 46773 95845 88778 89708 5879 47644 27909 81733 51087 20919 30892 7003 10263 59896 18808 36939 58881 67526 77957 85845 27077 53530 31765 8004 65561 58861 72883 22767 5105 87796 53028 85438 16117 36174 63754 55504 54160 40901 84729 76240 77886 10494 24464 9632 2107 24171 95079 86527 1662 88410 14753 12931 54005 5086 4774 88696 95011 46704 11508 76326 3122 21824 36189 43731 34657 61434 3991 94741 44393 43687 71398 26626 79993 29445 92638 34986 65655 4222 13364 77378 41989 63454 2373 10294 86469 1213 11631 67067 82190 18257 61943 30535 68428 27566 7343 13406 93512 54011 16707 84846 14059 85500 10770 93003 15315 49728 12910 85950 91205 13197 24496 40281 11136 9536 42692 36183 33242 45936 59393 85219 29498 85095 54917 6206 58718 88661 48427 37702 61010 40047 62079 4449 24436 69918 22352 40733 14853 49362 57865 47131 29656 29613 61434 42448 90386 11550 4505 92833 44620 63084 26026 82828 67305 64369 92291 21333 53302 999 7205 17472 21442 39794 60463 8437 70472 19215 96881 59906 65989 62465 86484 11687 12473 25862 16855 34722 72224 34095 19546 37672 85051 82091 1299 88527 45181 87993 19524 93106 99241 62140 3737 68640 18082 66790 29603 43850 44475 73447 62334 59265 55264 56541 97736 28488 87853 29713 75741 87083 37508 7575 73642 98439 81228 7662 1063 40142 76726 75271 56076 70760 26410 76050 85405 15969 82084 88888 23500 16309 81465 30574 27271 40446 92459 53093 42189 66507 28245 58400 24137 96048 87300 46821 16628 50296 43750 33968 41914 63155 76153 6647 25220 46801 96660 37507 7560 94636 45339 59957 33643 71570 36807 5518 88589 93489 18805 32333 79386 29358 16140 30599 29980 75543 61430 82690 65743 13832 35094 86563 3743 82193 24083 76522 73884 5369 98151 15933 24662 91129 6476 56414 48863 15869 63153 62958 81331 79783 43030 64029 6740 66418 6577 86279 22167 91813 83060 59115 95342 59186 37778 26763 47874 38944 88530 35666 8894 97584 63358 60842 87346 62929 16109 51063 5197 179 20997 36505 88849 25368 35152 27841 61635 71821 8921 10974 42487 41711 83539 74060 45061 24913 17587 48256 92157 33236 4788 35379 73946 1124 74216 79735 2879 47731 61894 92811 27768 36232 25299 12310 34212 59864 5150 77654 34832 15688 43632 69649 95870 74847 8613 4868 51670 306 32855 7430 79091 9539 36371 22077 71192 78215 64918 33915 81746 51822 45388 72865 44733 80523 6708 68745 66258 39356 53867 64667 10072 5612 49821 10781 86778 5790 42631 59053 50363 14802 46288 75721 12151 32382 45067 18505 918 52603 3488 13528 21700 32532 60532 66731 19405 13147 39782 72674 64493 81468 20145 79149 12330 52570 93304 56911 84373 96143 59881 39841 95246 9756 25231 77834 207 77308 94091 26550 75631 7912 36976 8371 82800 835 96630 32814 6768 44998 32383 34575 20631 84371 70831 94228 46685 63776 26669 16333 88959 27781 25303 60552 43237 9856 36217 34039 56650 22745 32485 75534 43423 59841 90814 31145 26247 95119 52869 49364 46312 16666 5729 96367 2440 24307 96917 61660 45159 63388 34398 58179 24879 30222 42707 14053 22324 22913 19865 53229 10127 35819 91462 19226 13897 58609 396 33290 56037 30156 69936 43703 72495 89972 3936 73046 24880 26059 10032 65409 40181 33178 16062 80835 73780 85080 62707 14104 20906 26552 39391 4475 59949 24708 30774 93480 17390 37128 53699 48670 53547 22647 70851 60476 44943 66722 47527 10709 30769 35661 98739 94057 17877 99980 14516 32667 20217 20596 94652 77297 44852 71102 42166 58704 73908 66708 72488 45065 77389 59301 77679 89692 59426 10473 32804 77465 18643 47496 68827 4479 54287 90768 60824 17867 6331 55861 33045 93441 96316 68792 54261 85124 4465 81825 66214 4503 14370 12043 19031 45114 44472 89659 31506 59195 7107 5612 37556 88669 63769 50940 88910 45496 46342 59596 70508 52033 58734 84209 61021 93024 33027 78112 15257 56204 95008 71197 89762 73417 20315 16126 18726 94385 421 56289 19837 96660 71440 11987 11022 88720 76725 22183 41208 33936 22 73329 20033 60835 48061 89651 2387 76350 20442 83408 40232 76494 85796 98739 85609 11482 57441 77622 2412 81728 40993 13195 43788 86011 70865 21435 67225 62974 70146 94756 58490 34039 18699 8496 61828 54057 69844 82925 56825 33204 90907 80041 12339 79257 93378 22226 30713 67766 56093 46683 99725 82088 9685 97111 96488 647 86670 94237 66507 76589 24687 9305 11609 36001 87189 15522 27036 23354 12500 83188 37462 61473 48653 93937 26724 55675 68997 40010 90934 91776 19795 66482 69467 25749 434 21756 52438 75096 70387 66322 67828 3734 65002 67674 79775 74921 74773 87855 88459 6703 22666 89431 89455 52249 73593 16633 23022 73727 42197 7285 79750 31755 79195 69745 88677 89208 82993 14813 64387 41603 75651 62673 70016 19523 22491 9149 58481 33099 29488 76117 14636 94299 30913 42722 84471 33604 84364 44614 64252 89483 97727 73898 88416 21588 61574 86414 30405 29556 74867 39422 11670 36151 65828 91863 51870 92311 57955 65382 16351 18078 63741 15214 81249 28511 38321 21596 66842 45793 35754 7408 46574 60845 75268 42523 74385 64942 67203 7495 10932 54816 64818 44288 92382 99921 48637 2301 25837 18152 93606 26318 18028 85789 15502 82720 36153 31030 27924 5635 34054 1816 2093 63079 73646 65785 27761 62393 97816 10741 83986 46793 97415 2396 81020 2991 93368 40606 88078 58008 28750 87905 73046 57625 3696 54803 97993 49527 60728 14769 90824 60613 36610 61486 34739 84890 88302 19652 18854 98863 53856 9005 44041 70425 36118 44393 73755 56885 16494 2035 505 39503 34526 80473 72073 58162 34156 71094 53200 79698 80155 41818 77156 23351 42568 58356 4826 70852 87455 22717 47907 14503 48947 5434 72881 88553 25348 45280 58420 27772 50493 89733 39279 52904 50823 61782 76673 23146 51596 74459 53203 92594 21816 80400 69901 66267 40521 2628 27722 70756 11848 28638 4240 87733 59365 66999 83627 39840 43875 35774 55182 44060 62661 59704 80935 52802 72853 40108 15490 35030 50889 64925 27631 61243 40690 50909 14129 69669 66761 92359 91847 11635 99106 18178 93247 73895 33087 7052 61893 23560 37599 14580 1570 93770 33455 35560 29647 94042 66803 71458 36198 75705 8755 73544 47484 90015 31150 79539 47713 50256 32538 55568 97239 13018 25375 34543 18537 27642 59747 8278 7303 19840 6361 5729 59306 26154 2960 27508 84782 34126 99717 42428 3227 81980 79959 44243 13223 15693 48895 17359 58130 76297 88077 48760 37747 51884 18867 81614 45631 64074 86983 58688 10407 23579 19019 6620 18564 67890 51094 83624 50792 97347 77428 71020 34481 30111 8473 25399 5959 32406 96994 77090 9685 1537 72107 629 63932 91131 68768 95108 2167 10524 99421 73001 40823 12579 7100 41025 4704 29136 52741 93089 57264 4975 56772 92886 77511 80997 55242 93177 89250 64121 85507 72444 18204 99216 2467 33614 46496 7697 16355 48162 57417 29824 95900 28156 21609 6965 1946 50051 89639 28321 55277 6444 31973 72030 6782 46141 72109 48049 35439 22697 36801 94019 28554 37597 35574 13435 179 83501 25686 67137 72113 49657 30809 12437 62101 79998 95863 28768 59360 78119 51897 44523 25636 6510 485 5590 45367 49597 2726 82733 25925 84625 44666 59133 80834 53996 60725 88271 89553 36712 38984 95158 33153 79379 51575 14240 33218 24417 54155 42041 24764 57545 1120 54836 13044 18347 68830 53983 48318 6099 70213 16734 35334 19587 96202 50057 22773 48031 1141 91465 21214 63162 74878 93541 54380 55289 59960 42888 27650 65967 2855 88587 54060 66288 86412 17760 25463 47629 24592 81631 59425 75617 79579 93150 49582 5707 26595 54136 91874 2610 36335 32556 53437 12040 37816 96193 25358 84213 27658 5252 8447 87557 81051 62709 71981 57464 7199 50318 66330 38201 20698 19221 356 75831 40362 50808 7831 33110 96734 98279 84395 2013 4232 20309 70120 70250 18968 86232 70536 68951 70197 38160 3540 66196 51167 49897 88095 88495 9086 7854 52619 3403 93355 64464 92345 11874 29300 93379 61682 78447 16775 85484 90250 92557 88655 32483 75710 93868 7550 97122 13384 93096 81006 33940 66099 36052 7279 29300 14134 74932 14471 54619 93297 74747 42998 4255 44825 43555 90920 57240 16428 74172 74211 53796 46934 47871 6160 72378 45715 99747 77561 77516 53022 62778 11030 41767 21888 44621 91882 91779 74507 97429 98846 62123 92985 58847 66952 48113 11218 33770 62782 98294 1798 90546 60857 29282 19282 29766 30457 10512 97738 26335 26075 80287 78703 98970 171 57262 12752 50790 79275 94279 53329 84853 87258 98944 57172 94471 96287 32864 27220 55198 57311 15898 79275 25388 20625 52597 17617 49211 9125 90462 78278 39642 51058 12577 61513 4510 20040 76878 69638 13366 25463 67206 39927 3422 93371 47244 49586 66008 75364 77557 75801 30069 31391 47161 30515 25842 95212 77113 73631 97682 16067 35091 6668 77742 13918 63411 82788 20635 92121 93464 47676 93129 8473 71951 13950 62004 14636 53108 88193 38190 55366 64821 29662 43111 22050 85956 16382 48721 23489 6660 89423 52689 79532 51526 73005 15137 81427 42476 19768 92431 84844 68030 43828 19321 66381 9970 52454 3793 51023 94510 48424 9292 21170 3629 81446 27427 12914 80008 14043 41995 15495 63583 3356 61236 43254 39374 95598 60202 32519 12895 17635 78280 74481 33436 18507 9976 76132 79065 14881 65629 59495 69182 34592 53943 51543 88695 76666 97432 42376 78167 92699 78000 67255 61207 20880 49137 80815 49768 8675 87509 9054 8154 41939 64616 6686 5661 56242 97065 21663 22082 43559 59167 29786 71077 26395 25752 23155 69792 64366 63561 92098 21794 28972 73096 75143 72983 30298 74349 54763 49608 26834 96694 90511 5203 9455 96586 99608 88665 60378 45310 18287 77003 37610 45433 36618 67668 8540 56646 63022 96755 53583 45226 28126 31896 98866 10082 13648 64322 80210 44615 44601 97585 63330 85144 2735 58456 38786 38073 33803 24008 80645 78321 14084 28163 84729 64856 57994 39616 7938 91714 10141 60717 65327 5573 54836 36138 15312 69372 50988 39384 87671 22220 64037 47736 34043 90225 88218 8680 82657 46108 77765 22530 6573 25454 26739 83570 35012 94941 72273 49770 54372 82521 76813 25793 22715 81930 97303 96483 17858 42328 66483 28170 11639 11460 61151 75335 64056 94920 46356 15155 14479 72852 19273 60646 74587 6035 48967 55996 59217 36181 33454 60220 74087 45422 36989 44839 83597 95994 50973 44866 2277 91950 62124 38810 60648 49612 4959 81831 47874 29871 73491 44132 72666 77968 56945 47164 36515 87690 38421 18665 34491 78336 43170 68808 28181 63933 82718 87042 56298 60300 17984 47364 86641 36926 57250 10333 44396 48507 61221 33456 3074 51041 62239 71721 29335 87079 89288 17950 14112 54343 55686 41788 56711 79062 97363 34809 57970 21330 87167 88209 4970 8230 40481 36636 7080 96981 29692 11288 63919 88028 17229 67554 78011 74110 76430 9636 69638 12256 56217 91368 60007 9907 87116 74855 58055 55254 22111 85242 45884 12436 82145 94238 62493 20362 61014 16758 34413 79526 69109 73318 49414 75606 53901 65401 22246 73576 65453 1049 68075 44594 28024 75594 2584 63206 36393 92020 35104 38352 20325 10212 68116 93857 41492 73724 30605 12152 97702 42021 25249 68797 63474 60788 70114 44701 50633 51000 50592 1274 19294 77535 41433 40679 22801 36100 4113 48179 61683 45555 55624 33062 92109 96578 13590 51426 78325 49937 80820 89034 6088 65542 10725 3326 15798 26513 79613 73946 77989 6407 79708 46356 52840 88381 80969 97593 50479 52926 35805 96269 89144 85629 21786 87410 94934 27284 52138 84029 4366 19444 50639 79148 5233 57987 73471 77379 76601 98480 30177 5313 1517 44467 26219 26906 77947 97029 29668 35525 60086 58377 18687 66479 35622 39083 27880 96758 30075 16657 67492 27398 71042 80563 20554 28309 55840 39447 17329 39700 10938 16989 92480 40019 27424 64336 43638 61923 95995 6549 18889 20074 32223 63403 48887 25138 71629 96969 19454 42463 82432 70800 66435 23406 15528 7534 75063 21752 46445 35702 51170 65150 59831 12253 3868 65378 8559 79169 76319 51649 22939 90012 7618 10980 37762 72861 5839 10512 83729 36631 36239 75847 28088 97945 40678 31179 94336 84710 94254 51801 92614 58743 37711 13724 16973 74055 64784 64949 5201 40909 11496 70608 44221 77773 2365 56469 33915 30737 98154 65916 31206 26932 95186 31654 63803 34746 44279 99362 83906 89460 43272 2999 19330 68563 43338 51090 6941 73304 91000 65837 69525 75641 90804 9029 33907 48680 9072 25011 85108 26472 50002 48896 64188 69620 14710 57291 51625 83168 33562 69728 68020 68583 39705 1030 75769 3084 17210 71571 3373 60748 88890 74122 90346 28780 62984 76155 75848 81286 31225 54614 30178 8690 9888 59239 51477 83060 35323 64050 45066 58446 19943 77613 54750 11467 44160 6478 30550 44407 40668 42426 69867 25817 61061 30988 89879 93330 25349 65895 52686 77613 66156 15450 96535 57883 77695 7982 78767 15928 64582 37489 55564 99057 76644 88115 21255 17092 4963 70700 20687 36845 94448 19608 28115 81392 6791 98878 72437 4060 73133 62461 64322 12312 44569 71654 38822 24596 75518 3209 82391 30679 92143 55461 59480 12581 76703 47604 51404 45859 85814 39697 75761 59530 47279 3060 57477 10024 51119 2602 53531 19962 14794 77545 8536 62657 84060 57325 1162 77828 55052 11487 15814 20160 57647 76315 76391 27912 70220 65726 13050 58666 60591 38118 73671 89059 47825 93830 55814 82273 29784 98996 62718 56909 13351 58004 32602 80497 77150 20162 33327 16218 1109 51968 16703 48548 62423 45451 7687 35964 20982 23013 58678 65931 51775 63368 51970 47441 73485 62536 5812 68192 41585 80408 40048 45763 55945 69171 81863 54314 61959 8866 62112 43442 30376 65255 93314 44560 93461 61863 21347 16497 87236 86461 28718 88266 11938 78806 47119 69426 58586 56828 49897 87752 58852 16629 39936 62131 4522 28896 39859 93345 47171 88371 90683 99188 549 64450 13647 68338 68996 53409 31072 59574 39782 88309 96402 83288 76056 99669 69304 68125 73066 80989 10439 59197 54744 56337 23342 27860 29667 6987 30869 14737 78664 62545 79997 93917 74598 8658 7087 82510 25837 40806 42409 43612 21064 27794 70220 33857 61027 60046 86229 3738 34774 12127 87706 41189 19233 36022 55474 15852 13039 23406 30212 40687 46551 13003 48194 12284 90775 71983 90236 54217 55436 32060 59283 34879 92713 62055 79550 32146 57938 49210 18330 16820 61969 67138 69572 95873 47919 12897 13858 64493 25840 82183 65686 438 14898 33448 24428 61015 98750 12707 84214 42930 21231 58424 82150 47850 31012 28869 70457 57657 85844 22989 68438 19967 54864 7388 19270 42687 20309 98163 5044 49927 89520 69426 97751 90894 45700 77532 62068 97046 63105 81564 10816 86170 36646 28903 84120 34674 16497 71631 56700 43167 49027 5584 88412 89588 4122 60980 76528 31806 22638 62396 20862 81727 2510 58508 65005 35832 90453 30191 15292 94697 22737 83672 11432 8933 48377 71444 29166 37999 13820 72639 51269 39142 91162 98115 15699 26411 62978 18526 72275 2280 81214 24633 43667 43335 29186 89514 43262 70836 99345 83393 90571 77785 53590 9509 39385 11438 15335 21894 95575 76308 24688 8029 24293 13423 85884 83238 2056 44052 35896 16907 78482 12668 38846 56764 51859 3748 98673 90175 27747 56000 63525 18606 32646 4127 43711 62630 64518 27598 71954 52871 24045 92187 46145 76664 88616 32502 33462 67193 83205 35584 20822 6977 80087 39843 49873 94979 6330 47567 59755 81535 99843 74739 40143 72025 53535 65806 35906 10561 12263 94129 84510 5279 49639 7428 89530 75565 9460 60413 21655 3011 44171 8013 38888 11487 97792 6128 8357 12773 17745 74711 37849 35777 11210 31643 3363 8280 79424 18192 9615 61047 77959 46245 18012 37176 43490 6130 51267 61547 14521 22285 36490 25109 14599 47520 59068 34429 37264 27308 47889 34396 36327 18729 11613 60355 34158 94190 44838 15141 49260 92068 30603 34855 65110 40298 33388 47321 50319 12505 47031 23728 8117 40819 8840 55518 88098 2258 26009 24100 46039 71373 35518 76989 72970 70779 21840 36934 39829 81915 4967 8205 96300 32836 99089 9527 10423 64758 43879 11723 47644 58138 80207 9746 71644 8959 19789 34228 2012 84119 28457 5691 2363 91567 25119 7109 50418 72955 73231 64920 93902 79529 82668 87305 49461 18441 68863 35405 77008 50333 16330 71935 81331 54958 36007 57541 38010 53809 8276 21470 80617 79278 68618 82830 58665 99706 76645 43345 57684 63457 10829 92345 52917 4278 14133 79205 30626 98301 65179 71951 60392 11852 12208 84057 20843 57158 59340 73613 69975 9166 42310 37356 74598 55960 37124 8956 9489 72494 39922 38257 64775 54516 3894 66507 50252 17425 70944 87697 55998 93394 50746 58098 50806 36858 39770 36778 40043 20311 47803 50442 6607 64578 41536 20033 36590 85019 13745 81706 90901 46177 26669 87772 25954 85060 38770 89010 20341 68863 1429 24707 24186 93927 47262 18288 63939 70626 68129 98327 85698 44914 67705 66430 36034 29913 95697 39286 94489 79926 7188 17540 89007 31989 46553 25485 72834 55191 86084 54681 39322 89028 6179 34625 61374 38257 47071 52988 43489 54087 32510 42617 22891 76551 13294 89158 40201 60435 12858 30166 77436 1361 75989 91721 92834 42048 9478 47229 78541 79342 81784 38485 56062 2962 96928 54983 35582 45280 59981 55591 76388 76567 51579 32397 48738 38728 14076 12880 18061 62986 11364 71686 55721 69262 97081 15428 45591 35896 43006 58060 99292 15787 75301 49482 15861 76480 47503 98603 68551 44976 12775 68734 95342 30726 89171 85589 67246 23564 21543 40332 64973 46097 72997 8309 84567 24198 48897 96852 22608 23963 56927 73325 25805 54842 41401 86891 17074 71640 29191 99524 54706 88648 83061 39657 92438 31388 24782 66591 36563 12752 75441 18242 36575 15885 97937 74272 76331 69366 13826 9528 85077 65167 37448 83574 39583 93663 65125 11077 40836 66660 87856 99787 44498 76610 9130 35545 4554 28200 32373 67529 67074 87834 17101 43452 15130 54929 55928 85250 7395 588 46920 72922 76594 12594 86355 71804 4140 10723 78365 66220 14580 913 83164 72823 50107 71392 67614 75207 37048 14101 31767 35929 94577 66750 25512 6731 40843 10855 53488 32464 96025 92596 81792 34752 67825 8312 24749 26067 48550 63035 39342 41379 74051 84874 42615 83091 14723 73349 85020 21339 51887 47073 47271 22073 86107 95788 83386 74351 24164 52449 40415 39383 7665 10863 17134 70016 785 71116 51374 61166 86705 29563 89738 45554 57688 68659 42110 3605 17080 16047 66584 24562 67877 43900 91433 10789 22446 58803 56691 11822 32177 85720 90799 89842 30648 44660 54907 23790 66447 86578 49374 5344 96407 4561 3422 32710 2262 42840 60104 27342 33984 17226 63598 18621 26473 37384 3433 75719 79837 3007 27830 37354 10176 46836 3496 38230 77388 24353 73135 19347 55269 24703 78720 76177 2305 68430 82417 72908 59994 39547 60180 19781 7952 31027 10870 31726 3160 8170 63943 21405 39890 3229 87371 6078 51908 41947 2553 98985 15388 71479 78540 38464 84612 31587 33332 3438 52498 63764 28903 41824 68880 81586 97944 44010 75813 15784 31892 61599 43026 17546 88768 67051 66600 45437 8804 80269 98336 65676 60026 30709 34187 5726 51953 9215 8577 61567 25705 11854 33156 43353 5100 21386 15071 5117 34474 26533 90467 36826 34760 77512 90982 7119 45595 6566 80753 15336 30407 67214 63657 38729 74652 73362 7537 9947 16488 80853 45785 17148 44162 42527 57654 74413 64882 86652 66238 39939 40517 77361 92465 85946 46765 8138 55774 3089 2344 80938 89704 7203 55210 79646 33362 77326 91769 2456 47307 19918 27392 46394 62853 52444 78949 68701 86258 78688 64542 51259 37643 76320 86423 5188 77318 37998 35443 12031 13220 26335 59916 36675 661 51172 34451 2835 7040 23421 56432 71263 75081 92469 95493 13653 46580 72567 91091 4737 23061 16417 3684 81693 88647 45811 19061 49788 65301 95114 18476 92215 15862 28057 20035 72393 84205 28758 89133 27338 50338 10193 35455 60979 83562 43181 95393 87383 29258 77007 54182 14041 11380 59339 77061 77288 70233 23185 39754 6229 74176 92880 62620 94143 20350 33467 59403 49058 32477 22713 3457 44100 15716 65965 6482 22727 99236 2050 98878 25249 24268 44522 3597 65469 90599 49628 51149 9608 16100 62663 64926 21426 4495 95828 16612 15299 35642 78655 86987 86569 62502 55747 78174 89750 41877 60925 96548 34702 99655 87460 42535 89440 7467 82748 91994 84561 30236 83 69514 94435 54779 97473 34534 59494 31625 28134 82451 40294 72557 95039 61482 18763 96489 50108 35225 5376 53838 1531 75932 96770 4800 38049 12733 18124 94256 29829 12295 6250 6466 78661 8592 24065 91294 69690 83007 26739 92912 62650 6120 58832 25442 52742 46266 35383 48353 20203 30195 81290 71737 64578 37224 92433 23936 99354 33325 37252 93588 89580 59384 61603 38595 52684 16471 48126 85650 4516 301 21401 85318 97610 58715 95443 63302 41087 20537 64949 4757 62802 32512 45301 98173 44281 35284 11910 27894 82808 28547 27595 19561 460 70700 40488 85909 78889 53821 86470 86529 60585 10065 10234 34995 83871 81890 9228 57421 13717 65898 9253 97607 71730 87293 99295 65990 77602 27977 12591 46623 89546 17727 92357 44034 15132 77502 17989 13078 81358 73037 71072 56127 97218 92281 76673 47432 76435 88652 39086 77120 68376 54385 46195 8617 24154 35372 29516 13709 10640 84629 87343 29387 42567 34851 58052 52347 70147 99660 85721 91517 42063 54415 51525 90828 52583 39298 27491 76263 58143 32126 4284 45042 71362 55510 44007 33841 98502 51012 56612 13931 86584 36435 11744 35506 95480 20995 3553 73734 69663 11631 75971 82408 49330 56187 29970 93998 38195 9980 79954 53942 92693 95078 99948 15584 77983 44536 21256 1876 28481 19143 92191 48570 26161 24787 63502 6817 37449 65802 55567 6737 6706 21624 63543 59782 27194 5286 34389 45014 98880 38789 56589 10472 66850 77258 52278 55703 72388 96804 14527 32219 16111 72711 91992 36816 69589 95128 77304 30375 2639 46932 84437 39964 76743 4723 35538 49605 18196 97127 68578 74625 844 44016 11272 24404 84675 25152 94423 36513 71090 4821 78101 96701 29974 90787 10031 46885 11547 53208 58551 86710 48759 98151 99589 11410 23932 72557 64540 64679 2580 12015 92914 74036 95949 21488 93012 29170 87737 62612 80610 78377 85690 56970 51094 65054 63158 6835 57122 9786 82093 59532 13655 71400 64172 86440 72567 61762 74817 74438 42673 39944 68128 52913 48809 24130 31841 91920 87560 26012 87535 34328 59798 74637 13374 81652 55574 64610 82777 6390 42999 11315 89147 42747 94042 62106 58454 16957 30336 36499 42022 33617 22842 33379 87174 52063 8858 84849 58353 80414 59588 42937 55117 14535 48059 52118 99856 21866 10591 60070 89633 1320 22081 44769 89082 75923 25453 98065 29887 38688 66503 40816 7105 17155 18396 89369 42863 29907 72525 97233 80286 37965 20143 28375 94868 67497 60754 26256 32803 98536 44102 79778 21882 77980 71374 36748 32506 27927 42821 82400 99385 85650 13582 654 2825 96582 87542 5901 13717 61892 66795 50985 64535 53526 91449 24169 64147 20080 42186 81523 14294 93066 96766 17409 38277 25630 18072 6607 65480 25945 63187 37438 9104 55348 94263 37785 5556 30242 13164 34772 15075 1507 33602 12903 54448 80297 28900 97691 82272 3618 93121 6643 92431 66954 1335 99526 37619 28977 89550 23648 2120 40652 73639 99872 81334 39087 74283 68744 94534 63036 47147 16297 66418 96093 56786 68512 34994 47702 2106 1501 58613 49481 79023 81903 83005 59733 22286 31067 10585 46382 28678 74163 1446 86495 30223 49058 5965 80504 40190 41197 63146 77125 19865 47296 52256 14157 61358 54155 55878 35730 76781 72418 65203 11206 62795 12494 59999 65489 5280 80648 38739 45534 63817 78771 26433 78175 43931 70805 19797 72059 54410 71430 7048 85132 16201 53792 73762 23615 61021 49568 2991 17776 43737 67784 65682 59254 34854 49409 87165 72990 5035 87254 68136 63032 27731 75231 80518 35028 20725 11414 44400 29338 3232 41485 81975 51159 63922 17074 32555 89118 22936 19785 91064 19872 80379 25835 88012 79471 38435 67574 48750 61387 36940 98421 18118 57878 11142 9734 20679 82896 7928 75021 68548 93328 41795 42546 66631 97935 28240 55657 50476 77213 61170 56824 45849 64993 90724 22176 44558 98749 89515 41728 97981 77138 46458 72950 68605 8902 5902 26742 77133 71476 85215 97638 55671 89593 8792 94240 64413 22458 28332 5604 68662 30050 92614 91771 27777 15822 85187 88103 4185 88719 64130 35597 87082 42488 81861 38599 19564 24153 17996 53456 34457 72720 61902 19497 78052 32120 77437 66686 85641 94406 37131 13139 12601 86360 63630 92243 53402 53907 13246 40431 57467 11180 48952 71021 21191 67774 1339 66697 38413 75371 60066 27429 2621 76446 81420 74258 69068 8095 55994 42646 68576 54782 95327 75656 87583 41099 12159 95010 16672 92066 77292 47059 19328 18333 29122 7980 3683 14809 44202 84895 22065 14370 83849 27557 40880 83392 26921 34798 15418 90982 47653 8253 84146 10420 80033 82694 72564 8691 1456 86768 15930 78793 4436 50074 88525 36773 16207 59012 23130 57081 71654 71301 68021 12286 26154 83779 69494 22337 77089 61026 52704 33017 1828 65522 81563 85079 67603 80701 85482 78463 37210 81261 13461 28938 79111 4718 97536 26601 41899 11553 96708 7153 27319 34720 14661 8869 28088 64294 90591 67622 71236 72261 9765 68757 84941 32458 40478 52322 47898 54611 36836 68709 39533 18365 5103 6387 32117 36624 82236 36097 87644 32394 32318 28305 3360 61034 89288 82306 72186 3535 74356 66493 40946 81186 49939 21250 65992 57912 71033 67061 49124 32230 8892 18878 8047 42963 30372 53459 45500 87576 88672 96679 85041 75621 54641 12854 83378 96385 89786 40899 25935 8878 14914 68368 66599 83018 72767 88801 19436 31531 72298 84288 10992 71489 11900 3049 48856 28339 53751 48304 7300 92604 173 62971 46194 29238 63720 61084 94637 58215 46445 76859 24098 89069 26385 20753 68056 55836 36680 60499 26511 29799 14286 78873 41818 49038 84965 77871 82939 1851 48231 70145 647 89304 76602 52633 67971 94482 67563 31860 87513 7842 28792 25784 56189 91060 67656 57784 3574 49359 90895 93104 50482 38947 10035 46107 98784 66341 7865 68374 48284 49838 61729 38370 98732 65790 53825 54203 23109 7163 64690 41215 75368 19908 19530 55601 97676 69129 51343 47444 16013 88052 73741 4156 64032 7327 4639 44034 17506 4866 18760 63624 76572 34772 7843 62210 19383 52890 88794 33479 60213 55851 80865 95519 14852 84960 93328 26017 85869 1242 87285 58477 90131 7180 85747 2184 75501 52579 41488 38431 20531 87065 31154 10943 11604 64136 65522 61023 28120 38142 18648 92524 17239 80456 6789 44601 90199 1916 60674 43938 61791 33963 31048 75103 77546 3822 35724 18459 37427 35385 55594 27024 62983 1724 49546 34816 63271 90689 37305 60943 62858 57589 97459 94890 30893 16122 74940 94682 18841 5881 6332 19907 85070 2690 23559 76649 82728 88215 76418 8152 80739 63808 58147 9872 58278 58746 36227 48264 21772 98889 44592 35844 81666 69861 75879 2886 91753 77373 15765 41814 51848 1869 99941 42738 8710 41653 75369 78743 6259 29804 75711 72871 4842 98530 11783 79466 44602 69779 57128 46074 50818 23532 51237 92814 67994 15911 30667 45314 3153 65349 17646 9671 98128 76062 78612 37571 93041 25846 45811 76948 2518 67818 84433 28863 54229 64540 69098 74506 60151 35635 10542 67496 95055 98165 44608 8047 57139 31803 10577 51914 17648 95787 97020 93931 81289 49732 17024 92070 81820 75289 6514 14034 19109 43450 64663 70936 50753 53242 14371 96273 31229 68400 98182 23818 42780 27089 23198 70122 4890 33738 15091 87887 9908 30029 19142 35673 82479 26307 12071 29048 69454 79809 53232 85308 81560 61399 18643 79835 10471 66298 61993 31485 48935 36420 96961 51729 61922 47254 27284 64387 83620 7939 45183 45683 60679 99232 16687 63328 44529 16903 24219 35360 94461 85316 49087 69657 29744 83632 75470 11973 13102 89740 5099 92930 96873 42197 13474 97052 28778 59877 34123 42067 85973 76924 3153 79608 45884 78365 84466 51098 33336 2463 13769 24663 90366 53706 6249 9411 76200 91407 42484 97586 27719 40979 78310 25549 17475 99867 15011 85942 34140 29998 33416 72472 46581 94317 95900 66230 37781 78935 95068 39701 77031 40096 11218 49151 64399 62414 36856 43777 96873 6172 70262 12437 75563 2880 91215 43921 74092 2801 88276 40656 36182 15193 4222 44294 31887 51365 31355 1029 89481 74971 19512 73286 93721 17748 77095 9612 78598 65893 67136 35078 51554 31926 82037 53453 53889 99469 87256 70268 70338 6424 46980 34843 31269 18095 94757 852 40060 18409 6548 5540 32485 84422 94969 2349 11120 80344 73117 20076 92173 62653 98633 87866 86995 13436 60844 23533 11140 92252 4145 95798 40860 40764 10560 17750 45859 40993 36799 79806 28818 11136 66577 8271 31883 44324 55612 83438 67119 12624 82065 83282 46206 36664 62221 44679 70193 37051 48929 48847 10837 1069 50429 85249 90373 99963 54525 15822 18833 6503 71136 24922 81666 16407 10772 42684 56768 50937 88046 12769 9226 74812 44504 38631 31791 1998 82140 32197 80309 66905 16913 95518 725 58126 64461 61270 64895 18125 34793 72909 10435 86924 81676 54984 50658 17698 79547 68660 19683 99423 81548 17731 57308 20517 67058 70369 27354 33488 93583 64144 51309 54829 54989 97435 45799 97294 97090 84619 67903 91371 45185 36470 72012 99827 33269 98733 89236 86585 58079 20036 55456 65112 79152 45372 27871 38721 77320 39248 60607 93188 15623 10236 47406 72739 13092 80971 77214 49329 99872 44031 34818 72782 19952 89427 63699 64565 33620 56388 62261 46269 62955 21664 41212 76822 67177 99736 67497 59670 19056 37572 86122 55164 80631 18531 14256 39005 53775 46017 9408 60171 57238 42636 16489 18302 96687 96708 42866 54949 40203 23248 98716 52803 60392 26807 8421 11635 72840 82175 49743 46267 42003 71200 93354 18149 4135 53343 38184 53828 3773 46382 21530 47817 25043 93853 6043 1248 24823 59503 15341 26128 17136 51941 25919 50699 76728 93372 19601 40253 60448 66583 80181 9975 74755 85953 9786 3877 78989 36588 48699 5628 55521 95387 8356 94120 19006 53371 78152 28834 81373 70020 44385 86441 44456 2872 4321 40348 65405 42251 9304 51106 94988 18461 55228 63666 48793 49202 52437 18554 76573 84888 8032 42160 20593 98401 58120 73974 22854 15501 89414 42274 7639 1074 565 57695 40029 26753 26008 5643 22313 78083 20341 34282 36853 94345 97714 1534 84703 88704 68065 76907 16097 14345 99980 80895 29596 20357 60009 87478 71348 18595 85162 16665 63792 61689 90324 99864 13960 25079 18620 67652 48294 79822 81321 85778 95751 69270 7482 13057 36223 55948 34865 58744 99504 53864 13138 58085 24559 91511 6492 31268 77162 46324 71516 61806 39428 82629 53872 92735 37843 80158 2059 72541 69792 8666 89243 16490 16292 16310 63139 64836 60863 43504 30914 2157 65668 4287 84788 42722 90570 741 67190 31467 95241 42503 3863 30788 82943 17877 10253 78591 42655 77930 96407 98328 144 74494 79136 79797 6966 11571 60681 62868 28943 54579 62317 17824 76775 52140 82904 90021 31801 12308 928 40906 55971 55655 97360 54766 6217 72375 53766 5216 1236 72762 58392 74335 24654 30741 76759 90586 43260 81162 10115 68194 81204 18125 64717 86566 78599 69082 16190 43277 17206 30061 74907 55583 89447 98406 84489 65680 83489 78189 81014 9321 25286 85521 52270 29347 43244 63684 76046 44902 12196 39718 85296 44912 63703 66142 92870 96099 56610 38116 84648 33711 48990 70863 9457 70415 43120 21775 18595 46705 97997 3926 34806 36991 82536 4107 61282 99720 15863 64452 61966 38217 27498 85924 27109 46614 36609 19285 61171 64429 95786 46880 51926 1389 62673 89831 99465 24701 49536 73673 8780 93656 23379 3533 45830 60262 45945 81315 29864 51853 85236 1406 90105 9175 61571 68695 55835 91921 69849 88092 23726 66986 12205 20656 94279 63157 54286 9477 83230 3967 76655 12464 18328 86591 65961 99286 79201 85854 7379 39506 95558 3932 8390 58375 77337 69541 30028 16237 71950 633 7777 72039 79840 37421 17825 36903 49278 12622 34433 97876 97315 11556 78203 27298 13498 2343 42191 37237 20250 72288 37554 47984 65710 47688 98276 19078 95329 95873 60764 81422 52695 78609 42630 42390 27583 57460 34943 65514 73358 658 21106 18535 95643 68578 72398 82441 92047 54463 47901 67492 74044 29622 59597 55916 72273 36039 79977 57045 92286 1462 33618 88282 43020 60986 98235 31601 45282 52505 80601 41190 93595 79255 12426 97285 919 93389 12289 57152 57074 90052 98938 6723 84497 67714 81963 10838 15424 67717 67346 22453 45066 39154 14905 62361 71479 2281 30423 45502 54542 72474 23270 12848 31333 61689 63528 10982 75563 29566 81399 13692 27887 31559 95042 64128 61082 33453 59099 74794 68547 93676 32705 88569 79598 88974 53676 65198 30467 58216 9749 54361 74374 35906 53765 81545 39668 75305 69854 25379 5220 57572 68209 8155 99287 44909 41104 7992 5050 62745 41550 29258 86780 95738 11937 53242 29485 72808 89667 63108 35275 67899 52401 10843 11678 90949 89817 92948 40351 23611 43134 5396 77330 89489 29521 80100 31089 46834 81383 35715 82322 42491 11185 45846 24701 59079 70100 20798 35976 88610 32139 87344 76848 95916 2306 74956 97115 50430 47227 46295 76846 74375 80691 9429 13782 78191 98601 78669 58706 63414 36042 16661 83850 94791 37169 74400 93150 14882 23054 20238 83005 82994 40790 17033 33344 44189 47580 25854 75050 42814 78794 76553 36672 83640 97870 94644 77929 47343 51812 78060 3349 52522 4982 12716 43041 63930 90234 27273 89812 54672 89965 79060 21113 72422 2494 26399 75084 60332 74144 75667 17435 99688 55549 61916 9687 99331 66666 46272 22544 2380 79746 36071 33838 96773 76488 23119 98168 96977 5714 43744 3954 26006 22347 54034 54482 74115 94492 74737 37273 4605 88296 68329 41114 15592 25228 86253 53612 21232 59428 6633 29551 30687 49245 55269 26608 34069 48630 87015 45184 88392 60332 55616 22215 38764 42909 46763 69642 90632 3391 27627 62460 57193 20018 339 2399 66969 30863 83981 98077 92867 34441 38098 47372 31492 33315 39385 46909 50660 62060 98800 5649 1664 95611 22961 63185 15909 73690 32767 31567 54124 4087 26356 75038 58130 78756 67004 83630 16946 82059 98296 91733 61373 88075 27355 26284 32767 57750 16419 95379 51624 63971 20748 45367 68503 13906 41891 96853 79627 57863 61202 13112 87667 15188 5234 26749 80462 87508 29908 22872 76939 3670 72237 49231 9459 1540 55550 19838 56761 35299 25193 43356 86248 40845 63621 47097 28569 16065 55824 70438 25812 324 59098 58222 37864 90234 31349 89344 76503 4766 72019 10149 87070 67033 26338 71981 93702 12832 79128 84709 24726 31663 93531 10249 34571 85777 62654 5787 70685 85232 2577 7629 56847 98106 22271 85194 84297 17413 90203 59320 76385 1413 60052 69892 75449 10984 10441 35868 56502 87740 71029 61705 8299 44265 29500 58462 99418 84643 56072 96524 98351 39545 5619 20631 1500 21468 73802 16 57042 75705 8717 46863 56263 81539 17111 3227 62450 80225 53360 17677 26709 44943 14313 92173 72453 97753 41631 82714 33185 22379 28594 35175 336 44687 31064 22252 79917 63676 90269 39547 90126 87061 37108 22972 67052 35016 97852 25167 16003 2046 91234 60985 38802 41888 99422 49475 80586 10550 21090 89457 73081 80610 8951 12659 16324 3795 16832 61636 25551 80136 44985 842 66395 4657 6855 82686 86800 32410 37660 3900 10307 60209 57641 45133 82980 32189 21812 95088 4018 47789 19625 68393 60653 24491 50479 28592 3411 96907 55093 38107 55201 38990 49533 32160 91122 99382 5080 47190 96458 22161 72356 70353 57390 9048 70964 18774 92854 48242 42491 67082 62650 19448 21720 30570 44773 56479 15571 2546 26130 22828 42773 87914 22819 67594 28130 7639 77400 47730 5265 56370 54687 22700 58253 52573 39826 54919 44889 35584 97618 30138 71005 60676 26123 81423 33937 28699 33581 11720 15358 55644 21185 64931 64122 45984 6362 80451 83404 39581 3401 14189 10445 95029 54361 94015 7906 78208 96662 50369 87264 58901 17959 98163 53507 29780 1863 70109 23588 59698 91003 64307 77905 75882 82469 32856 75944 24629 29209 66505 93826 73364 69808 61395 94672 86180 57943 34202 2480 32479 44050 69760 26013 35079 82865 23098 17595 68840 86863 95017 63661 37753 88282 83226 5615 56085 8465 97909 98809 10408 55679 16156 57898 34635 15967 3440 3142 95812 40672 80074 56671 35129 45410 83534 16565 64266 87509 84182 90021 68189 82945 14341 61653 21763 54363 56865 78848 9858 45343 50244 95883 84926 61424 67339 76356 15382 89849 91085 4068 68211 70131 75370 8331 50290 96249 79213 97277 6837 43232 43040 31375 84577 65198 91344 69979 87968 64256 2942 79857 44777 8061 67006 65451 29615 79951 74428 87121 81841 51913 33028 9531 35115 44029 50409 35896 41345 9637 1390 54676 65387 85314 41007 96111 85421 86579 34503 83942 20708 2768 17338 56696 52704 61850 43100 50314 47437 59601 94216 4385 97967 84282 87399 33358 8115 64389 77725 59381 76238 62619 80642 7559 58679 34981 93918 39528 99443 45064 79716 86622 35081 3461 32613 48803 41070 84707 53360 72523 84539 3218 15195 4806 35878 21052 97485 63814 32325 86508 99541 9645 23738 79965 49932 38733 35977 66198 16920 96877 83972 85534 2732 73955 52172 25526 81554 52638 60588 84664 15213 25636 25091 71914 85707 31266 69576 5401 10884 98404 4715 20441 90267 15259 58096 88046 32515 58457 70933 69092 6491 46863 87386 13472 97920 18391 49211 94158 37220 55230 58288 83571 58874 87248 57931 51653 972 45121 55168 860 35048 38543 33348 1911 52834 30950 80873 7439 34895 85355 5413 12996 26354 30444 88826 58670 40798 60866 60011 57729 55552 96298 54893 52279 72585 26028 18162 81075 84157 60491 33172 29490 32569 80781 96068 7694 66706 44874 6745 66451 30933 85163 75422 67307 30229 25318 6680 25545 24657 45332 64593 56743 56203 4145 28090 83364 32945 58274 25121 15632 54665 88071 72927 13219 70056 21792 32861 71638 66857 22562 54497 6761 5995 60183 98483 83034 58792 90666 92385 32386 15171 71119 76135 61420 25441 41526 65388 3653 80388 12545 22129 48711 97295 77688 99729 88180 41091 42116 25623 84271 79737 55788 13528 33933 15827 5343 22010 40476 97615 40452 28007 56928 33206 93686 44914 31833 17228 3940 24397 6114 83040 21378 13953 82288 17806 80905 21610 32278 54542 67848 91097 76339 74406 90012 63242 87467 96394 60708 78283 25096 64661 99155 18785 91724 62163 83952 11660 14122 66168 21095 21128 43922 33618 58238 69134 4010 86860 41656 13524 75624 45899 27240 85356 80761 89013 9140 67680 63146 14070 15955 54406 18467 37659 2516 65003 43819 42745 3169 18002 64187 60914 92429 15278 98210 99602 98494 29028 32482 51349 67313 15234 24512 91928 35980 68162 52193 6688 11415 29029 10782 2385 96421 33818 98853 11743 4000 2560 93861 20118 30671 2545 60920 38035 16230 54333 71155 2218 29850 2410 42599 5746 7047 290 37433 24859 69785 25323 66377 50694 13910 71036 68174 62001 54015 67524 24514 37741 66293 41817 7019 11210 83965 79014 13666 49452 6591 19054 91280 66035 58066 38593 32270 86286 72887 51521 10594 12510 84720 67154 86871 98059 47527 15440 32281 45056 58206 19438 96096 46741 4991 9262 74793 85606 12054 63035 83828 46855 72598 44209 52318 61898 90097 24346 71298 45157 33828 29827 10258 22213 53608 99380 48720 28082 12942 21699 25857 71054 65439 90622 77006 64509 46807 96291 71245 76413 77343 53612 11639 26592 9189 67702 65871 70718 70104 52758 3553 91299 7629 89400 11946 81916 95696 84384 39198 25669 96030 75416 89332 18949 46916 4764 96127 76309 80789 69494 23217 41919 3638 33512 48220 43635 4394 3845 18619 52745 12357 56986 98240 22615 78546 21207 19183 43768 69533 65134 84042 55981 38456 8248 52653 62555 14700 27061 75636 94844 15510 47797 23318 64230 48446 6423 3528 60153 78559 88644 89473 58562 45036 88200 15836 13841 67423 18200 22679 43108 91149 27888 47395 80664 77631 90052 66762 57546 65905 44476 47900 51128 4670 75923 85272 70032 59177 51074 96712 32424 82871 4274 78404 97876 52073 93217 52534 59731 80628 2595 38280 3888 2835 64026 84684 65604 11579 20982 37694 8188 21127 42421 59156 88551 69806 55997 75177 13739 263 84105 64204 6972 34356 59384 79916 83616 98996 77379 38098 94823 53042 92365 72440 47887 10956 73301 93373 72519 11204 5030 10844 11840 6440 58079 72069 80210 35927 32511 6579 94491 79896 237 87456 48402 67817 19477 12387 76233 32000 76373 8246 2327 93585 55914 51940 19744 71586 91052 69307 13168 95732 25867 72076 42183 83957 31516 59769 38710 91167 9978 51146 21375 61920 88906 42662 39425 19751 75761 14137 18488 9439 29969 61740 45251 35708 66928 45568 40377 19326 48355 73092 89400 25691 85563 99696 98839 24715 93047 48863 71799 85729 17952 31238 97216 63943 88181 99768 52027 55481 32235 85152 21156 45948 61424 34633 17255 98972 42396 6748 54960 17932 14914 78928 9826 70594 52564 94141 91052 14227 56296 57618 68176 45481 35715 48911 566 74386 79135 97287 1358 5022 13958 36913 67845 96146 53705 92682 8273 34086 68561 7176 69649 81129 14136 43880 26898 84689 13588 218 40680 58003 55548 59250 66769 95038 37454 87321 30338 31128 89854 21605 94429 310 95953 34291 76580 93233 98570 5737 57601 47829 98001 20895 61761 91620 21902 2699 79170 40860 28185 55093 20326 74511 41706 99693 28684 27794 37216 78358 96402 20132 27262 37039 96693 27227 90735 80833 95033 80718 12127 11218 36128 44369 45001 96719 20451 60298 56141 89482 48403 83251 61363 35929 74636 33984 28696 18429 57507 20522 9321 42857 84364 24054 64245 84092 67892 8755 60996 39350 25036 82668 97889 99142 35972 40407 17889 45979 23416 2893 47168 34283 35626 86921 5984 26621 54939 12712 35108 68362 59552 68469 29367 17767 9399 80024 80608 5820 26210 30992 15232 17722 66592 46670 69839 56068 36845 88281 74338 13440 33646 39363 886 80195 36344 20501 68289 91563 85259 5626 39473 74994 78551 22831 9970 25571 66127 9154 59142 6881 13665 13269 17318 88070 32222 25793 5080 75885 52974 1820 68206 3406 90779 85588 72588 74643 77469 27996 55285 32223 89447 70869 73449 57387 23753 21112 1661 18077 35893 31405 85709 57221 6754 34297 56007 59485 46666 11318 4320 18377 1425 52234 83144 55877 78387 23083 59033 94271 51802 7106 99249 89832 30092 89247 44258 67628 80608 26606 95879 9673 48737 74731 99866 25621 81873 4678 40254 1518 14543 27868 73549 60749 16177 87034 10242 9880 67218 81730 32488 11222 27535 81056 66263 50972 81574 54975 61154 89520 54986 36167 9185 78867 37419 73858 95204 69302 95159 80511 58101 71134 23991 9815 56826 78394 81017 84818 82914 60574 35048 25889 57115 46181 11198 72395 73197 75960 7280 51722 37012 91447 75327 66442 57457 79772 7494 65016 11147 63589 66823 45953 92321 79623 74636 79273 6626 41835 32137 18290 21985 49964 72042 97492 34352 4266 85485 26221 32592 57362 8849 12370 5681 61664 10728 83216 22623 83608 32950 41148 91800 68758 91649 39732 93179 90083 61543 61025 45809 3215 33522 74863 38053 59429 48319 12134 68852 55345 49981 34745 69437 61790 62625 85656 57725 76637 74378 32420 15614 37767 90415 74064 71937 82734 15024 32458 65474 48433 6034 43747 53889 36347 26059 63260 48820 91238 38553 68749 63252 89915 96474 43308 98087 96297 86266 29801 95147 74199 96283 21819 63625 76161 65969 73281 67465 90803 55030 39472 21893 58730 38979 11926 10952 74076 46908 80760 4004 89487 3652 90886 80418 52942 44276 50811 83645 77788 32423 93653 22307 64684 71578 995 11599 54920 61887 56421 42892 77392 58754 24989 26326 43203 36599 89811 37814 69666 58770 13103 61863 69431 33073 10367 56549 39807 84513 3718 80031 91217 53975 76699 73694 72560 80555 28898 3445 54590 78935 41854 98653 21364 73867 98095 22752 69513 84297 82664 23404 49532 38016 42701 75744 5550 37265 16272 16089 51313 68800 27736 88343 64196 96965 89088 70474 92871 4535 57423 43764 13235 72492 78844 3640 66005 44979 27539 87022 82865 5180 23823 27346 55434 20670 72974 99047 43767 20630 22038 84456 88737 36825 86903 31875 52094 24310 72622 16289 53760 13028 65040 9965 21085 6129 83808 69186 97243 79039 63365 93443 58849 99325 74300 79573 63481 83106 10900 4460 28930 66805 44824 59517 8373 65035 37379 52587 285 71443 55508 69584 95537 85914 90995 35965 20642 22494 23955 84903 96540 65932 3311 2884 8038 1751 37250 29814 98616 43507 42256 12010 14187 99098 72201 54461 547 5633 68739 62001 91161 20690 84036 44489 78426 43916 66441 65880 35553 30260 76576 13612 86379 59747 17720 27725 70100 3311 70274 63356 39288 76988 69476 89964 93936 17892 47865 20077 1626 55704 60565 33873 584 4311 17839 18643 30337 65048 69237 40127 38619 50167 27454 9421 70211 60367 80292 71658 37283 74288 32683 94690 82350 10724 42940 55948 1616 18896 4552 34296 98836 83775 28612 49028 6155 22788 77935 2997 20658 31386 63513 50581 79159 38718 44760 6706 74033 6577 92428 93951 74668 5883 84837 35538 69101 38380 94285 42715 41738 67781 87871 92332 62988 31664 83215 94565 42220 49593 97037 11619 55989 1871 84124 75470 407 9619 30914 27925 93463 80169 35012 31645 78679 74859 44550 4872 74787 9053 32932 97714 48403 15219 34380 45904 7647 76996 36470 77271 17315 77187 81647 8240 11674 6540 84444 55961 46227 93485 13671 10811 40793 48562 61862 43069 21420 90237 68217 92193 9666 12968 14797 91229 43214 73836 26104 92489 34280 8286 79852 24716 77692 82225 35758 67378 82956 18002 83735 76820 1496 1926 34225 22092 58651 82305 385 45459 54936 27218 66805 61508 7319 81153 78874 87215 93762 52599 73379 59040 56337 86506 13269 19103 88633 4843 61006 15170 22249 78266 46993 14898 81526 52921 96124 28920 98122 2007 70496 91326 64098 94240 29391 64860 69264 71933 81235 84799 745 23026 95069 58452 63007 25239 50677 95588 99242 29718 72774 31687 3327 946 1119 45330 40353 60335 68866 2045 66272 59023 21042 13216 99254 11604 75764 90237 87362 64124 85410 63225 30704 60077 81528 34578 36706 19999 65182 93143 49328 97286 46010 21231 10423 25223 39387 51929 60748 88919 98964 97823 36334 21578 62239 70257 76545 35381 94240 44785 60666 81812 70511 92674 49451 17284 62749 87798 41148 83788 42363 85044 25652 25076 80817 59795 18342 75455 86174 64695 43735 67794 29449 33519 31588 82138 13806 31700 74217 10399 61138 12586 16013 15156 47963 46163 75176 64509 23635 89988 20619 58959 49917 56252 78939 89005 27653 24130 70018 48792 84534 22712 63442 82984 30044 84493 17472 61141 76096 24588 23665 85428 29415 19770 51487 55301 69723 29830 38976 93003 89423 49026 92192 16606 39169 49791 2801 3390 11564 62853 62619 52317 61004 94245 92017 99239 59582 56630 83982 2720 24843 73299 72460 19597 11755 16604 61493 47205 56276 59906 23789 12541 78472 2612 94259 28209 65959 13154 10622 80687 48936 21188 95602 23559 24668 70699 88129 38819 3508 33467 60928 40106 42471 84903 99657 61062 47429 17995 50652 74934 55361 75682 10867 14977 73089 19938 7286 454 63002 74511 29336 67573 1737 58768 88477 47838 92946 90959 73551 16495 41302 16907 63872 90104 46531 54935 80047 89618 9530 9835 14286 80069 22006 73223 80473 92382 82345 87168 66368 9463 82303 94526 94234 48589 49526 31489 49438 8314 9105 33794 65454 50184 11843 67123 34147 81326 12833 72824 72054 78444 59010 30673 90293 35353 56481 16424 47972 91894 25822 52283 29786 18193 98390 61713 61653 40797 67934 1952 45416 14514 91730 52177 63735 58951 21680 16434 48671 61103 47992 20191 79886 98259 53122 22307 69703 46028 93042 6670 98947 56625 92776 20179 87198 10361 47367 1591 10182 4694 98431 68636 94773 61137 24240 98646 35718 25901 80069 63886 79558 80598 23373 64399 14676 73152 24632 17797 58253 83397 24271 99506 88911 82787 89728 87702 34649 23238 53453 14334 94568 49167 51671 64352 57814 42059 91778 91596 15155 33260 2823 59992 66838 4513 77231 1212 84369 99952 4649 44299 59253 29243 79915 15104 31501 88559 27312 54174 98555 68967 51077 30444 3144 46077 96336 73027 14852 7009 67474 84958 68658 15483 49413 20176 72273 40283 78195 57955 74356 32541 35171 26270 83990 12274 60803 98721 38163 35644 37446 46943 6478 2563 23142 83610 45279 7702 46270 24194 63687 34280 21182 5276 60090 15552 22968 75802 25126 26265 44332 9270 69454 89428 66747 67205 39841 48447 58634 71292 28489 56234 35668 77561 28686 44548 36594 41779 77611 90300 54593 42093 81653 30730 3684 94896 26426 67596 38315 65035 79331 74551 16219 3242 3795 43306 75378 31286 48264 38542 79508 42637 5947 9034 73115 2345 17242 5893 89135 48626 24044 77592 80412 68088 65734 30517 41868 50872 30753 55627 21684 47123 26515 16067 10758 37670 80395 30525 19464 70838 97149 99114 90553 34685 75093 61423 8827 4290 64923 47087 1022 5103 20880 77084 17590 81871 28058 14763 55540 38765 99195 27740 36660 92161 83689 17733 57460 60982 69385 38785 69813 40665 56895 82370 22381 57508 65366 99835 12440 19491 44119 70617 58597 30148 50012 53702 3037 70684 37213 74737 91314 2709 26261 61643 2705 82968 846 91648 77614 19810 68303 29870 32991 93953 31272 76566 22096 34944 26014 6191 67792 72312 41072 28524 2171 25684 18769 15644 65638 52431 56661 69791 22849 94539 24948 33173 95070 51428 18837 87583 72172 2303 28809 76987 82554 22102 66141 89999 48353 18003 67597 25449 84941 36407 66745 9179 16053 28358 85595 75682 88323 46882 12415 5247 98296 5798 68779 85035 28060 4202 6061 5145 30916 17552 8574 40311 14028 37288 52637 55402 26859 8875 14623 10225 8064 82183 1583 17516 16454 76474 30931 57144 31640 24329 61597 76725 90926 36621 91792 368 6957 9937 13198 19598 98536 66684 58400 95260 36315 50 37151 16615 70234 14530 61387 66538 66413 2471 56527 53807 49779 27524 55242 36719 53772 45595 51493 23377 64203 37903 3993 53479 33467 73780 84874 68718 72821 99687 18992 55872 68840 6936 6006 50550 26954 86986 38913 39191 19703 80469 59590 67489 11309 86337 50698 50272 65575 85090 42660 42367 13143 18722 39087 57938 89754 67257 59699 58845 76248 27164 68531 55101 19239 70606 73602 45968 76894 78860 75757 50061 57584 75286 40346 29249 47156 95159 8599 31448 83788 45396 80079 32798 77607 42950 25622 52344 93527 6939 79838 9691 30482 12801 50407 53459 13715 31899 23389 35457 76699 80844 43876 20176 21006 30228 80082 82037 82210 29912 41061 8311 9730 98435 39508 74506 93360 82571 33924 34676 34405 92810 23363 89767 36739 73482 24700 32636 10040 79083 29076 687 91022 95682 67659 94019 46704 30919 20370 49648 47240 84435 58022 18321 76325 65416 64225 97916 69422 10511 96327 68875 49654 73368 75950 86591 71872 58255 7563 13794 71698 70041 45618 92575 50420 18533 34038 99351 97934 26344 83423 63848 66160 23302 87384 70040 88639 37821 41791 24219 83877 95647 51439 83668 23099 31665 78769 87603 83459 82218 31655 28299 41831 79925 32501 95020 59367 19674 86922 7293 36693 77791 77243 17492 82109 66406 50892 94688 24829 31993 46769 75247 40146 30159 68188 79441 92573 35441 34085 79578 82004 88108 6803 44605 6355 78616 98587 68896 38023 62620 14044 94602 37319 57846 22396 20804 83211 92570 74701 92861 58615 75796 51802 44368 7580 6946 67826 52655 43012 56735 43286 65928 54059 96340 74734 84053 95803 17696 70358 54640 83395 89874 9599 84568 36899 94155 20298 60565 37541 8719 87730 56831 63896 47440 61188 61392 15261 22305 40432 41457 46776 45972 99421 96252 18207 64455 17029 96227 54516 12026 45345 92117 39235 69467 37043 93721 57534 68116 13371 38328 99404 83741 65207 87015 36577 43006 48996 14137 42147 58462 20246 41736 99183 2893 1334 53174 68701 95679 86290 71700 10923 41181 30685 38928 73824 60997 23634 82752 12113 88310 52577 27129 89289 95744 73242 64868 28129 50444 93968 57428 30927 61904 10695 41522 86589 29857 15570 18542 3557 21513 44052 43771 57718 67778 74023 57234 31520 15770 78595 63068 99287 16358 9502 2981 9653 92113 36855 96677 77653 93754 66180 60924 40734 53141 12748 83197 12331 62485 56493 24415 7659 68017 35692 24160 75516 65923 46883 14494 45799 21849 38806 69592 28589 79444 53710 59958 47899 30702 80135 79294 91081 91693 26166 49113 95583 17448 63278 22326 59055 26036 5471 36590 69242 51035 44093 33400 65959 40942 3959 60085 40553 75370 35897 85794 87572 83248 79262 65085 51569 95916 52200 87975 98234 53784 559 3838 43448 97525 74161 72074 62826 24449 58763 94181 17871 62907 70179 23201 57230 85788 36935 48697 75256 58970 73422 76619 87958 88022 78753 79791 9059 4339 75654 65597 10171 86242 94688 4393 33785 58562 47415 23583 15806 36735 11876 51056 78475 51494 21543 20706 51706 98077 30716 69151 49107 5512 46347 80629 15285 53045 6058 9035 26078 48369 58163 26192 31642 59921 53354 6253 71605 80636 40616 33101 77571 54517 49495 10 40614 51514 22440 24045 86815 89888 68714 90250 95664 49946 13137 35619 41320 84084 85259 61809 7964 72828 11985 56724 36485 52464 3840 9560 49179 8343 23047 4669 52349 72089 81738 92470 79220 66351 2059 70285 95286 30038 37439 25893 95748 99211 47754 41726 94216 13521 55152 15474 24611 3802 40138 5352 59461 66932 33625 59993 14267 8015 70854 65103 97924 63595 89614 44287 72232 24331 40405 95587 24303 89215 98408 92207 23012 73122 68688 28310 23920 17119 36761 17789 46000 15985 69175 34282 96029 72521 17906 21612 91902 65616 61365 30591 92689 41663 24260 51113 10166 64042 91005 25909 81270 81633 67059 64203 71959 8667 44795 77109 43994 59468 83664 65349 62419 75151 58232 97825 5718 25564 7508 54579 95138 35514 69250 28414 53200 84162 1429 98676 2669 28646 47986 22047 19966 12 44304 9487 47910 79647 92333 32452 63666 71922 29974 84155 27104 58358 18289 1267 9251 34529 8291 76379 33216 84886 96140 44912 53202 88289 38289 29959 3474 16431 85804 15726 66253 64683 37662 19413 27973 87692 83861 37214 97613 15754 71637 52628 48101 94194 98835 27882 95149 62390 73741 81566 3036 82527 27714 29425 9086 67410 92940 26096 94239 1976 49376 65915 19875 37976 89241 77258 84683 3677 17701 11520 2302 42908 6487 40973 40445 75284 71219 79909 21636 96739 60294 46647 31268 25532 9998 7828 85781 11321 94142 31844 69626 33760 53328 85695 44681 55599 86929 2062 75 17797 68934 94119 70854 36176 2736 59116 35491 36497 86167 14221 86123 32283 95993 16490 96014 61787 90739 96001 70349 91235 55988 77931 83642 49994 62163 46213 96234 77261 52497 20126 24787 5643 96339 38550 75798 77346 34717 32007 23313 44351 37907 11080 78218 44958 14107 52592 85898 63456 33614 52588 83636 20089 88040 85071 44028 55972 17480 70168 36649 85338 47484 63207 29235 52454 31989 76093 98813 45945 89582 80200 75229 46465 77391 43240 18631 42056 21637 93937 56053 80289 95004 56680 77167 28302 9452 37156 19934 47766 64760 62846 321 58049 35828 71648 38966 44663 2618 24525 22950 36672 84399 72826 36857 63286 17192 99021 87736 92458 49935 22340 99153 90839 42130 28053 85046 83558 83047 90821 14129 37086 16391 64978 51979 82740 27161 17653 11486 1501 66537 98265 41098 29340 18580 61802 59833 97897 20874 71253 89702 42459 59377 98673 86508 28743 50125 91571 39434 43415 55721 18224 29805 79448 71379 29101 26204 67416 34033 46743 24046 32181 45587 26784 96530 66572 38842 833 82418 84228 32013 915 65054 85314 14149 86452 67950 84508 37736 7868 51698 65001 41448 15028 79835 13394 30058 45565 58800 39937 73458 98467 37376 73202 83991 24605 33223 22131 31771 43931 42056 93068 97651 30286 66170 60221 22112 84941 97669 55021 29214 86803 67072 29535 34296 91546 90493 8505 37965 56553 12012 69172 19625 21614 97337 97983 61947 43354 62136 18589 70392 2594 18210 47446 15006 43382 59670 2407 98900 46328 46752 13784 86874 46384 74005 75148 83675 14146 8991 76137 15797 46735 62185 68520 27585 6093 84849 86426 11477 38588 87272 39492 87631 58394 62547 66113 29406 44409 93740 87200 4712 98864 19511 79705 56475 27990 36507 76326 89118 71979 26503 79585 86249 52127 55163 6434 92369 52898 91861 31912 63934 78311 79250 65964 72673 7557 73848 35842 37969 60398 99998 13145 81100 12298 34004 15300 77718 59166 79352 34561 87879 68940 94915 9844 67552 95137 13220 15154 93656 5716 11563 59321 40075 37924 56963 78741 16233 97099 5763 36035 21570 27651 56411 47690 89732 97622 38122 93002 92389 64147 12828 10689 97001 68057 48047 13125 83524 29097 41318 65699 27560 70745 99449 2400 60918 10290 9725 99578 37337 35530 85525 78097 73959 96403 22768 70028 64302 23786 52285 79582 77765 96541 95394 88797 53035 41366 40693 75650 3683 20359 88909 56451 82709 45600 72287 12291 42810 83451 12137 86658 45368 7364 80001 42466 93810 69228 17443 55819 92300 76341 42349 50886 22523 35238 70354 39189 38162 97801 44506 78244 84534 32513 17644 43493 68353 55126 97884 50615 86822 44341 21445 94586 75242 96684 38378 86295 1153 30840 12385 59829 27409 48300 71111 94900 13637 59863 6732 2497 27212 27427 25165 39175 79816 72669 90130 63463 77522 37162 75927 52990 12819 78008 24987 88460 82790 87449 31013 49010 64029 42440 18086 93059 77901 81712 64083 98741 20146 76681 66269 73836 73244 41937 7973 49413 35330 78922 11919 33230 40776 10423 93608 42465 48483 89560 46272 67089 94884 96715 66698 65078 22683 60036 98469 35873 23751 31116 73480 82939 26398 15148 43868 63565 33617 89413 47592 82548 69493 34972 43395 3975 28247 63163 42327 39085 62588 66893 18307 20331 59303 10780 88856 43632 93757 25048 97617 86828 61251 77154 7332 80989 31339 64679 43230 41913 85804 7458 74825 48352 81840 95659 25445 47755 22965 49038 86557 19724 16920 54089 60096 24496 45307 53572 66173 25339 23819 81399 30870 45256 88001 11758 35426 35927 77690 73020 62074 72191 92060 66912 25886 39577 1758 74197 98375 89240 6756 82135 32328 68057 26820 80021 18426 85839 75535 28325 60577 82486 89754 5541 8229 89867 78154 41276 31248 21562 83501 25857 49246 67458 10586 2211 47211 59091 21730 82603 99603 43340 49753 6159 31771 7057 3473 63244 16676 15565 69806 48845 20906 50567 27346 69647 61381 37311 82190 39926 32533 90189 98402 47619 17878 65713 93861 37332 1877 64041 24311 86546 90016 91618 6507 5441 29430 48625 34484 53563 73888 30112 37157 92700 2070 94952 3082 81902 65318 19369 9043 84959 9056 22253 81231 42798 19892 16530 88081 5748 63336 97482 12635 46812 54567 81279 35379 37887 45147 90690 26979 21336 81183 76289 19390 13392 44266 38249 21485 19517 74569 39281 37431 40758 35415 92889 7295 80341 88996 30898 48095 95177 44450 3139 49142 1475 5241 48214 31478 66375 92963 90082 19426 36587 96536 91132 75323 80422 7065 58709 38197 67553 65128 56418 2156 93027 44353 18057 81044 14217 77919 24387 79281 13272 40887 47404 30341 16009 25180 61600 27065 89387 83213 46972 13358 61245 22373 84850 51990 49430 56186 45199 20667 25033 21381 71297 31903 43637 55519 60085 50389 87917 32976 18345 85313 41041 76164 38077 94180 48887 27452 32502 82617 47797 79650 19067 9723 39281 98072 99063 64398 32387 89994 79606 83463 34548 36639 57544 6779 96197 47671 80064 46954 14286 31894 76107 35359 39138 40527 35267 89198 68910 81337 71075 85295 98628 50357 7676 5595 80914 86810 75001 85844 27860 527 72476 78638 48011 72232 63910 49048 20635 20411 32123 22880 344 99976 94393 16043 35801 62078 20491 81893 35832 66924 66848 66870 1690 26093 97373 95633 39826 7962 32466 91137 26622 14359 57244 99311 77780 76605 13628 35568 53508 12271 35554 14442 17178 55169 18454 16338 878 4463 76375 45574 68292 74061 35814 12313 63003 27703 42726 76502 87126 81702 53230 25852 73133 68325 92970 19853 31823 69803 20668 42205 85616 2217 88247 51399 39980 55923 5795 3896 96310 28430 7156 2694 98862 92434 53751 90210 90403 71839 12819 84853 77653 29936 6904 89623 84061 73175 39846 24440 24310 99691 87468 20269 40511 20742 23500 24143 13837 88125 61767 15405 48425 2545 85975 90090 16303 13408 75327 25085 20606 60632 65484 60177 38195 21540 52477 29982 93844 41784 84906 25133 69047 94502 44299 95254 73501 30889 37537 54838 56160 32026 94537 60698 83789 42869 27791 67347 62803 85217 18481 26379 36881 43159 12685 19531 33372 79920 42686 82780 33621 73275 34762 15894 88263 87856 85896 72001 116 62310 87080 86623 14821 41737 49571 48850 32368 56072 10749 79069 27107 24004 64189 2018 74443 5319 3515 17860 9533 875 39640 31751 59283 55739 51050 16633 64136 88181 33832 54294 69414 56626 22088 98563 65 435 60536 82651 96496 27674 40720 77712 38311 68527 6086 26424 46139 94252 42341 20779 71834 82503 24571 15029 23692 43689 93670 85270 6566 25069 71214 97154 70080 59050 93287 78931 32808 41778 25284 26491 5915 71563 69811 52276 89946 98011 14161 11059 82706 65540 76843 58073 88294 2746 31036 79908 82725 4501 20908 77707 23637 61970 59239 52787 65731 13721 70503 709 88217 54750 76407 33290 10170 93626 35858 51398 90558 97594 13009 74333 59009 79452 68894 21684 85504 67433 94861 7460 99094 879 1113 96879 96714 71705 90675 97964 40350 981 52492 12521 39502 3391 41579 38231 55082 9170 205 8568 42706 81755 4852 76791 77896 92594 74859 2447 49101 37509 30825 45587 21547 44511 14572 17776 43191 55120 51665 12969 35998 43993 75394 23757 19732 29039 67506 60688 677 37838 50166 88629 45298 79578 92769 69706 45114 5341 19138 58688 11496 80829 1230 75720 70069 21649 52250 4908 49917 44119 96787 81861 41988 42682 65881 85840 46927 66196 21996 73816 31861 23639 91285 14044 39167 29902 22800 85956 52597 8569 63915 3521 42570 16028 80012 6445 75982 30911 8652 91516 98319 22523 91562 16604 56334 58520 44134 91342 9424 5653 32126 71041 93546 69287 79866 67510 41089 12388 60608 66811 27025 96911 34362 781 71515 71725 18184 43382 81605 34356 70216 13791 45081 16466 67407 58408 75264 43422 57914 60305 53761 15438 90133 80019 84658 14014 13939 37660 5911 79882 94363 91338 17783 65093 96795 56996 76863 61286 95246 42272 50921 27320 59155 9004 60740 46476 49399 49149 27426 13048 2903 46314 54105 42416 5308 45388 95088 73117 84726 97863 71200 45416 23560 71720 99442 76457 99958 43425 96099 76354 36177 90280 43270 51098 65511 60238 68634 93945 5958 46845 78191 55380 17899 39035 26128 2732 46950 19749 75832 43183 92832 95145 6864 71132 72899 37589 6470 60162 94829 46226 91819 23940 33453 5706 49909 64625 9760 53716 33756 46365 16519 7189 55412 764 46584 91060 52344 12681 98379 44309 20796 30129 34843 36917 19239 85238 89035 64978 52353 12704 27080 48101 23082 73067 6741 25837 89476 55370 14293 77243 89132 78208 45849 56646 46205 19095 46193 56812 71013 3849 69099 52255 99977 53523 11412 50000 24332 45569 70674 54825 77614 75770 27057 8125 10594 38566 84142 73967 28226 51040 92634 34753 45208 16603 88535 17402 40815 74312 55900 5498 50694 25209 89221 69964 86114 73166 50062 83483 99114 30618 40416 50293 63326 43674 65940 56309 26920 11086 31236 95367 61216 89506 32829 14529 60862 9209 44979 29757 90118 77238 28221 35835 58664 22241 13225 53383 54150 6912 19894 4435 71832 16765 10989 98478 7955 39361 38316 22059 93802 95746 73685 64971 4528 89106 22950 69066 7001 52795 81822 76308 98227 4503 39363 81093 75043 61025 48719 80891 27260 68710 76485 76944 81930 34133 51188 54566 60659 15412 35022 31260 73044 79006 31505 21612 39366 86516 89140 28041 53666 61757 70945 46224 69029 31625 48311 90695 56704 93663 91973 14495 88165 5114 53403 85430 6225 15184 6677 57811 46600 88084 44628 35567 90241 60665 21413 74195 69546 24504 38454 38249 37352 9732 151 51226 12080 41284 89237 26984 45121 54983 93043 43671 44817 36896 33762 91811 22413 52420 87914 86357 26133 92911 73226 41098 67855 22935 72398 99152 3525 96347 98251 53774 92366 31852 40670 13414 18961 36979 47648 56118 91803 90636 37052 19967 33150 87413 18446 28315 45455 87116 20324 59192 90005 90877 74912 51272 35307 68815 1865 96923 9376 10595 75430 81095 83001 92760 59486 73016 16252 93551 46786 26003 87964 41391 72790 82174 61779 60658 60047 53220 94072 14298 64735 7314 28860 75280 34977 72470 8287 91984 60785 74015 48 71846 63114 98979 41451 53072 39532 74801 21605 74442 11267 27145 48536 83618 72070 19590 69864 30197 33082 68956 45770 95219 4632 91989 50898 4898 56227 34380 55667 95584 35231 9143 95944 93647 55145 34330 91422 11898 42724 40755 13876 87254 71053 59865 49903 52230 81409 90057 61883 14389 19669 98933 49116 59369 44014 87593 82942 17658 48737 44195 8245 81731 5117 71946 74466 23375 50873 73699 45453 58353 99757 6147 64015 50182 44383 54157 27412 3328 44376 41407 5345 13426 57256 39739 28475 90223 83542 3988 9209 75365 98922 30078 3268 69878 29702 12319 70615 55263 3792 32961 92492 78859 14730 67692 15812 76799 68235 94171 25450 51734 33977 28768 74142 21085 97675 82009 67174 28206 45938 59719 68903 89439 6951 88465 34955 5869 6612 70266 41755 41833 86486 19677 32065 33596 63454 87186 14911 36054 39169 71107 76652 40551 39695 48535 75802 89532 91497 34439 23972 62787 47816 16060 67917 4520 90313 76159 87877 74286 69383 78395 52497 56767 26374 3321 29549 19365 97046 48665 494 12481 73925 91555 18115 28590 50834 53275 97834 77183 38549 93974 84503 60152 95688 69100 76147 92347 61158 31807 59365 41486 61428 85378 74583 69821 44765 32081 30686 13976 69024 44248 44877 70268 605 99002 46751 16334 93976 65944 84792 99743 63857 55693 82514 52974 15016 84048 10325 53324 37760 19164 44196 9988 50371 35010 54324 19718 1204 79879 44782 29215 45525 95588 18966 47919 67534 45026 83062 25441 65186 86523 10938 23316 48255 81342 77329 93716 450 77270 38109 13047 50547 40002 87289 99562 15 40064 9494 37287 69663 37140 99696 59824 63447 92162 20362 31763 21595 89082 14886 13596 53725 72225 47133 84775 69710 50667 60748 81363 24792 38974 67611 21563 34219 7367 19851 1066 73899 15340 70688 35376 6788 1717 79253 68080 58417 82824 47370 68798 43934 82356 53465 2618 31685 11064 52327 5129 5145 62010 5463 64322 49488 49194 17270 8706 33480 62873 83805 99709 57937 31318 71035 71644 84457 53426 45298 65635 82911 15779 88358 45879 25070 1082 73228 22977 26040 25913 2076 25661 76644 86465 75290 50840 23332 52110 18851 7447 98283 67466 43576 34154 45517 33853 13129 19638 90669 46389 17947 74325 46870 46099 39436 55403 48529 86287 33927 72367 22133 46031 65843 82027 21616 44476 78531 63167 33685 8696 59815 42869 84924 95181 49089 25944 42054 78555 96160 83128 55838 42883 14964 72061 45608 86753 50383 41155 94938 57645 13674 24176 92185 9078 43432 91818 25317 85600 50295 72481 83890 26781 47213 97204 8716 66090 82194 24240 52270 17264 38041 8631 20414 68712 43648 68951 92544 54287 93298 12491 40355 48978 98400 1102 44022 39885 85308 88744 61202 98206 20086 56891 34195 21353 4379 55162 34541 84080 3653 8153 9913 71714 42381 37137 62602 45550 46769 66326 52813 93916 73604 15511 89793 52553 9184 8084 22378 88689 54088 51069 46765 26695 82567 81875 84894 29683 59661 96252 17880 90002 78009 94609 37946 33343 68812 73933 27785 33875 33802 53459 15550 84046 15673 74989 60372 10816 29180 36170 40836 38536 56476 7728 5148 23782 95064 67811 3378 92125 24 63595 99839 74128 5691 4476 99637 51084 11028 65745 77917 1960 91074 77481 44943 28883 34906 27347 56383 12020 94986 96684 43234 61184 93312 43257 29098 8428 89560 87611 46181 89581 1425 93588 72862 89450 28370 3636 29101 57250 68573 61736 54901 78428 98871 17770 94777 60106 92305 15259 77681 26023 14356 56904 21033 23401 2754 5487 80043 52244 5186 99578 43624 64774 90053 38634 36422 79242 51171 55331 99542 41062 38011 44109 2577 4498 71122 81301 53690 65740 59098 64057 51061 37796 20617 72948 81883 36068 8329 47435 24864 94882 67724 26386 72950 28124 91059 39687 18349 49710 93067 50366 96992 42440 14112 81543 24641 18095 39538 31174 41994 61289 42904 46471 50944 63172 67436 15647 70096 15382 80403 62563 4947 52792 57960 38931 82666 97768 50998 95663 89784 20549 81872 85511 97863 34430 56030 20989 32710 13038 56375 21255 20275 47562 95950 30869 2735 51180 47412 30499 32354 3938 9297 15830 82062 4955 18605 58979 59655 44903 20764 99121 85351 3183 55728 18792 96123 83003 32453 10658 81869 87122 60305 50053 78629 64257 93315 91596 51362 36128 69418 42923 81999 73967 30690 95297 64723 48890 58936 43543 72940 1754 29652 8573 42569 59976 35208 44455 29774 14637 11501 5452 38057 70047 77066 51168 40936 50007 98153 72614 49334 76344 52166 37631 53273 84055 92800 56409 35498 46128 73756 92708 42397 38068 83355 80555 3976 24748 74774 32149 94702 64134 46874 56927 70746 78831 76643 46022 69062 16157 82089 15182 83712 10884 46905 34056 43144 30101 39228 60310 24724 68109 96663 81619 69519 83468 4918 22339 51148 48249 85103 14915 14565 93333 95304 82850 11049 51050 43147 92111 32828 3872 4607 73949 71502 34306 2719 72878 57752 13823 28268 65929 59719 62421 78465 3114 62933 21091 21231 12416 75506 15748 53507 72767 82062 17818 31006 38077 91690 59785 38756 74936 48211 91059 76201 2031 92591 35965 22136 22346 69580 13894 73346 48782 17429 37415 47754 13888 1819 21455 13430 30065 50776 21878 36011 64727 8266 91710 43119 20311 85367 41177 97811 50711 11518 62591 29046 57588 83660 19485 4683 32709 41855 26833 80730 86267 25452 79907 98215 71573 58571 91800 95139 25997 4059 38350 37359 36985 29365 7729 73356 94345 927 40913 54706 47523 34901 93313 76239 12655 45122 33236 63307 46793 22592 44125 43567 83822 32210 1741 99459 80682 70148 9605 82852 77124 53839 70796 64238 57688 47340 40784 67110 87191 64281 84201 20873 16377 21756 30037 82919 94933 78019 43250 23167 13557 96575 66542 39559 61175 99351 38173 93712 22215 86802 37545 72620 45783 10095 86541 67241 21702 78170 19742 91934 93754 85608 43712 70591 17868 91110 64366 90782 45835 16556 74932 26160 81768 61058 14552 8665 9098 13476 57159 37963 99990 18516 13833 71705 91599 75414 94939 86634 31288 93128 95128 9958 37712 58039 82670 23216 5806 89643 12376 73510 94533 94861 76497 48696 73694 6838 95617 13017 33117 66281 16588 49567 27103 59865 751 92877 99429 84148 98424 2289 94221 32539 49434 83536 64990 15644 50463 18189 35717 95814 92781 79224 78294 54553 17651 48924 66960 41227 54487 72304 72191 58879 37394 51312 76885 89155 16275 13362 90805 81273 82258 35125 99175 51321 81185 38733 59642 29022 84725 37061 89208 35906 79170 95960 45274 87669 65901 49352 45477 54801 99873 98429 72859 97409 47488 7941 77342 13464 37207 75387 55500 74382 75156 58812 28984 60945 16485 67313 90870 11480 6489 45099 31278 13383 36537 36602 11024 64970 1043 40184 92237 61456 36151 68887 77772 81125 90610 41660 40003 4619 24225 3664 10433 92033 86952 30575 76435 72042 54744 4179 17484 82658 51232 7226 63305 14286 33844 96445 90155 57234 79956 58048 76847 3264 64743 99438 64620 60445 80431 25341 1190 42836 11543 42539 45237 31717 5704 40314 20672 83793 52884 92578 58648 4579 32878 73740 59511 456 9830 35499 67875 38018 15398 51387 80594 98170 22677 50986 2454 19125 90487 44876 888 53457 36326 86902 65453 18458 73492 53224 8760 20396 7517 22214 14820 78602 31082 19512 94237 40788 62454 41217 76886 3147 84547 44052 1624 81089 50094 46272 63506 55787 85819 131 80850 97504 28161 85023 35694 90527 45543 98582 39062 70972 34586 10652 58489 65674 70473 19475 47403 4308 62252 17954 23681 83580 30712 75842 49124 57771 2852 67177 16799 88636 69684 86451 59893 62344 82170 4053 25484 95545 9623 41650 39505 40561 75170 96095 49658 43546 69366 88334 79958 61279 61421 76657 39001 54771 42851 26938 56730 63022 89884 57692 14906 90504 15999 21602 5882 19927 56994 24392 79646 15083 93085 62008 46559 8124 62869 32958 45727 62508 39008 54196 69842 74666 72168 47973 95613 50609 31561 45275 50995 14511 41837 81616 81951 69945 50601 25794 77845 80620 16578 8254 80927 97829 26634 52795 61400 66316 90033 81498 54174 59314 19185 87448 46294 41143 82471 24224 81076 43286 67043 25181 17125 89495 98363 92835 3456 24885 4310 28268 26353 7314 81911 87743 38529 21602 22878 26708 95479 60114 66303 21678 36487 42907 90827 98656 48878 43702 27969 26174 24721 31844 26548 29140 12085 29392 53745 89247 2811 36999 1589 15549 87497 69346 13091 33444 56931 35772 39745 33192 34887 72723 24683 93078 55986 56603 57769 36632 27074 56160 3061 55760 87377 21318 38938 21009 90071 6536 11270 15055 28903 85593 79321 49204 93098 63125 19205 56184 63945 38404 42337 94872 88966 86533 14920 47594 26839 15670 3592 88895 58169 21415 63795 1845 30393 79544 50788 94935 98066 34496 12062 18814 52298 37421 99396 52345 36673 80602 21688 22246 27357 99975 7877 88165 33450 14764 45528 77107 59029 78591 57882 81775 9440 8299 46010 54710 95845 1419 40594 95839 22805 22654 93743 72652 55996 52235 42709 61322 37683 15693 70571 8872 91994 15117 55511 49275 52319 93334 40560 86758 47991 21036 69951 36530 11650 43442 26551 85749 85036 71531 95210 78252 56789 65895 51702 37206 36104 38396 80242 58399 94398 39203 15800 88250 38366 78384 37292 67698 5207 56854 31127 13549 68104 74064 59695 33560 21352 53207 38584 11177 49176 75072 75204 72769 64243 99982 480 75345 28305 67751 43006 50048 74835 5916 44132 54898 46848 9798 19010 16891 74278 13867 46485 77075 38326 15459 27730 48729 59710 52272 19198 51927 2412 22897 54602 6643 74936 13101 64036 24045 48662 252 56221 77576 57351 23740 63638 34304 58068 82154 17851 69615 39798 70738 6446 51943 67225 42016 56889 84822 16905 58563 87755 78765 24810 55859 40342 8402 13853 75952 91333 15484 94974 11333 13630 19175 22317 76479 29200 74070 99639 92367 29921 98331 34662 28606 99454 52428 50874 2479 48101 10116 34264 55081 80825 76445 98957 31379 70298 96572 33547 7449 89483 15772 87959 69427 92474 17931 74874 65982 36580 27399 10322 39498 50485 37122 61680 77872 12929 18736 4891 69626 71540 53658 27716 80278 64922 7737 4350 90935 1049 23057 74420 5727 45027 96874 68310 9733 24268 34654 42140 59702 85875 93867 53385 85988 72795 54466 88682 71257 45269 87264 60429 13629 39006 84882 1463 72268 59086 80831 43854 29847 4109 4167 66026 5192 1314 4132 61355 74327 39964 97287 18711 11729 96802 19295 76227 5550 54708 79772 55880 30904 1809 19285 46646 75140 82521 47027 65728 73210 33557 32712 3569 28401 40160 16467 65974 85953 3 23300 58233 49640 36603 97885 2747 50017 28316 56444 63095 99150 48347 17030 67881 77550 26834 27579 55310 7228 75814 6014 75609 71528 28490 60726 66106 88302 73165 48711 68891 33414 9125 21533 47203 28423 46377 71141 95719 39546 2333 71788 17467 90740 11846 32235 62046 9674 74825 26703 58137 85335 28488 39753 58856 20580 91055 27 42620 70116 29771 34238 82220 27885 24193 49933 32675 74944 18941 60583 87017 74031 56462 4038 29635 39021 28297 83965 9518 88112 22196 15300 70300 75740 87264 53595 7189 53641 63486 37370 58045 31396 70202 36986 14333 40725 76491 9991 49215 24145 31943 8580 46234 87872 37295 71244 42455 47955 37831 93664 77616 25874 23754 22525 6072 19135 19727 3624 98236 19604 58046 3672 34237 5133 74349 83327 27386 59003 37915 39597 56932 36909 28467 16782 47166 15427 62617 13240 74311 50786 97509 96199 65316 5376 88400 40991 59031 64340 99668 9940 96806 83739 53137 68202 45194 58842 19306 3003 77706 24546 86298 14360 42933 66874 40434 70336 46396 7610 79474 60471 7880 92043 9740 33210 21049 96417 40407 35211 94633 43320 23624 78669 8949 47043 13955 32193 94361 58235 1125 1481 67985 19869 59489 11232 5034 71845 11773 65738 50631 44294 88533 45849 5567 30984 43731 62450 89373 80805 87225 99201 72541 87495 13533 86914 92150 24612 278 66951 37153 20428 11170 6272 43961 3398 80444 65078 12149 64740 58249 65360 92197 99726 76364 13968 2433 66732 72908 99589 64282 48681 26870 54153 47039 52182 72984 88842 57342 46681 54256 37876 67492 19128 51043 66794 9165 25978 20151 20973 72636 64588 7042 70018 32725 78820 23007 6840 84837 43314 61593 91400 89190 90657 95384 55990 96256 32354 2625 80030 54046 5809 5777 15693 94114 18386 36511 38419 39918 95503 8957 55738 13173 50984 72784 68912 85492 11166 27712 92436 15483 24796 99527 78484 35244 164 97361 35964 37203 67148 33670 20899 92557 59515 41391 32727 67684 68255 2028 26264 66732 90460 44840 27124 64124 57534 37681 65162 36707 96896 42827 34765 3 66850 58282 79574 49003 27748 4993 83223 44877 90557 56720 44734 68883 33027 28741 68660 21810 86582 34861 67454 6620 19613 60764 78192 83888 13009 37062 24948 85640 94112 62514 55480 52487 29909 80671 38874 30321 71246 27484 24483 47379 19166 60676 59946 68374 72785 89974 22332 42776 54017 26207 94621 75911 45670 79433 76210 77273 86314 77535 30478 59180 14036 7854 12765 98409 71529 89183 79557 56534 74865 72552 22372 30014 16627 86822 49819 86342 85750 26605 28145 88313 65868 39908 78104 96182 65796 80722 33057 8743 2037 30741 90855 15856 40938 38637 99985 94352 80556 92086 93585 16871 24191 17974 40977 46172 70663 31956 16497 38960 65594 59537 81886 99720 62483 15038 1028 46312 85085 36814 89069 51815 67206 20896 80975 18761 72587 97699 72023 30700 28564 27851 57276 88571 74431 71813 43066 53249 78275 56667 28678 24302 54739 23060 29696 1708 62261 55739 73937 54755 80707 96290 82199 26887 86171 23022 35851 63394 21350 15035 99918 59319 83427 35112 62184 34878 59767 55826 31319 83909 76603 74857 8865 36793 73022 92523 91674 61699 89084 79657 6455 7038 70117 80806 31182 23581 55866 70114 47859 54824 93922 90432 80930 93726 34329 72602 12724 61018 38447 44715 41394 6951 44177 34599 34758 66649 97648 86557 27357 97776 2239 82724 52365 97127 50570 3884 63694 82075 36876 83361 44766 56085 39921 16028 59163 18033 66909 89204 25194 26028 1540 82768 69387 85719 52180 35866 74248 57533 60930 91997 71566 38635 19593 28963 81905 83676 52911 4296 58716 26644 77168 80788 52382 48981 89359 95162 33971 54630 22393 64380 57908 57168 84893 9746 7839 18616 62998 20913 3410 54725 70265 45961 62749 63166 56516 83317 40609 65527 49007 67401 79578 49215 81481 88443 59468 94751 97199 5824 8189 67810 86509 19964 19095 61356 49806 47192 32427 1773 59702 35376 12672 12828 14131 71190 44972 60135 42005 78620 65914 72289 39446 92779 74688 52906 93757 90308 72748 99492 70707 44573 7441 29425 70974 86253 4606 20647 67089 72374 37570 77898 5762 42419 52743 51059 66588 30416 50474 82882 44533 5768 71403 23795 80727 24799 5228 97316 48955 77211 73840 23850 59156 6140 72593 67167 32962 49833 49426 82569 34731 97449 51023 8797 12359 71984 54282 44497 95694 94661 13035 31381 64555 17543 6420 34310 9715 34183 67189 81475 78115 95595 78190 20721 92608 36839 18344 46193 37551 72500 9006 26903 86418 23962 59239 24557 87895 87183 19133 579 76808 92612 31937 77156 5049 11892 71589 16279 26487 30381 28746 96328 98068 95058 3833 60458 86919 67979 13723 80483 15570 58460 21055 79313 59507 38050 55321 62892 42908 96111 19829 19590 32883 5904 60229 75113 94977 2782 29171 86731 37529 74128 98759 14546 24343 63291 21253 16476 16267 49845 404 83717 87791 22294 707 19225 63798 21680 75439 74502 47205 34536 79063 11254 68779 26115 84794 55518 94852 52163 40588 48946 36860 49679 66102 13240 18725 9915 85231 95405 71534 43497 8476 58342 57859 81042 88449 49523 705 49772 76425 51189 95609 23790 75511 69378 56364 23225 53182 95943 45556 89533 33166 44944 6473 66618 11270 34127 60321 33892 28063 73974 50250 74898 10783 54651 11544 35534 9597 30587 10547 7065 67688 10899 15981 82362 38131 15415 3237 43588 79007 53100 5673 89187 73545 239 93672 22219 98734 88780 25658 49369 53639 98582 77733 77106 30799 6063 41111 47917 88859 31266 60912 4997 63657 11932 51840 77864 58841 65277 529 29245 99938 26575 20674 24928 59883 30951 70314 21896 60366 83411 90910 44967 20868 40190 40541 22919 86674 86121 59662 20490 30217 66078 69650 91762 46335 41837 86576 31008 99217 48615 57236 19786 12258 92048 47805 97239 69337 95067 65488 94213 52850 11457 31931 87276 79127 49639 10239 1353 30420 17324 57516 24107 54772 15504 45140 80302 86028 95786 54037 10041 62446 23220 68944 27816 44302 59176 18380 416 72806 69040 43333 29276 81338 80384 62212 53659 57625 64747 23363 51174 47149 42944 28405 3429 64216 77666 78147 34198 97915 2708 58503 38061 63969 67327 10695 31292 95967 88714 88753 48524 64764 54677 6336 78981 17588 61905 18445 18373 62716 49522 24785 89573 91021 3466 82692 959 68426 46455 88832 65212 60395 45373 83150 47888 97752 5936 87064 31417 72644 79591 20391 10027 70995 3203 76388 92565 63146 73710 93633 33763 11967 77120 74580 32869 257 40786 83551 4914 36006 53362 4706 94858 36652 85774 62854 40840 2251 3199 92365 14506 49566 62887 15108 88820 84959 2144 72392 69220 73496 58998 95399 44533 53297 68002 8954 57155 73167 13359 99721 68481 40735 31372 20760 79926 19281 44378 86259 63755 50569 39970 34817 1230 61558 70737 14081 31796 35797 85508 45813 82904 93012 62596 90564 31460 3536 13214 765 72634 98896 52961 47408 7741 40764 92934 770 54145 66879 4225 25337 69186 87038 17437 66497 41924 52949 16220 34309 34352 42572 79715 27745 41729 30646 23554 81013 80192 53539 32053 5848 84167 37315 13337 42054 34750 66558 28344 44954 35067 29171 42911 23379 76768 32370 51287 85139 5091 91234 49390 5139 9829 61396 39430 35151 24606 87072 16885 95294 96606 33949 15114 81306 59692 47279 83199 15780 62220 67309 77776 30071 56053 7616 28898 19277 65158 2501 81798 32188 28460 61554 22619 15085 41568 4167 83128 70374 28366 34759 39855 22625 55310 79989 4929 43778 91207 74416 34651 72780 22065 70759 54231 4813 89680 29182 15849 32371 51693 42002 49392 95601 72988 80872 3288 16993 98126 89635 3779 82905 24719 29517 83862 581 29857 4793 75998 29044 77133 11978 30812 95493 53827 61773 22613 16233 92113 47401 57392 15761 74884 66488 88108 35937 68336 27661 89155 21565 94319 92070 3662 86797 52835 3456 91672 9369 31252 53906 51028 28253 34489 60717 66942 74968 93359 69172 31853 27051 43907 73232 23351 4325 71188 85785 29305 79938 91612 88807 35554 25587 54095 24754 65505 87607 10412 62529 25702 38937 73343 88091 56908 28428 59658 98584 5607 45777 69818 92596 22078 653 43763 17715 75505 45695 64267 78261 7662 71782 87616 80603 64812 74681 89972 55577 45721 3897 88248 76333 31120 81369 57396 61562 11211 56391 10710 60699 3470 35387 24115 15723 87729 42459 31631 93777 61391 46182 30884 61465 46457 60615 57243 50664 78217 25251 71311 84760 83575 61754 65858 76255 17349 55788 90093 24213 37489 24302 48541 98065 77129 45550 51488 27462 95304 23559 17487 62148 35920 58708 10394 67403 36420 19118 60995 47747 93873 55744 76579 94472 78293 83414 15882 85043 91223 67516 62942 85663 3306 67283 66901 41994 40741 54867 51368 53577 22722 1396 19537 32584 94204 58090 76428 7799 38671 74521 90642 56038 48520 81852 82643 83696 3418 78024 27409 64659 47896 69179 24962 72173 3905 11137 93615 63433 49093 30432 93589 59352 62102 5554 27928 92148 99033 40676 87157 82337 90313 38339 50841 71988 92835 72733 65298 7165 90134 60746 41342 90181 84289 79143 63693 55485 52752 73048 59324 99464 89450 58153 24682 88508 14714 90194 38063 55618 50740 22218 60756 70726 99578 48518 39137 60659 96442 40191 62678 22305 64432 92802 3547 78460 54840 81570 2362 50275 19735 42850 42752 95404 21983 67557 5853 45745 50547 25128 62915 53004 81358 61726 75774 95134 5323 21596 45334 3709 32653 72092 71729 84778 48439 60862 92951 75524 38312 2360 36629 15075 7030 27380 38964 54457 87324 9645 51782 60397 61720 11777 33133 11024 6385 39033 8586 79867 11142 70815 56864 42889 24874 80534 60658 88427 51012 42419 79902 71717 5163 10628 83453 37180 12376 47010 26711 8937 46324 59661 80359 85951 86247 45808 73636 6198 32918 35744 1383 96563 57035 1077 72731 18834 33316 22375 18584 55210 52282 67706 98343 24148 79927 29165 19673 29014 17976 75188 18931 46912 9008 84593 38898 83990 33724 79919 1812 90956 34837 69181 61691 24724 88610 42504 52359 99425 65805 51995 16503 78793 22060 66711 60048 93536 28575 79765 8899 80407 86251 52383 67988 60710 33356 95528 10030 34118 73592 48787 38501 27584 75758 81589 53541 12381 11278 85258 13299 44484 18457 87156 32898 71044 20202 4222 3057 91197 72133 69683 87027 3866 41984 85642 19376 18133 31299 15139 91750 29942 99179 89504 36419 18008 47897 48416 86120 66142 78028 34164 33202 46141 57102 29562 39826 53513 42321 91942 74893 78536 27983 55090 27740 44431 33105 95826 47689 4001 55759 17001 22588 14016 20808 83345 13657 71787 50918 28003 19749 81285 6990 5136 72696 80705 93866 80889 66230 79098 5166 61358 98566 90525 82712 30494 60565 371 83103 62948 37236 20155 19397 576 13497 58233 24287 44280 27626 45568 15518 53476 66181 97482 21983 11029 25998 91896 50125 23802 17078 20750 16254 72795 37193 61617 36631 30932 92110 9162 76574 98 85833 68826 2714 31192 23101 91023 67260 15851 21974 62811 18192 47960 13941 53650 51766 41178 34084 92143 543 31813 28954 45284 80896 91506 28552 79800 64650 94317 95675 90889 92793 81367 85228 28533 19489 94873 55754 49505 14850 4492 84830 38202 11154 19823 21214 34779 63616 51842 88935 61583 85894 45245 13812 51263 30772 6439 46537 72988 33650 61696 19022 13516 39997 18701 61584 7868 15670 46940 51294 31242 9592 57045 71601 12103 20440 56856 68982 49197 49133 85364 27882 29999 42283 44450 65649 490 68857 27318 75325 57498 23132 62775 56889 93734 41959 50521 29407 19895 30680 87143 84149 57549 71443 44395 31460 55736 39909 71164 95654 24134 64952 86313 19202 8086 84830 3327 76664 58912 87140 2911 99728 9837 325 66089 39440 12720 69330 23099 19573 35653 90745 59165 37520 48357 22196 82319 56295 99751 97862 16115 81902 54556 60346 77863 41190 3543 86297 3804 36634 75535 21726 84005 11316 80556 66521 49101 69680 26802 57162 70936 194 35258 90262 4627 96104 71328 71697 63966 21937 78563 53245 36401 64296 11041 92001 45952 66245 540 92815 37623 70716 53824 33839 87427 65837 67539 15729 5294 26330 97332 38885 5091 11215 36672 97880 81218 45285 43908 12324 73952 2072 52636 24719 58868 16555 74198 5046 90348 46491 42258 45605 27070 1761 22515 75770 86666 75952 31007 27575 57363 52217 73590 21939 71884 3202 66177 26256 45354 24704 77887 19010 22653 23507 34486 49233 9459 97897 13337 19490 44098 27047 47461 92252 95976 11401 8143 58000 56521 29323 18850 29436 94813 55491 85839 81118 5237 53023 83092 46763 1493 93732 39800 38498 35791 5380 31397 36060 13060 38732 30781 83683 83569 56136 34354 50679 1710 31241 26520 85400 11021 80510 86222 48342 59507 20134 17846 81139 45382 20963 99530 22576 10485 7518 7408 50838 8824 83654 27585 36829 51808 76655 2341 29228 59986 53356 94416 17619 84788 18391 45889 10252 56192 94674 69119 17345 91199 75308 73966 7828 39292 67659 94737 36089 26929 33753 44878 41457 85187 62057 13629 11559 10759 48774 18601 62034 79135 95713 74378 64411 78349 69246 15726 24044 29492 51858 42793 93674 22803 23484 88717 92048 8238 97120 49866 97557 48137 90195 16196 25618 48124 80323 73036 35576 98906 74262 62212 15330 87293 54230 78412 8340 2409 71922 32588 18411 64395 19920 69772 74449 92890 2591 19036 51514 20268 57122 22879 34083 52964 63725 38385 87042 36625 13936 6114 48870 25264 62309 66934 99887 26195 82341 44448 16187 51404 63944 88533 90776 18451 54036 61459 26731 81315 53296 94278 1567 87057 3153 87515 79557 83009 89756 22882 37695 95362 6556 60586 16300 84256 45664 43068 44231 46245 6122 33511 57634 25198 59838 48387 39034 86393 88114 1864 24081 72919 65416 82273 86230 10932 9217 75254 85134 48065 37965 20793 50096 35652 94735 17027 53073 77420 46371 14940 39836 91245 20869 77021 5020 14384 76363 83698 10619 72373 83841 30647 48753 49971 55627 19158 26581 89387 51058 96200 37787 71987 61189 91706 71302 30623 30602 51711 12597 97468 86096 63495 58849 35239 32721 38011 69685 75506 16399 44906 98461 22068 30203 95641 20522 36261 29678 56233 98980 55218 78584 74699 62601 354 25919 10580 97647 10771 74530 93573 67266 31909 60955 33520 20003 14320 67730 10126 12654 2941 71358 36053 31743 13391 14328 49715 2840 80152 57492 71837 42960 47724 3923 11896 67707 89619 15726 68402 26217 99058 82808 31428 14225 84443 40792 43477 39335 53836 26088 14830 96772 5158 5935 12684 68767 49405 15702 82719 74431 97394 68531 59758 65289 82816 80142 89105 11345 31117 59373 42678 6521 37878 18112 26544 22595 99878 88682 13643 1347 50179 31188 17784 18540 53167 47679 66359 34026 95002 63157 15041 60914 20021 36273 41701 18700 64220 47962 14779 21923 87513 85724 52858 31577 46018 8655 70050 90754 23967 92806 75251 29896 42485 16734 14016 98032 26511 93421 87443 24055 70523 44277 61326 67354 31252 6147 67056 59932 559 32179 90071 89638 64094 51914 86772 37081 3242 2597 47426 5948 95663 27670 82559 87231 56801 98204 59369 68124 64248 4227 10850 61176 33579 25966 63938 84162 67893 55132 2306 85636 61471 50415 98011 91389 67482 82646 73536 45578 62378 30555 74477 34668 95714 46865 2430 95995 29450 17056 45437 31966 45206 83500 74966 50200 29373 69212 46040 23362 73190 77991 3975 93589 73638 66141 36803 20655 58135 82748 47633 92782 70494 58481 44375 89381 36088 97053 46720 84938 21785 35371 94047 94934 78150 6978 25105 23705 49824 61841 11413 99469 92513 90140 15702 54392 59410 26898 11614 36186 42540 62741 98560 82370 66915 88476 98056 46763 30656 4528 15973 13374 84124 92930 53708 4 90774 63875 99267 92064 98970 11180 97124 17164 98630 40623 43258 50783 75395 70497 39335 58991 65790 46835 4448 25225 20398 16984 83449 32043 84819 47303 21679 2186 81577 72111 87751 12322 33317 54179 76110 14359 79837 78311 90260 98868 93597 45593 99841 54963 31219 67345 65610 7696 74292 46849 83683 26358 50168 82380 55400 24935 19396 70702 78890 62186 5803 89983 49528 55318 69468 12135 7849 71421 2170 46417 26736 88792 66443 74385 62732 31713 58889 36365 68697 76537 21476 99397 41298 51690 8626 244 64935 92178 72329 27084 88022 20208 92337 30278 72394 85082 22746 39261 33726 99200 52197 56729 63901 21357 78086 85718 69800 35232 54370 33271 94304 35630 28625 9320 86178 2151 55758 26399 99217 28101 15998 50802 19957 25270 91189 14926 14994 16490 53164 30386 71124 63880 28439 48593 56997 45909 27241 58166 73946 36986 50860 58368 55662 40854 5550 50272 60612 12620 7709 14461 81107 13134 44009 33593 51728 27042 29641 94975 49798 91212 8029 9983 62167 59316 48107 72361 36294 38625 81256 85981 35499 21852 54890 51539 34293 86299 18230 9126 62036 12390 23234 63561 37525 91049 32937 2847 33518 81442 97701 52202 14827 92713 60635 99560 23043 19381 97436 21524 40581 13633 74504 784 38913 10668 93116 95669 30966 9926 22161 77755 25471 23091 46876 2410 4789 61298 97625 51755 9674 7620 64172 68403 74482 55564 8929 90723 1160 28380 29054 56619 32930 37266 91972 71476 48568 66989 58413 44664 69454 63363 75012 32888 94021 61731 37641 72291 10712 36677 61778 59447 90028 41897 36733 20210 43086 79370 16482 31073 69870 17011 52759 44726 15208 95723 16411 41286 6096 19383 16528 71639 88839 36718 89761 13656 1571 10981 14972 59557 43832 97732 79181 41698 84854 89504 73258 8907 44483 64534 35181 14177 85869 7193 13339 38615 57417 72720 13811 83444 49885 32611 27755 73184 73597 35817 66640 70463 4275 11763 9095 96865 31384 84033 45239 24890 85430 7438 75832 24592 72365 96346 411 74342 73821 72408 10200 97168 31134 5371 96915 38554 13635 68278 29597 2003 44123 37868 16508 61175 99789 38473 2024 70233 59014 24205 74889 46290 49586 14323 80786 13252 6050 44204 9288 82735 97858 79718 98245 20620 20767 17548 21856 44915 26064 59592 98793 67036 92879 72749 11001 49679 53260 44497 90082 45770 40347 95810 98665 27958 22074 84121 2962 80848 62278 97245 6989 64678 57415 15922 32831 21826 29240 95721 1945 4188 10115 67682 29193 3085 82633 4614 32216 21567 56604 86534 63041 59868 62818 41441 86702 70718 13853 44134 79427 82042 55140 77173 46789 34586 19039 26370 28322 90428 88892 94853 81665 20119 15235 34545 23536 18747 6320 97268 66937 67060 16505 16397 93781 74651 11309 36173 27308 11251 87328 2030 69392 89027 86432 3812 50331 941 59505 57038 75547 54628 24476 73898 27323 90991 37899 97738 45756 12481 74125 2341 94789 29846 47489 69062 28036 83839 12279 64549 6439 49755 47032 97751 43754 23485 93701 57478 3348 9605 1810 95994 83721 8202 86116 52394 67446 81033 28039 10233 39800 70596 58042 54929 7479 95701 44157 20754 47330 76480 62567 28593 81288 74532 75060 21722 81545 17783 12789 50054 67590 85863 94065 43953 87697 17932 56454 94957 91809 64085 61435 67852 8665 51097 11599 26920 78369 90019 93003 48600 63919 43588 95746 27274 42589 18841 37282 51899 52792 78317 3605 50398 20219 81079 80209 30945 80194 61013 99272 17022 75724 83156 35288 74844 71998 26413 29562 2618 63635 82814 23311 72716 65675 74067 27996 93506 69774 76884 84735 71671 84334 21802 68118 51748 42795 8098 94168 54100 89957 20508 96334 40249 74174 60941 79282 61192 50689 3799 77131 14905 75440 98971 56712 57089 77237 8256 43931 61790 26251 12888 51929 97760 77561 83306 2008 34897 1743 32127 69591 15112 49653 99148 3750 57645 64828 34500 13425 24982 1172 20122 72243 71840 7391 30089 7960 34609 96702 81695 69015 5586 64851 43535 63453 7642 31704 86544 95711 42549 43280 87112 64039 33098 39160 96695 44638 50297 41404 14859 99274 69645 67818 84925 4896 52922 44095 7024 91037 7584 14655 4584 63113 64988 66424 97188 82726 15918 74017 95074 58404 52102 33101 56418 92988 75126 32952 89777 65012 29261 32178 65786 18783 72742 80499 48124 58580 92969 76570 88088 2628 44018 26615 93258 98958 77574 21937 86040 40328 23924 64823 12283 3830 29067 52881 17015 82613 68702 73126 57682 12361 5747 38243 13461 46484 41699 51654 63696 24657 15057 29484 52862 19444 15264 99790 91117 82729 40475 41053 63632 16262 29088 12665 60750 45323 43642 49079 65913 93726 75928 34007 85034 29749 55872 39067 80385 86742 14245 56119 99417 64339 15603 24102 80313 38578 25871 26807 64828 37688 46477 2339 19174 8076 74033 5208 5950 61808 82423 64912 85998 71370 92264 9468 83624 31458 60176 14575 33140 12736 29950 73261 15839 20434 47371 59181 29627 98253 33651 7514 41574 8981 5744 97805 25328 61879 59137 36406 23492 60162 88587 26481 1412 9400 90440 70125 4500 57221 70251 28058 97854 45862 46750 47568 83758 88243 42119 28153 62042 20522 27085 49707 1336 38663 96897 85129 85398 90777 15813 43248 62832 81977 38492 9694 81147 11195 52366 29336 98606 27685 36320 81334 34695 43874 59500 16335 70141 1517 43131 84636 36816 86181 48161 26391 669 73538 58112 99697 80554 19191 16016 97755 10128 99522 93614 62331 45310 32335 25823 1816 92445 20332 60343 19429 48700 48624 84809 69399 66401 36376 8121 48554 19666 43109 84564 91022 93485 13233 82931 15695 66034 24005 86684 33890 45656 1008 7425 86018 9613 37282 37652 89576 70472 54415 82827 5383 14737 92499 91208 97178 21653 22361 78704 13012 47556 15228 89487 29963 86897 80728 89878 75187 43187 22298 75107 94238 40183 81272 5308 43617 17917 95785 17589 21104 45789 26307 60029 19895 80060 81819 12323 34274 18141 49294 91264 29629 49242 94891 11364 61712 8318 30335 56254 91904 81765 96462 99570 2347 86550 8681 84220 75705 90781 67806 874 64639 47382 10806 37167 22489 64353 61984 39121 90961 74055 1384 31184 78746 6142 77865 72696 30825 29826 69845 11027 33706 76669 13934 49447 38899 30284 77294 47998 79736 86900 85137 52276 53442 45382 86839 1027 44663 51168 47667 61808 32173 11650 81716 93892 81561 5683 52448 56767 52149 96825 42827 32803 42942 52920 24519 82521 96287 27611 76590 11129 50190 43142 6683 66901 16300 15700 76033 35706 66597 17575 70072 47965 9132 27880 3218 45097 65836 97770 2396 65151 83268 20777 36150 75119 87779 1903 35171 82129 35330 59694 36003 70766 48312 18687 3017 90153 88279 48343 15473 52657 96735 52056 39419 7636 87970 10531 42185 54952 34785 90322 86826 37437 90632 98756 85401 63660 20007 75090 95606 89617 70235 57622 57588 7944 68077 46316 74388 4243 51639 52176 53730 6110 77930 29521 11983 46929 86409 1290 17761 5809 50545 53852 1902 58525 45199 8255 43552 8973 10853 56394 56417 25420 77283 45477 20196 69108 45346 95053 97121 73879 82445 99198 95627 81700 22198 77485 64758 27856 83465 9355 37049 77891 99985 55334 10263 96254 69412 97563 19719 62921 95536 56711 599 1416 79478 73005 16799 72947 89201 28763 50612 23304 45577 6646 26860 5309 66088 50984 40879 12355 29449 79804 64627 64644 90239 25681 82330 2958 11719 17572 63978 40542 59711 6470 2760 93186 29362 65297 73402 81509 86802 23532 1656 5186 9995 26298 27146 54902 67172 54469 20731 72988 95259 87722 55089 56712 20631 329 91358 73022 42197 65035 23932 52559 89270 88176 77820 75732 11981 52635 53038 51038 23392 99221 64355 60896 26864 65463 82315 11516 90754 5329 6748 97635 36044 74591 58390 32425 14309 83665 25390 80861 82569 33655 69320 92010 97055 85942 88348 10041 25666 3024 32957 44664 34649 59138 39240 17026 69391 75463 80123 27602 25887 32981 46315 81752 34372 43796 83207 21390 71375 87125 10836 99792 73212 16556 10371 53263 14568 43741 83439 21684 73234 6930 63753 14818 50310 90901 97162 1752 62897 33514 42439 75005 98590 61726 42014 21370 30646 71220 20581 69513 15569 52819 3512 33298 76511 31611 93275 97970 21746 6926 41499 51248 60248 12963 73299 56100 78465 48449 82824 91875 39185 95630 26525 90745 88580 34264 59954 86825 62178 59987 91104 65488 23629 67757 50303 2808 81271 87361 5170 56526 16290 29349 81145 68436 84739 33293 70526 91265 20866 28511 66251 20833 38823 20381 54628 40821 60397 23101 89336 68077 5386 34574 73226 81690 88257 6656 72188 1196 69071 89328 15006 56070 41637 2359 3555 33543 69232 64569 66 99026 8159 21916 46116 11370 58502 93010 84784 58127 42114 23590 2697 46031 57611 14070 92848 95730 15087 59951 28705 20988 31805 91427 4942 9784 23380 429 36622 1165 67700 43628 14294 79882 79303 25738 62127 96740 17107 26633 67814 64129 60776 57751 3462 48167 1526 67722 31477 61305 34990 26076 72201 16671 40992 84241 3543 28342 7059 73217 66797 84127 75188 11427 34649 74322 45792 17787 78048 28018 17917 39404 53893 61665 91589 19937 65970 19096 79679 30880 24780 74391 34677 72984 88046 44400 22207 99886 762 43196 51808 2715 86838 18487 99468 94782 81336 17420 18375 52967 26153 93175 81819 62529 41731 43362 17611 37894 89337 47219 58486 48358 60757 17945 70705 66501 83785 71729 54382 40097 48394 98011 77551 31112 8366 40638 59643 37053 98368 60760 42038 51592 37670 14966 51334 53337 32834 74704 23099 5906 77150 85526 31694 64380 81728 62321 92220 66229 48625 85556 3924 66471 81694 71663 70789 64537 24441 56906 21935 31997 49105 26824 55659 67161 4715 31940 45582 99520 76585 46618 33323 96388 56064 17714 56178 22529 14546 9735 64061 45201 2265 58875 10071 93233 89768 34343 73123 95053 77648 44362 7920 55679 55440 96286 60294 99727 53694 84504 80681 87318 68778 32116 78676 83 62399 48813 41655 61632 60499 68398 78594 56331 37498 13649 38053 9031 6657 76584 67415 18866 63 98854 17110 39473 24338 70840 68740 25118 36386 3745 48190 68110 4741 66185 24047 63957 7509 29671 80087 66455 27186 52087 13301 89666 1631 34752 17659 98002 53185 97571 93741 84917 5622 1976 95689 81977 45703 39769 41185 11293 73557 4840 16897 61457 25034 81603 89997 28061 66676 12688 61423 72451 9667 65903 96797 10555 3419 29385 48500 94652 68129 72563 76383 77285 22072 38027 22077 9966 84837 57513 30769 18105 50105 33337 69331 63630 44085 18029 61841 62800 8066 40944 28049 9886 98580 49079 70204 82482 79527 52759 11848 18861 6646 4173 55572 72734 63720 73698 22142 59114 92018 99555 7621 9379 30319 23840 85200 11075 6703 97826 74215 19042 36499 69559 1770 9302 33588 85194 28174 64046 92664 39599 36274 61469 64730 81098 18060 64358 61710 59606 80925 99876 99989 70127 69445 31799 63215 70492 43598 17835 81586 39312 20681 16567 30572 86655 44194 52870 89025 81321 89107 44990 65573 11918 7090 1539 25450 53620 74 18210 55662 68816 36577 91637 21305 66310 63060 37669 6157 45714 78376 97594 45406 30834 48996 18430 59819 37815 36795 24270 18049 4959 86442 24368 70903 49389 48890 31665 10232 8898 97526 25860 99092 6513 67176 21449 45889 84883 2206 10229 87767 74973 28206 47422 21373 65219 42485 7873 37525 36052 44628 93377 88710 69737 43506 47018 56535 46912 13328 69515 68431 65855 34795 11143 72121 68046 70349 9216 89919 37938 8956 44768 6098 29130 24338 92387 4749 96875 56025 79871 9040 13692 18953 66311 88104 69749 96304 2037 80589 52616 18159 50002 95680 82185 18059 67024 10124 4418 68190 74612 6297 11500 27536 34187 73358 23241 5465 18177 36633 32544 25917 30083 618 50788 42651 16670 77215 95714 74318 79577 24910 9599 20301 74914 21374 19310 9926 7486 1994 97096 52953 60192 81274 77496 42988 10463 7771 80880 68266 60099 89360 68128 75649 35756 6287 52620 89548 32623 51490 77624 8940 44698 44592 66420 36451 26524 75780 63132 4882 31497 79309 26924 42003 51075 44903 88133 14353 79621 18782 42605 41367 91377 1750 73062 23180 36338 80463 84251 14367 13181 91807 24818 41622 97424 37265 31376 20699 74501 36509 76727 63731 59338 47589 92805 90590 98575 17504 35798 93943 52807 3315 18350 73141 66476 59682 67044 43626 61260 40812 69107 24624 95369 57157 96628 22563 96161 10575 15743 13168 206 83497 64692 16538 94122 52586 5731 34128 1837 8329 41747 23463 90643 91769 51167 48962 37410 19597 6844 4689 37110 26742 35673 73677 90126 95606 31429 30079 98150 52787 95020 78271 64892 43972 4966 39742 73655 34084 14625 30284 74840 32752 68329 23411 51235 41730 82681 38552 91256 82021 2578 27723 19888 82730 57188 52890 68055 52943 56295 92386 35261 70692 63502 29956 98272 42289 52466 20404 32146 2558 51360 49242 71444 14945 63841 22747 80843 52386 88820 5146 19135 93330 1821 45188 63497 61309 78772 37394 91957 95840 80567 57867 90252 54629 9975 91211 28969 6971 89534 67934 45675 26360 53346 30834 42316 10797 23437 39056 86012 56545 38355 9442 71411 29229 86043 20101 24132 58363 46959 32756 47458 43513 60624 20792 9094 32850 76832 69227 89085 90399 82300 44339 64391 36862 68683 50745 17321 43424 46494 37694 33974 33356 48275 22305 67345 41058 18780 88092 11562 68903 58171 68798 49919 53286 98517 77730 92514 19743 93634 89713 87159 6192 23775 97082 66711 86989 38906 82785 8108 59846 49227 62875 79033 27870 70493 25471 13238 21532 54697 32050 65991 41552 58172 19625 67499 69927 61687 39050 61606 69339 17842 74113 58034 10795 62484 79533 53298 80118 30560 24651 44988 28909 28137 77352 78986 45554 41625 70090 21582 74992 54457 63216 67281 96662 20965 4909 18855 19473 39850 60816 68203 9623 56836 51637 48025 67961 85518 91928 23243 35192 13925 40076 38706 374 60547 49936 78009 1170 85456 99487 25441 57038 60556 40684 71240 5362 27676 59261 17776 50662 30805 41817 11926 87560 37327 32705 42678 80400 53424 68313 49249 63237 52853 67436 86680 51490 14694 7921 12472 1002 43824 77201 54555 98162 73494 92754 83214 6811 30362 94534 43795 67740 5126 52303 26882 36380 40270 62171 17092 26326 95980 481 43113 75138 48819 45640 74866 47639 42728 65886 96536 31488 38470 75003 38708 82922 22165 84119 47808 30952 62133 24511 45216 23920 19309 67164 2939 3994 53578 1542 61550 11781 37594 75313 91444 9938 4872 32655 61261 83962 76916 82027 39058 75454 55893 74577 53011 12451 16504 71509 73255 18057 20680 60662 82830 72991 59471 27888 15412 36795 5013 50289 69876 234 27743 82532 6898 23104 42234 95001 53735 78436 65285 28718 21083 77096 14579 44163 72797 84634 27652 22454 14859 32331 86906 6216 19860 39989 16257 68428 4630 97930 79988 56247 51245 25032 45655 20605 75763 9301 11293 38596 54062 30121 6574 14645 43301 54809 11226 90779 30986 36272 55747 17083 4158 2207 73657 9306 85147 38633 29867 70863 8597 99716 98599 79940 32538 92595 38585 60992 3536 18075 36079 79620 31040 51154 55492 25099 81850 67487 64302 57409 27439 52378 48677 31648 50959 2763 47969 32283 87848 94198 19705 96246 19410 25946 57551 56360 10255 95445 72680 82193 20524 50390 28254 83741 9544 21790 95236 22807 61500 90736 10029 10229 78863 71262 66820 42356 88573 66990 15423 13223 12756 31090 41565 30175 74294 31602 80065 7979 65771 3768 9403 37833 89692 71700 64852 11930 27558 30356 47010 74037 50527 91444 15088 91365 4623 96922 10231 15601 8899 90383 94196 46947 17292 34420 74692 56477 74847 3960 37823 76722 20861 61103 66965 84394 42678 68337 58315 87869 56887 11625 10003 41268 49588 92807 72178 13778 38417 10807 56049 5533 90953 18221 52785 85476 18927 64305 60430 59392 68866 78893 53806 86701 52981 17687 3044 21343 72064 26491 65166 81437 55790 48280 98445 1099 87438 85840 77748 47999 63922 6506 65858 32318 80944 43717 5857 78353 37447 99685 5613 40153 3152 27913 68539 29324 11512 15436 46653 97856 31708 56966 21543 77061 42016 97367 34215 88469 70479 37642 93487 36407 77576 28595 55408 98855 32477 60551 34563 16593 89812 57889 6525 73078 92602 54802 4975 72451 58074 17704 28989 10627 40448 95338 52185 22517 85343 40930 96454 50115 36010 82931 85757 58775 86238 93073 21275 64853 9274 58923 13133 48712 6960 85164 24867 79788 97858 32456 55368 79472 88135 94116 24377 17660 56538 29484 78967 61747 55320 4542 63401 67492 26510 3663 52834 71244 38268 70175 52755 65544 3335 49303 78543 79348 91540 26710 38860 58188 85338 6804 1117 25851 33068 83453 73028 4245 19641 55519 67801 45102 24252 48988 61380 27062 39602 97417 21214 17735 89623 50712 37783 18434 22301 82309 71821 44389 75461 27960 9022 62097 79728 91826 58699 9624 64380 16973 36665 94352 74361 20657 33598 90285 59433 11285 33254 5717 69144 74118 51740 28963 85590 12650 15546 13165 56909 67483 99498 75713 27255 86520 42582 4331 72010 38333 76316 15466 16980 77516 15601 47564 95570 73609 34088 49097 56648 75603 68784 91892 63531 22570 70260 47411 65390 84551 8212 20965 6737 64215 56851 31123 65216 26076 24438 39525 10133 44162 97510 2248 97558 28398 10356 76942 18942 47805 73169 2305 6794 66520 13647 53726 699 61692 45784 67414 14607 91911 93154 49309 42299 17203 4314 42167 57797 34926 67027 87961 10607 83782 66920 46565 24573 902 90340 25700 85652 21513 42873 14125 30738 47064 58010 39417 21298 10491 48983 75388 61693 7438 97256 70326 18006 9381 47752 373 911 11992 9097 66648 10722 44274 37477 42125 32313 49440 14786 89102 83863 11222 83793 60916 78727 14347 65973 99975 57675 4961 27400 93980 86362 88421 56121 34246 3218 59384 32579 55672 27723 89173 55536 46141 92381 7806 70347 36166 16808 31001 13260 21009 78444 1683 44340 37596 68084 4507 65811 18997 74080 52779 32003 59530 70613 31569 10458 28555 33790 87162 6225 12333 6610 6821 9585 39689 2847 76280 45102 19201 85101 82219 93555 64389 56343 74446 4563 88000 12704 49688 51051 21404 11276 36328 7459 10429 62394 26653 78235 88538 64964 35024 70348 24184 85667 90982 71706 66341 71487 80089 21557 30737 99378 92215 61841 60551 91834 8208 81959 39805 63310 84808 8737 77918 49994 57583 81381 70481 38302 49573 71314 46283 22303 56674 32533 66984 18093 83723 93229 93144 63857 60967 64059 7442 34321 33718 33244 61033 57890 80888 93662 25557 22825 57098 56679 46457 39194 94308 52342 47849 94258 8385 55779 51525 26683 61268 26874 69811 46627 75690 96066 54236 20000 49132 30365 19337 70347 31245 33256 91310 22466 68202 6080 53560 5834 50181 1166 2236 75492 99589 29113 7303 51738 89080 94599 36112 49771 69228 82916 19236 87730 2867 46466 93733 7960 78482 18862 2798 56358 20327 45544 11749 55393 59169 43836 87892 82278 20709 67527 6289 23365 74436 17923 24949 70979 46235 76399 77668 79706 83264 96441 61017 96503 42851 79118 49410 96024 58250 34405 56040 62859 87287 74104 13678 16052 70554 944 30211 80309 53034 74086 28528 78698 36989 10364 62136 17827 7746 59610 78914 9042 1076 51089 71981 61567 88656 93591 32305 87372 1364 18471 14755 95509 71097 61624 3300 58378 82859 21312 75311 47081 64274 86029 59625 74445 25811 35640 42100 55400 42545 81393 23685 95838 78059 58170 17438 11688 53420 70824 40821 20287 95498 81684 64613 55084 69680 89168 15151 18935 77886 63606 5818 67297 54079 30442 5124 38948 64560 21585 8765 46973 89209 36534 4214 39231 47581 3512 63268 1902 28514 80251 51029 97891 27886 55832 1336 93570 73658 69265 98874 71398 15309 84952 78367 60614 5055 7921 97023 25530 96948 24280 25376 25707 50960 30889 90852 24458 62201 52799 58362 87685 18317 6193 70556 49318 96168 65652 35324 25865 25399 24510 26295 45504 86507 12086 28716 37903 36234 79451 58538 3983 64975 16497 99134 59332 54787 91070 94318 37455 42750 24802 88124 97694 69836 57887 17691 32721 54969 30035 39456 88185 83643 76553 28114 41622 74489 79192 17877 7914 30900 50191 29239 76075 65967 19743 50298 516 55597 98571 58871 19957 71024 29601 622 27074 47693 36887 54537 95266 10914 90132 56829 41966 18636 42397 28670 97361 5651 23016 82194 90706 10029 43182 22576 57899 89187 20377 84986 5865 39454 13141 36592 40614 1010 63903 13972 96623 61047 8369 2857 9041 92962 25440 78534 7108 24767 93326 30593 91631 33925 14331 23520 84577 49110 29929 2421 7503 29174 96218 24838 53511 94142 38761 84198 10410 27215 3558 35649 32204 7652 82807 47256 14467 98416 412 82304 58274 7645 56940 50761 98326 29657 89901 72226 88655 46109 64921 48263 39881 64116 87214 36347 68690 78406 452 31014 81162 15550 62619 28199 33084 39244 73717 46096 66862 71367 52456 27344 56004 49780 55310 55180 68876 59483 50173 7737 47584 46704 80548 73475 71227 57632 48775 83861 15658 3721 8415 68361 67516 15229 30334 86528 17739 87872 3799 12515 20139 71955 44411 49550 86851 65756 19487 336 87843 69007 80364 81317 310 25336 23498 71524 18866 66072 62912 12644 72876 64591 18990 31468 32355 39602 36405 21403 40745 34325 42041 57401 5259 22221 11125 62510 26019 53666 31261 38538 42079 98456 61707 34250 26684 76264 84284 1550 61520 46875 19627 33878 25127 7178 60336 87271 8256 19151 15682 2216 90360 86064 88055 34996 41297 79359 55426 56593 91169 40494 56414 31104 2074 8252 23117 31527 89061 70011 18293 63868 75517 93070 90149 52015 40098 81986 75998 44081 2032 94435 42179 56162 17889 93439 82376 88185 66345 74331 1141 49657 30136 72593 92148 85867 12353 81796 80346 9238 8983 79857 5186 48320 86823 81076 14500 648 87378 14995 28550 10344 31629 64199 3175 85802 24698 17499 86941 69391 21872 37530 85168 21197 48869 94071 36339 71267 33318 40872 59024 58657 66663 94477 69098 64516 7217 19078 21344 52526 83483 36553 7174 7361 63695 67710 46187 25035 89529 62412 76370 31021 19018 79457 36276 36107 77201 67706 6 4976 61153 38925 57167 27214 98781 5360 38965 58364 60440 90422 63443 63742 28108 78423 87402 31981 87268 24164 909 48776 23596 94158 58606 10139 63388 71562 36617 30540 58556 3108 71528 96652 81439 4072 69124 42736 9100 88883 54110 38353 57916 66809 46266 94671 90690 14054 75522 99976 28381 25200 15821 36529 47917 1074 346 1273 13747 70246 99806 73913 83945 18299 86068 39271 15569 3594 85463 84806 78381 34250 41481 65562 45686 12328 95022 20547 16425 66956 85892 21434 24409 51792 78720 37903 26957 10403 40438 4860 51958 90090 60431 5134 20541 77554 15470 63809 43882 27705 19474 12613 23590 85267 10513 43516 86951 38063 96904 31742 12137 63250 43425 15546 83600 636 46377 93220 40520 95781 91099 35874 46127 69623 38464 25772 99932 46981 78270 35042 18374 33299 69813 16715 90789 31183 79206 37091 94391 15623 710 45304 86608 85629 48932 40304 39615 93169 70438 39627 39258 36628 8570 71656 2915 96344 61630 31488 21328 86751 10478 35066 86482 29568 94462 62764 66490 76390 62042 62500 77159 93289 21460 10826 15490 72562 80531 52252 9164 76597 42043 51158 1299 59885 10832 38580 59367 73554 74036 61585 21696 45618 13490 94934 38319 65639 254 25829 10241 64683 14916 37677 52745 4282 99448 64467 55219 83022 93159 47732 33448 15865 61416 98601 54105 5587 30887 3077 76207 42656 98765 24422 90659 56643 13870 99949 25653 16581 42567 80141 7209 32620 14523 28824 82691 62504 79259 10876 88509 96110 84923 77150 49820 86117 15587 63062 76975 72856 65034 34768 60684 72870 54402 63562 94278 7994 62849 69030 93869 92133 12778 442 77622 85333 20951 96706 12348 51867 94943 95609 90895 84623 78377 37462 64078 2307 27049 54861 13402 56668 39404 88039 29686 90475 46231 67980 63738 21014 6538 45377 6470 82812 42041 8823 96029 90139 39750 51034 76106 99484 38212 53486 2489 73480 78331 91485 67677 27392 21047 12551 6782 45987 59772 49376 65619 48048 23639 4584 13307 33087 42725 89236 5627 67749 23747 6527 46558 7452 23650 74598 90460 50477 55232 5672 13368 97087 54891 26682 56633 79472 17566 82086 45853 3103 37107 50522 58524 87478 90862 2130 45314 9751 13901 77524 55360 55092 91665 79649 3365 73239 12824 64293 34035 64528 14928 30294 15629 59303 61752 42233 7376 63793 79707 51678 20265 22749 32523 24981 41933 87363 36720 71531 39387 69078 14011 41751 61556 73502 72862 35033 39330 5441 44045 30980 99991 33341 5971 23507 20530 16736 94347 75784 66945 80993 77851 2897 55960 80859 93632 62420 24810 63199 42968 56714 95085 58271 57162 17808 99788 6140 907 34375 86331 36158 10104 96149 45640 68042 96558 93640 35210 29744 71465 94347 50296 16817 16097 12201 50636 86642 88905 82182 50808 21570 59192 8311 76583 51330 72899 84726 37612 5881 74266 42354 76495 24798 47762 46051 21005 38632 2550 46912 26177 57039 77086 45845 34536 74725 87042 57351 76506 68634 22085 20175 76250 80222 66792 25468 18133 86481 21561 35239 46188 11507 66468 4453 74866 38389 7137 8759 49952 12186 76535 23892 31600 67347 60267 38478 35452 57688 35150 32928 66206 11617 83968 72172 8183 38117 19098 70428 67723 61275 27215 3350 85402 89290 33997 4648 33074 11620 36725 56904 27838 67185 41238 10853 45548 70276 68427 64882 63465 36049 48295 30668 93882 76351 73518 3648 87389 29842 1525 41431 84554 83043 70490 90731 35611 44425 11546 36032 34719 18588 4988 26227 85467 60538 7476 49961 82976 92288 31358 38167 9577 25997 84967 66740 42675 82971 75882 61622 88564 61275 20681 87081 47557 55181 61099 11649 48710 43053 86050 87710 7028 92232 26855 66990 68454 3316 16926 23475 57490 36833 56801 22772 56946 68166 9137 29628 68939 39281 54826 59021 70407 26737 86585 32434 73379 29726 18415 16044 65607 13800 23038 32699 87447 84819 77819 13900 57296 21967 51221 35323 80554 49069 58084 1169 18041 72621 3869 31613 90159 61972 83378 76213 35856 87005 95068 89836 49754 87157 72307 59543 46306 76501 6529 88807 56914 90728 77454 6791 8143 4551 98386 27410 1938 31262 69054 58745 83304 77377 10031 37132 36107 56028 6715 57792 17579 99798 7254 89917 41806 6599 72360 26268 95730 52409 65799 72584 53499 97441 46421 39319 74748 19833 83746 75745 40022 80978 79706 97923 85953 94860 60395 44216 44779 26770 41019 78414 86239 6072 89529 17099 71195 67033 93553 15348 38404 86122 45782 56039 13821 61150 46510 92208 36597 47713 53313 71330 71562 36360 66876 72851 85363 83990 15453 14787 38135 83946 60672 93889 15675 59469 80757 2482 10246 20799 8433 11812 95497 20701 7793 94184 88535 55712 22078 32829 35799 93312 25527 69752 27450 67066 43138 77528 28408 32979 51135 5727 41082 99788 26397 26405 5104 27934 43675 97863 10644 42079 2414 63102 68046 46267 20453 1640 25670 44600 74118 61127 81814 45996 65295 34588 42991 64606 29880 52731 79948 43797 40448 35297 80886 60259 66553 12445 46810 1875 45123 79286 56471 59039 81563 6747 46873 63720 78230 74889 95351 25150 14028 42616 7930 86838 94309 73096 24454 94066 47128 20291 22949 97027 59379 82004 41619 81958 38017 97547 68884 84232 84960 14487 6605 13198 8590 73364 71101 52144 6139 8529 15712 73550 11795 58573 24127 67824 88504 9190 99544 48833 32331 10920 14119 64297 6116 79378 76919 88415 27001 87540 56212 39209 66878 11205 47821 20340 5863 50671 14906 69724 68609 86760 14962 6243 35988 67175 10888 65351 38698 77480 17057 51756 67189 82494 34704 9009 85014 52793 89367 43203 89210 7505 4376 19880 89948 31311 72689 3444 94446 80907 334 60083 97797 16250 21753 29910 13006 75837 75697 90421 40491 13910 63316 28086 94995 39984 86936 83705 89052 29663 51147 16686 56457 43074 54839 48135 25487 11694 76850 61015 20498 91034 26495 23403 83904 72942 60936 76110 46979 13527 8270 72811 5470 36974 3356 96697 49633 45345 5741 29648 34495 11652 75688 6959 64315 4945 26797 81963 21963 45413 1849 53924 58502 51418 53015 77618 90862 63761 17456 57603 91064 75055 42943 17519 52719 5158 76402 49300 9266 17282 95493 34463 54153 82400 78789 46344 2484 4795 95923 73318 35596 9053 56760 1926 78112 28432 62705 74577 35375 71136 20229 98532 31278 77016 43582 21647 99328 17640 35571 20945 12935 75379 92806 73348 39950 63753 75043 82168 18133 46889 96028 95120 66379 20612 28024 964 1227 59279 83669 45668 83706 37467 41885 67538 95937 72038 10800 18014 21575 95237 43960 98461 52779 69063 96271 495 58469 87442 4634 14575 43134 41278 82697 57359 70660 41879 2271 32242 49333 67492 74964 70566 86151 33296 76447 45000 6310 87559 67430 49360 65139 87652 52677 49910 21237 32451 97825 50268 37423 54737 43523 33789 61678 6631 22839 91075 28658 28795 75929 62368 29305 93317 48151 89417 1481 34245 20394 65109 90718 4321 26086 61771 24477 16560 81835 83224 51676 44043 31053 6251 1425 88698 20147 99680 62075 19979 13043 59577 54605 72579 86818 26760 52183 34086 16562 48117 85298 39190 7351 29997 45319 71191 71078 97451 50062 28779 97834 16249 1575 85681 3072 63202 16744 78843 63111 88481 26243 39729 97017 12414 6783 39605 76876 3900 58840 19152 43272 46251 61393 36086 71381 10708 69431 74677 84395 28205 69842 89277 23600 4047 57964 20375 6905 1654 43175 40472 99222 97635 7885 17923 53465 12730 93854 4400 85444 40768 56088 43706 99390 89339 71416 12433 21896 94355 29 41263 62742 88118 71611 82977 64879 33651 83787 93796 75552 35632 53032 69196 28575 15762 35131 17386 56617 11207 21598 95202 66701 21772 29094 31593 75498 28780 62347 38295 12824 69859 20860 21612 43265 94370 33638 18870 94482 59933 71446 92895 24040 73236 9232 81022 42086 17186 50169 43272 78412 54363 23708 18825 8668 65062 8844 71925 93783 2023 39623 11067 89480 56243 69487 10628 9346 94310 17081 37534 56667 25815 60671 77768 22162 72736 28062 87835 43516 43296 72677 48857 93166 67189 27916 41135 24239 7931 47214 25017 44140 74957 45933 9802 77546 37816 37839 9233 66722 79775 70865 6402 17942 7730 98887 87180 54140 44065 89885 28485 80589 82905 45953 98528 94463 16681 35114 9122 62270 37513 30161 91184 55186 42054 56535 72290 64944 51145 23135 79732 88991 52363 28689 42222 78240 69148 5093 51048 67834 6770 85353 40639 33594 53472 55241 38337 63380 48187 68195 75386 8452 61111 61723 16734 53153 5618 44453 25958 10449 81954 66618 13829 99944 64718 60828 72460 12214 19068 56248 57714 42648 99286 95144 42563 50385 63143 7867 12174 32256 69761 75987 51473 74992 601 46326 19723 90310 67859 24244 27095 33298 10617 77160 60242 65975 78339 10518 97596 37186 16698 95843 48483 77883 57011 99766 66185 15801 73905 42442 8482 89419 16202 93089 22886 4170 79397 39348 48449 65753 81303 54443 28448 3994 93492 31097 34174 26568 7679 1862 47589 33671 24004 24192 81789 16180 2651 28255 3013 18044 25751 24608 54667 75262 69356 59045 92799 2475 48542 93764 29066 88872 13045 10184 75240 30374 11017 72561 48364 2100 58321 38273 93399 53234 34127 51688 70208 24355 5507 85069 64687 848 48897 98247 56241 96825 45973 15675 19433 3318 87095 675 98694 79873 44162 22393 67994 75439 80916 99289 46020 99089 11667 88687 55008 63311 50242 20618 63798 40602 60512 81573 51715 81498 2034 93717 41588 41492 49566 25681 6327 38666 5280 40841 71780 69330 21837 89487 47337 10932 71445 93024 42218 33693 18903 68274 3377 63264 3664 5047 12133 55630 99187 48039 90771 44106 45938 15670 40679 75115 34834 22356 10283 98550 77166 33201 87167 17524 6066 62031 88998 63946 90495 56824 97299 38021 14989 4163 31272 54185 73873 53277 52956 54666 78635 2962 37973 16762 41861 42762 80988 7548 5038 25188 72714 60326 76444 75501 68255 81151 45002 24178 7341 2441 88005 74312 88896 16048 63534 21840 27868 58423 87889 71147 25655 25648 9339 40806 35324 34986 83781 55940 19059 69403 40940 61416 85449 60501 72704 13933 67799 41978 3210 77906 21935 55524 64873 30488 98824 76336 62709 65827 3766 96332 37739 52842 70881 6435 9455 69371 35849 24479 44934 28843 78999 8949 66324 1682 5204 73425 13441 78347 39918 19734 26355 53105 53382 26454 46322 35182 35019 30189 70183 64998 19947 49934 27771 69664 42598 36571 65131 71653 28879 58022 76433 51896 59858 17601 36635 36570 12789 29838 2872 543 87160 19050 80387 90307 76213 11185 38464 77816 26224 74194 54558 3854 45767 72797 25408 11848 37173 98717 3177 52392 97967 62237 82024 123 70288 41601 71578 52827 77792 9957 79328 33673 88695 71206 17661 36896 65001 84475 25115 30073 50694 32790 99739 85917 57232 76024 54152 8938 24818 35318 90297 1743 30331 91273 92450 47348 1956 7941 56705 30901 25091 36511 5418 68213 68251 34677 26006 61050 92421 94684 62520 36441 69326 70045 63240 91104 13509 35682 21245 72777 3747 78248 12970 39358 1856 72827 1682 20646 12659 74786 5873 51272 81915 58908 22151 69395 63891 26401 1996 96598 69307 78776 67281 93064 78113 86145 27687 38843 65634 26727 96631 25656 47746 73435 12506 78662 5061 61324 18817 89369 62841 54949 43900 39656 70070 44145 19352 63722 4438 17824 43399 27973 69935 1478 44135 87202 78413 28797 46726 17812 3588 43935 98690 87314 8754 39151 70556 75262 90607 21112 15429 24234 13441 8507 75972 10191 70865 53044 99631 21819 45127 98088 17014 33283 32139 24185 37622 79825 2380 91711 41187 40260 36023 32648 31773 69340 15893 41492 6736 26024 66265 24528 52610 31278 52724 43260 56109 82359 46861 92931 83619 76166 81283 44030 65271 36759 30287 91568 20825 10761 81639 29722 86484 78475 18083 27616 37524 66825 52447 56888 70040 7398 21423 65231 28201 30083 40006 82365 4252 20197 6418 28050 59898 62756 96982 45838 68721 19258 63269 73665 78670 54168 11917 58774 96167 72726 19625 29286 72313 622 48839 335 9620 54841 50047 36288 72407 44851 45997 51700 81655 95658 43621 73812 80096 35308 41202 1004 93106 53773 36598 96376 81157 56166 57239 12221 62642 95210 31162 98289 51284 70273 68378 93931 4404 37378 40254 46038 66427 76801 57549 90764 42389 46297 28658 6879 34520 58495 81353 42792 59523 45488 62841 79921 45208 95854 16420 51359 18781 44039 65041 13212 66206 53395 76321 87982 82703 76577 87282 44497 92342 53642 78681 36807 8778 51212 60909 57833 38702 73875 99249 92677 75915 17392 35042 40949 68433 69044 29676 80295 34811 70653 84439 58142 48707 53902 62449 41614 34443 8543 93203 66326 53479 71002 25860 85528 79486 86127 95274 92907 75031 86046 54883 1402 32691 41418 59704 95349 20276 52208 47214 36690 93001 69814 1295 22271 99453 3940 85575 66542 71009 21362 30995 95058 3384 21949 68326 47469 45190 20486 41319 30528 20491 25697 14529 60791 83619 4098 25389 16079 33886 67182 7821 21163 93222 37300 92890 4197 95833 5912 4429 26297 2894 24438 28822 35185 99726 16729 379 40738 44472 66160 37482 90683 60962 13736 67188 69268 77896 54902 67053 71523 20402 77801 23324 25109 13546 30980 73502 21107 90015 63430 78773 5639 96014 68729 31133 50525 17902 79211 37670 51946 33745 63866 78927 57656 73489 19906 55022 54082 47331 54073 53309 37710 33904 94829 81324 87980 27886 59486 45623 66877 14330 12104 31796 80471 92069 72394 80865 76225 7239 61504 85877 32826 68431 96768 93377 11601 77169 41044 84349 58513 35271 59039 87696 28271 22056 25501 84457 54995 86528 51877 74382 96644 97679 12855 17423 91970 65153 6694 52373 5131 32780 59658 78069 33307 315 68882 49405 12422 4788 40568 81773 10193 40865 16961 73634 38052 26952 77656 77252 9531 89893 25876 54081 47862 44960 5751 77308 66894 58163 44782 5112 36731 68669 16569 41921 98180 89989 64443 72063 20219 28574 35436 12251 87940 71837 26339 92903 5127 92878 81603 69490 36611 56591 62508 42629 66398 76073 21753 64930 90809 28515 11724 69967 24128 25116 24207 4804 82744 53042 23985 17465 78482 7911 22477 73130 2676 57440 36086 3031 49957 79680 43714 183 48865 1806 45077 1147 5033 27498 97652 26798 80732 80682 4794 75415 11475 9197 44643 86645 82664 33218 63953 492 87312 58685 35868 72952 59189 48274 27009 19728 31668 60495 79704 61549 96771 89383 25254 58924 37035 21108 28557 1116 58060 53794 47010 20008 41729 11825 6917 10161 18793 89517 70793 96124 96976 55945 88149 91499 19589 19791 52362 5838 72462 41484 70430 54740 84418 49738 2423 96510 95472 39467 45310 77368 21178 78887 30303 11723 17839 79780 35842 91475 64526 68416 18313 90163 98308 88763 80436 40589 46787 3699 95315 19123 91342 71627 69052 3126 63620 48194 74334 18194 75079 79812 64710 91984 80158 8626 79753 44082 85088 92169 33061 68940 45549 92733 95462 6241 12006 90503 53906 6184 80770 18590 65321 63858 98415 43590 12368 65357 65357 90790 15731 90594 75558 83312 37588 93341 67528 49096 52393 15398 88479 43431 96746 66130 25224 43988 14308 3631 81328 46951 35120 29928 91547 24796 62005 73873 41352 79779 2004 90302 32604 66990 72019 57056 92067 98916 2275 80195 71257 50128 25878 80152 3125 52505 51850 50360 60429 65187 73746 4922 74013 18463 42806 27295 34437 25253 97292 47232 89995 1308 35160 51370 35608 73567 52690 17883 97530 98114 37033 98413 88509 52570 7131 57841 42597 85682 96029 37268 33361 8243 36754 18807 54052 24028 26243 49782 42580 3653 88451 36351 25938 47493 50309 62643 28406 93007 24424 92060 9588 87044 81022 8676 82292 95339 9613 7923 21105 51562 32885 80321 90095 56808 79362 90558 2528 45507 73056 83944 33652 26420 91785 85390 64989 94649 37034 99333 17324 71822 89019 53272 6130 95456 56750 39164 82750 73870 23839 88726 91791 11160 42029 95414 81839 44523 61779 35241 97595 40341 68701 15009 19905 50858 17280 17523 72925 83993 66437 73216 49288 94227 80732 62242 68129 57585 43415 29799 93709 96568 88528 73047 2508 24821 18044 70468 3939 43068 34393 48485 9796 11882 2461 71349 60639 19901 32988 43283 65216 37584 28245 58277 45294 79185 54559 84006 62331 50371 48618 92483 90555 96166 23425 96860 14533 26104 20166 7238 79870 35631 26999 60256 51565 85700 38716 54339 53283 38571 13929 87604 96509 86884 25808 32490 42639 51532 52856 76455 38871 68562 67565 22051 91385 55331 96903 77994 88954 78697 13840 54581 82612 49747 59821 67241 9578 61126 34696 82465 41028 37928 84683 75103 64358 53649 55735 69752 61941 31716 10380 34085 22046 15947 92430 36801 93545 18485 49585 76732 67242 20365 28269 76189 26900 85843 32109 46260 27438 17608 14194 98380 93313 87979 74486 84824 65636 72013 67605 34261 59353 78877 36879 18506 15817 87240 51381 77770 49175 15986 53618 97191 73600 74433 96511 59095 51127 15116 20825 79055 18019 5803 82456 71544 58092 42511 17181 13753 16906 97279 92182 76959 63907 7761 55042 11205 94937 53796 13589 48445 23310 19519 56387 47818 81487 85559 38978 87107 85115 99475 68846 10347 7725 17436 53136 91116 62096 58935 5879 61193 8097 53778 67006 362 81506 92847 70219 3631 49490 82509 55825 23066 58203 31327 870 81028 59942 3795 33370 99488 31074 41526 86384 36026 85582 66984 87745 55887 43947 57684 83035 37884 29979 62959 36019 87881 66850 41972 31191 92359 88237 31735 79282 65026 49098 56699 78793 13268 34522 24648 72718 86694 99649 86206 37580 1441 74412 46699 93582 83296 80029 16063 47504 61891 32031 69447 88413 67913 85307 7715 14957 9641 80201 3347 17649 35595 73172 64688 55970 54012 86193 5846 42267 80969 63206 13732 66290 952 7169 3565 69836 31356 1637 20048 89471 10007 65926 464 94312 71008 43412 66448 2049 17144 24559 31550 76813 9623 16217 72363 6220 73469 68400 5775 58740 27342 11319 69280 96650 49393 8269 1160 39853 51295 66276 42994 15550 89734 92892 58247 26288 85383 25397 24947 53130 32033 87755 49218 12499 35800 35021 74429 30338 41756 96801 41153 89881 8473 65708 6592 52025 40168 51493 74739 41515 26754 70325 34747 91411 28571 38489 68658 40247 10611 60316 24219 25023 13208 4701 38136 62242 9108 52612 4390 57937 26412 86928 71276 63811 90880 75622 49086 27841 7357 90030 26547 23798 12019 72944 45908 40676 38400 18217 37673 91457 12687 25927 29323 98706 43933 85727 89100 94976 35372 77267 39968 99702 8677 80956 1479 63489 32132 2678 94018 69040 35188 23621 96254 44181 14521 81469 44491 35109 61313 85170 42652 77559 60131 23641 25461 33310 95905 58868 99081 14140 78967 96522 37846 86593 18622 22274 49968 66219 54083 86044 75721 30354 67048 18061 65196 59980 94360 83387 4984 83377 71155 7761 93855 67268 20687 94249 847 6421 44405 10012 45313 27130 37017 40213 30842 74333 94359 20977 21196 19909 8583 44351 95815 12831 51547 66934 84611 12437 47372 47862 20209 21552 42223 77160 78298 10449 82326 66478 3093 14935 94803 51295 87858 47180 59002 6015 86177 37836 2512 6742 81829 13319 12800 53068 56948 63247 21556 10822 31043 59031 13719 6632 62742 3931 85141 30928 69683 92371 8327 76613 68692 20441 35110 99556 11445 61464 82092 35866 3513 90256 90102 47574 28061 6716 38126 76156 22062 42875 85362 33670 22797 69481 76582 55601 77975 17391 98260 17857 52855 68490 87025 73976 3983 22144 92231 4348 88192 28594 68481 43047 27602 44324 26928 61570 98811 80785 759 27816 39127 73358 20929 62651 13967 15495 26817 52396 8002 74105 71210 68118 21786 5109 76064 15517 16691 38904 2781 61202 81372 40324 38955 25165 90115 31720 7341 895 34921 53662 24480 29907 87177 60189 50726 72375 72808 64227 48756 86659 68654 8305 19616 42102 79590 30722 39170 2700 46441 85187 36507 14105 94908 4538 51576 39674 75206 7794 93920 69745 72886 55478 60538 70032 11062 72704 34820 25090 23515 9485 39233 29010 85600 47674 39505 80348 31526 45905 9106 70066 46533 49445 90172 57728 38649 35383 30706 24538 96370 42768 71561 64505 38383 61264 71328 48319 52927 89580 66623 45986 40795 43439 64611 90296 7548 43924 96212 95561 6764 50321 6129 16 71887 86826 7912 20102 91187 34657 35567 69852 78372 22485 40416 56363 99010 13585 76524 9879 83463 9055 59241 73383 9916 81547 55168 16895 97557 29301 67304 72271 78010 36072 44513 85800 96425 9807 52400 99347 10491 1997 69877 29197 49325 81717 66971 91520 36030 86396 94168 22883 26359 63459 50212 78365 24206 60046 36795 41348 14699 15666 56322 66372 88582 55457 79262 87460 81230 30673 28648 38787 34668 62381 61613 79711 17833 33752 95152 33008 73672 37393 61576 97442 87304 82149 32954 607 33751 75754 21625 17866 39549 99931 54743 99792 15276 75140 93579 20561 8111 84506 74537 75352 24411 95841 98693 49591 79372 26913 1770 70700 69907 73142 93839 24155 45460 79039 44518 68899 69044 2562 23561 57379 58098 81058 14706 43880 30933 28423 4575 47325 46375 42045 8718 67871 5847 62215 27685 7909 56471 59004 93685 2056 51227 68695 99564 28552 76713 40009 7726 59216 92093 44079 72541 26166 26082 81838 74303 6239 97589 86693 63987 42732 75360 31486 61596 52665 41933 78994 80945 83516 31189 37623 8628 95859 57189 8480 21111 78623 53097 18132 29669 65832 54785 64668 36706 55019 89022 68359 80104 93141 89308 2835 55322 32195 61217 13171 56743 70425 6377 24756 53752 29796 58982 39018 94498 25462 84369 60559 93928 69254 85343 63746 92357 56424 294 42617 22528 87977 45497 85214 90073 10736 64057 43894 15243 43338 72082 28206 13615 82722 1903 33139 42055 27949 35230 75421 32426 61633 39088 58868 73577 94738 83068 95927 57714 7685 97582 6297 1659 73700 40088 86105 35660 29809 7145 23767 86662 96275 17801 84960 93687 15841 58485 76596 94813 55392 36987 65657 40725 53717 1795 15238 32382 29074 52042 12055 38319 82651 77970 17941 21335 70314 61264 23296 77326 9074 12962 25974 90254 743 51254 96671 30109 97377 51977 19024 50511 75911 74833 23970 32582 54933 52435 85243 28046 93599 13641 3782 16190 95300 23146 38863 91294 15786 69874 87725 16286 20997 82977 48718 12323 23492 37941 86645 49187 39312 89693 75931 53666 76763 41051 23376 12974 57801 46213 37066 74488 89633 72853 32770 54295 41254 82969 49189 60202 22456 14821 95498 18923 54157 10568 33803 61035 3390 47105 99561 57369 48388 31099 12719 88629 75824 20812 58172 38557 21063 82364 69360 77537 85652 50916 75159 2567 39733 51685 35124 79229 3677 49320 50527 34674 95626 37969 6946 67392 11038 46929 6218 89051 89832 96030 6062 59127 70755 31713 68809 2035 35902 80142 39517 89126 390 60157 97922 8980 93329 96661 69572 26429 64246 29461 15046 52517 71984 12505 49906 35174 19010 59839 71516 7656 11936 22749 67448 66447 58719 80354 44947 88581 72485 35911 10390 56113 37287 5979 96916 58660 8991 28048 72005 85626 43687 3799 32683 68552 94663 4582 65516 85094 6155 99064 4560 34041 41115 43649 53229 535 56571 61628 95476 15934 8166 99858 43787 95343 77384 91423 62931 63193 95892 56259 82959 96709 31728 63540 57243 56876 63705 93952 87747 50888 14263 91399 93926 68094 96901 85018 85128 85664 50130 32301 72425 46784 5237 91521 64816 88063 54370 16697 9364 90857 27583 14569 45072 1887 50058 2054 6441 43540 70565 83094 15153 74385 90268 49995 2201 73481 39087 64362 36538 47815 8127 21773 63782 3353 70582 99823 31625 70027 90191 72817 84879 73688 82848 96421 23593 79608 79819 63697 72955 24111 36668 84439 57317 34290 95176 14261 4901 42513 33228 3542 29546 73967 96542 94461 71536 93041 66565 80905 73501 12358 1316 89800 86075 21762 53624 23215 91211 64387 36112 39937 30690 4318 64108 839 77893 71661 74580 43044 20165 77863 71191 30684 53852 63401 87665 58554 41436 68582 71021 82261 70657 13389 81603 29424 4564 32631 89983 99104 54304 82579 61214 20687 53616 35290 19739 24475 68695 37083 46375 76460 80766 52873 61834 92264 34888 19041 54927 61293 71589 51862 34285 84095 87092 60034 60850 28797 6346 89297 67285 46287 2620 45967 86187 5125 88117 30444 16568 73488 26877 71512 73962 95460 19668 74506 29410 58082 21562 80673 44906 85394 32644 42047 87057 82022 32137 29829 10104 74498 97141 84583 29609 66230 32825 47411 73826 86422 88036 51533 90618 7712 59404 43330 70357 47416 5157 53062 43648 72063 74626 49283 60615 97999 40966 48055 26189 25730 74082 47348 38830 17511 9146 88476 57760 4043 44374 1667 5997 75002 51449 44740 20210 63978 45842 47304 80576 21793 80516 88621 99424 21185 43094 37948 64999 86953 5570 3383 54811 84158 98422 6023 46907 85194 43583 73460 3085 60029 83563 10609 58633 37630 76334 55709 46868 52942 4433 91112 41719 35781 142 47154 85392 58107 296 7220 69533 7085 57226 59299 24456 48753 9869 32652 27601 70625 15478 69495 76540 40981 73912 11294 28099 31969 6604 95017 60726 91930 71860 23901 63423 85856 94645 78381 49142 22929 95515 44941 49429 4033 10682 18653 729 95398 20558 10128 27482 8401 79664 68034 28266 3756 17421 7909 36837 76486 1878 56924 92662 89770 55145 63821 47596 3362 29560 71657 80065 7604 50173 53390 38590 35190 74548 14770 8481 13580 94720 26572 35985 58922 6619 61325 59159 92197 26070 17523 19464 98108 84534 82272 73451 26811 96148 33191 793 29522 19768 85150 40018 3116 65270 19160 15969 27885 72364 92323 59759 22976 74416 88983 54729 86859 759 27851 91798 77489 48696 43904 19841 90122 94215 87597 75728 94789 85276 66933 47543 82916 39463 20406 7468 82932 10269 26049 35919 43224 77765 23685 37086 51329 56858 97769 5477 74782 14779 79770 30843 96127 13599 5203 80306 94576 38620 97015 64824 76612 90701 4188 11289 57522 58906 3723 41274 2662 96799 78531 37152 10157 54060 59403 99231 18163 1534 20477 1506 94097 28002 14969 14005 68273 49377 37348 66900 32631 18033 69993 41377 35775 10698 71981 67771 96749 23685 36454 74277 21892 19896 25941 4815 13555 15748 62190 97747 27685 82201 65390 16184 19480 1711 46412 52383 92837 48617 97082 80456 56114 33274 11475 74975 76344 3118 93824 10805 54744 91233 57535 19575 86831 92672 20198 22480 64169 13559 88865 27104 5922 41515 31495 98220 55736 40712 20250 40307 50900 16766 46771 87268 31878 21627 17548 57704 61758 51550 25812 52952 53125 74246 49767 69490 25500 19156 54319 58675 27442 56018 28589 35851 2737 42069 21175 51269 55994 61113 9823 43815 54964 889 64543 36681 932 59602 45562 71805 90714 83927 45231 92491 15470 78281 43766 92244 60340 23781 32631 8001 73046 36902 95442 30928 87468 64396 9147 29880 60340 44516 3637 22782 21147 24908 55555 48741 82112 72585 21698 39588 82597 96845 26713 9193 32043 58794 37132 28035 81496 20644 73993 85889 48666 8773 26396 73134 52801 85113 48881 71597 26879 82908 25452 27917 79376 20665 81902 33282 95834 8419 38254 8722 96229 64468 8983 34323 46264 43369 19873 62696 44288 77862 92553 49825 8242 44892 89772 90145 54620 14875 50518 49889 10157 36731 63703 99720 85090 5004 31681 37567 38763 71426 59282 91770 91265 97362 64685 38979 56931 60340 56199 8565 28336 41656 56823 65678 83623 93476 81150 3773 7354 81168 75467 39750 32422 56604 22311 16827 31062 62540 90089 89747 25227 67336 30785 24477 10756 1972 68387 12031 10378 33648 64360 99664 60390 70095 26494 99664 51842 87618 21537 77920 54677 88970 8369 93989 13716 95636 12531 43291 48837 83401 49263 18469 67842 30827 18168 60242 77124 89824 76115 79874 21671 71960 99194 45653 84209 48027 53394 12255 8336 1214 89055 52255 12035 65517 92301 17758 72336 45733 30021 51910 44933 4472 97463 62981 74783 65198 35488 3017 82794 81120 44928 41850 96950 88706 79898 38676 87272 28231 3545 58376 52045 7976 83911 95570 78420 36379 80321 22621 17152 44041 74870 3823 22195 32798 78859 41676 26949 10965 46480 62353 70333 77435 31830 26956 34241 46595 70123 94685 78221 31574 61673 48518 36265 73925 8684 15923 6194 91587 78007 310 50094 54061 20208 15998 18667 89263 9334 57093 46711 78976 46636 36302 11608 46068 28778 68592 56427 25408 21114 98238 18442 20600 61504 78894 93999 48987 85791 87645 62239 54415 39110 62743 1638 3946 52394 47810 52970 62008 29779 23985 59450 17787 57112 75772 76771 34772 44973 30940 10397 65314 43941 41174 65876 59096 6259 42508 70663 85496 43748 5856 54362 88621 43752 11908 69129 5503 78232 97161 11598 92105 79277 27975 78576 4375 71993 79338 93919 43914 43185 1015 71739 74034 68301 99912 28213 86596 90198 88987 6860 71485 32876 87007 34943 28881 8422 79608 49801 15759 36261 60855 36608 55582 5961 24522 72782 3259 21970 98054 62005 78521 78299 36374 60886 88538 85873 2985 11587 30032 54433 35714 64602 73769 88507 54681 12328 1393 41197 89438 34996 41317 88607 59956 25986 52598 78494 49662 67556 56899 2490 87272 58918 87528 45116 78830 85663 4080 53021 46268 83353 804 17483 25280 64296 39997 92170 44365 19198 37328 54309 27309 35002 88056 92808 56558 42087 94266 89560 24602 551 50466 77421 48349 74069 87869 89764 37891 30110 98866 52006 31999 8561 84350 99371 77149 90834 76518 6939 41145 51011 66108 44554 79326 80634 65875 63758 76683 10701 28114 67868 59868 22823 12285 93294 83844 53883 95321 13060 91570 57222 9094 53202 29305 62180 70983 56743 40688 40746 14821 18636 3169 59876 80563 86598 36063 72982 93072 19550 76026 68628 8827 24889 98122 13348 49061 89152 60524 86272 98960 64021 54195 81528 37228 1667 77533 16974 727 69950 5368 95736 86353 10196 80995 80240 7163 78941 25927 71310 49210 76952 54749 44062 31922 98474 94305 66552 6258 31655 60812 26622 78176 75711 74757 40348 2900 44690 42100 35895 44609 10430 39242 72841 25253 93076 90674 34855 76999 62252 48259 86656 19191 77792 30303 46856 31876 1056 33970 28520 5475 40314 78488 7115 56063 65221 93854 53976 75525 23522 39226 40482 89521 840 10300 53897 48316 61243 63035 33640 24618 49345 17061 88005 46999 5621 94842 57941 12648 62716 38137 80487 88212 59553 5506 73759 80174 41574 13230 2506 18896 91345 3732 32670 65917 50781 23072 83993 7166 40227 74731 37760 15095 11535 15532 62129 97915 17850 23265 801 9109 39630 91130 64941 52423 43290 39943 87287 80175 37725 23892 74242 93568 24356 12954 989 85592 79935 79008 77776 84576 50539 9439 62450 71153 8402 54991 97243 80876 11321 23373 27893 83494 95125 52898 24652 86300 13357 71105 51526 39042 10587 89592 64584 72128 69353 86455 80600 99980 54839 43513 74787 93953 30033 55291 72386 24152 75477 71996 61739 11115 48349 48749 66933 81607 34086 29669 33700 18902 48720 4751 8834 44533 71651 574 87031 54876 50753 37096 50772 34640 44091 97802 85847 6675 40612 32042 63225 40939 54110 16273 90974 93813 14938 53449 37004 40243 21917 54487 64927 66468 68288 98204 54859 32048 5087 17838 39667 36833 74441 8447 18340 83877 93792 51492 53345 20325 16058 648 81365 29351 51423 61835 48174 95886 42274 84264 91846 52269 77082 81970 8024 87884 39299 29392 5141 28263 8946 96976 69711 37342 92737 96292 62473 95122 24844 76731 2092 34886 43334 60181 65847 94641 9301 94089 49832 10411 80519 50505 77287 66237 19196 54145 52548 13905 62947 3673 70820 72707 87037 16965 14240 70504 11216 61161 2776 13808 44178 23162 11871 55607 74866 60042 42681 35276 95935 64399 20490 86722 54810 89624 62925 49710 7590 85214 4440 24682 28271 75870 88146 79783 18849 75554 53317 66554 74220 65065 58535 66754 6605 69454 58299 12432 1046 35086 31219 87436 95036 98725 54247 28847 95701 62069 88376 35694 31198 27790 37618 16729 27021 47929 56823 65470 51575 83586 63657 58128 58036 195 42768 19615 61167 15333 28415 89172 20488 65747 13446 51766 96837 8020 67291 22715 51247 10013 9348 42005 23010 13845 18400 46982 43469 51836 37151 82891 39 67963 40940 99595 96153 52644 43866 78658 4355 33629 56469 50732 56420 21881 94358 42749 59271 55715 44191 26637 57730 93679 91314 30768 9577 39386 13147 34889 60349 43663 60564 7424 18446 35151 20306 41185 22051 77197 98821 50505 26002 65789 54746 68401 8509 67719 59727 11630 50950 96068 11447 42817 85642 97303 78561 76229 44307 33416 47889 46781 89644 23953 99205 31924 41520 47217 46031 30040 27380 92915 35346 57131 86947 40946 1155 82928 22262 16 49984 33609 40494 37805 32686 39644 32083 87362 9031 56734 75514 16873 17414 24169 4729 2618 26915 57876 93666 14997 4812 76848 96799 26526 99833 7789 92309 2871 28256 53435 21149 80617 99598 12408 37838 56435 75507 28275 13141 16780 77397 13187 33539 77700 8392 72187 7598 91329 4529 42833 24533 37086 82268 17548 42154 25628 1421 25681 93902 41378 21968 16546 32250 58497 66720 59451 71925 60301 1385 62519 10253 34824 6911 52033 36077 25395 27198 79514 83236 33755 48051 55611 48884 39411 92956 18190 42411 5433 34856 93010 59738 26382 47015 59487 71402 13959 69781 31727 52034 84023 99193 94351 63540 21918 89276 70645 73677 83353 35448 5166 55804 28750 71715 72094 26846 69509 73494 90045 16027 33343 76959 36021 90399 64689 35 23076 27851 4499 72786 74745 32199 91831 68627 72867 58932 42848 73893 83424 81228 19953 34867 47451 89308 3670 34047 95240 41632 92916 35954 55915 22732 12675 62797 74679 41062 43286 14143 64167 97724 7262 55092 52117 7750 22885 49149 40609 12877 18451 48470 41243 30272 44071 21592 76423 66337 63462 43924 89157 42875 53538 27273 11981 81167 3494 70974 19779 97599 12204 55450 11476 54009 94676 45187 15756 31150 3047 3507 18733 29899 43010 873 61275 12629 32876 39105 96784 45572 79888 94297 59580 52956 29599 31241 84065 82215 98213 83200 92155 41068 79826 4055 74391 14277 10341 68282 9909 24486 6451 98503 42279 52379 79993 6197 61538 88643 22842 70354 14299 73908 82268 47894 17653 24861 69809 31317 2793 46550 18312 63221 45695 99302 28301 40583 4468 37864 27404 55401 27093 47456 20001 45132 80233 69658 44533 18376 91328 48262 51888 9721 60797 22871 25902 96543 87709 73854 14737 47725 73394 52599 33072 72954 88967 52095 53291 1450 67954 33609 83532 73772 51669 12867 90910 95873 32485 222 41045 10020 94959 60688 16961 89172 41614 24540 72676 19659 26537 18353 2423 44916 61212 14731 10094 33112 32237 73052 8327 71653 54778 27478 79984 31834 80006 14830 86272 48202 30617 66298 9087 23588 35508 55351 43954 60281 87040 21298 57436 3030 54724 76377 75308 15178 52138 15968 61026 85817 51003 66188 38276 32071 67811 27908 62813 74694 18513 71884 39859 33225 24247 71640 95767 62028 73125 47498 24666 92851 15304 57691 42614 62359 74868 49565 91701 20857 73634 19150 98233 5795 99420 38489 27022 3678 67859 22167 53521 58101 23139 33929 91373 65395 15769 90662 24226 85613 54042 17992 14775 68925 55686 68821 49345 49115 81733 88074 1910 45526 77716 85512 21406 54323 98737 76508 83321 71192 78201 7633 55155 14663 43349 89330 7816 3328 32825 6421 16371 95265 98346 28825 9963 73544 77091 67840 68545 96321 42130 69083 38342 9742 76718 18123 32005 32260 9000 80758 42144 99679 96459 36465 9019 98738 56211 53557 79718 56612 98436 17957 44093 53205 78564 4993 2040 36886 74 3699 42156 90812 64037 91311 84468 85954 77727 151 88434 26117 7478 47081 70928 61661 56503 31871 76792 67325 89333 74657 7204 74799 26942 46135 70592 33907 30750 36297 95039 96556 31076 22320 71100 75811 36209 9848 4495 80931 72892 58827 95147 15960 83717 93224 33411 28367 90842 80047 3706 50387 61084 42026 92152 8876 82565 29158 55898 76669 2584 75892 34054 75284 404 72657 75016 10295 71792 95056 88397 19596 57638 49229 29876 23192 22732 41310 4732 18973 82494 82679 17230 68651 55814 43425 18045 57726 5456 67073 43913 80193 98883 44705 65243 4533 78151 27969 10525 7309 8570 86821 49526 48447 34276 9683 99043 91550 52374 77100 15434 1715 14033 9334 619 659 86962 22605 29800 45240 30558 58516 9644 86323 77419 82006 20941 71460 77649 95083 43035 11908 73354 43576 77213 32665 91934 23533 17996 82571 10535 92806 56506 26883 98896 3408 71282 91046 59877 92529 9064 14953 63613 55852 24999 34112 61332 84229 99954 62446 53153 86781 54824 20689 84713 49863 93048 33059 46377 50773 71474 19909 54338 75997 75754 78101 30353 12274 73641 71374 198 47656 25072 36328 50350 90414 12900 27772 55687 77121 63233 15562 11277 75541 26696 96355 82634 60226 16197 25273 7549 41397 92083 49908 95867 11773 56312 31322 1176 14183 51894 25149 37392 42035 89459 64430 72131 28893 92424 89792 90400 40484 20059 79187 70781 11957 72036 27119 79900 82761 98639 26006 62091 54610 57629 9382 55004 41081 75840 69450 44946 7402 37622 272 48355 26153 68276 49046 73283 14988 23751 19003 5937 92353 32558 63663 71835 25209 24533 81113 41936 42072 43403 69764 72640 31779 94917 80114 59956 59605 21381 41468 64738 4574 76486 16901 23011 72221 76290 73688 44887 20576 90396 88211 7345 43784 81949 34732 50970 76525 82271 72033 25309 82250 10611 79471 13884 55970 8932 45271 91321 74037 50440 59206 94281 54691 77413 23218 99914 75446 58997 61456 10161 91343 7297 52743 46233 27788 14817 9834 94704 54304 40323 9216 9615 14623 69299 4134 90044 86060 132 92030 74268 14789 31343 45531 29293 11432 17845 90772 57798 74840 14916 23482 49495 17001 66454 7503 52174 71961 96767 17869 64545 62382 76976 93162 37960 1766 74667 2110 39941 3669 50274 52420 37614 78710 48760 39634 18810 88492 96251 36786 48147 15021 1676 91805 17178 30621 32232 76934 96546 75217 47454 46085 75623 12654 73581 34625 25520 73123 53923 48945 8142 65222 38090 71692 1978 5721 38941 25410 9325 21406 50485 70129 75717 93356 88709 73798 18675 36468 41910 25676 47185 78776 2528 17304 66106 24798 99952 58992 64900 90967 40255 82458 67552 13038 45158 13966 86207 14623 2031 84260 12407 53037 76905 6493 49909 91975 45737 25534 33281 87422 77146 66433 96949 87652 81521 17369 18994 19381 48285 179 89727 2622 92572 2637 67411 60444 39878 61489 824 80366 65127 18012 59392 7613 7375 6703 27537 85259 24781 63167 7923 32989 81337 43943 5259 39822 88406 59704 52620 58613 33566 6619 1527 12701 73565 57949 80398 33157 14710 59779 72332 45028 88908 4222 7095 33594 3004 6902 15355 36111 32338 22887 97647 74211 16639 47107 73232 79670 50284 7497 43494 3912 69762 28842 66189 56409 63362 58262 68197 71516 37135 23172 57492 49312 96032 418 85831 25356 99662 47673 31395 49661 7468 59172 76778 13218 17392 65311 14458 91084 28531 77067 26642 30436 70041 58219 4975 24922 34100 70359 75552 66293 74899 5485 64720 7018 14976 60151 58717 6126 7541 25620 77715 33126 1931 59789 2245 30255 51235 23630 90449 17637 47581 43883 27047 79178 51012 35895 8653 54608 85189 52067 79580 42167 23603 16297 33501 4729 43819 27628 62652 3723 69395 12887 79126 89334 87588 67735 22092 50489 55617 95048 97024 92661 68028 31417 49231 87801 2245 98235 98435 58618 60794 92643 39985 37930 43118 73932 54585 39454 33622 53270 66093 41338 29708 82662 67820 45164 76565 17962 48246 26886 53928 97377 88718 32653 91618 96189 78307 89911 80656 48812 46634 68994 12305 74568 50632 28678 46582 15950 70159 46309 3584 54648 26133 95682 79394 51017 93003 17672 81999 52840 78104 80393 41891 57027 47584 36033 97357 39345 98068 693 48053 22277 81538 64114 12953 62699 39380 48552 76013 80994 66986 68270 97749 85023 16335 99689 9802 72119 18770 99297 48693 45711 65884 27817 93231 44042 75024 51451 19157 80182 12230 22932 60347 85476 9667 99436 40422 96366 51569 48677 95325 29520 59885 28735 89367 11907 43055 35965 20040 51599 39137 19528 64214 23130 61154 93408 63120 17298 43495 21766 51827 42777 13096 2428 35874 80730 66632 21103 34277 16715 86701 42271 31913 91873 36750 78118 66706 87303 63113 40254 66011 93444 99148 26943 27654 37178 13802 21749 83622 76043 44009 72287 7474 42061 91004 78271 70636 87717 56766 8404 69171 78716 90012 85473 94120 87329 39457 48987 74124 80638 36123 33488 75287 86312 76499 7614 9090 13713 65028 81097 99288 92822 16645 37078 81226 23661 75959 12527 38063 98649 7899 47593 91287 31370 16922 68862 63126 29611 77084 87122 38758 46059 7603 78536 55586 7434 16606 10786 5705 6232 99252 84329 72437 81652 45970 84779 86206 2109 88460 47106 49597 87441 8381 76355 59133 64022 30752 76227 55990 22591 64711 82641 3439 8880 67638 81021 95803 15720 78401 76929 3655 74824 67056 19683 61170 45893 25003 82412 68393 32007 69189 87186 1188 27684 54960 22529 75450 60229 15867 33361 46955 15574 71006 34757 50482 49119 52344 25717 4702 25256 86808 74725 48658 5681 29572 30381 42493 66816 34068 36535 74186 944 46148 48364 29723 55018 64913 94784 40987 90449 73932 73530 72158 61602 71415 55510 91860 2251 67232 72072 29481 96922 24259 21984 30451 22467 65912 74282 81421 9167 60090 1328 56144 98288 25305 66456 4898 89009 81993 78655 21807 88396 38900 78236 72423 32512 88168 95279 97916 43003 67130 32265 7817 5851 13470 12806 3948 62449 79701 5834 72407 28187 52955 63754 86139 78646 47405 9663 9203 47762 73885 93438 1408 4987 65732 16159 80238 30845 39105 40137 7437 54319 85027 24833 59718 24846 16850 27695 7593 73952 26988 154 25611 91871 21090 23692 74821 81342 24561 32988 70591 45561 24723 9412 84826 72357 88353 15596 65100 65098 23019 82437 63529 61153 64183 79933 88909 11591 90438 71693 94902 52952 53184 44963 67948 79099 70649 39279 11774 49165 12101 79417 99247 47263 84411 55756 25754 11558 66569 58960 52068 69830 7019 46040 56548 19881 11450 67910 84765 20546 79692 18855 50290 18804 82490 67158 51906 7421 91333 37330 82708 93118 20 84409 33668 65015 88934 69071 10251 76502 90510 4907 42586 113 73189 81773 71442 40619 31275 99687 35583 65601 83909 32680 10760 99210 80711 98260 12805 84738 69434 81843 46827 7730 21024 57521 85155 73465 36487 90171 68736 2455 55742 30315 60461 44770 26681 34878 89002 9654 35704 7816 54009 64952 39904 57072 15594 73132 54586 47391 23334 29223 18614 14996 6491 46934 29144 53752 9864 61992 72130 41764 35192 71852 62693 4191 42855 35014 11558 37387 82830 54224 57364 68851 34859 32584 93698 78780 81702 52228 73447 49611 98673 77766 6659 15889 28177 77124 79556 38738 288 26030 66531 20908 45085 40563 88327 20824 35867 7213 73834 65397 13038 49256 23422 53689 73985 7439 88144 66844 70925 43233 37057 41520 97161 78959 79239 35348 15746 35260 81430 18245 24320 38134 51531 25800 74052 26383 64634 5868 6931 31275 30248 20262 28956 35148 18364 94701 79344 84024 94696 69581 62743 60251 38885 94518 89637 85800 41525 56197 43049 47147 30804 69375 30500 6253 80822 74264 5450 60286 16731 22819 96119 26993 83993 2929 33377 68584 36337 93466 92326 77023 92926 66803 27752 66006 31873 36013 13537 68956 79851 47075 36326 2096 60583 65683 73646 22705 93841 29155 2264 74578 2631 73570 16104 12122 38181 24674 78341 954 77024 3955 84006 79947 63233 23075 62252 79282 69103 46597 72623 29037 17483 36729 38134 77949 78198 28589 18562 86800 92057 3392 34392 50634 17856 29197 25957 51287 47369 84750 40654 28109 48854 21903 51537 18018 46560 49457 17819 91639 39376 59801 19355 27092 37633 42528 42575 85130 59166 88149 53387 94432 19982 36466 15221 1859 67482 77816 13031 28144 36157 80700 49237 42116 29084 20061 52361 89157 895 69501 8554 71406 86263 53631 22205 2434 38232 25017 40902 84538 59399 67150 96769 2215 86705 34538 49396 13814 57834 12625 6485 49556 62856 52492 69146 26293 47985 87035 67318 656 16761 80494 43702 82538 24205 39364 22710 4470 17058 50507 93291 80007 87926 6341 95534 10775 73356 9075 35788 21959 58605 48706 51072 79144 94563 92738 11986 42614 96775 20784 40064 86363 69607 81220 71829 16573 69301 61982 97014 59501 72093 92784 61049 75142 83104 11779 67500 59111 96529 84985 10150 14976 37442 52926 79223 92110 64889 61769 61949 35780 72013 51494 62525 16894 36816 72236 43492 60689 41244 77117 2476 81837 91993 60041 67507 17851 96362 73019 57482 98718 146 63629 47219 1677 19460 93048 64111 78224 61600 82679 60581 83384 32633 93585 50318 2721 31416 55186 625 57460 46067 42156 68113 31934 13687 98641 52270 98145 38686 19553 38737 50656 6554 86498 29811 78610 93613 2193 26445 60643 20557 8359 5316 11745 89216 51859 94632 17309 97331 45832 96298 35022 90668 59225 89635 46540 79880 74532 3874 79525 16948 66060 64836 73591 88159 50266 73604 18037 55888 20383 97615 1304 24467 88063 49327 3559 74169 90064 62605 14257 61042 40644 51623 84529 48860 404 99089 92546 13451 10677 39315 3647 83763 81923 92965 65999 58603 1393 11471 1153 73871 41527 95462 60792 24316 15400 79732 5265 10214 86016 34441 32033 99606 96006 82193 14094 72821 6616 73736 78913 15041 49860 79749 4412 54125 58057 86018 41774 27808 64320 70796 83552 24151 19615 6999 74812 67229 45070 96848 27602 9964 35237 12996 70237 38480 27576 10419 42209 2014 9445 86040 34121 51836 10979 62043 90151 91456 16027 65622 77084 98569 2074 58211 96907 87002 67716 35082 61577 84871 18113 2522 21438 80823 48309 78741 87208 90260 97959 87490 2580 58777 25782 8076 93487 5187 74630 1657 97600 5711 63585 50650 68081 72764 42516 92052 10272 57851 76274 9195 29729 66862 89958 70389 4742 64270 72692 26887 91578 29631 39999 44232 34631 89118 26863 56128 42406 16474 77272 85857 12007 43375 46795 2151 45351 71786 79858 74075 62340 21480 28240 78447 86556 57627 43536 33403 92324 91181 35039 77381 67762 91791 23642 548 7214 13236 50248 37665 48657 90305 28068 69500 82973 93302 57169 24959 99013 23418 21905 74692 99234 91004 1706 21888 45509 33068 97348 69695 19563 25668 50372 37972 13739 72143 32092 36761 1805 65421 59147 22271 82726 9573 68092 45966 89725 37136 42498 55578 22448 44485 82960 43391 63318 61652 5274 77171 79671 69322 12870 69218 25986 83349 89898 68489 32435 29436 4609 55079 19610 28222 8638 79959 49456 76533 48735 1836 1224 50484 5857 77889 61163 54570 58768 86396 978 55846 42887 82790 63470 11937 9271 23788 27297 49567 7285 89851 52980 40436 59085 93500 10662 59557 69402 99343 76855 53005 40214 23324 47109 38157 14841 44551 30489 56410 51447 67027 32609 6749 55792 31680 29338 12809 58759 62178 74031 14700 63107 82300 4013 51933 21346 77996 66709 12023 63508 43679 53340 33341 67366 62548 10536 90992 57067 8622 31615 22650 21408 56293 15276 53265 68941 33609 57730 98195 64771 18444 16086 14230 46451 51433 95297 24930 71912 30327 2670 19565 54040 4924 86851 38239 19908 47167 57431 9638 99962 57942 6837 99392 40744 18507 29402 32131 34470 37920 40573 75148 30177 28285 55419 94107 75463 95975 45207 159 60822 81356 27314 59856 75978 96753 52566 98306 39358 96062 80721 50028 15738 51461 70799 6952 24653 64946 21699 48130 1086 93421 55445 78237 62193 19092 66863 35855 57248 97955 13563 17538 90584 34155 47781 86425 93558 19718 46828 88609 25790 75089 45907 92892 17657 83476 63222 44397 61204 41859 28066 28799 18501 55007 8638 23106 82064 35740 31293 48420 95736 35952 19399 14853 80340 67616 76690 32323 43686 30675 33031 27926 14882 44426 92803 75826 87951 53607 80439 2300 57540 37456 58382 19839 19722 16157 16873 17502 75804 54362 17485 86360 60620 74177 41721 79507 67120 20795 15249 38599 13714 6176 55298 96017 42711 93708 57622 42256 37554 61147 27681 39167 24284 54051 95367 66864 16493 38291 16834 91817 37912 10706 42741 26862 93553 45213 41537 20021 32667 99533 29045 41652 75019 64776 65617 72456 30372 4365 68674 37766 50850 49009 65018 33773 39587 19585 92522 88889 7445 17100 61200 11226 38482 867 73849 52718 13009 22752 31058 49752 98063 35809 27641 94447 74675 41077 91136 39863 95796 27122 20151 16669 23402 22356 6350 32961 4007 77115 12960 20062 40021 47626 77296 75887 13586 39950 55957 57047 4827 17562 75967 2027 73388 8907 13094 74766 74863 4079 61719 44332 65079 56905 865 9936 69789 30115 5207 90512 81744 99957 71609 68902 54821 24152 64237 78052 55010 79111 68116 78748 85625 90729 22305 77448 35300 72783 63313 90854 42901 31833 29842 32262 2069 44297 51745 98499 79066 36394 39593 45564 98534 89949 86216 39406 17181 55344 29391 74915 87567 39819 37688 63847 88596 30571 35337 47481 67368 41625 67367 29807 47573 27017 51372 51251 30815 50176 12049 53694 3424 57814 54578 62952 27983 78327 55227 85194 71670 53123 21070 67966 78039 44623 22629 83090 821 64123 67420 83158 89146 38747 37768 44667 21866 80000 8463 5305 74339 82957 89569 41273 21874 15375 25763 77884 88335 74136 58306 83748 93425 84722 86948 8251 85457 53393 85053 81145 22240 4246 21957 7150 54816 3414 60225 37512 14839 68975 6844 19617 1501 72215 27196 47321 30394 72821 50874 21473 73064 93600 70971 44406 67406 21616 31899 51144 30448 36320 41602 57464 54568 75159 43867 94751 6142 60121 12585 516 27916 29561 37696 7856 19160 89756 60073 81070 53363 28701 46942 589 87912 77392 94998 7875 64146 59796 89391 45257 82759 47454 57978 46666 69144 29132 7131 67681 83399 58916 59522 22230 39344 58900 71117 10458 94026 49919 54082 89536 29013 38904 33352 21396 10448 11360 21014 57285 36452 61575 38755 25505 22211 39048 95546 20065 31500 34964 57153 88660 34321 63154 49174 59123 42907 72455 7406 89269 80727 37808 94170 47370 94030 28374 46170 69315 40796 97298 26814 45699 16335 36537 74145 88485 25911 15360 69142 86875 70993 37141 94499 38443 69723 5812 42536 72107 58686 39099 73552 45388 21853 19664 77087 14807 12625 27276 62048 58928 61771 36706 70560 34721 68290 70887 73402 74590 29902 7418 40255 7115 78425 18577 59552 9816 45591 17707 44531 96600 65214 4277 17037 29324 79888 56234 45947 73973 56637 63141 45299 97425 80196 52630 44981 22553 73645 22723 43771 39113 72403 45719 10216 59030 88351 48398 76831 15030 39064 25549 49507 55618 55101 10388 24701 85958 14591 51029 77976 52529 8399 19085 86982 86784 30861 99977 48375 39512 77839 76911 56178 26483 29781 21199 81228 91508 35994 23790 9748 92276 40949 94692 6703 18736 28873 41362 86465 4788 46866 4928 35616 79697 91712 32437 86774 88828 55803 20775 8365 23061 78277 95949 94547 73234 40102 90426 97188 52373 92707 17171 56657 14028 24563 7357 10054 28699 88672 80276 22962 23208 71771 59240 79383 1367 55439 45430 19991 79054 25657 571 51898 11047 75433 55413 44124 37021 47903 73273 82675 19012 59648 75601 89332 11696 2830 379 69808 46950 41159 36324 19922 71257 47207 27522 2701 57341 8926 41720 82112 68521 59697 44194 24603 13672 93938 99713 24151 99890 43300 2375 25612 33673 79026 66926 62592 37526 34744 35693 80170 88557 27594 27327 48405 15181 20346 28667 56698 75636 46494 48718 73529 68335 25892 11466 87716 78023 93590 47983 98358 53182 46199 25320 14691 66171 41841 48412 53758 13693 89013 87138 43683 81052 4313 69484 5853 53638 69932 31367 14875 83498 73672 11939 37264 14693 26117 9056 82994 85249 75280 76132 89957 83211 43631 14565 40520 71142 37900 52103 24233 29643 90110 56046 52249 56881 68228 71633 48907 33209 82193 99885 57616 6696 64375 98675 76309 18784 8846 81442 72677 59030 26750 75521 30916 11464 72447 80946 46526 86993 76373 54162 63649 11068 58461 90816 94711 61931 99498 50259 59695 20126 78956 14928 45184 7457 72477 43444 52351 15492 45754 38114 22949 51976 13700 26415 15504 71872 30192 70090 25305 58956 13077 4048 29245 59714 33235 24190 47078 19657 63630 64781 78939 55920 82587 8906 78740 49971 97739 94701 81360 97190 92146 68376 61964 5880 19596 32397 86275 38287 25909 86900 8076 82804 70092 718 37465 60370 12043 60308 91096 39590 43591 55911 37596 37610 83389 30647 31135 51018 21623 41202 14303 7845 8586 28910 79947 61524 59240 9463 19414 41475 83900 14983 83676 37593 86778 30369 26237 38426 61611 35469 11629 51266 54895 24471 44563 96617 33883 91847 88925 76254 88768 48652 79088 71543 3978 27131 63116 43587 84043 34416 55191 26882 63028 9459 39440 67979 31939 90776 92912 87359 37032 9377 46731 99471 54416 43157 23016 91292 3549 76304 26968 2452 76826 30579 38554 17316 6376 74409 1926 41478 73 78581 4290 14313 32233 86188 10612 3522 21465 76346 41231 87610 23621 13818 88861 62822 10024 18753 21370 98491 82848 32870 43649 63839 65400 21141 44811 67279 28015 9403 19441 73596 73279 30894 51931 50215 84926 21751 26164 95291 96234 84804 72221 44698 88102 2469 65330 2556 46999 435 67600 16212 41750 89748 88994 45085 38993 92715 69885 43175 23645 76336 31545 27441 2668 36212 36981 36302 6311 27887 88832 69444 75010 17540 23042 43407 91166 55054 9572 27262 14811 29329 83989 25469 72968 20057 95635 93884 16196 84843 69045 28497 23388 85109 63922 32748 16677 57619 16891 31666 97016 56523 46551 43112 35829 71506 82708 76060 77700 68368 20024 12021 34282 97868 2332 41295 77735 42881 70483 75539 25898 9726 91910 74098 22428 95827 64454 31164 39858 54606 75765 81907 59174 90855 34994 93036 42422 29630 48457 3025 78646 94928 13589 82208 24432 37459 32036 12583 9680 40714 33813 35688 3683 75600 73363 70042 73364 76401 69889 79765 72546 54128 69618 4851 87632 51730 95782 49734 87113 8591 49496 67435 3003 51034 56836 20650 44979 93148 77785 24481 74304 19349 1708 37054 26432 48493 75998 99236 13429 82604 95030 75738 10477 33345 46854 67498 45029 39996 54115 25913 69596 81176 67370 59895 41905 53117 25510 45161 82154 76278 55140 99302 14062 29847 5309 56666 17086 48429 27795 22786 7409 6593 58022 1626 90342 15018 63584 16967 58688 60452 67230 93629 24653 81951 63683 18152 97093 69837 82749 18218 53976 8745 20374 69782 77911 41004 78341 64191 3416 18921 77560 40125 3689 1759 65241 40923 92735 10595 69641 52798 12491 84778 67812 79315 26243 2927 22855 95856 12008 29432 93146 30800 82840 86538 83370 51157 19242 84201 65885 18677 52311 24591 81527 97034 24738 31986 36879 83490 58771 4571 8340 61473 88917 96389 85243 36356 97363 22057 35169 69374 74637 38069 38102 24466 70225 12594 54103 23702 9456 64310 23119 81604 40722 16226 63858 14230 61622 40000 13517 40763 74321 7476 53808 31855 89486 40777 8760 48643 37039 44678 20764 89067 59907 93453 23287 72704 63766 22591 23822 540 63824 9012 85228 14110 27516 73474 2414 99091 3953 32121 68726 19863 43327 38963 45572 27290 27845 14932 53731 91014 60811 78788 38722 20141 64191 3559 90018 7868 76428 81435 89153 53589 76686 70066 48511 9398 8183 25711 39842 8600 87526 81768 46392 13615 60004 32641 67403 64883 66615 92426 9053 12325 23951 96008 23419 97275 61237 82464 76880 68767 17000 45712 8244 62049 13274 74050 74353 8667 21327 79687 60676 90926 12081 77993 80178 5221 58341 30742 20670 75668 27148 66553 28209 71713 4144 27916 5243 79826 26088 41965 33283 38118 51399 21453 87338 68163 4737 61600 58427 60158 48409 79774 27375 58343 65229 92032 26417 83587 96171 92248 20964 66584 5428 37182 6342 78082 7568 6434 75913 50812 90052 8141 60887 10880 25997 70172 53471 25810 82427 6565 47454 26592 69357 38299 96668 13124 14361 64854 21446 55563 87929 14010 64503 85332 85745 39367 5156 18387 77066 46332 8143 61031 46976 70839 42602 38004 66407 69281 70938 62524 25873 37428 10423 64969 4682 6013 79141 72181 86529 75409 75717 18698 50615 56604 63385 62636 56683 2340 40160 64785 64910 40881 79291 89604 434 94785 41349 25185 96834 30331 64558 69382 99797 59979 52149 89436 36869 40546 10773 31442 24767 91955 7308 68738 56615 2255 44366 83622 82493 19270 38146 52664 49047 94901 79370 43139 48856 36367 99605 91775 7962 19687 17541 42224 69672 94195 72643 74011 1361 92803 93601 55500 96506 37753 98008 38498 58883 25417 39476 17131 49514 48896 48641 55058 72381 49986 19937 890 23608 8354 55824 84286 272 13690 15120 57358 8160 32806 43229 85587 95574 91946 61994 27140 63477 61833 51284 39712 51286 68057 53257 56055 86729 90789 28886 6694 6809 68748 61085 26035 12910 92815 94944 95103 38617 64698 37454 44772 76512 87847 95124 19131 28507 5622 62420 66737 29545 99985 77155 79650 38763 28359 31650 90432 75341 2851 65358 44746 94202 80404 86762 44297 11070 52857 676 9744 30402 43957 65206 25505 54631 71961 53863 64398 21392 10189 39183 63416 73612 9134 9906 95901 21332 53982 82014 14555 89936 18458 59846 49363 42560 44369 39446 51842 45980 38005 5255 94989 34551 42330 56211 9990 95726 39136 60437 44405 56556 73189 56414 28631 64935 6362 74714 66312 12089 84642 69472 84354 24687 67640 21065 10608 31665 31117 55835 21428 43064 85254 20406 60699 97808 14855 33543 98700 9466 92263 29073 48592 4160 99479 80533 98341 46640 54506 55657 746 65486 1763 68432 12646 33017 17421 99602 6229 56223 34167 41325 91667 8053 44302 47063 62231 93022 19965 26594 90535 42399 19558 91408 25191 93987 66781 32726 81507 37432 33218 50605 15148 92133 52378 90485 58570 14254 21656 3514 13888 73561 54179 89801 82144 23216 59717 96059 37049 28199 44753 97437 54041 98337 74744 55746 82945 29631 40909 40118 9072 88561 95464 70523 68179 58530 43688 14182 9587 25731 53913 85892 38097 86965 4239 16793 52678 84560 50278 37117 89463 61369 11755 17910 58428 29821 4154 92288 55233 1093 93195 17687 86779 66085 6878 37993 6299 6793 87898 36654 92581 2677 36903 87824 97297 72314 65044 10955 7047 25337 95608 55971 51984 63596 95610 11551 33740 74488 27855 13088 52057 90331 66294 90111 23626 2649 5201 65629 48560 11234 86265 66488 43963 58489 92325 12121 69472 88514 18430 87802 74002 72925 72975 8985 50477 42603 34656 35566 88851 40256 68967 9467 97550 31285 88337 82135 2176 74374 49789 76305 53715 99675 46957 26171 26372 41161 61755 40556 8839 41189 80029 91349 37057 21159 84000 40803 47380 92244 9218 68414 57459 54742 98460 807 6080 60972 58061 94635 48385 84298 40940 9972 74998 33572 84211 92165 54692 66283 63391 36956 90902 9076 42156 99257 99090 56870 13251 81839 73672 34845 45154 54523 62191 39919 75165 32569 3715 22838 44974 43461 43400 83964 31059 57869 64935 33952 80123 85972 409 24923 65809 30150 21249 39546 11630 93331 36957 32191 54954 30583 67494 64100 1243 38232 62511 54421 73199 77039 29248 52672 87860 56070 59714 3196 89457 15222 96763 9692 81563 79739 16510 7478 18520 4588 69377 44600 27579 13107 58122 80805 39873 39626 89010 46458 90083 80164 98058 3049 7370 42441 20929 2268 33251 25093 53406 69660 4583 49524 21756 92973 13693 11986 53742 57089 70608 91271 95552 53108 99231 46607 75166 39825 87068 71336 18357 52954 95823 20542 11574 36523 89033 98167 68276 93663 17288 28874 55030 90529 49633 43305 8583 29297 49164 11338 2865 91223 4679 69178 63315 89380 5544 8074 30170 17214 49448 6344 96521 38352 28354 85851 12962 16153 89538 80505 71602 92749 99985 33715 47507 33885 22358 5841 41398 3474 25872 13440 20007 14148 34978 22541 85707 17917 25588 54226 81015 57619 25039 44883 20753 91933 94084 50422 78880 46362 29453 18224 92507 19663 51455 53578 13836 82456 1055 30659 22939 24940 43333 58246 84587 36080 31001 92641 94351 9291 85974 72251 90546 18412 60351 30386 62463 95244 75792 72000 82638 3942 26990 98113 48990 26371 15639 49872 12569 82310 80234 41714 65601 69084 86969 18278 34411 24474 78403 21631 74866 92451 10655 47829 30991 38543 90331 41565 84268 73402 33568 5534 95844 36920 13805 52119 41917 1642 51143 35530 4208 91163 19167 38850 99687 89149 31721 42701 17929 22708 19735 69870 73189 56066 97262 75303 34595 73091 40122 41578 26597 88688 48693 57922 16677 3850 9513 65330 32531 99201 71071 69297 79086 77275 24506 52964 49478 58081 88102 1210 57604 69333 68806 98211 55731 72463 276 87362 56372 52567 95260 26162 12838 29634 71407 24896 62485 88257 36399 84940 75010 43223 1966 78368 39228 53294 25698 34604 85698 95854 95751 91675 29925 81714 36131 17946 71242 26004 39434 32157 83493 12571 20225 15554 61977 40721 74898 7069 34370 88472 61342 82919 25981 79567 83784 7348 86720 67257 90712 52126 60958 59345 44825 20990 70101 16517 79247 73086 85001 65299 58067 1446 71073 91412 91268 99573 428 64087 65537 40035 24064 92879 17526 31366 64180 68741 25874 44215 11543 97881 14540 69733 99778 2999 22427 96529 61581 79215 57589 99642 29832 15746 65850 55069 71939 56637 10936 26685 57465 23439 62840 64216 20805 95128 20297 53272 73339 62795 86265 3886 54715 1029 89223 66546 88183 14636 23136 57353 18868 46736 15755 95767 53080 5769 34074 91652 58121 91810 97300 84590 15216 61364 46420 16408 55839 39565 5556 76522 32880 52488 50643 23448 8010 14234 24577 56540 53368 73131 82418 52655 64677 16888 49876 43342 8137 63038 9464 33600 76795 73554 78516 38104 58858 67693 22378 97375 56583 75684 91826 13946 22272 51613 49410 95047 60812 2362 5287 28530 5269 22504 9427 1732 97731 52903 6187 65087 85079 69581 43165 84670 50784 79199 35780 69697 19436 93708 3346 61749 64452 38565 16763 49988 39076 68061 26491 13787 30266 54221 92888 96631 64710 88518 26408 26149 24563 49099 24744 27455 40785 6962 28949 58191 60867 16168 63487 87504 18799 95297 17053 65461 17190 62137 52149 71793 76553 39483 46178 20889 94305 39797 38720 5035 52812 12863 71529 92656 16411 9002 15971 95338 70197 50961 87003 61425 58575 52062 86261 74983 95568 487 9999 76954 57818 26572 95152 66357 29497 60420 70951 2452 78088 22513 28873 48975 42189 59796 72158 14132 82410 40593 28813 45238 42482 55040 50925 55376 15879 39581 94098 89431 43201 94916 64269 69562 62770 44737 82522 42212 95270 45655 36991 87820 32808 40786 38896 95223 49127 46188 32886 45942 73353 14114 41781 68597 8348 84490 95176 99227 64219 99972 65448 80736 86972 6113 30220 23369 60910 61619 65438 82963 95925 46328 25113 1467 1762 2133 65633 56204 49878 96904 66411 82298 81310 89873 39381 55127 70913 75811 78424 79183 92758 82849 28464 98386 33833 1021 57584 84938 50950 34781 72607 86837 52315 34044 71817 25564 74618 77748 88211 7699 52729 39029 19530 95703 71695 55677 16069 6027 97873 17271 75902 44387 84692 10940 40655 78234 13005 78266 11153 21195 60402 42591 89177 36685 14388 86332 30862 3832 77665 62704 57687 19056 77563 45261 73351 163 89849 42800 47189 17460 29630 48463 99096 42162 63274 11431 1922 42934 9495 49192 10025 93992 5531 53711 15056 18153 53357 25426 91913 80847 40482 42781 33211 3451 83753 24375 79032 41595 4951 77615 44863 71493 19678 44169 59687 95933 28613 26612 8707 18161 83235 33624 8355 38741 17885 87270 49761 77789 57837 1416 3297 7024 34907 73143 1393 77179 89523 45308 7149 23354 75897 99431 77459 18822 84258 81017 38639 23859 87759 55306 15457 29994 88564 58795 38941 5289 72511 54017 67324 33604 63470 92241 76893 42786 40647 78274 46827 2779 28797 62132 53543 17993 17893 48191 71131 62852 81085 62528 11734 53076 55653 54656 86536 1184 279 44957 1215 54167 12951 59753 7116 81313 21709 85837 17185 15924 60088 38809 10129 68214 44068 82537 35118 44411 1429 52574 64555 70019 75160 56504 8249 18294 57347 68420 80431 29493 38730 75758 81682 84354 51052 96216 9064 87083 63232 93626 90570 55133 62891 35047 78432 10137 78581 74922 37618 98550 8622 98857 30959 55712 14630 92533 20959 71587 60911 89282 82365 65494 263 97820 20462 65915 49793 82866 95797 74312 67798 79419 53788 74263 76503 5078 52937 14927 55169 75074 23675 87825 92702 46068 59830 37522 55427 12553 33690 81485 70387 97023 44647 78471 2190 37637 21800 45249 70556 17710 91166 14392 14703 89232 70531 17653 48830 18456 33805 14162 94241 29585 9048 87509 66653 87082 69079 60201 29194 91750 82554 71663 54488 94843 9654 50188 48420 27628 46098 70253 527 49389 54949 58402 85594 98009 24620 78975 27997 91756 63067 1614 53931 57830 99257 81254 54064 39650 15678 90531 18336 72456 87778 18288 11597 85285 62278 58115 99495 59452 85297 97415 9394 23727 74167 11369 2553 71857 9030 10639 40043 94166 81164 1701 62286 48274 45722 71607 89560 63120 69705 61240 33899 35559 96847 80205 37484 51418 85818 7029 26891 62946 88592 99837 36724 41600 30887 34554 50641 5944 48055 30754 96962 17837 93159 96891 49149 6371 90978 43576 85477 49993 99390 2842 99911 8263 93069 77770 15186 32207 57733 16610 54895 35943 78068 79340 62919 97805 75954 10340 71311 51254 4466 63002 38093 12336 28198 68826 19478 70983 27802 19971 38530 22388 13711 75462 96643 89287 42194 53688 32693 35515 32731 5686 70472 67528 75860 26479 93677 26072 78489 9756 14466 46847 73928 88899 55439 60940 83187 8158 23688 26058 46690 15299 23423 19138 24841 8547 67247 81226 86859 84322 95124 95869 24858 29565 61923 47415 44287 99570 99329 34544 2563 3632 54433 1651 70292 54867 72516 66588 65051 45141 18012 81491 1907 6246 33454 92149 40023 49849 94045 92182 68169 67790 71302 42731 38904 97231 5435 11986 40383 71160 60687 60771 70651 7513 11547 77161 29774 31616 27736 99631 94318 51529 94709 71793 6828 53225 74918 82116 78204 82346 41625 54801 57197 28715 42678 62764 80062 48505 15414 65169 68381 97846 14397 87855 12425 5680 97942 42730 31358 28131 20351 99260 62351 86837 54619 74349 51770 4211 85842 15721 79300 50001 69432 81980 44608 2763 57234 42929 7888 77006 25431 74862 15499 88171 32246 65528 45646 80702 5857 66186 95922 15362 12449 93384 6419 58781 12729 97658 89923 19451 84367 42308 17986 63297 42893 97123 97236 5191 78947 93445 88844 50039 24079 84151 81199 60167 59225 19503 22614 27955 36859 43450 90275 5892 20421 77230 41489 28508 55019 65907 41751 66608 87289 48775 78855 61432 3533 80978 67393 1789 9840 68508 92553 14998 19074 36222 49564 13024 22741 62167 66310 22020 95285 22068 61124 71465 57953 25695 59117 38547 46058 46421 58649 76639 55515 69145 47628 45112 51825 42896 50331 49099 41695 53970 12424 22516 91510 60303 27567 67732 86168 74655 3030 13778 5526 30773 12096 87086 40475 72873 67615 51648 61871 28247 52458 67917 22141 10109 15265 21937 75155 70407 93416 67741 74434 56368 71623 37694 34370 29503 81328 33333 90034 53586 56893 83420 17241 88557 70134 82676 12821 82257 38967 16485 30062 49504 9123 97163 4095 54893 97566 98168 53925 90506 42781 2542 31863 80498 79102 66423 41782 48396 64800 80927 2569 73184 13279 99030 9964 86896 88250 21767 73818 96518 89400 30734 74874 85424 87121 36536 15354 76788 53366 86821 84650 9425 25511 58051 26525 87973 96583 53616 20288 73306 3132 90771 76615 17130 25753 3494 58443 70939 51321 8817 62416 18129 45510 67702 75716 75330 29980 74629 19351 31818 39976 68089 86393 39634 30238 85427 86273 30624 17167 10949 96487 95925 42312 19779 12979 98387 63430 16266 68184 75163 70726 17285 67524 52807 47330 40482 47394 50532 25933 69810 50420 78953 74227 3938 62184 22544 61025 88706 96552 782 45106 1081 62595 36302 58704 19174 16779 11258 19156 58885 59980 76354 37113 23852 37811 52018 69418 2821 51929 79401 73412 24122 87029 63206 21240 60277 89587 43169 54125 73185 4645 11166 75230 6683 72953 33192 63574 39716 18670 96516 32972 2137 59068 13033 71386 73740 10269 48237 24724 60366 41466 47754 71977 73902 6236 19056 57025 94393 56861 86901 41287 60663 2916 65069 55702 43680 66146 93172 11156 28659 670 94808 79520 9911 50665 90417 98631 16224 20245 43646 99022 3146 60935 44475 40084 16217 16459 8172 38721 95505 30122 77840 93731 31964 66309 83960 75883 23890 82528 8508 16674 59177 26029 10464 27548 49574 59474 76446 21871 94681 57207 4762 71936 66355 80347 90767 57909 81632 7383 18199 81538 21366 86234 9656 49062 23693 72371 19105 30989 83463 11101 47169 42015 78205 91645 12660 37831 34171 10146 84186 97399 33460 78259 71950 74421 50261 2320 96444 17463 87240 96454 59740 31178 7145 18979 14088 85114 7618 34095 14992 89286 82210 79431 35006 65694 62199 55116 51335 89447 48207 4112 1981 21260 56096 36732 41375 93742 44603 72794 49112 8325 50277 30921 18311 92379 13190 7294 19385 64667 3437 70326 47903 13128 44296 66678 69295 64765 50041 50694 7661 3480 24678 54428 5468 72428 79956 86740 54724 73151 32413 40951 48091 93277 41662 29512 90326 8945 22397 92781 638 40980 367 60685 95148 40168 15983 35230 67928 1810 23658 701 14193 41574 54442 31289 60322 44885 2029 62720 69461 38782 57208 37008 59529 11224 65736 38917 37820 6934 47029 97492 71395 75837 82203 29507 8735 82373 9253 4250 8033 66455 25262 20706 69335 98937 28660 5590 48829 77412 33541 71646 61604 45924 63118 82572 80855 42549 8510 27675 56803 77460 13202 67420 28149 60497 24768 66584 93067 65602 74854 14808 8665 50562 50452 1246 74032 87071 49139 53790 19361 74785 81861 72817 15315 62696 69940 94828 79782 86417 54845 38184 13227 78863 90950 56939 16265 41748 14064 52856 6684 13094 40139 25536 9847 61952 2726 78110 35013 24736 71320 37123 88219 15033 61639 76275 65267 68258 10322 34381 34268 13735 13350 60733 96322 44809 65304 91891 11626 93180 72218 75505 44485 20652 24454 78009 76434 32646 72331 81745 62105 88874 44578 61691 26045 9555 6181 41700 72728 42741 4965 37901 72451 44312 17394 17838 60368 90114 86793 46697 56711 97989 17171 88127 46052 71153 96016 66859 77899 50935 17135 17487 46349 62506 18397 81168 92862 15549 85835 924 13861 47614 2361 65722 69204 30129 45656 26980 86853 74093 57716 15149 77540 85975 7240 18321 70813 86948 9848 86484 51885 9469 40204 65759 79117 98112 26464 29214 96455 1906 59419 49665 9565 53540 51696 44984 54433 27998 37561 31345 2744 12800 41273 91103 70796 67432 79308 26418 65508 63892 85252 4906 16714 16845 10846 41284 33951 48442 14257 60316 35743 6472 5143 92663 54294 15904 74999 72543 35489 37212 3258 76502 82312 29598 78636 11437 47845 89561 63214 52677 58364 8770 23873 78269 38631 97777 83311 11601 86406 73746 26186 27306 62915 83315 70154 98976 16004 11633 86582 81839 60705 65091 22258 78551 98268 28714 95500 61300 90422 79216 76749 98880 58174 52236 96917 21957 1808 40457 30146 55273 68549 53203 33251 33926 2095 93162 49656 78721 18907 39469 73599 11030 91371 60922 81809 18403 74073 72509 99939 38085 47453 23233 3938 37866 68229 31572 82565 52776 2057 87978 19164 25641 53429 60297 35903 493 18741 53318 22913 59249 12094 77121 41318 66536 61140 99646 86266 82270 41222 58474 73465 88774 84029 21141 77411 12486 6337 26265 75535 82217 53493 15914 15749 53232 17967 29493 63399 66114 44018 49903 25624 83224 186 47166 60488 7023 67821 96010 12373 81679 52337 55304 3273 93864 98148 93277 27937 78774 21382 82700 47206 46624 93085 53443 30515 40960 79890 55208 4789 41659 45357 35479 47343 24236 44924 63659 88934 3856 67774 16909 36027 51889 19338 38735 94814 4099 15817 99230 14200 17347 16930 95763 15695 38157 17827 28520 62983 30262 50811 62505 49182 99827 34436 46803 8259 65408 12851 6418 12620 60122 38836 68154 73523 52544 90865 57094 19368 21651 38808 53525 34817 67257 72978 73893 13760 95533 76879 97863 30799 40464 22853 13117 41110 48133 30133 10116 22573 67238 17343 73960 47883 54323 42920 84455 42280 48377 4480 74622 44587 20656 22945 28419 68216 43380 41901 33455 52420 60205 78010 13611 70379 64378 75246 68670 72275 34071 12975 81829 24640 84070 98355 31863 60591 92371 62816 98346 40580 29045 22823 71669 34677 40266 25730 59437 95196 40600 23652 37757 27005 38521 30910 13694 28858 32271 30686 16484 56097 79175 8837 97657 67821 35164 27098 24977 11091 20318 95732 25871 94231 59403 42779 93551 9078 56627 12420 86885 33762 1498 4614 98215 7417 37941 63886 22905 20207 73593 67999 46475 32827 13833 82561 62870 32300 18124 8974 2778 41383 46029 30282 52086 71378 64810 47652 81740 76849 52637 14946 5533 63489 97559 8201 63204 22500 42894 92147 57765 11617 89471 21884 15103 26937 5408 26866 43762 9974 58662 27370 16994 88875 30656 46054 43720 17491 25843 34586 92178 69430 52893 57825 35461 14003 34414 15770 44429 28704 25598 68347 84799 348 460 40197 12017 89763 50139 50012 2410 83861 46873 67621 63545 63274 35065 93442 35581 71184 69922 76518 64996 43338 34794 41952 42573 79410 34360 65597 25792 67769 84874 73478 9436 16717 98061 96969 97349 77246 52103 71276 48042 64924 89542 41050 853 81625 28061 5526 17948 81118 3670 18421 9444 22628 32939 18508 86477 2063 40411 6191 36130 94819 13951 70770 18226 18155 10060 38961 84829 92526 30528 78885 14031 88400 45868 96395 86871 90592 11830 40927 72673 66705 92562 9125 41761 35796 81800 41575 64852 16011 14943 94854 69343 48307 38820 22074 57915 46264 24889 93633 62185 94218 7587 36684 16800 29801 85 35515 99652 8275 64381 9269 6582 58597 43474 1626 88157 5009 52736 41314 18703 85621 67059 4243 58150 68321 40670 3322 15251 82553 20738 53604 49059 52666 86985 30211 45478 70120 57297 86122 46866 87709 24872 4517 11757 31449 30747 54492 62040 15368 45338 32061 46498 69956 87045 71576 10723 15075 38491 43307 96304 32185 3282 7904 57572 69190 43891 72168 11566 76713 25409 4566 42365 3480 12562 68650 76567 57027 32064 22716 88402 31633 49989 81503 32429 15979 54987 38465 53298 27814 62976 46139 52289 91631 73348 57516 69244 22765 5534 58533 71840 36551 59679 55038 56311 43199 61850 65939 17799 70603 39304 86393 25625 53931 19976 81265 61870 89289 97808 82016 94226 7685 58097 79227 57480 9723 21682 13253 61910 42057 12593 75461 36441 14548 17783 13464 99553 52100 57989 80107 11758 50620 93930 35421 16873 26541 74782 94970 90080 96439 84690 67433 51244 95263 27507 28434 29365 55321 3897 15942 77124 19484 32345 52560 44332 34437 30008 62399 2353 42621 89410 90690 6957 79702 14968 29174 35697 73318 80186 45707 26761 1290 42036 15461 64538 1854 81435 75523 39940 90854 39734 23125 9770 15275 45434 44309 7960 21294 14670 73829 55548 36823 59140 45797 71640 27840 7143 70570 96909 51787 24700 91688 75214 2261 49506 37466 23615 53630 83683 32392 41042 43589 50900 80320 70676 26290 57722 86258 57774 45727 30510 17533 47509 2667 55960 13597 57037 16849 59378 84886 28779 89956 92907 51669 56297 42869 11089 72134 71931 69088 60437 27744 56676 42257 84672 38408 88876 24227 96166 31704 83552 48076 16198 78462 71695 92718 32480 51182 50800 39594 14953 80193 13258 56050 8719 17216 64622 95574 51183 82785 99074 62629 17712 17824 56136 47542 38487 15035 6416 46169 66718 88215 88658 42940 46898 30718 9529 78358 36096 57579 39349 83493 1891 75825 48419 69511 84835 2567 60764 79178 87051 69412 18169 1376 5537 69744 83852 42019 98612 9605 20027 61867 49804 92142 56673 28259 99160 9816 549 70845 53912 6645 33182 65200 23051 60376 27272 79223 7999 97417 6986 95175 66044 99303 18327 37899 96712 10425 15802 17032 79880 45181 63827 36536 78118 60106 4077 16020 49674 28947 2700 7622 70734 63783 88420 17019 12463 64323 2104 6253 62063 50306 43102 15220 78453 9487 7458 32868 37562 91814 36319 82178 45850 7975 58668 59697 83258 1806 22258 62292 3996 46024 58960 86598 86853 72795 19808 86526 48898 9703 64282 2177 84140 72030 70657 77042 54553 71893 56822 38074 83010 88035 25318 44029 97658 10414 91518 96048 51731 72440 79144 41597 39889 26019 94876 87914 90996 69731 39018 88768 14453 13800 25020 33751 48257 19231 86323 45830 39052 44576 69816 40294 6508 28668 27249 80551 47702 61444 78354 65882 72232 43056 78008 61238 10264 44814 56845 10568 48489 49873 63254 4751 54859 21474 99975 78759 30221 67320 7799 5519 45848 77875 12681 98443 28970 33258 73767 61111 49724 22247 13860 18670 95005 84814 1662 50229 29631 66501 26342 70624 99684 27403 49884 66886 78176 43078 49750 23570 84116 93183 16614 92188 61498 74940 46237 89459 87366 40759 30298 73849 57046 87748 74650 95270 39968 11669 51883 19522 10490 90986 51761 23349 78882 96019 98929 26598 18776 66021 81746 23671 82531 38740 27466 17912 30026 58343 46231 56570 82549 41916 84252 48861 52916 28655 57440 65362 43679 73114 82141 70516 36624 68389 80763 43488 68155 35660 15295 76159 82315 64284 37663 55708 64677 53196 6630 45422 65483 83028 25538 23480 49129 70674 92086 65762 38807 3073 68351 95299 48396 98866 90967 6633 75573 90274 60005 30000 58655 94562 52801 92767 67497 56726 61754 1827 19794 14657 24506 71617 23114 84856 74475 39369 9272 11827 52910 74424 58833 1589 54595 51211 32260 24799 43571 23423 47043 65697 23930 82708 63520 19646 85375 59277 18659 84390 66542 32915 94203 96821 62080 87488 20264 24177 13621 43371 10944 2338 46336 9158 70624 61523 16989 89089 36464 23894 20426 16751 17694 25320 97196 48683 37385 49319 23263 83418 53022 24242 23343 20359 8110 29756 88115 9303 50221 14720 61100 97452 46257 99510 4981 39442 94430 6471 78894 8173 74345 12058 65096 96067 81608 96919 91726 69384 23077 75930 45063 73734 66746 86060 58399 41508 93904 512 4771 34131 80062 93188 53834 74204 34054 43286 77714 28719 62636 83697 52918 30555 8637 36449 72134 62354 17220 11715 4127 48019 22586 24307 99271 66554 83297 89679 31727 14682 47148 50454 48698 75816 59607 20843 68851 31295 6147 5970 59597 1288 18004 33121 79884 12568 54220 66663 81869 41934 35336 45402 46327 87023 94902 40729 83878 67697 96533 66597 65140 88643 13640 1437 97605 2704 34014 80876 61765 70202 99059 45401 99076 70133 99180 71652 78915 98448 76131 44800 43972 62974 54587 80204 17562 52000 80149 94324 36814 45267 19376 9096 61067 71188 40978 66729 29312 93919 51336 75028 92841 33196 98873 18823 3225 38663 66075 44898 81297 27181 84333 6844 76387 42137 8094 85575 95166 21724 18720 54189 69756 91395 35784 49825 80442 98492 73402 18893 75375 31647 24335 60240 22019 64446 75881 25550 95602 82547 56479 52992 90575 11737 58318 38804 15276 37141 62955 12992 85696 81498 53430 22518 87523 67817 50649 83938 93029 77610 46968 21046 41736 18409 95120 63857 59362 20442 74097 31683 53171 53826 31175 56374 97306 74273 49883 3347 79086 57057 66432 94809 77574 6955 90552 60072 67774 84215 54618 72092 57639 79667 68428 13234 20374 11106 6209 46117 28326 57995 16884 36008 83541 40214 90664 41427 30559 49613 37483 82862 56113 8978 18207 46145 2535 66069 17302 66646 85832 48227 84984 35395 31034 18176 91657 97229 3072 23829 86164 35864 43048 19085 18428 37590 91339 38228 9376 73049 2462 89500 28853 81136 58015 87275 35471 50770 1918 15012 4097 95792 64710 44385 99819 38054 85694 86894 5591 97684 7234 31140 11722 16288 51170 88439 96162 38879 92962 41295 18916 46925 38949 36518 17966 97981 16533 70798 79430 82763 17163 24569 37988 38826 76658 90178 9394 81093 6302 14981 80861 51739 51670 50588 71842 41031 44583 41690 39833 75257 22837 61473 90881 58318 65728 14985 32840 69406 12747 52694 21841 18567 23794 72057 4090 25955 92496 8496 10992 82287 51651 64024 950 41230 43705 36422 30180 8510 83476 45690 75059 36573 1111 81536 94463 4404 78998 16871 77435 9137 43035 37495 28724 28891 85143 52761 56285 54429 50522 13530 83934 24201 5404 43524 34031 78806 6983 99377 7550 40756 36850 24070 80668 23751 91228 868 23063 97731 74978 33299 30597 35338 27968 55929 55086 24534 1949 41331 83070 78479 39927 58085 76490 66051 23657 11519 94501 92132 28715 38587 36364 20775 370 40489 54982 92487 15301 19481 29994 49644 68495 91799 83307 75065 85475 5007 14500 50677 30865 38055 8202 25765 8789 90130 25626 22582 76562 91044 21273 74932 30992 93127 28433 25545 2452 89415 38610 83729 96368 60922 55565 41651 95560 32997 5632 98852 56921 68579 9396 46881 87206 54597 32813 73026 74634 88299 19847 12922 96397 96190 29365 93702 37611 9112 15401 12664 67732 60123 48872 91156 25286 20690 12870 28410 53806 17670 52781 66921 83751 73351 79496 3324 89606 12834 10008 48531 12168 65857 4498 44679 97135 11589 19162 44114 28284 21597 8359 60098 85978 68016 36743 25741 32408 40991 51301 69687 47046 95783 17043 98977 20215 94998 30455 62521 82217 1703 29999 87582 30358 75188 94955 87482 91228 22257 20509 53360 69105 81129 88483 9946 90139 11191 43191 73359 70216 39896 62326 70766 89266 34307 28046 65514 21107 70630 13189 52005 15348 31042 8085 81537 14508 27974 25685 1811 79841 49505 49692 80046 85470 53149 94975 93051 42486 44459 26907 84324 64337 63634 68053 42735 70182 42767 38876 17639 68490 85717 92260 69038 89714 81969 91010 79449 58676 75860 85716 30576 78677 90191 44742 20955 95062 62968 67415 82941 16020 86403 71567 25424 65397 3278 31319 89887 90460 42348 92435 82399 37297 41666 33342 21907 77894 6554 21620 36474 10308 84209 83949 69215 62264 72903 17384 98559 43269 39449 47036 82558 3096 19197 12400 33999 83148 13158 13579 36852 54756 30393 79974 6166 41160 24203 11092 11678 88775 75789 64729 79939 6657 74525 90548 54791 28765 38479 83754 79295 27507 87575 93124 23938 55829 8096 47301 59019 79768 74284 91207 72726 94971 94230 94534 76370 50419 64052 83990 82691 24151 95273 97571 55212 76537 28186 2431 95135 54639 98488 93237 80912 94122 19490 2235 76265 37231 46276 83456 7279 95898 7969 61783 20893 21030 69045 35845 65245 37906 1932 89683 45995 93898 98954 83739 75424 28996 15744 86370 16632 12840 52891 16079 34944 9123 24609 43608 18590 80078 2431 89772 36039 28042 40710 33198 1144 40103 80731 3034 5753 35839 45705 94687 26920 34389 2804 30665 34981 82552 61070 86868 9664 26903 27209 52916 44508 14920 8086 49943 1515 67771 65487 38385 5610 31079 84999 18850 95074 56417 55454 50393 34439 97607 7592 50941 94055 57949 39409 90534 40619 93558 83057 41281 60682 63650 94015 33181 75131 17386 15335 75207 6170 82964 56314 33387 83171 41408 61533 31563 8614 61755 57464 41956 99596 10417 35538 92291 66058 33254 97043 87616 27318 73927 41186 11988 36317 50434 74046 47542 48420 76621 43669 16748 41516 10000 48585 67051 91627 21751 5172 85754 72597 51424 33676 32367 5806 5377 20030 99295 97511 59120 76141 11106 29092 48904 49321 15381 15439 60912 78191 56349 58301 57820 71006 21581 35309 4476 24494 35912 98701 17677 84404 65261 88178 20077 7531 71327 19110 24583 30533 13738 85685 14305 20117 98548 52630 86161 81568 57685 17014 93133 12404 40032 99669 13539 65372 57954 864 10346 90408 61785 30935 77770 45576 47487 39250 22086 80231 87236 50269 69358 22699 38565 64199 23480 57436 58024 89058 24177 8894 94252 19882 68186 51970 82133 47996 56754 13003 81587 55675 69720 28609 51046 87567 57765 97538 49195 56276 69776 61606 24237 12853 31990 49348 22495 33270 12347 76272 50403 56149 67516 90761 56571 42858 53102 12274 35460 38239 12793 80599 72562 83819 28381 15562 48198 98682 36749 71000 32590 97781 28359 48427 7626 52625 34760 39198 64733 87379 45379 93516 17058 50330 37161 96184 32171 83540 64418 55062 47852 59180 64622 61809 42689 42886 86417 74566 39057 91320 76293 52060 29827 61349 66284 43437 88230 60400 7225 64210 49143 45557 59921 79500 97263 29700 94125 20885 23113 73208 29016 34605 89215 31750 60693 88556 82833 11410 83761 89371 95291 62318 70814 28898 10714 71715 1464 26562 18626 38392 65868 29421 75116 97161 39570 75973 7604 47174 12093 73057 7582 11765 86118 77690 46738 97738 43918 26040 49490 69746 99822 12911 39490 86903 17894 49124 24709 36842 93680 85634 9219 6454 43710 43134 62217 78766 60767 32129 45693 8021 87823 61256 74455 44313 4804 91121 51166 95919 8708 42170 48202 76375 42534 98478 88200 12902 64360 86797 69597 82147 53988 19567 40968 47645 21771 56117 46993 22706 70703 36360 97117 95571 73836 42602 49852 82889 538 70587 77790 77004 27403 13736 96480 1625 65527 95864 1882 43514 86435 16557 35361 62427 27574 77955 77169 76564 53299 88276 33528 20150 37012 34476 28428 66270 90242 28985 71887 72842 26787 56939 60891 40433 16592 13067 40480 20230 96876 73849 9829 68764 92526 48488 52335 63303 67673 21180 94270 61140 70299 43081 17377 89655 31907 34783 88297 92646 58909 19807 63740 27328 88755 71778 76630 89059 19771 64333 32103 97404 8659 50388 41761 22362 69044 8583 51630 80786 79002 23414 85961 66976 33713 65367 98394 87653 53997 60730 8058 26075 79779 43481 28115 27873 46468 30871 98914 19299 92174 15758 48641 38515 19674 267 1696 15800 75780 98835 97000 63383 85207 45538 94673 89110 96258 62487 2506 66809 6725 66354 52036 74859 99243 37050 40216 11206 70684 62096 46987 67247 92074 98148 21757 67830 22928 74131 27516 16558 79841 67093 33037 3280 13933 60744 77581 5365 99735 92067 89840 31465 18675 51147 70858 45715 82197 83194 23203 58593 16180 50038 39085 15737 64327 350 4500 4973 53653 23574 38520 77032 3984 1684 93611 18084 83790 23550 27327 55986 12154 44683 87600 23336 23035 81332 44584 71828 90504 73245 40386 65354 70204 11054 7475 29100 57882 84455 5686 28700 43438 39378 99605 3503 71496 73877 93950 28612 97163 38315 59288 997 99671 33601 88387 43388 85052 83562 4688 84838 18999 4265 82108 53901 89734 9760 20578 18343 31064 3670 61724 58647 81197 50540 68169 73735 48732 37373 78753 30911 41396 7839 13226 3358 41625 19748 69090 98943 79180 73199 33459 87104 73972 51228 12814 96501 25708 6905 80428 55889 55732 71715 73984 14765 4545 25440 58323 44548 80571 9038 37676 22621 54696 67284 21378 91749 949 94697 24143 70782 41034 72414 52835 11793 42924 27717 77240 15363 56443 60756 69439 10907 8252 28507 94446 32844 76465 86752 33999 69837 44156 21805 37599 33643 46659 85096 56535 87661 75971 8883 95478 73734 33626 845 17519 83757 9951 20867 84805 2769 53702 15215 85953 70891 4809 3400 950 43729 38241 98958 42762 10049 97819 44663 92405 15165 61004 37475 77584 98359 25441 35418 78197 91224 73545 36117 95832 90666 88035 84425 24023 12543 58978 35351 81716 43782 37983 71647 676 2870 12148 89909 75784 42088 10479 61393 48012 64544 51756 34148 49981 39850 66185 22265 1443 79299 74985 18222 52898 4210 13721 49818 32201 6486 92140 25239 61559 24565 54482 70564 63352 29204 73569 84863 97289 46158 36788 46731 76301 62302 7790 74273 28717 32697 6299 30640 62348 61589 25266 80953 79541 68046 25453 90445 3328 17575 55250 96067 8741 50687 18368 81322 47742 59561 76426 69722 55066 28623 43254 31393 55823 9332 36552 40418 79244 87427 81438 86823 33621 16848 81245 4056 96076 6036 84145 76907 44543 63066 46711 19251 78142 40121 36586 1069 44854 15981 38898 71234 96367 97685 59181 39446 71027 68745 13782 94182 63679 58587 68299 38968 71834 51065 22217 60496 89709 53118 99687 3668 57417 74721 27 45015 97495 40263 62807 58713 92839 74804 34307 24110 91234 97334 53250 48827 81966 48392 13690 77964 70160 27804 98905 96559 49227 37997 38147 94813 80240 78646 94620 88197 10581 24121 65516 30611 55942 73919 91915 40810 31158 52852 4667 90824 35517 4273 16003 1039 22944 30631 39416 60687 45297 46296 85310 47200 68877 29906 7091 12149 88967 70417 17606 88153 20803 40147 312 1434 65023 65297 17789 66792 64443 23965 83693 96877 59317 70730 77269 83985 64559 70920 998 82014 83562 29501 54042 86822 7273 71566 64974 38510 94774 21822 31093 34963 82470 58756 41117 97826 81016 81596 29831 40526 1611 38012 88564 21827 30099 81880 86906 79548 19367 38794 38689 94671 62861 24077 43361 68155 26106 71653 80090 51711 94530 16599 52652 64024 43905 9296 2104 55146 30832 21340 12095 21922 37043 60076 3440 91779 95984 82268 7223 39749 46853 51598 6850 52500 28268 51367 48411 87863 91798 54041 37346 40577 72531 66027 71073 98719 34236 91152 21483 29000 74582 66573 69270 6665 42495 14672 69500 84815 3339 61777 52698 66056 13121 99875 44019 86410 2732 31859 67646 71860 60220 95259 80340 61867 77509 72551 65966 528 90411 81409 34081 84420 63332 48489 13383 38402 44396 87359 11978 83380 19667 48586 76630 77084 71232 82330 40990 41212 79395 42454 16055 33114 68976 1739 36644 84485 12149 51198 88457 71370 75618 2291 40203 51818 76070 71418 19538 46432 25338 53295 33468 99294 36209 88208 21223 97498 40291 46102 8604 41385 8361 74400 18960 55444 67878 51579 98687 90934 62446 95611 51088 15750 50653 58064 47193 77121 61430 55754 56736 6409 23150 11115 25197 66085 77055 69550 20211 49077 6948 71351 35465 77608 66732 97231 32330 52647 6530 11515 42303 5165 69825 73431 80528 76454 23773 98147 14783 52776 10054 34488 28154 3405 1532 19233 61572 93020 71845 576 80331 4584 22634 94372 64023 92844 86598 68096 34954 79515 64146 54194 50537 65604 79004 42374 93871 56389 82745 9501 41685 63243 89788 19532 30752 7349 75901 28667 85637 6696 32004 60464 44294 27909 33118 34319 78462 75952 2432 81047 15250 6177 26533 49495 23879 4734 81716 94669 74995 59378 37886 54294 97033 37929 358 30978 33172 15767 45469 4822 22106 49495 12246 59858 59196 26198 65875 23867 66595 70695 17536 38993 17696 15274 39470 97144 27563 22024 8932 4072 49461 16073 92401 45075 45700 19118 66912 34945 95792 33697 82920 30651 97094 38414 96259 91453 41990 6681 86645 14953 76581 23483 46896 48133 16037 36238 9056 94430 32491 72628 19925 38908 9985 816 55112 19649 28706 76289 63407 52873 96226 59210 20708 84615 40793 71346 26767 16687 4184 25025 48714 80895 76248 78019 33013 66906 91809 81100 96752 76122 31869 87020 84924 29642 98997 37504 68826 66824 31443 29972 73654 23893 51611 68597 15590 14813 10570 23103 88269 91681 33566 74723 32045 67767 18884 28017 82779 5126 83907 91580 45979 34293 39829 24162 90398 24055 33012 17005 89627 46563 34033 39059 1347 33731 30454 33083 35985 97477 8001 12428 26573 38981 12592 52822 35914 43727 36439 6536 8021 42256 93684 37333 42083 323 87870 2718 3504 49931 38027 69777 3985 48783 87694 70164 65950 79196 61316 82312 1001 57800 36442 95977 53445 99234 58694 43741 78052 58444 48690 75911 9819 84081 2746 35436 67999 48417 8421 4739 34132 18860 76348 23539 66653 78551 83771 60558 28296 1452 94906 96425 12208 82957 81237 13722 50141 13080 21176 36351 85786 76677 54918 94028 40204 91492 37663 78127 36605 99130 23290 57648 15408 72343 77484 41226 67650 69345 29975 42165 49317 39536 69166 79809 11849 3045 41781 46904 63290 38186 98173 64899 25135 12880 3684 5365 21831 58087 58500 18939 74766 14470 5516 91698 59814 48044 64132 18064 18982 49497 95686 92310 32568 81591 56112 24442 97334 60840 73606 15113 2069 51319 59849 56962 24123 51577 48610 83545 65733 15615 27538 82400 53765 99969 15988 18699 28142 23219 48055 86130 50319 31392 70831 22537 44615 25804 79804 65207 68126 60490 87968 79433 26298 41287 21463 88757 46704 31818 22114 7349 65414 68574 98498 81022 79956 6364 59153 86062 25183 13866 79607 79146 46828 3310 16388 41604 5132 81038 14925 96833 76556 9102 91308 8160 43358 97194 39246 74545 71737 72463 1664 28621 22143 33802 39695 19851 30776 85682 23126 51919 9521 74212 51651 3007 83781 6183 95073 73617 75967 62845 7432 9773 35464 85316 14301 61969 85252 71830 9191 18364 92651 23867 77847 94304 54049 33368 92049 77718 56602 12032 66524 5100 56055 57273 55705 77472 371 34150 22914 66068 65710 11227 43401 20951 51657 65771 42425 35813 6430 13974 63804 79866 68604 12830 15415 78654 6778 49758 21595 38604 87124 89684 38055 80532 94807 13999 37700 46828 60326 57398 72728 27373 73110 77291 65274 52532 68358 11584 1491 7561 64495 56203 15988 51148 86004 64356 51546 90426 18375 49949 51056 21536 93924 81342 19668 30304 68528 35763 99762 49105 25150 28485 59216 28374 24400 23202 38835 47143 39148 96329 48149 28089 69677 27114 95945 47580 5870 23996 36116 79518 3013 60703 34567 63882 14517 48852 67442 9739 64921 54951 802 13147 54995 42342 44848 30989 81799 59742 54276 63001 47775 70073 14776 75851 62209 36631 69841 34127 87329 92607 10730 54688 32918 68278 11333 53397 47740 26889 10332 4227 58867 30730 53936 48827 39315 72328 90001 81466 77472 94920 64497 81651 30055 16316 80556 98923 77600 96378 97121 87433 5767 54448 27105 62418 5893 64898 34238 62642 35958 39638 82358 43432 4897 98950 44125 83622 71461 38906 20705 58664 34386 10240 19021 45423 17985 31689 92989 87833 19276 24826 27803 87041 96854 14343 40589 57430 6986 95922 21671 60303 35754 35016 26094 66973 38160 36521 97677 4209 98259 4742 11413 11903 26881 12519 92276 90752 26042 65593 29412 91158 46526 85830 58127 29077 13465 41642 95615 35172 39004 36735 3568 57810 46329 435 13837 89376 2829 34706 56572 77133 28839 85122 10392 18374 59600 55316 65921 1516 41026 52883 35178 51724 28129 68718 411 33879 26875 66729 76879 28962 12610 93664 94472 63255 6917 63028 23693 39585 82393 51065 87904 17945 79791 38351 42985 61111 45602 66194 83417 91321 24722 53697 66799 62784 96554 98045 63954 58703 92426 76952 57633 86163 14202 81747 47466 52457 15382 84148 38400 62350 74722 32767 14790 35269 99515 97311 5472 95175 31527 3005 8968 42303 23220 44270 77382 81174 13572 15344 55376 2767 44075 36575 51769 5742 94279 81272 74743 78378 45006 228 14956 2767 37204 79845 10060 1166 34325 17448 94329 41553 14320 69987 34898 82998 52702 4494 20798 7937 21985 18447 10486 31808 16873 60387 72855 58348 33501 8307 31135 84814 97789 78977 20492 38548 68496 2690 44120 1924 10496 56256 79407 37464 13250 49819 93786 86904 61360 22518 77633 10368 48720 77513 7773 53069 56874 7787 48878 52870 73245 75483 97075 96360 79677 35645 89144 18281 25783 236 22761 90179 82910 75024 62771 93033 33885 94234 66942 12335 11197 535 22866 63777 19813 17562 74471 48341 23623 88005 64592 16173 81900 96571 30356 32496 46421 38477 37407 31041 17784 53948 19918 80127 65985 89538 31359 71753 63871 799 77676 62799 45600 33548 53360 48852 72864 26073 51653 29912 76652 26380 49287 60909 66509 72049 4719 71979 60905 41275 5690 24750 60177 96760 66053 11806 56387 26829 72861 6258 13318 76153 75731 49872 79700 77535 81921 5736 21511 36333 17772 39165 17089 18295 10108 33245 69990 1734 31459 67043 5222 31558 73643 89227 39203 27010 3895 77110 78574 9151 50304 28183 16764 95116 61178 902 35003 19935 31901 32258 40695 62679 8020 97048 45062 93801 85020 92477 33990 3490 54068 36039 1486 57015 71583 79672 74944 92537 71200 67083 81623 14058 5618 21494 54766 8766 58872 78951 23745 94885 58814 66343 80873 3856 39823 50961 66115 18258 62052 24752 46038 44101 63569 63374 97686 83875 56111 91983 93002 92513 77192 27134 62013 25166 34909 79118 83553 45480 94932 24022 61560 23042 92308 98111 59545 30321 7313 2879 24084 1179 42284 27615 28978 71222 39209 39745 44337 62698 12541 86904 55387 81575 18352 39158 8951 55500 26544 44089 84939 18064 91045 10208 63695 59308 62197 16560 90950 62748 14561 2711 41386 28524 11752 64731 74921 22721 64057 91501 11383 19227 25947 76853 81562 94043 95920 27210 82310 56140 26456 38565 69576 76902 45611 82792 50586 40214 14791 43274 52522 32301 82556 29076 14755 25911 34910 2538 87222 51197 88424 97263 76589 15767 19403 88793 40719 86421 40693 68100 10468 45954 51512 38341 46513 38519 44367 65434 24297 81168 59559 53640 90836 58123 67737 8766 54536 50809 81802 97499 59334 1293 32123 77218 93678 47217 30839 7747 99540 35928 83019 99622 93179 82840 32732 88992 55727 20736 43714 69504 28826 10923 82918 7085 74507 13729 17881 42141 20475 2102 69057 50724 32456 98439 64681 92193 28004 29394 43287 18282 3803 73667 39220 23001 88606 21501 57369 31086 37004 20786 51403 52495 58404 75682 17127 67900 61382 46371 47240 69734 4413 83470 81518 22131 90889 24652 29550 39823 76762 90032 98115 40789 36467 91800 85161 57088 27846 23442 16114 3932 28165 50839 99738 77866 99711 2131 46833 51244 85606 52677 14444 26014 47910 22031 71570 56197 93082 67354 57759 97136 56892 17362 79719 36798 47560 84821 62653 79482 35954 65768 52466 35758 19070 53011 48914 72054 67728 10828 61200 7211 20851 19100 79578 68054 97778 22212 71488 12605 17417 92789 85422 10978 13967 49839 78210 96974 2864 94903 24418 54434 55704 77529 30161 5286 53624 38314 34853 5963 45319 27099 75034 2638 94165 51130 37942 90657 85129 57628 76691 73913 15853 87121 3290 41262 40810 91750 12418 95138 32817 32470 7454 52504 4354 34477 19554 1884 82932 94899 72739 80236 8980 95868 14423 57023 71974 55798 15454 61579 22239 85830 84462 55507 54105 79412 81319 97962 73237 3740 15245 60943 67626 78854 80694 10705 57414 724 89636 73707 47277 58338 14043 88779 45198 78182 98363 38853 91251 45517 52746 4698 4163 39944 58655 40658 36665 33394 45712 60993 83293 5339 2565 5865 76042 83665 83922 98424 42739 20227 21087 66438 5543 12543 93500 27996 57885 70324 96941 32975 20489 96096 89188 84768 21027 78589 51696 45792 35965 21257 16898 5726 82014 55605 83936 96764 49837 17248 75642 2063 26214 48865 92362 96123 67487 94232 84291 84826 81713 30828 71506 69177 26195 26379 67737 16244 60009 66035 57541 66234 13652 86863 5551 57855 64834 65670 49471 51415 9775 34980 15654 28377 98397 16234 55918 98660 544 26243 85343 42394 14712 5637 39762 92008 12147 27929 26736 96095 47140 37485 90723 30614 22921 50431 33499 31081 4492 47549 10315 59263 68136 84143 36550 30151 61110 83537 92855 31755 32718 57832 53340 57099 40358 15980 70161 17929 74703 10939 56494 25136 61446 92438 55968 14366 503 74874 99688 62511 32695 37119 49524 64617 5193 21877 32804 96781 15553 79269 10137 35442 73349 70758 30154 19711 47037 10253 82566 52584 62872 15566 31085 39306 5862 1594 43030 18975 7808 23789 41342 26171 29682 73419 35578 21378 20324 3256 47258 52318 11236 86992 44823 1159 5110 9500 42662 65122 88514 95888 71928 16074 33308 20637 34068 57859 89082 82164 27127 16504 7021 85083 21709 56428 53873 18963 32678 30589 46001 98061 28240 32868 99809 35434 2164 93455 85382 93384 88024 38294 77549 72712 40361 69466 74169 40837 29518 68869 46306 89374 43307 53544 5375 53353 47290 90578 37251 96916 75541 5720 49560 43175 39234 97078 75638 27857 7325 55580 80626 29329 74715 21416 9200 48403 70083 96119 64800 7449 46167 63162 28964 19467 25476 35675 17808 27198 38065 11729 89007 1310 86517 91366 47718 84795 89004 31112 46217 39073 35415 42209 86138 31598 73769 23625 84035 91954 3450 62111 36187 83355 86545 8007 99604 85024 47843 66417 54012 61105 52288 54190 90695 70427 73580 52329 30903 38841 35254 98139 40268 63923 22679 52988 63857 27011 31465 96945 81386 44212 72166 74886 59512 64398 36312 17028 46940 54318 29948 83635 18845 31544 85734 12027 20405 7660 24033 53069 63220 38467 53590 66399 72384 86466 20953 27117 43136 59263 86008 72489 98070 95977 8944 18346 68 8684 64057 46761 70641 33307 18772 43140 49857 88968 39859 78284 58161 50467 10782 96834 94782 52840 12381 53556 42050 98653 92178 37237 85486 83641 15866 57796 28601 64052 17541 17504 33738 35874 92909 91086 97312 42913 97388 52189 33048 81193 51403 63370 4317 87550 23728 69702 53857 57722 26913 49673 24329 94178 81431 11695 35102 60569 44378 44866 73434 15614 60556 24032 94699 23074 64018 69631 15235 963 29994 42594 50233 54383 62952 58158 49106 90324 44887 2722 42603 18445 38567 19691 60808 63231 32680 80034 56114 75563 76284 99747 1854 63161 57671 13313 67749 67564 72991 68024 35788 9106 9123 35712 42164 51013 49324 35777 71507 86629 53030 8395 7874 34822 94077 78266 81302 5197 86096 55292 67344 23111 62950 21421 58559 706 70208 34637 83267 60445 40428 76151 54900 87732 91632 95246 60810 59493 6873 44916 72008 73621 9511 87921 70639 36823 20320 81825 50990 15124 25133 78729 22908 70641 25407 15019 17759 98452 4127 12744 41172 45491 11229 53365 35643 63479 56815 41094 48370 66048 40308 88683 87608 3762 45378 89300 91022 5570 61883 1871 40692 59125 41106 79101 90342 52661 33161 83385 55180 64042 20300 80683 42496 85661 91805 74133 5093 22908 12987 51269 29331 5781 89212 14580 15375 12806 3444 36268 94582 59993 52211 90899 58836 35510 86909 47127 77669 43104 41341 77621 50059 5118 43803 45555 24479 62709 16773 73845 83970 41083 19430 93136 48752 61528 41325 21054 685 96632 5156 39792 70093 56559 75104 93726 17752 36576 28026 93578 41974 49241 36676 30933 6562 18769 62865 15284 14939 15966 59517 57754 6344 31040 43476 73970 60512 5201 20257 85653 94181 636 1843 84987 18363 343 7109 6796 66988 91729 16098 40668 72036 59710 16965 97745 64960 71455 99794 31271 92931 83970 37609 49761 48498 10098 44445 28144 74846 18736 89368 14233 13401 28890 60816 67379 34753 9380 54289 35476 56304 89674 55389 64875 46422 33692 66020 33116 94198 75824 56727 66275 12433 37946 90319 3275 32061 81870 60412 95084 14911 57005 18425 25564 10536 39257 83382 86684 59012 26705 67804 78767 85866 71154 15975 30539 85189 51929 96777 94016 80147 5221 98685 57034 30652 69340 30892 85707 73528 67883 10198 71954 30466 5965 6644 97129 47296 28521 22424 42160 13808 20651 97716 50453 51561 28643 40862 41841 9897 95505 8113 6035 51757 41669 20911 44085 35687 64369 59959 55524 70704 37270 19563 2952 46518 83385 85622 95646 74892 8084 65324 91753 56514 32423 73579 55981 55420 65038 23174 81333 82357 7317 79205 53910 71521 70927 26205 88735 3488 1724 76130 42534 83419 94020 50452 49293 18871 64250 96813 71441 4790 69690 36182 24452 16000 88430 97809 38817 40357 80466 80990 61926 66947 7385 47523 28823 924 59536 19837 95039 9937 41442 63429 50966 388 67819 91982 48127 80536 28404 25441 87035 18700 87859 10984 66302 95096 87089 18530 7966 9567 7257 32716 14721 44653 17355 60428 66762 87293 72088 98624 77614 93458 40452 45215 14869 96625 18495 5407 8636 16832 24960 50035 94944 74537 41952 27191 93363 5082 80065 59788 92411 1703 7647 96663 57 22466 23468 82945 50526 85810 87121 92751 86309 47241 32612 19722 20420 29546 59581 88604 45834 20777 96391 77322 25684 48595 93080 26487 10584 16432 3943 47078 5734 13179 41759 89601 45967 5569 34281 28827 20644 77607 97631 79099 54382 84694 4908 24703 23065 91821 31183 74499 70110 1773 24325 30253 24012 84525 38541 23784 30697 86465 44538 98462 94230 13684 87529 79439 24655 71507 6311 15682 70754 58446 1920 3776 36681 48130 71065 26409 1229 89795 2605 73309 90586 12772 32156 45720 2777 57128 18232 45245 19947 4742 66023 4346 62704 6093 53014 25339 19901 70700 17271 45847 16496 21542 35833 73956 99802 95369 23070 73467 38446 12075 4638 30646 82185 35860 25049 30214 71290 44483 90930 2055 93467 26227 59385 39249 89294 84360 82059 5120 12037 55595 62685 80113 35246 61181 64838 46026 72515 94386 23070 11738 11380 93367 48950 80832 73910 23035 61300 58875 28782 93585 91838 27723 91121 18159 78952 30394 640 66054 68488 65318 71018 63522 7249 28251 59056 27612 51801 32116 76376 31180 36885 2055 5167 73297 85415 23984 89759 35716 61661 65475 44663 44235 32761 73649 85161 23533 58408 37767 53290 22118 32452 90407 6566 24856 11508 80350 15019 34603 62462 99355 20283 92017 5343 56455 49949 47924 65833 32531 23155 12939 32623 43162 35382 31805 53954 84284 27712 29861 4015 19708 44731 72515 86484 39464 75679 87591 59839 49825 84001 87741 88909 38849 94251 43100 35525 79592 89783 95699 17551 18856 93527 74215 51527 78624 48789 40383 13156 76362 69263 14438 30130 22278 96205 29994 21758 97224 44667 95630 42240 56332 67794 9748 95017 54696 68363 55206 32663 33431 11783 72242 74871 65094 61386 82457 62775 12151 86905 54288 31189 76421 125 8231 25477 34484 76766 95898 70652 55034 95298 14848 53939 79776 32503 6078 85248 90116 85336 96245 8237 78076 52171 28643 26166 9380 18933 51749 94782 65214 60554 49802 81052 33900 90033 30728 53258 12160 99891 74928 95082 94663 52169 29567 59466 18299 80312 29131 28328 448 64615 93407 3239 18384 98785 35313 49467 80666 28192 2536 12833 43718 32720 67387 79954 41712 90221 9592 39586 81144 60513 54158 17821 96183 31636 39827 49449 79441 52282 21546 19993 49869 60184 52266 64259 40129 27043 10096 49570 26985 85852 41455 90957 2925 40028 33484 39737 20820 92320 146 18830 46218 56807 46527 44525 45306 2683 58247 52825 39146 11527 42745 44278 38553 11339 93790 63837 9146 87747 15745 45158 26555 79966 30773 44964 93270 9867 64136 38349 48569 80779 81895 48676 58547 68043 1055 48756 30178 43607 23955 5408 94416 69426 4606 50313 58113 40156 29068 8479 82228 62456 82510 42819 79924 96581 28024 98299 32814 55140 98314 27346 38485 7481 3081 98985 15653 87921 76321 5625 32307 9174 41689 48845 72030 45623 49884 42921 6148 4301 54501 54425 44115 27648 21174 48668 4406 37226 28884 31500 8637 27646 59893 73380 20974 29516 40935 6085 55797 24369 78592 88786 24026 42428 18660 37404 38658 94920 77051 69068 14391 19971 8433 6172 75891 6765 98354 8431 5789 85982 59064 73213 71837 41069 92085 69418 97014 11715 26675 33031 7858 35013 54821 35225 98885 1213 844 53420 21789 50146 19716 82941 1892 96203 87340 90569 72664 5698 90226 80446 71051 16095 8918 8795 13682 61123 53836 62282 19470 70697 73146 53016 71481 90140 8602 53233 53149 32041 68771 29980 91598 13076 46442 90478 42580 2711 57920 71661 76443 51225 60937 87880 35132 35571 23703 40290 40974 98477 36715 17062 51550 85212 72425 31128 16204 3996 75781 61359 57240 70386 52640 12178 77896 68859 32643 48427 58667 43710 30486 75943 68127 8785 18387 80336 20071 11495 77129 79602 70762 92837 38616 72674 21691 5531 44681 69548 12003 36439 6432 48329 65876 3133 68012 1833 79711 29528 36193 76490 82615 5306 22834 45666 60006 10690 20095 59115 80007 16961 28027 17150 91485 44389 61714 64386 30578 66301 80480 99345 86346 53309 85155 99698 95538 48906 36486 70282 27954 96884 37660 12584 94120 21866 97228 24850 41896 38040 4343 21026 30988 63431 43360 88501 76838 23671 50068 66530 91328 25019 55877 92253 74419 39183 75277 95185 86166 98063 2630 62808 32070 51716 11787 84264 91383 96699 51844 24657 66257 64694 70648 90717 39342 59302 92116 69676 24537 67896 87659 66204 86612 79208 22532 49766 87535 88322 15470 24202 79073 50624 30205 73872 31148 54356 90973 72270 27149 33855 46903 92901 53783 44080 8912 23320 87810 68167 50575 88255 20136 32865 52905 68292 52740 32375 63687 93751 43720 44828 26060 26295 60705 76354 16263 8997 83099 69819 8837 5919 45896 15361 52587 69025 33818 65516 89671 75777 79049 63414 3728 93887 66187 32244 86758 21126 60189 48903 98924 38828 23165 41946 75253 80024 79450 85663 9584 3667 32270 15802 92871 40554 45794 28195 48601 28731 38199 97508 36780 95157 24985 50953 79227 82671 98084 66025 99259 93365 68102 25425 54611 41460 60211 94782 54502 80634 3365 12260 27603 5620 23815 64638 39937 69543 98918 27839 92393 29601 2965 19406 55973 41133 76388 82441 64665 11934 65362 39755 83382 47078 34542 88794 6726 77628 32745 65311 54062 16753 63559 22723 11763 89880 40643 28716 3738 75564 11314 11002 37090 52017 20911 28255 37900 94637 71238 31455 38265 31265 60967 34631 82016 9006 34043 89458 73841 52765 72149 14474 30544 31964 25003 15701 70088 33462 63749 80511 87854 93313 41037 93250 38195 94464 97683 84246 11311 87879 9715 48334 65908 80164 88739 50352 77070 12615 92968 90847 30783 73671 38785 55904 97694 75130 54414 11207 70900 24156 49654 97564 29637 51830 49722 43176 74539 58556 67869 31341 95828 10887 34984 63640 60040 18052 30837 94108 94289 87913 2618 41200 91477 25740 41094 16825 49475 15358 73196 60556 22898 61660 85165 22322 51999 50517 47498 84736 28259 74261 7390 26085 88651 55944 23377 5054 27248 62700 11880 99458 48023 53499 90838 975 36286 49341 85170 36768 82515 83184 22353 95521 25104 71557 38310 46084 4242 37528 26389 38878 59820 62662 76810 97632 54245 58807 10054 20529 57933 49482 30253 64632 94094 28145 80942 43648 52685 93122 66061 311 88606 70571 42356 62410 37345 6074 99782 62882 77144 91459 79632 46530 12565 89186 97851 59466 6339 52615 22614 7831 37670 9533 18865 63587 24520 57407 3051 44144 80389 27486 53421 34873 30624 36202 19385 3005 85174 14497 79666 87145 98909 61224 61283 89257 57382 97504 17636 22028 84766 71529 76605 7474 83963 42398 58808 46937 43149 31356 64387 65228 27180 80606 33004 30873 3133 85439 20684 66674 99799 51257 28294 64321 25298 5407 14019 38956 96203 15079 69108 81386 43451 75260 17860 32249 60113 36241 33267 61 38019 60912 99059 39853 50277 48190 70323 97910 8193 80000 83562 11401 19846 19675 23206 58766 8823 14126 67050 42975 94967 82618 746 47451 73742 59851 19088 38976 12289 83600 30950 49081 14486 753 3632 54341 44034 61300 3383 75732 63481 36785 95851 88435 1228 60055 97763 97035 17572 260 5209 21383 96379 46505 74815 62376 11272 17948 48060 50274 24497 122 10937 17585 59146 23677 80362 14183 46266 26028 84605 6285 53831 80813 19920 94838 78314 73859 9593 51533 40830 19651 31678 50889 78860 23579 41248 99722 43785 49178 27053 78117 51497 28226 22633 2722 14627 97540 53395 38500 74304 7095 78905 43650 92701 53068 97626 87624 92170 37936 86912 79833 58888 69308 31623 49457 17144 59604 34526 59481 13513 69546 43206 66677 40159 10013 70849 23782 11348 90043 72400 92179 70636 56614 10701 30175 74414 54528 75430 7742 6888 6960 92229 48073 92301 46991 58805 30024 46642 96292 60031 21617 95883 62522 145 50381 12672 16590 15947 25 10470 34820 20182 6869 54576 46166 15971 78317 13683 66135 7714 62893 90812 2239 46367 67914 93569 4566 66364 78171 84850 10934 31163 86756 21338 19594 34003 85716 50912 30535 46097 66203 45764 84824 16479 35509 55136 86984 28370 73129 71003 92506 50679 41596 70639 69741 77602 47004 64204 15903 70459 71248 68801 46647 92180 40422 25590 86319 95505 27853 89033 9712 82393 67325 53349 85522 40433 167 55376 44918 85827 33106 46230 63840 38506 6811 97699 31463 13107 49781 35559 70299 81564 11291 74481 80964 41039 41917 54194 7296 53607 87611 71281 19935 475 97552 48966 94391 37783 94059 49059 56275 59908 11520 24033 20509 51615 34124 57599 43212 22412 42104 5980 267 92625 67999 25231 84095 13707 76346 73688 3595 59767 3643 21641 2385 51920 26317 96245 69012 63029 82326 98155 34485 24564 92351 27475 53192 45514 28673 42644 60911 99403 40173 5987 76633 28948 52234 83072 98586 58809 56515 87242 4819 4091 91431 71106 15665 42432 97022 90401 2027 42037 76172 62843 56590 57640 45669 59362 14638 46560 92015 55894 64664 64760 50875 87129 58064 67869 72755 60137 65584 45435 40578 63771 30220 63255 937 6773 60029 13614 9327 98612 75373 8446 38404 44870 11605 88890 95310 32828 23697 71902 38817 84350 8493 73294 42723 94638 42293 8543 90485 90517 95215 51703 16805 38288 80292 84523 35891 35241 69489 88920 68928 5191 92931 58766 5464 2222 89294 90431 81423 91445 54869 85248 13294 16492 3908 90524 75056 33447 52386 83294 26947 86644 68319 40861 38364 18054 84129 2862 41338 87907 11979 73665 85652 77040 85269 54190 5941 20047 85427 68549 74077 13099 50297 73730 87397 90889 78678 14154 61937 51855 76545 94348 11073 81778 22445 77508 11800 89205 48667 29775 67754 97297 38001 96348 40656 55827 96187 45767 57841 20264 25944 44111 80638 2115 10886 96503 73253 63729 52429 69209 98097 33072 93262 69560 61090 26123 62526 13069 33373 45425 10732 97560 77928 4297 98871 85551 25625 16855 55042 16879 6745 20589 70323 83416 91047 23606 64295 81841 20609 99411 27344 72149 86636 21206 79739 24401 253 62829 32186 58903 56424 91914 72637 84369 96440 56293 27952 96096 7201 94279 16502 13947 50602 4414 40542 97627 45997 28840 12902 69707 7304 93685 71064 99169 93971 89011 44950 57274 50348 83245 46911 15742 66248 64136 63000 28341 5637 43658 92816 23151 46933 15864 9032 44411 73015 95485 78652 35321 44332 12549 9425 6600 44942 18140 1307 89531 67182 87542 73880 74622 36764 79831 15792 56513 25683 63881 20433 77019 45829 71071 1452 7848 58904 87882 70375 41861 72480 49192 32429 29459 78295 41955 7926 87846 3890 59040 15753 79986 84083 18781 4421 40984 38240 91251 48304 37977 17444 20930 93575 63240 6673 44609 5513 64725 19131 28139 2541 14270 34498 37298 46812 63494 69980 36931 47334 57973 99453 20613 46331 35147 38789 39805 82014 34700 19765 37374 45616 59057 78984 55161 51917 62673 543 38770 31652 57574 87781 59828 98271 96635 68930 51786 10253 87300 15185 69943 50492 68727 85752 12298 49357 66525 6493 85540 10400 14314 55145 68322 93389 68022 1703 15723 55857 5143 9745 29769 27429 37845 66023 15085 13809 82922 45912 52976 71250 90957 94693 19178 54326 78968 97528 15197 13852 63513 14496 5303 8718 36158 33050 29520 61296 1502 28687 23256 40153 44766 41580 15624 13522 81400 45133 59478 21603 15045 85005 18169 77035 82447 86562 81220 3183 52644 21946 22043 84210 17835 55236 26317 3803 45645 79264 33798 54791 85426 57625 66537 45321 93916 36662 83266 34578 76211 80558 4492 5473 1794 56548 34057 36824 9054 3836 87902 20855 18475 20800 1240 12879 46945 77852 88969 75661 85066 79874 19390 80620 8293 79299 69922 23752 31028 94513 53551 31710 43721 82619 37809 41389 2960 78977 62772 78801 26856 88938 22560 15980 51882 91533 98679 19012 12218 6017 28120 60644 11026 15150 77888 51929 27299 35317 81944 62844 98619 36971 37839 39783 37267 61286 78795 21842 96812 12410 53685 27968 36392 86745 75106 31168 4597 15332 75379 21260 39821 87355 62836 86323 80280 29564 19662 4017 35426 77263 47003 24406 37279 11986 54655 534 84985 59232 69056 27975 74048 29998 81682 82010 65251 2743 89523 2387 43596 91070 77292 84179 67586 59760 45555 85798 81666 16343 69796 90231 90699 5 1944 11914 95538 59128 40178 59515 29658 45305 78474 56565 14832 90044 94516 67512 59825 78363 54305 88898 27699 20840 66237 67021 63970 87058 19399 20765 70540 39501 34946 67358 23159 53535 73575 96698 67330 79614 1839 88886 72649 31392 24154 2835 80693 49208 54250 78291 37968 2884 48529 17027 96942 5318 61454 54525 71826 63561 64414 88777 66858 96060 67516 26948 83120 74542 20005 74034 58275 56186 1417 71083 62731 56926 24872 27578 2925 93510 28088 42315 39059 27385 16342 62284 47873 77390 90762 94530 94657 24974 24087 82954 2917 59411 11497 14764 28181 54728 41265 82433 55264 2093 73910 45164 39532 78478 6221 30101 63475 97380 44656 34762 56606 50683 52385 11511 78971 29116 50074 88919 72598 92391 87427 68022 12089 21915 80220 69873 93841 37088 66855 65744 39074 87886 91436 10963 53138 23026 48168 85084 25620 64219 68558 24229 36214 76871 12494 51785 74968 10408 40570 90143 9634 9658 49296 4138 48772 4791 24868 74918 91541 27108 4318 7116 78546 22734 32780 74905 98531 40338 63122 43159 81603 21466 81654 87940 54698 21393 45949 43653 27501 21918 73192 84058 93648 11929 75516 32158 68814 84738 17774 40281 6953 32413 37606 68687 83735 49180 29535 11380 76021 84398 18557 90239 34838 31291 41095 22000 48919 45766 62555 78447 52449 62348 89401 23401 54232 86692 54548 64708 99284 22935 91289 83107 61342 81817 13830 89018 25448 25246 14204 60930 63238 56677 42398 96068 9420 870 16240 19430 95065 59033 41559 28844 61367 53042 55682 26804 3523 22543 38971 9809 44580 36877 78537 56937 54787 73517 91733 73892 15179 16242 35375 89740 7733 78217 92615 75855 36400 2433 73238 83776 36229 19540 12907 88003 35571 89223 30192 55700 53487 18669 90255 35748 23265 98078 36955 26610 33760 40314 41363 98998 94536 87709 30008 89389 13954 79834 96091 80162 93780 66025 34506 70267 28042 35708 62960 96528 41641 85471 97274 91370 72420 76543 5273 98777 39599 60525 65000 27115 3974 18202 23318 41900 62857 84366 23122 85655 13429 76061 33937 22628 77609 16050 28489 13328 5243 41428 98821 7851 94276 17315 88292 97756 71518 81513 77699 65857 65818 94177 58166 82418 59759 59989 20960 95934 85309 92482 82904 55488 30014 39149 27516 38357 33805 40216 6224 91344 84744 65112 18415 99239 81886 92229 64777 9057 306 79338 60493 40921 66219 850 42997 20065 13747 28120 2600 31411 12054 34093 60769 24000 93052 32427 90663 414 53304 80227 48520 83873 1411 80136 3932 51935 67848 67725 53595 74313 33809 55031 49779 78834 56582 10294 73960 52933 42285 51938 49081 33990 77883 4737 54190 78416 56166 41072 39820 48571 66403 18101 76041 15825 85963 10719 81435 83286 20997 93744 36587 12646 5754 20186 49057 15633 76210 27476 82763 99508 16627 94307 61547 90716 1586 43126 51792 70944 29191 53126 33532 90562 17163 86984 61774 16574 43195 88174 35596 6603 6986 5778 33390 47370 82969 2338 88201 80554 69503 68637 2435 26521 95977 15456 91088 56471 15110 52068 13278 81964 3959 75077 92165 72695 21257 33341 76111 343 86943 49244 19025 32306 26171 26688 38126 77502 55135 68559 48690 43817 92294 28810 97842 24533 15082 64988 99137 88940 23358 14919 1105 99403 25776 41332 63932 58284 88787 7353 32031 5393 45876 93699 89498 43616 81778 30211 35255 47594 95758 52205 38862 3642 91005 30641 54362 16889 19093 34525 82384 27878 24975 89763 29006 83811 16986 32282 56863 87067 64511 93302 26046 89699 29708 25218 71482 62312 14331 86267 25322 69829 93097 93143 82510 92723 26079 29795 86395 69932 63882 75045 76467 16591 8514 96296 86005 82259 66955 41476 4518 79321 28775 37633 58887 46603 46211 93497 20590 66915 32971 56501 84897 7060 45416 58985 61860 31956 55324 63758 22634 19189 29685 23539 53343 94359 83331 50807 76831 17950 59413 49992 54430 92532 55901 76687 61689 34343 60781 14065 65239 65012 33126 51547 75856 63227 42547 65765 76266 98056 77859 68751 23926 29384 44879 74672 78163 85745 95206 78 97467 53778 56802 24460 82173 41186 90130 36822 79141 97317 22950 62276 61500 15895 36729 73509 88618 32596 87562 73819 37556 57521 96474 37173 23527 31640 74767 37768 70523 62207 24473 85618 55160 16327 31240 58682 82609 52704 88697 84670 52610 13019 80318 58627 30574 96955 11380 68646 15619 48847 19743 96606 24026 59090 7983 94253 32865 20574 70554 43581 9504 53051 1732 94907 55260 76119 91124 11975 66640 28885 13013 37218 62239 55721 50192 85365 5818 56870 19833 68012 48397 24391 63336 32970 37500 61973 28829 13736 73210 71967 53662 10050 70270 91023 10841 14774 33094 2881 86749 47489 3774 30173 44773 54414 9218 38649 19745 8472 67242 66057 66880 72032 58564 39265 7975 69608 95568 57290 77486 46125 24612 67652 43803 91261 72713 97572 55041 58952 17946 53010 17841 34609 11719 46646 65283 73949 33102 68893 62279 77384 24993 26639 10858 12902 98977 94753 11333 1293 89842 49099 43023 22913 63986 38984 54158 99834 62387 89585 92797 11314 41592 11277 48344 14315 58796 73700 51286 32100 52641 3282 62656 81649 12973 51186 6578 30728 13164 99591 70503 87887 96991 72500 10003 72152 92761 3740 71517 43539 30589 96930 87559 44888 63550 4913 42858 35495 5564 91930 9946 42319 65577 81460 28966 5441 3890 37835 69634 80549 39452 5881 4803 5927 51286 65451 32550 23664 94571 30027 20121 37945 83742 35769 83158 84699 46242 8982 85277 54383 34840 14613 34389 28562 47534 75215 38729 58945 11543 88914 83583 29875 6507 46670 52972 49669 2093 77696 6962 9208 3297 11801 74658 18496 13064 59775 69074 63135 57289 6163 43061 54990 62279 86398 80434 28717 55370 51968 81444 24192 87553 36156 71216 8833 7800 69888 30321 62075 23238 38125 18063 71482 63938 83116 15519 46269 4177 24357 262 76203 59165 2048 25009 37851 96448 15166 89924 41316 66853 28771 73069 73589 61971 64800 79523 12766 99726 26678 97840 25292 24539 48428 46882 46863 67089 12370 89361 17631 70914 15883 5790 10636 40487 45633 4348 63611 34964 74184 15659 14207 85597 33962 2039 88970 59173 7743 61633 82861 20317 55566 71809 67969 69248 32859 29735 72363 57284 75986 194 69144 16776 17873 7315 96955 92013 41450 56818 70351 33385 7157 42201 77349 13961 90974 52104 57595 78337 54471 58748 64437 89088 66904 57913 36528 26324 76708 40468 18734 7347 51504 6939 68827 58815 61478 87146 37422 66800 31083 80337 47796 18179 61972 75391 69879 78303 26927 78372 41591 90900 93711 7668 7980 87950 40052 89548 83250 50243 87929 74122 64398 73457 66082 63200 23826 68100 42932 69765 62650 199 44521 64232 61217 8207 23358 36084 21149 39488 7181 43510 89755 22790 85725 11716 26187 58684 24562 29967 36546 94870 44652 58689 53721 74974 64971 18739 53745 54473 28083 3254 36209 12693 33476 74668 79021 60582 34600 22194 2466 88847 79705 69336 6895 75845 56761 39971 25685 38742 18686 73472 91549 95664 97859 20741 7788 83205 97012 62800 44456 10733 43601 69106 87091 79953 65228 63565 85088 80563 56425 72804 64582 45410 56800 23726 25971 81562 65521 65981 34765 32293 58352 61188 40492 58026 25846 45254 88751 45452 82980 9741 37465 86824 52871 95029 28942 13687 39845 95305 26187 53089 82956 87350 49360 98908 33006 53371 62492 80809 43747 62395 94396 32965 50386 99307 41924 47714 88358 32545 66829 98650 19253 27014 57912 40784 96068 48805 73574 7445 74756 27716 51192 57748 68365 75759 93303 18086 10149 92966 52461 29536 53501 97210 85159 99615 20933 77385 3222 69807 51772 95276 28910 60505 96539 68663 42764 67948 45303 31959 72707 47069 59139 93023 52271 17824 96792 25115 24923 97559 44222 42787 54600 98996 4041 67431 81646 73863 99386 27750 80615 78019 29175 59287 39585 59943 46829 8635 80203 74715 70731 7821 59101 36640 77120 80027 53983 25508 75731 69681 25229 69680 80235 54615 61694 85040 69857 7734 95933 45903 79576 15269 94374 44225 29859 44330 65729 8990 5081 28742 22628 19803 19163 40089 13909 80764 36680 25586 40523 89233 31373 61379 9431 48105 4017 10132 37514 82134 86547 30124 2766 2622 86543 27689 20716 6887 26162 56225 8257 90454 65210 70508 4228 10994 8984 88619 62928 451 95427 68380 56189 12135 79609 25148 52713 36472 40001 51447 85371 97049 4285 10944 18163 3393 79887 77492 18084 23191 37036 53446 25647 9469 9502 33584 13781 80779 33631 16852 44811 29069 61785 72051 33742 83072 55995 99066 67908 8723 94719 89796 2334 36154 37705 115 78170 75474 20668 3084 70237 36311 38068 97077 46806 70119 32651 87318 42050 4467 30887 32065 74270 95047 56583 53295 85192 54548 77300 71808 70196 93034 45484 74396 87794 71095 21592 72856 84436 81845 7638 82873 53407 34188 39938 79339 36790 19919 99569 72987 2465 15557 74359 93187 23926 62394 80638 72667 19985 30544 25664 57946 14248 8386 24461 80982 22714 5003 9755 99951 8194 28533 543 27733 8649 98688 79444 55109 80112 53101 86777 76546 83961 59422 84720 4987 25945 15662 92176 2234 4871 33854 74012 47030 98123 64642 43968 41047 47257 12099 26770 92969 88257 79455 12249 97035 79003 16643 90299 92030 86682 23331 54560 48095 93061 4583 29046 38200 59805 94142 84893 75317 12225 17835 4197 96687 59163 20478 15587 55485 80206 48031 67883 75179 54818 19378 41 25161 95117 46319 41138 22353 44765 36725 88569 62719 72203 30728 84422 67176 41072 87810 33277 54377 90030 67013 74806 57893 14658 91554 41388 71021 96188 32712 62283 79565 15622 45341 66962 39195 19617 8460 59924 47213 49971 8345 85200 42856 58793 2403 1654 62413 83252 14333 70913 17427 11718 25892 34149 71176 11183 10852 82059 78670 7066 3780 15679 31930 56167 40587 94957 61972 66477 5303 52467 94589 44820 38153 53819 38001 97398 53397 87924 97184 86963 64752 6970 66507 62654 11252 22546 56722 58175 42219 92493 70532 46033 11972 72628 8198 8936 34847 72261 63397 37628 5400 58223 51972 98785 28700 36666 9146 84576 3244 4364 17592 2958 75965 60212 50239 27229 25241 81744 14385 83759 32973 61574 67098 11922 84604 55880 98069 5941 28067 3334 98314 14728 10451 39893 74730 22765 19412 15663 79898 48611 80164 23301 75128 83268 9253 72164 84817 21264 48561 28055 24300 75381 90086 64843 74242 94570 35761 12135 38867 20016 14323 84596 56186 19891 89473 45022 41699 61618 36279 72587 85663 58849 30726 57213 594 89407 45309 62429 68856 91080 93117 86625 61088 35179 60324 63430 5676 95777 88191 27273 546 51872 64840 92362 33536 67538 64637 89234 79385 49273 79220 43477 78207 35455 67150 8258 45964 28996 41670 19554 95916 88315 43249 20883 81911 43956 76170 22438 93673 68744 57074 15746 26758 43730 60983 89135 77 97898 2731 70948 81801 30235 95136 27443 88424 51781 90859 97558 13862 72923 69837 43020 13689 77599 51780 5799 54881 37440 59834 63409 80532 29657 74934 86917 63812 86846 18157 71683 41737 49695 54424 79441 92949 58492 42144 46571 53052 58661 49429 43060 96825 5218 94664 14683 26229 78250 94426 50022 90499 37613 77771 90096 51061 59880 15951 9475 78819 16493 91459 87117 75201 18444 94741 99559 57987 56543 50263 34921 15652 28158 8609 81517 35858 87010 90378 73058 34506 13296 87983 84219 69580 81652 60121 17664 37661 88745 12294 99614 3166 62029 20537 8071 74135 3587 79843 21169 79411 48034 24075 95867 24824 62970 52370 52092 88386 95351 15307 49963 63580 96149 79622 46770 93743 92149 5395 80940 24771 53349 1440 37181 69571 87400 2702 92503 87585 44527 8182 70540 78484 16666 5005 67207 89049 45247 16765 19794 48082 44838 86057 14874 42596 5257 13359 91434 83452 21652 79020 83315 98360 10539 54434 10979 16650 63496 98886 91235 70939 94464 73441 69572 63825 23375 88840 61716 554 3253 47438 41045 86141 78833 58974 53653 43857 3899 46102 88182 49324 39187 43135 45897 69529 78385 35159 26640 42419 77750 69812 36943 9232 8056 63779 4786 36312 14754 56943 76946 69072 85237 19907 40732 91416 39318 31795 82703 57137 84674 99500 30973 97736 78172 1619 4079 57958 99064 79486 58023 75376 3988 69295 22801 80514 8399 28116 46564 28624 70557 44682 93908 93563 2808 45761 5044 72084 45968 13658 53244 42456 32961 23533 80056 57142 88108 60344 69017 45811 30713 73161 83618 2781 37791 66017 44755 89791 48580 42735 40327 84041 68382 92959 79840 80840 98140 99631 89140 90016 21805 314 30631 74327 54625 75199 90891 41144 91188 75720 85885 31589 59520 89725 80067 81994 12060 80562 52711 12780 64230 17538 31119 58941 74801 53361 49173 65875 12441 4016 68122 10843 26207 83836 90825 8020 72282 75360 28722 10042 17687 82953 23126 69286 58056 84768 49381 51255 41711 22114 22397 39684 41269 57695 65887 30135 43600 24390 70670 90410 26836 60952 20939 51773 10833 8669 93693 8341 25408 92457 41771 14035 22630 512 7662 82179 27752 71280 1579 69774 31665 15611 87414 68413 51149 3595 96365 70902 9263 36213 24569 10725 58597 17791 50495 59419 13661 63274 95855 20297 8322 7784 92882 96685 99889 63621 95912 76074 72855 47206 43901 6910 24116 95392 56820 61432 26252 37287 1975 91536 30468 80718 84466 69496 55500 37772 68616 78253 57382 37030 73432 98885 70951 19333 66075 1550 70039 58054 92378 79747 21761 23883 96330 51769 9776 28153 41604 50844 51057 81534 76925 41406 8150 23205 38228 58461 764 12193 16847 10583 47335 34409 32666 19305 96832 44966 44617 97108 91029 21787 83863 85135 20686 18932 79368 97213 43081 73791 37298 36483 65513 72719 40692 49355 87477 93007 81137 43295 22927 66309 48696 13604 7172 84721 52671 15950 20485 46954 20501 79313 94884 23600 27475 22980 97397 74217 28105 66021 26264 27932 11545 36683 62538 42897 67824 29758 42788 14838 27118 88655 61203 3429 97079 77333 67694 80553 35887 66383 22108 6496 58854 30323 61598 48484 28132 22183 78168 58688 43830 27337 152 65655 28324 67973 69260 72854 14615 2822 76555 20123 56595 33981 25976 70367 64642 66886 12034 9955 50834 82372 38352 76635 67036 32060 90479 33225 67201 74411 32953 26628 54850 25508 4339 22754 72853 50522 80152 16000 33251 52509 93776 63775 61384 44734 78668 16733 90320 69475 43655 90842 65770 43792 5898 67207 78626 82651 19265 54583 59038 61303 70419 39715 20915 36346 75684 34922 33725 74715 89009 61516 71098 77995 5614 61165 77512 74934 52788 82666 47085 78378 92925 61265 18031 27065 82934 78505 55958 9843 22657 82390 34416 39414 47484 42582 87711 64670 12756 99968 8747 7944 27424 96357 90611 72007 25523 66425 88937 75796 73039 41662 1494 90854 39612 74686 45653 81365 20194 56948 74829 50158 47089 72672 33097 54226 85660 45514 97956 53693 77576 85344 5012 61903 91073 87764 17086 67726 38830 46461 33025 70925 25504 15916 60384 49119 1575 55152 37974 35071 55859 76274 44138 55940 85659 36917 48132 44347 85447 81328 32943 1462 86064 60851 28584 91876 93627 86863 93673 22619 68165 4960 73707 20537 4708 19630 59741 99693 42147 99186 62659 54900 38827 35276 51114 58578 1527 25815 77219 26973 70755 378 15359 95228 31121 55407 24896 36253 15501 34354 24377 9003 21183 86897 62963 46834 37505 89130 7319 74719 28873 93044 37371 33448 16105 12284 66462 49193 54751 84770 17779 50839 44735 76460 52233 36097 16307 99675 88659 13550 24132 91025 25438 37167 671 78443 29001 89075 39745 17191 38652 89296 68430 57491 70528 16803 66395 53973 42702 5829 25131 42456 13199 37657 59441 88535 36431 60575 76219 58929 51492 30352 39824 8032 70321 82229 19164 39666 65390 31386 69621 19214 61182 39016 86311 70157 25865 32223 30427 57442 38530 70155 62309 38804 89653 34980 69140 68968 12787 42392 53051 48978 19188 58891 46151 59491 29300 98142 89004 67426 18650 68790 10670 73685 18045 10521 73656 3394 79892 57527 66374 11126 13736 11423 70481 97855 21733 60449 40573 36691 8462 10660 38771 82809 71356 90521 88261 68814 12184 65536 43685 29526 48146 61847 7064 75463 28152 83236 66356 28928 77378 66687 14601 85417 35011 75558 38340 90984 23329 24578 48096 36406 1800 84876 44396 54033 67409 66611 69801 39614 78898 24340 2803 2498 63230 48908 55397 52976 32598 9516 81336 43388 90648 79725 99495 18316 53854 98151 64080 11408 14367 64810 65521 53107 40008 13554 87768 8676 3818 26236 98903 67666 15890 65982 3520 96263 38548 45539 74595 53782 56358 40515 37530 84753 9716 77295 53615 36147 14159 34773 67032 78309 32099 7434 55694 74885 83523 2095 71234 23973 94837 79243 9118 21746 22762 87739 80331 56330 27327 74969 21652 22739 33101 43777 10132 96110 85545 53993 686 35398 898 16303 39484 75828 87140 17811 8169 6287 89404 91407 15305 45914 67600 30160 74848 51545 22531 60377 11143 26096 52892 35816 44765 37602 76149 45372 58503 43328 92056 33219 44038 8383 31039 40619 88977 17096 79323 52170 10392 97758 96041 47787 86464 85693 54477 63623 87087 34433 20172 96930 29912 39218 54266 89631 88611 29704 71066 25967 89479 89739 98560 75962 49236 90208 92575 95477 11574 93049 74543 67114 12958 52428 68960 48428 17450 26386 8335 21586 14689 43321 11731 67205 29034 63360 17269 30316 66203 92641 15470 83767 11337 25237 94587 64676 91670 794 5141 77703 20368 19380 24801 2429 66671 65341 16427 4263 12807 39962 22992 91378 4942 87471 6798 42360 79514 43447 82100 28932 17642 17837 82573 19959 43466 5722 64928 56026 22197 83004 55157 40537 21743 16479 90406 16047 59360 38776 56058 60661 38453 85201 40078 42431 16777 34199 57237 72876 42614 13952 86510 99289 80230 37652 14025 76157 91214 91011 49193 93465 97017 47115 15490 73782 30372 71818 51710 42667 36888 50894 19152 44656 65243 94681 13253 62990 55893 29217 88240 27918 95682 94935 24965 65657 94946 12738 19645 13835 11184 11583 52757 5761 59599 53189 71674 69378 57566 86356 93261 96184 75537 10248 94837 53290 32271 70541 10262 70800 95725 32746 87177 47630 61926 25464 72495 93036 81544 72193 38934 83664 20748 53538 52468 9717 93444 93881 50317 51507 20566 78911 57566 8967 1606 11862 90129 48394 75437 71256 59469 98725 55164 24713 14143 19774 95939 60897 53244 1240 6453 2780 69616 92801 79713 5617 51038 77772 74101 65076 47941 45419 86481 31099 12096 91164 33904 9363 3761 45858 43746 18879 20406 12969 66702 7649 23758 26421 99081 44774 51624 76531 59474 9664 13494 45368 82445 60959 27279 4146 3461 66030 1703 69620 49402 60593 96675 54904 81331 59074 91296 53726 30717 63516 5232 83129 37336 77476 92462 86222 3432 22002 88022 21179 68087 70777 63230 13969 2940 40790 37117 26688 56250 31226 90374 5832 50199 94396 99917 56066 87311 37907 47518 73483 86819 18064 97968 8539 62825 42909 29180 60741 16790 1633 29939 79252 86744 72700 52121 27460 47615 96461 81287 63515 73214 65804 18252 29611 93020 22605 90334 4376 30694 97914 87869 46592 39174 52585 61491 10088 99030 32400 9798 42676 64600 83332 66214 78658 91560 90342 56413 61652 13660 66530 73410 55450 20014 71405 36665 77901 43482 97293 94286 43188 5392 69876 70287 15232 35986 48477 19273 5413 47052 47636 20172 55828 35333 67096 5555 46503 49590 98012 28073 24489 18153 8982 18356 22826 12756 75832 40028 82368 31684 72166 67961 45952 4176 33014 14580 6597 51438 42706 37715 89017 17850 67339 45697 51007 29801 95762 19557 37683 61881 98070 89910 21816 31166 91108 43256 60033 20433 94424 15724 50549 66723 85335 46268 10458 34626 24085 71483 72696 62813 46725 86826 98909 81605 88900 51331 77031 36805 92568 71499 3932 86221 9934 37121 68493 73036 28516 44421 76637 98279 25814 60522 74011 45485 12 2265 13068 46079 6358 82854 94058 66474 92105 44060 57081 94884 5628 59417 94003 81034 85065 57110 64630 782 29759 11450 56485 93675 69168 37943 74070 84143 90176 272 68836 27110 19268 85476 17692 53998 8253 4009 14012 46834 47123 88581 37191 33738 53000 5768 67096 64269 49712 80008 67494 44140 82854 35231 1450 5598 50746 98605 19902 47638 36908 5800 87838 54182 13449 9584 33498 45990 15835 39571 1263 54044 86264 87195 42534 71813 35703 32013 21576 65883 4303 22484 87622 25039 54042 99590 28826 38480 56785 57164 75199 19277 67847 12250 59495 64531 51884 45789 60068 12417 67962 72873 34464 54295 97777 20034 62587 48434 79012 35318 87057 78514 93879 69366 21569 95158 20413 81684 57650 69593 88717 88409 15006 43938 63724 75565 85017 52922 20283 35797 38108 1892 76149 80460 83779 89726 31567 26622 72488 15060 95746 42534 45747 72060 62265 58422 51451 41979 6844 43287 46373 6273 31230 2157 21882 34824 46422 73019 98775 59773 60276 35502 76607 76416 87602 38129 42878 47346 84490 46506 76850 6236 56539 84687 82929 17119 14101 3277 99249 89244 76458 95844 76119 21323 50899 24440 68509 95621 55980 59466 48098 48878 9876 34211 18513 15284 34903 62792 61875 50875 42840 30568 33046 66147 40840 43038 85414 62080 48954 2482 34164 38593 19647 16782 42269 48186 46026 7382 29386 89812 22991 45042 66066 70388 54602 98192 86228 3025 94661 18685 19070 61778 54159 98839 64551 47505 43584 53136 95239 10349 65175 14268 63910 44647 17199 60397 14949 94771 44094 2752 9024 81914 36762 45789 89427 93510 43563 24967 30859 42709 42280 86948 75575 53695 13115 70947 77550 27188 71868 15043 19488 87963 48348 15653 98848 67072 76457 82516 74068 85069 15667 1923 41078 20549 10375 95763 6142 82174 32744 27732 93998 15525 36287 74416 76694 67518 23281 32159 93837 19639 88735 34446 12307 80124 52075 84926 96774 30431 73644 22088 15193 25595 99592 69236 48181 90381 5412 22378 79687 53453 57531 62248 71897 94094 38977 36145 83855 71991 32213 37899 10683 52649 34816 84538 84079 29654 26677 22837 7480 5692 1277 99499 60319 71153 22865 12334 96992 36416 38482 87211 52579 487 39802 13270 24091 99485 15168 18789 91380 16435 97949 71863 12328 16265 49773 3973 24541 20392 17775 53124 52242 62848 82033 60764 68938 56434 5334 91901 10487 44016 12728 40784 21799 34509 6962 85043 3270 30932 98919 82623 41783 92882 89999 1687 71463 22655 67696 6498 74646 29791 20899 13167 11624 47772 15386 37870 79257 61090 33357 35714 30083 38519 33116 81718 91937 6340 68735 17170 56105 26877 49627 76922 90357 26242 11505 30712 39493 99359 27287 32644 92804 37130 64147 81456 19942 63737 39647 1571 41069 20 816 11178 2757 24429 96610 78723 34887 77326 17037 70168 90589 37156 36763 49942 99015 94183 19291 59439 87266 27770 41154 33073 76257 49732 95169 89129 20956 7566 83757 62364 26759 58337 48515 22708 3783 15203 751 70451 52410 8020 52415 53330 49528 32079 42815 82591 72308 11207 9678 56114 22765 61786 99611 65668 25995 65750 52954 49613 89752 76570 75791 56314 32346 1952 51541 9609 18854 52308 522 37838 96619 8885 20987 8363 36012 33786 31286 48783 96383 63427 30396 49507 66156 56660 18438 8942 81122 56524 15417 6419 74558 45223 87135 83121 21453 910 72052 52293 56 53382 67217 70589 46728 91654 72277 94301 36182 31863 98005 99406 50306 16775 22785 4685 19773 24769 66246 61383 25564 1904 1731 86780 5965 8421 21787 58103 5582 84155 10775 83314 68945 4613 35364 55637 1170 97643 42744 78885 19552 37211 18266 62693 1253 61919 12422 14854 67193 53433 58064 82444 75536 72255 41304 61463 48470 98184 31216 23823 9181 12371 16412 25203 52530 36497 29523 25179 46376 443 48272 56097 85269 13839 88546 95336 65974 68777 31566 79947 7218 18973 12186 48838 68245 53996 73950 4432 30405 53850 27288 88006 12464 76901 54378 88371 72358 18857 16538 34961 31083 43810 22220 51820 7123 60495 66857 12398 23951 32413 45015 58878 25639 63487 49785 79825 5053 87864 30870 74456 45536 86125 82516 16722 4574 92530 7839 26254 49325 8371 44808 55535 60680 4447 39376 54397 75510 27459 3695 16650 8267 65522 64638 75892 23879 82138 86884 54692 63954 96240 28824 56358 42209 26500 42820 94806 15402 47142 99758 3057 66341 23692 27961 3134 13528 44688 82919 24215 93873 87904 52962 90463 55867 38847 36407 63403 75965 98122 76099 19 57237 28766 63044 54958 81678 27354 18841 10766 35887 87733 98944 50650 3378 67342 3336 26171 85996 12892 85530 33277 91021 13457 4378 67536 68442 58327 1311 37356 37769 15881 57737 46499 42512 14676 82794 20218 11662 56373 91719 5541 88864 19891 13364 47912 7291 69187 51158 14738 41793 86271 1253 45914 42382 93706 14700 35021 20013 33188 48459 44743 15845 75393 36996 99803 54958 10720 28515 20237 13960 12471 45982 7405 70106 47283 87867 15258 61481 22136 16126 82889 74172 90999 7087 50283 21703 9462 8016 49010 33997 64294 31479 21450 18441 40896 12772 52631 32670 12034 10489 6442 49247 72181 64997 56626 47593 96333 40509 68524 94497 59117 86164 48664 31771 96519 16449 87960 6158 627 43458 40857 36234 76093 82994 75327 47171 28881 74101 30603 82339 82697 28263 6654 23533 53103 97608 49568 73816 61700 81023 21446 13120 20308 15575 47492 92306 19099 97223 72163 20524 90751 85132 23143 77518 38649 99670 93963 52392 24398 51540 49012 29495 94725 16097 47121 39522 61726 12707 62615 80405 97993 70700 72645 16848 17594 14736 18328 31012 17342 81729 84300 36309 83044 44619 3982 71876 35746 25412 64568 8010 77446 69656 28845 31437 99510 45088 35346 82251 97119 98935 98403 54346 23543 1650 9549 65611 31052 2218 29472 49630 62584 50112 2767 84903 84513 38622 66371 88713 36788 46781 18522 95255 493 9262 45239 1541 3710 43636 63209 60089 76788 26958 64015 4155 31132 77856 61630 28661 62362 69349 55816 71532 3756 25862 69268 72195 73729 45289 34200 11241 29703 35357 12087 19323 32466 27665 52503 39878 47655 14164 35161 3416 92259 48668 21901 63249 77553 30749 46895 7419 88694 38906 14251 34529 80552 30162 82667 85890 96950 9978 76977 3648 42239 5772 70408 89658 87426 73840 77982 72314 23247 26059 97154 81050 6408 67679 83636 28627 1800 10393 47746 75875 87238 97600 66026 69192 27599 48198 55252 47096 61290 49647 82076 31783 16722 50603 69209 63623 34578 18184 60314 51869 22292 84079 37547 9651 98381 11630 72122 35042 20829 44366 67569 69172 28808 70468 88235 77130 88347 97241 19104 73746 50723 78765 44998 17941 5750 6723 91485 43028 18987 71561 8735 32621 69341 96480 28043 57850 5461 88178 34776 23700 98865 3354 51615 36653 35545 11348 65183 77004 1437 65365 60018 54874 92062 54773 53657 67833 73623 68800 63275 30310 10460 41137 12159 30766 84726 46133 6201 80882 81950 73293 92156 42810 4127 2055 18200 43987 47694 66720 70721 39489 19678 91061 64276 56792 54819 1886 93992 12995 4213 27559 47179 94458 47911 29250 96698 20368 7724 25519 88134 33739 7659 73873 6586 24054 65922 90942 99726 63698 19609 94988 14620 57019 1773 5378 68631 95336 37527 77484 63014 95742 19543 2679 6826 13766 99447 78977 56653 36585 32744 20475 87695 92721 58097 57500 66553 88137 99004 35412 10710 85533 43374 33084 70826 42841 74181 78395 80993 72558 16870 76165 189 76675 66768 54750 85196 79335 23675 29848 77754 18080 7287 40400 98583 17304 32471 24689 64156 78743 21478 61991 99311 71372 24779 42025 55638 45682 49814 76160 28905 77049 48636 72124 9285 32793 35722 27431 1426 80603 40848 27552 53947 9813 45482 24541 72619 23119 99483 78087 57754 27453 81972 18075 7716 47296 52213 83780 58749 12666 62099 99476 37640 12009 86626 4658 88971 54315 24617 52783 72741 10383 73557 84872 60311 2115 81751 71360 75698 85144 84758 13672 14553 85859 11488 68493 71449 78780 34466 59313 50873 98782 28043 77644 1570 24613 84746 39410 75946 65510 9617 77693 34273 29885 79112 41930 61555 62266 14369 83957 90408 81752 89916 72448 81402 45571 31542 19779 33297 92902 7937 54064 48064 5003 34788 62860 94305 41804 62613 5196 83850 10969 71450 584 20332 82043 61624 45180 27705 33354 40174 43305 95271 62811 8939 77842 31005 49083 16885 56401 26453 58828 46364 45158 73910 21628 34503 2619 87502 57852 78581 89350 56621 9455 25011 36221 57476 83849 94391 98396 70212 96212 59798 21656 93379 32743 46594 97604 59990 73549 81197 37888 76091 48814 97821 25199 99283 14783 48372 30641 4867 16260 11934 16971 9658 25344 7622 48871 34253 93594 80983 47498 38030 97794 84356 56268 21373 93100 2775 62225 88345 57628 17214 35823 5392 85668 10585 30800 3422 4050 27455 58008 47222 97597 41755 73022 24222 87232 71885 87373 25735 56975 76471 88592 27658 26654 67508 24567 70977 40813 76685 94154 25077 59348 52604 88067 13528 82295 99931 48112 27101 53173 53034 37056 26633 4674 52532 15307 93734 42647 71132 90683 82584 39592 59635 73978 31775 53523 71104 97188 1928 34096 86519 66439 49982 78241 57699 4566 84222 5504 94910 87520 38507 96854 15304 79450 77277 5686 2437 55615 2207 19157 78413 31794 14383 23710 22912 90143 71520 66716 61780 96936 15682 6984 89756 27604 68882 7278 19846 8367 44280 19020 11692 75529 58565 87642 91542 29783 60851 55664 84090 19817 49964 9852 52866 62892 26085 85163 16952 97411 15217 74611 57755 13798 98079 34760 10945 85030 59004 12515 72318 34167 18394 20145 28953 3029 50854 39248 68580 84519 56263 76564 23767 51130 18738 51341 32640 46363 80101 88295 88042 79705 60855 70935 42419 91252 37734 78358 67924 65114 43566 7078 43366 71248 6354 12987 15587 34928 29815 81663 4481 83637 72505 80490 27042 40469 42 11106 80928 59981 39102 65763 20568 60441 93943 95720 35520 69950 66277 86564 6825 84515 68446 77755 58071 18345 24356 25379 37301 75875 96061 24609 79868 39194 45548 92221 72790 85916 81490 89596 54856 87294 46059 30231 49020 40699 17473 97157 47688 48009 21 10262 25532 62217 34329 74773 19849 72758 12097 44714 81449 60418 67589 97812 44411 15050 85104 71021 98743 45022 45739 76712 40505 50407 1512 27021 83350 58136 77068 21576 15214 85919 40292 3248 27891 84056 52014 30487 69711 91543 34395 79413 80414 85571 40980 26377 11387 19813 30014 88449 71866 64125 80166 94096 80703 25052 78852 24380 57230 19321 79773 72764 530 71851 32211 69035 2127 92884 96722 23088 90587 39853 24027 61281 84675 41655 65597 29096 8371 43878 79442 63040 11939 46695 61987 98701 44500 32231 34730 40341 23433 28442 78879 28179 15658 75588 34291 8583 94319 44689 74470 97862 61054 5685 34671 46631 22664 46708 29819 51454 60936 35381 53980 73100 52335 96864 95786 70547 5801 4878 15377 32134 34755 7961 24240 46232 22373 3438 29541 1288 95911 25375 96021 84482 10100 94492 11844 50345 18698 37533 47163 89646 72094 30412 8624 69562 84469 52771 39698 4777 26310 43024 38486 43727 14350 68346 12114 7662 8117 79237 38856 35301 13165 50643 24307 48969 61617 63576 48426 85812 34599 4130 40252 5751 53007 38748 2708 14950 27277 27487 61706 42124 83618 57514 29854 58422 49994 6708 21109 58142 79648 83515 1824 5087 1253 4276 29873 56568 29685 31952 17735 4880 30320 60033 25388 10670 63448 71325 68301 2723 66835 77209 97908 79289 50862 61145 68203 63554 40540 69535 44741 75297 6205 77101 23108 88340 58577 1148 84274 82014 45533 88645 23630 7668 49544 95669 89459 72395 11548 73160 98311 69256 91222 95796 55182 93064 30538 47164 45081 97913 10251 97613 94342 77813 95970 55567 24338 30307 5894 87344 88127 9331 74442 26305 66074 96877 89545 16246 39481 45516 74802 65610 88974 1120 49673 17830 27728 43947 68819 70475 18995 91209 86645 59356 39036 98310 98122 51171 13155 91373 43470 80145 90593 93633 12844 90154 43310 9450 81086 47767 79491 28209 84436 9520 27723 19258 8445 28093 11378 23465 47424 6396 73586 96019 24593 24472 35278 97506 86035 6658 82625 38954 66291 19223 13209 97052 50497 23508 232 41569 69539 31140 57301 30628 75383 4385 46056 10792 74737 75100 2339 38086 59310 48063 23702 34771 59678 54888 54219 25395 67104 1751 92974 42388 76996 71929 20245 59537 36589 20591 97398 48903 81893 22716 59578 27251 58820 20526 91848 46225 25555 3977 70034 82092 46733 95307 99279 32007 10481 60694 60854 14176 7722 82310 44833 73108 35326 40681 54492 13001 6371 11194 90546 42314 79286 38178 28363 51696 20593 85948 17388 71297 74311 20134 28959 67907 88208 52187 67820 88315 38938 62709 80540 31024 14213 43072 87577 1662 80069 9547 28471 92303 92845 39839 25320 38417 8191 41451 37871 65149 61071 34751 263 91389 66960 23251 9642 11791 56004 44202 19521 24413 76142 66375 74006 27709 19291 71288 89196 89228 81389 77259 22080 35757 63043 67841 63019 41117 62294 88502 3563 52765 53944 56925 89201 20613 60702 19813 23433 24639 4715 44069 55662 57000 95024 18193 26749 3520 23958 95483 78309 74410 46679 35717 84488 34106 4008 11494 80165 79896 39083 15457 57177 36284 21432 67057 49118 23550 8158 24894 27353 37140 61259 87971 90160 56935 5079 81114 86633 47214 86193 9628 24272 9256 70638 66579 24664 27500 43583 65285 67310 74035 17295 61105 36861 62922 78685 52234 32131 48106 81437 40984 55038 20768 55694 54544 14601 70469 92934 50401 3534 81901 53121 28889 60321 56899 66234 9591 41261 87447 15195 75173 83462 34583 42490 16460 57372 43518 81948 70627 31661 56549 48194 59864 7603 87874 53615 68299 55918 80230 86243 75029 43488 31364 14511 67066 96085 34548 3773 76892 5848 85801 58163 71780 31775 10888 71895 37007 49082 15146 36700 31583 15232 55416 41223 47782 8820 84417 699 12908 53740 65605 82269 3364 28297 21341 78638 91757 14647 87940 89362 9067 49874 38541 27196 87759 92778 21504 11541 43619 52700 98630 56798 68560 72773 91717 87411 22736 64994 77102 9945 54454 47612 80748 23392 75314 44292 74793 29522 15465 40126 19739 46634 93922 50816 56098 73636 81129 74721 96046 17578 51461 87072 72241 50342 54089 70760 16699 37234 31845 34778 51548 54405 1575 35574 64129 77987 59059 27294 45246 39899 52804 71336 35867 80533 87386 59574 19547 24113 12236 29820 52496 46322 4535 91805 88912 91254 10008 22464 99517 48358 58408 24600 72972 24534 43927 96533 48754 83212 3423 69630 53530 40737 95362 59686 94582 52430 41431 21113 87049 17476 90676 7185 42186 71122 22314 34181 51295 79442 89624 60741 57884 86092 77175 64158 27882 40636 78420 99975 14127 49079 86534 51086 42288 72556 83486 58214 1464 62340 73076 4296 49885 19132 16559 94415 90713 10885 58540 7839 15196 50284 48935 65045 62778 27182 86378 40495 10798 77131 58847 71574 94516 64594 67550 23120 81647 31873 76641 18710 62283 90546 48817 90215 88971 17069 26037 73783 24760 77418 45332 3217 55993 2315 88902 40930 51549 53096 32344 8703 8283 1367 73438 40059 33656 80743 54756 19247 16510 46438 96861 57322 49736 68734 27663 97553 1076 52720 89781 75777 61273 30503 34675 77423 66982 53226 21422 31612 27156 50460 80494 5232 92123 48592 99751 55080 24002 20470 87231 8109 63578 36972 21408 75320 6591 57698 32469 58231 53137 76519 93598 75967 21814 92252 75703 47424 55036 4524 12823 36322 3660 30345 58956 86309 52739 32966 3244 69694 42026 8501 66238 31173 45053 70713 61039 78243 21126 59112 29351 73465 83263 10723 83125 9786 59638 81851 38103 65233 19451 86952 83965 92877 80672 5770 33443 53014 18693 9016 46192 95321 95775 97213 50577 41929 67175 49802 69953 59934 32595 71862 94357 16370 94190 67256 69607 80256 28698 28516 19235 3523 45081 84313 73598 34724 48572 47832 58013 33739 98241 62494 63575 93500 15667 20518 69208 65320 89459 95252 32968 9395 53403 52274 66795 45249 1627 75057 54638 59497 21465 95269 35288 6040 19030 49631 85955 79949 46074 10968 82873 25472 18885 83963 47815 63359 5850 23736 542 29930 58909 90663 84229 52396 99461 71446 84955 17946 77542 9915 21932 92831 78426 87822 42790 47026 44803 90408 48938 57940 85242 98560 22465 39876 84199 82357 33108 22683 17666 2881 48328 52843 58256 49958 24275 62732 12419 98566 40585 6529 86881 55428 47535 20734 79261 68657 93794 54583 88032 44589 89944 71136 48878 37950 91300 57336 92504 2073 3304 4875 33353 53423 91425 89564 38405 74620 54214 50598 61466 88677 72006 95033 49692 66989 2451 97264 79766 52319 2466 32700 543 32750 32136 68030 85671 1887 16540 60609 46220 49525 36590 50451 49925 7488 51424 89393 38860 43634 7863 78130 38671 45161 75176 68101 71746 76124 80739 14555 2354 38511 659 7522 73192 89787 51991 17474 34376 69550 92168 68655 2268 45145 24735 46781 70802 79467 73604 54547 17734 62465 47887 12184 94644 79225 51300 30953 9241 19970 45110 79647 94164 55812 94000 23515 45894 46988 2442 44959 99830 47838 1404 85961 19491 57460 66680 50533 81047 19280 95237 39270 49970 81017 45149 22080 6961 70239 55010 2808 29892 34376 276 93631 3341 3132 45362 45513 9858 97287 19197 25498 59681 78463 30502 66471 29160 54159 41676 53595 19072 26391 1737 20004 18354 48341 47273 57216 75464 6616 47032 37242 84561 10219 92479 97127 37811 28587 54586 10448 40229 37802 97816 26503 24481 92459 95818 82231 86337 62490 39526 66190 66500 20080 88356 50600 18035 12147 80398 8146 54850 41817 70033 60167 91902 70021 94270 74463 65396 52674 69600 84355 2020 21043 10485 88384 66970 1186 77446 51213 45949 30888 45149 20243 58118 41182 39918 56630 84679 10787 80631 73895 85664 55141 60843 37767 52669 6644 93093 85671 74900 49470 92789 15524 3081 43822 79788 92298 56159 38549 52793 78409 44244 31996 57528 19033 16223 53740 48467 17584 76549 76621 46887 85030 4574 95516 49377 94915 29864 81680 73430 29154 40262 85479 6305 20570 3185 12345 37070 57326 16318 44264 49696 8485 77287 4809 7354 11664 98722 54672 95952 39041 94482 40837 15925 25812 51669 36264 97525 27199 81449 21092 48246 55801 67549 37888 61581 80337 80819 48413 97085 53731 98956 19875 13278 61330 35919 93620 63522 67557 96829 99541 38302 72349 89790 95905 92437 43071 36813 43454 39353 3590 78086 53248 56710 95128 37925 72240 41669 19072 68835 28485 4375 65748 80575 32199 9319 19411 25090 66519 92451 94866 79986 5241 89597 42948 80079 56679 64354 69807 96167 7453 90198 42053 4139 18827 55912 15449 57667 45694 3659 88800 21184 66669 43863 10665 55718 81022 36072 62099 8084 68088 91545 55978 44242 61715 78932 75647 71741 22535 65698 81078 51857 48648 54406 90375 3031 45528 47595 7793 44303 42732 38850 51231 57937 55224 21947 96699 91528 55658 78942 79645 20527 19682 74436 33932 74375 58098 38732 78224 8303 6720 70961 68934 94232 3696 76016 31578 35544 35293 29936 19302 450 53747 3815 85072 89582 56129 96305 90781 7677 72310 11848 55508 2186 665 65300 32833 17598 25043 36697 18164 63773 77990 29474 20759 554 56950 50691 65307 75103 79936 54810 77693 74794 58555 83857 59745 71477 30858 48470 84058 54844 30158 44478 22772 93133 11499 56068 36296 99317 47986 83848 14653 71421 79412 99286 47963 63848 96321 74652 60710 78288 29074 57026 4788 18447 96221 6415 59370 59934 84996 20016 61304 96018 19108 24507 95889 46783 15213 1966 5220 82885 41830 7752 59965 65327 73034 3757 12384 80513 50516 54919 46984 62018 30204 28605 2579 63901 51811 57264 93549 1897 66499 27697 95460 32862 78940 45608 63970 57410 93440 75181 89097 49673 25822 50799 95765 32175 36154 38093 90474 9601 83114 89514 67676 99895 31925 64306 45172 23285 34011 60713 14982 5025 96789 37139 61999 36781 50895 78089 16410 11162 45617 804 54097 95890 68091 84171 36842 87353 81400 17162 12898 41530 83911 54174 90820 71245 74204 2803 41949 71732 39620 46568 83105 62758 38728 70197 94031 41631 75630 99682 23552 85947 27546 75932 74377 98009 86254 25083 16278 69030 93110 91217 16795 80666 37512 38728 12553 74839 72625 87254 88064 63523 73781 59824 77630 3889 86376 27815 13232 7758 87467 81142 82703 60479 7444 40055 5618 85224 65444 35258 42064 63421 75398 17267 5330 99343 27724 92407 85765 45441 26888 81705 71311 8297 63061 10767 95827 8397 20230 6918 67722 82965 68398 9257 16978 39900 55974 12331 97521 15281 95728 32125 77711 94865 65801 49707 60619 41860 66705 40112 67041 72121 1266 50590 93143 72443 91386 81973 28276 84364 76646 40547 5338 38484 2752 72351 94636 93076 60279 82075 738 98769 74054 8295 21549 47537 37469 68314 90949 70006 58978 3263 21089 75478 47297 80313 75817 88646 27950 64911 82592 65152 66215 39301 29305 84950 1958 42911 86534 22768 41784 46822 52210 13326 70032 14178 33827 57114 16626 69290 55431 70649 26206 49318 90723 57149 9761 5131 59410 55780 61031 9851 10058 11941 6970 4910 75319 26426 10702 79145 20164 14869 36316 99022 76099 31179 20185 32577 41122 24063 7207 42212 95481 67920 59402 56350 25421 22460 55614 28692 56130 33808 53823 68630 47982 14119 79495 15890 28238 21196 76331 49100 57609 25826 1545 96394 11457 22659 68610 2411 75299 5697 98648 90998 82548 53629 19248 39107 37511 50344 51680 84255 56929 58124 19273 10178 69172 33160 42660 40890 5063 8012 87048 34327 51815 98565 45044 52169 38776 86354 63204 6993 61744 42121 85657 70685 22803 33492 13977 88662 30758 39407 20608 31289 75704 3567 88680 83616 87252 80665 67142 73698 89673 51447 94323 78673 98418 39169 45125 93237 35379 12042 20200 73556 39469 59727 9646 23759 74772 89547 7360 70546 29547 21584 75729 56701 32546 59857 20667 23176 28269 44976 6100 50437 7536 61060 46715 45998 32367 17363 98546 641 2328 59089 59475 29175 54306 34819 19814 28541 95836 65849 5283 14666 62592 20148 25078 63246 23956 59858 46805 45782 32429 6830 76858 29130 32576 41558 28973 5407 28327 95737 25315 94446 94886 87205 46599 35410 16910 91733 834 63302 43732 11177 22318 71988 3696 70227 96611 51103 66609 86113 73303 14800 17312 6196 99240 47902 30283 92819 67532 86116 65202 33560 64572 96132 30576 98385 78393 50686 78405 1308 48719 46454 36714 29144 28683 44806 76491 13464 65341 92080 56094 46432 2428 62855 29479 4571 7752 64487 24033 88324 6079 37429 11348 5530 67023 3275 40907 53884 83930 19950 36653 21479 47203 47293 91159 77953 39356 8685 62294 48415 70363 49577 91125 11294 52176 21264 72115 89052 79062 13325 60819 27441 65944 22204 69846 10405 47938 43694 95858 33861 10865 39256 18154 33131 99255 75904 15495 95368 2443 42281 14689 71809 44806 55618 99472 46199 51346 5257 22159 13604 36721 97989 66241 84143 79769 14910 74889 26086 87761 4800 62643 81174 86858 71988 48755 42324 43594 16607 16444 1540 88239 41639 24204 37545 96989 95590 87541 70012 6516 28355 73888 22509 70519 99059 62194 12706 98107 42020 48592 65362 6412 4328 83573 21267 58325 33927 97951 64937 48799 6975 4738 17737 82665 64110 28967 80890 55591 49832 86969 34998 9122 38926 66928 46740 62138 63313 83055 58264 74352 26218 51797 62324 57725 25829 30526 33886 27671 64997 65296 76639 84422 95878 54212 40190 22719 57660 40691 44974 67775 64057 73491 87190 69306 13749 75454 5968 3777 21458 72974 70064 35156 71331 27956 39339 13117 64827 6374 58369 27009 32784 55351 80775 83069 96575 76390 7002 66927 30743 45893 64293 98022 43719 23926 52692 19802 90810 34201 51096 88310 98166 81675 46731 43939 21694 42181 8209 20952 79175 75488 8400 60381 83648 33135 13829 23992 6109 70660 32840 95732 19794 93004 2531 14893 67265 66178 34507 33773 46057 14521 71991 78282 11218 21286 19363 27212 62222 74287 31243 46160 87340 65465 52863 27549 89581 183 10423 13765 32388 28568 67796 47900 80086 77308 54179 37167 68989 59656 20710 58209 72375 54575 64781 29123 78303 54213 40533 10286 27738 21638 83936 69872 70959 77304 21538 98043 60845 42629 4877 38191 29435 63831 68110 80732 8196 99222 54588 3587 54687 27924 3969 81004 79758 28348 73235 28398 14926 46610 59557 3563 56335 64937 18269 60746 84237 81210 3206 59771 46239 32481 37917 38760 56034 71538 43876 67553 32986 94326 71112 77348 41081 32489 3597 15835 39142 39973 11152 90030 20946 30741 18248 84072 13432 51709 74968 70237 61896 70740 89879 54494 89248 69147 75981 98270 44023 84347 41572 87407 17210 6741 38538 22751 79930 9798 90594 46086 85802 53493 14971 81626 72899 1557 34754 60804 74774 64407 82340 719 49199 8916 57574 84190 56696 85470 90515 8152 42303 69709 88401 78377 88830 15522 58353 86845 82175 76461 44604 31118 68383 62221 4922 86200 40471 33402 31793 96882 19181 95331 65284 96264 48043 97364 7154 67575 66597 79342 90488 5305 73971 47586 42457 30966 99774 48679 7412 91016 13856 75990 84350 18869 92005 79117 61143 92529 29013 17074 46895 5643 80328 83486 58588 41369 79232 3068 42102 5954 60957 35799 90455 96966 86325 91439 8574 25523 89019 38027 95988 39947 86399 39304 42000 84899 12534 82297 14363 21665 88133 4588 79790 57682 18743 29308 90938 32814 36798 46425 87130 59686 97898 49284 19858 38133 11114 31658 12004 35394 74698 61101 62790 39499 13008 99095 64168 8369 47634 64314 277 3880 13666 57556 81336 11513 10503 75316 54482 12121 50272 79307 2731 8064 89594 22137 40519 12102 69407 766 28646 89909 73112 159 50017 4296 78706 63549 2484 92391 72170 34557 6283 77212 37654 9800 91636 13794 80268 13801 92207 54820 39495 28078 35484 66237 12516 7195 74390 93323 41312 44153 59496 55200 89231 64238 46716 63534 10181 47169 24110 83672 30473 67675 35840 50359 70767 70993 92804 73517 25411 3934 24538 65386 98474 41892 74524 55732 97914 87843 45535 36537 72130 42455 68929 47898 7259 42914 61548 85724 27356 86702 54514 34359 77084 68787 48074 85819 85395 65205 55422 46344 32755 82899 58898 67245 24277 39466 10883 54283 94240 40450 5044 57607 26744 66442 75776 79407 60564 43249 17050 83401 26331 24681 89520 52735 326 93819 76900 40469 22303 48700 90427 79001 15108 39093 90117 41814 80011 33815 93729 10544 99091 7282 696 78617 72785 67205 57265 43439 23099 23189 51123 31918 8197 76845 77445 95190 72658 62101 80884 20193 85323 69745 35654 10606 48224 15588 63460 2047 99056 32389 87326 213 65625 17867 61476 80827 1696 35839 52198 74075 11572 90991 56489 20903 12089 94242 48711 12412 6087 96553 16064 88477 87899 74467 43707 73720 5856 42212 52792 28565 70012 95030 62848 72690 67625 34330 39063 77750 8974 46905 5314 88535 29847 34225 80475 54179 45263 65597 25937 86809 79843 83467 6319 89514 59161 15384 89547 66947 74116 70734 60791 16669 23520 24018 97629 75928 82851 77471 31097 37691 42756 5870 3605 43900 86649 15985 1252 57123 19783 71797 71899 42409 42966 60991 11423 99524 21189 69378 6011 38222 3543 53374 18827 88619 86377 63247 26766 29165 73665 24273 65089 96155 74444 70209 96628 38996 52205 55478 43091 18539 85612 53815 64965 16481 99870 95563 25914 61977 85076 39471 52274 16330 17865 12451 13277 31197 27022 53427 6911 35269 64095 52946 17627 19211 69955 44657 76728 22379 76040 60913 11823 52278 78747 8539 47880 42647 438 79965 95505 72556 74998 88044 88093 81191 13734 97440 1543 42243 39022 18581 7551 24111 16808 60655 8020 41949 37372 19960 83077 28951 66522 27943 35179 91804 31565 32982 92018 20582 70253 86257 66126 56753 57823 91493 69668 47503 22185 54854 5426 65241 51189 96571 14252 44095 34542 3860 82553 80462 30740 53192 29457 51861 40005 3287 36418 27887 43254 44987 97676 83259 8859 75374 85925 83348 11820 83630 76729 19866 17397 60852 13121 4362 60706 64222 39077 50365 11716 21555 32010 22687 20652 6765 45784 98624 78224 69696 42681 61156 86090 332 31171 89839 55244 72327 35680 31975 25228 48694 70768 29952 8377 57720 288 35360 29481 27755 36616 23684 27672 10575 861 36918 8083 29199 37298 84992 4932 19337 92937 20894 34537 2653 48224 42820 40187 76778 7897 98775 27920 36296 19911 65630 42136 2893 61228 34738 7995 1968 65089 37959 97912 82068 94823 60088 72825 29471 61875 60918 96813 44395 81316 32723 38364 60606 57469 65002 26183 98949 15956 11963 95660 60080 7709 96440 69294 12188 34945 46026 79448 19189 66140 98986 72280 86711 7972 48622 59223 63915 93761 94730 82469 32469 57579 42967 93130 84477 71489 82365 54501 87773 80024 87920 87446 74550 2753 39138 55584 51934 28602 49295 54488 26938 55724 38496 21752 33454 66297 1746 47110 79999 345 22137 3254 27308 60743 32683 64609 70727 82116 18772 24634 51161 33155 10454 29582 21340 75852 76956 38835 10794 13199 38675 37960 44066 59769 25923 68632 90325 8882 12239 69769 93650 64995 41293 54357 30205 68751 16028 70502 69023 38327 19445 33919 94811 90964 5760 23799 49746 2339 11823 68040 83331 93728 64974 15055 82229 84578 45779 91931 36648 18098 22972 20567 87002 39790 79143 58644 76524 53291 14551 41339 19543 26951 71537 88425 33437 64535 38039 15858 18708 31473 93581 4267 21004 56416 1804 87463 50648 57398 81612 59071 10772 98360 51885 73871 2822 81403 42780 47663 72203 75502 485 45142 53673 5315 2774 95005 57178 81073 36559 90263 38728 83339 9726 65663 48564 58107 92266 72318 95091 27576 26545 6807 99602 68697 27237 43854 71317 55182 65960 84105 80466 40739 23302 51532 1168 47895 69550 34393 47556 88267 19130 93681 13457 80282 71195 18910 19115 35083 41155 19401 60615 56064 45438 94035 17131 25217 24163 90783 10509 96592 94380 72867 86277 43444 43996 22063 83995 62114 13701 92472 92098 12747 86257 18476 63506 91896 86406 84244 80183 2935 17214 60360 61773 56936 58816 99623 61583 50737 50994 65217 3696 15704 96827 8880 59187 86294 3911 59420 97516 71055 65415 48131 91164 22538 33478 25140 79604 24825 93335 16363 3282 2561 61328 82890 48445 87502 72912 72285 43232 77566 5094 79169 52925 3520 30808 35981 36769 60458 59494 22872 73245 14380 38798 61087 69516 6904 93295 34719 11230 90873 41986 26670 62281 34517 56636 91708 96809 84691 37115 233 32330 84953 81720 23759 55997 64977 14325 46481 12074 89189 40691 59890 3199 55703 61812 35049 37816 38283 54197 76214 90817 70071 22553 64350 56046 55874 30755 44066 43810 43418 49742 53103 63698 45264 58481 57359 51248 26227 22052 11182 35662 81325 58462 4273 63898 30876 55965 25743 91303 96192 84396 43975 75668 60598 39111 12027 15149 46875 92272 40047 68132 61303 21179 13268 65016 32503 33720 11630 43756 9998 5341 27809 69292 40454 29864 36772 86955 24639 27544 23123 19706 36172 55640 8702 26579 41577 43334 48869 93823 81495 39669 94151 68106 65061 617 12163 69866 45945 38980 8249 12486 74056 31 49059 63711 55896 58381 55619 68781 67130 73621 93528 84985 89102 83436 91357 26145 19152 15620 71584 77292 3401 2525 53913 52053 5295 69410 72073 12633 76389 67380 81216 83344 61107 59379 86992 93838 842 53206 806 20198 37738 28015 6510 18665 72158 60048 31633 31947 68860 68916 79753 85648 22710 79761 41884 75435 25279 81055 46557 42414 5858 25640 55442 76758 58760 63156 15491 70442 27271 83001 90490 11419 97549 26318 31696 76513 90237 83041 15657 60451 58013 18778 65586 78294 51518 94633 40567 52667 44443 13009 17561 91377 72561 59839 55801 47934 15367 31902 89693 20662 24423 46189 78553 74389 61384 82148 34191 85548 61283 62557 34624 58299 39915 49454 57680 38353 63549 63352 84485 98497 47882 82772 3323 80142 13852 59542 40243 70924 94580 19401 79384 59044 64439 88652 61660 14062 38284 42747 45240 24317 91559 19147 70140 49938 82878 11897 4880 36269 72130 89940 87250 75841 96930 58404 65726 78301 22787 14988 22495 72018 12574 12158 77675 19607 83175 85407 59289 39932 39630 41230 30245 24482 31105 29225 34369 60023 37979 7114 33570 7709 63164 97185 42660 24979 57511 53615 60232 46225 14257 27460 21 54442 68087 83237 66949 45666 5121 94238 7713 1535 8813 85953 65043 85411 92014 40924 18561 65069 64041 11874 34240 62479 81689 60774 77652 86765 79236 19509 42676 33715 56278 55416 64663 37737 70398 59419 71581 85959 74468 56357 76750 11536 66850 32157 25304 93293 69288 19950 24622 30736 82894 81935 73627 72763 34940 94943 73154 8848 41804 46391 60999 89421 37805 83283 51991 25400 65068 44831 70643 9612 42859 62457 26749 76268 3230 89696 68131 45318 80320 75528 76673 453 89442 12892 27177 78610 2386 93857 2448 31741 11057 52114 80299 18762 13577 58871 93439 72705 83791 73510 62178 56687 93320 90255 71549 92240 87851 39214 64222 63037 34745 188 17126 99908 95167 29147 42711 97072 58813 15898 51788 85384 82330 46021 81016 40961 41637 9078 63403 71424 27442 43856 61792 49397 16552 64049 15657 85188 46973 30056 14493 11093 80453 21716 44348 3268 86692 79380 513 71907 72247 14734 33649 64240 9384 56621 53442 30056 28399 64108 85863 13412 19583 5676 7334 41089 78779 13013 64659 45882 71794 39448 70187 14986 27937 79177 98567 39141 74012 97382 80467 64153 29281 96741 57632 6130 934 68191 81750 19241 29138 5138 50227 89806 40611 24020 29920 98419 78007 31433 64521 48733 84399 82275 46680 91591 15624 7533 89351 36878 42120 13491 38516 30820 16552 83840 65619 17725 59501 32319 59879 86809 48481 82342 46008 66733 60670 72098 98175 48325 80265 10546 73935 65717 89542 5754 50503 78473 64559 41263 94847 14392 58425 96819 8693 80610 53174 59888 63532 32578 15101 39253 2813 97334 75476 12675 32921 66426 73202 46859 66301 19865 27973 22585 11054 13715 6747 5642 61132 99122 79703 39397 7065 24846 64130 84450 86519 69510 23346 88012 42683 95215 74448 97450 29186 42610 6857 71183 10202 22082 12229 22 48643 94699 21973 20914 34019 8384 25564 40324 89026 65932 27011 90997 82998 55537 96076 97069 38053 22451 42595 66177 53167 77793 65003 83435 64488 10257 91366 9400 75947 89600 61413 29376 94009 62817 13455 94830 14494 45210 87012 93206 560 15635 59970 30372 90452 22063 47954 45771 878 26517 56719 86925 22619 70167 42362 72686 69746 13395 3799 37680 43128 19253 22505 16976 48554 8503 37073 23012 74107 49677 4058 36800 43418 76491 3158 57553 34540 70389 28169 12171 5011 9053 48960 33367 58388 49205 29771 17471 20583 13078 13110 62728 43945 43477 5249 39328 14792 99518 26936 11937 19206 92780 29281 5454 65727 33541 96601 66692 52010 4981 31110 76312 37680 59640 5419 27055 48507 97070 2157 59136 99923 76790 66425 5055 50554 34757 3299 52103 43091 56410 30288 44218 99868 17132 54214 2912 57958 42544 29845 36109 6606 21368 45385 7519 99320 27087 43615 49045 65067 69955 31645 43868 52081 46576 82367 25506 62521 59675 98520 61004 91221 97661 33712 46439 33292 95370 50497 23796 86856 70261 95641 60771 53437 35439 75074 20365 26020 52631 94533 36244 6869 6152 29788 30957 24690 52538 33583 87419 644 48112 16387 73963 286 65788 13821 23394 11611 63169 60396 7549 8833 66468 69850 80491 42793 4022 1864 26256 50392 66046 59310 37184 79565 18378 78726 23456 13483 39416 54665 36713 89197 10215 62647 64470 33834 33116 43151 31727 50630 24917 31078 49292 41154 64097 59892 10101 70975 89188 15880 47235 1565 46203 83217 12459 37434 58936 12840 10070 2136 74985 58665 19947 10322 1646 44248 43515 59846 50959 58162 85007 18426 80968 37963 36041 38120 20745 78109 76998 37473 93081 50331 17110 52643 37126 991 25576 96755 47301 66789 7643 18625 11813 97273 65859 79368 95573 84047 73751 33029 21229 24210 18237 22462 9150 19024 11601 7917 11946 39483 42087 69423 13927 13066 94984 53437 57174 76355 56116 19810 72005 15177 47245 39431 14818 43096 76343 84814 58539 5300 31255 7789 6000 37107 64433 4813 47784 56121 7406 74449 61761 70672 22361 19102 7169 31127 73098 95808 46596 85629 31077 26129 53344 85538 63086 86868 99696 48260 84173 11643 21291 46161 16103 98130 79480 81763 42062 61256 41590 81363 55076 10765 89871 33028 54085 93464 43191 25412 78876 12092 66084 94461 91 20786 77362 39172 6632 11227 3533 16824 48593 92092 17078 66164 18105 2277 80539 38450 32168 3323 51738 3118 70288 4454 96596 68670 59451 5508 56349 47649 30477 30714 61830 66619 49089 92311 10925 85598 34513 21674 71536 17768 39637 8815 6749 11231 7563 64207 92640 17343 31516 57637 69270 25521 12220 23257 3179 63117 15845 41766 74040 5175 57584 58076 66387 29912 85501 48176 31974 41392 31513 64757 44846 79340 59045 48548 97232 79295 36867 69762 58924 16680 26542 21432 53843 36209 60633 58402 21496 90607 94284 90687 3688 27492 16900 45871 72533 96875 63823 87635 20995 36143 13838 37606 37233 46928 72066 93903 78729 26050 25551 67345 45524 79059 23970 99419 51360 74179 65265 38443 57773 18594 53233 75854 63210 23949 59466 28624 63115 46209 784 54209 67916 93109 12828 84886 70292 85588 26471 16259 11586 13208 45801 45654 95925 77637 64090 61192 38186 57564 59521 18014 17761 82518 48017 73178 68794 31490 99117 74335 76399 51151 40134 19085 94279 29640 14194 22850 2953 3088 24118 60084 98143 65179 64859 81787 32909 64024 91278 91293 2650 69408 33644 2469 99537 69429 92120 96604 54145 92392 92740 70713 86203 97470 87742 67167 37563 64756 38190 8935 83394 24622 88757 8514 42626 77139 95719 31201 81411 96111 87409 86184 42909 77648 76310 46368 19135 54215 66513 1189 48944 42989 8172 765 31136 10364 79791 59649 95614 45234 1659 66369 81307 49297 80874 60795 31332 7192 96123 84149 17782 83863 19649 39462 68046 10020 74564 45371 30232 90694 31258 39711 7719 34712 84214 54906 57078 62009 51283 36207 26260 25025 62203 81686 18483 99272 25327 43685 54878 72568 35774 58046 25548 60914 25307 61653 7799 70568 56085 22268 87549 33905 23628 87008 56398 62849 67043 60974 58997 83816 61069 18688 62038 24424 72865 83684 24606 34276 11799 78704 44366 89678 12556 55332 84139 84842 57079 15118 96370 64078 29016 27357 41184 29089 68660 10026 36984 18920 16242 32969 29519 42360 92083 97073 87409 86742 46105 25298 52720 65907 23414 59322 63367 13369 36319 57102 1365 78325 49118 78281 77457 53780 23471 65054 61359 11538 31350 80378 96010 73545 33327 12859 91533 85979 81540 23613 23589 29331 52485 37424 41180 26551 57513 29042 93987 55792 86936 89663 92512 16752 96743 97307 41849 40260 10454 72735 67451 73342 53534 93435 39288 2600 25371 68990 27313 94569 60479 10277 31064 11580 42613 11841 37162 83279 97467 29260 90727 24214 15171 73480 62336 25907 79502 12507 4707 91713 96317 99442 80528 47416 55377 78262 7686 48304 55581 98432 56539 48521 859 70243 7333 85495 98606 28412 51597 5180 99515 96129 89565 42758 31777 64905 4508 99793 62763 19656 79481 1829 54826 97014 9053 83444 62234 44492 78301 40752 31924 30905 41098 86446 43590 9656 86371 26106 82892 73060 57134 36250 58627 18687 75603 18616 42579 25857 30217 34283 48100 12659 98246 86856 5910 18639 85179 9848 95597 77945 24394 6213 95056 41923 14419 10222 49898 62697 99024 52980 79785 51295 5034 55434 80889 84512 61790 8812 2836 59131 75280 16144 28790 77111 13964 48007 99707 22405 60830 74658 86770 73956 11127 64794 59836 33249 85140 59710 96970 83303 22603 75689 28646 2730 42644 96092 86414 26832 40110 97822 67742 5443 79116 91668 84471 60433 55231 54213 32943 74150 37521 60571 85047 55607 73103 49863 42357 9301 50753 98016 97076 13665 76831 71397 26352 70903 81716 63893 35071 82167 8661 87653 70639 89395 98082 39782 4982 8854 91162 85797 98874 34627 70708 63319 53379 18969 9831 33817 85486 75505 40962 15216 10972 26775 657 75809 67299 28713 75617 25358 40732 6303 56188 69440 33403 12074 14732 87933 19968 33673 78436 58838 13860 52328 58326 6805 59371 66606 1087 58596 41814 2404 67915 1457 15464 42833 33805 84090 81360 27004 98230 26304 19982 64781 46543 30467 75822 30219 51309 23702 5655 33278 26732 10230 50138 49081 6095 631 48412 30215 93043 40816 86506 48766 72634 57953 9064 24020 82906 92714 78286 81990 6855 7089 98510 76636 21018 63203 66807 34364 86334 58829 52207 29750 35799 59636 34838 45792 91310 53007 41398 71508 34436 48311 98643 81559 3392 64189 74083 27829 53899 52572 72823 41164 95134 39573 12316 52610 73910 44554 18066 14505 31464 99434 69953 71054 22805 84318 60250 20102 35371 62234 12219 3204 39973 6466 4981 20662 78132 68332 3780 111 55799 9672 39435 8411 43942 97472 93700 57315 86895 20204 8098 77449 26317 65068 85865 94950 188 51637 80902 57017 8006 10367 12104 38189 42637 35069 63592 22046 88419 31002 88493 16017 5768 27427 88094 92554 4228 28003 42459 23536 7506 70196 51867 90981 35913 94862 87766 4437 36613 74897 87346 13898 50166 53051 28613 49684 32354 77259 83147 95587 26635 15414 35230 39422 23104 44510 87210 2660 41845 1301 51095 1266 55488 75986 46582 9097 13626 76419 67355 99621 69063 56247 78158 28183 72160 51117 9048 40560 35577 32851 84888 70298 19265 66859 58835 22035 25189 9774 63148 16338 15124 42631 85437 44026 32746 68823 81065 23769 21764 54514 552 39986 91313 53142 17766 61839 41471 48230 81434 368 41509 16833 4771 18128 16111 94225 99431 596 70441 92821 68518 96534 276 51583 40467 1063 42513 5955 17966 19620 75720 76964 71030 95370 36796 91574 21180 97531 35759 23228 81421 64803 41016 40402 24346 6834 90436 66015 24141 46228 58699 56992 40524 32343 51601 6 66842 14250 55526 36861 32994 83714 63319 93222 63111 39615 13401 89891 22710 68909 77261 21859 29664 76381 43983 29938 91442 51171 30388 12379 42391 67694 74054 15950 10266 74744 99710 34246 79336 92778 19893 21952 96149 86997 49961 29714 73072 21786 88099 11923 20071 35823 11079 75515 33468 51846 11908 53613 96172 12678 76724 94769 72254 55968 16493 98256 84935 3910 11699 14725 28682 4765 1229 87108 21765 9 76237 68863 58823 51540 81609 77370 33979 50534 69054 26484 36303 24678 26155 7238 57413 25952 16837 13005 39399 9895 385 91642 38095 1430 63005 92885 91276 22559 58996 26909 12481 13845 37415 74875 65639 45629 56723 66253 82408 23239 98105 97477 7259 49340 59916 12267 89194 35411 88371 3148 91698 33282 22883 42801 79830 34416 56649 5474 55980 19183 62404 74161 8549 42281 47953 84273 43509 9552 24082 2086 26869 68622 56322 75958 6265 50146 71840 79440 84980 24759 39502 36772 65105 95099 6285 35219 3309 55296 35430 49682 65532 18178 8794 64132 42164 87656 95381 5750 51423 31064 41251 77930 41524 96263 2457 4079 45794 29396 42019 87412 23243 87681 95543 57216 2428 70079 42080 51955 45659 35293 64434 47140 86157 84652 59177 78156 75061 15106 4582 67365 95871 73434 25243 47451 28413 48328 42538 10722 36928 9832 50239 44154 10746 11836 50343 71663 33564 99576 9318 87450 4437 22800 33626 38713 32742 59576 9180 60698 92317 80052 34273 61254 87689 29747 48604 86500 31578 29348 87544 87046 25539 67946 65857 18701 55556 19459 79330 72691 60267 93633 71059 65443 94305 80177 9975 22174 66412 21532 78978 25110 62197 3463 98502 74907 42801 95725 16794 91642 31142 94398 9267 36649 55613 39946 80465 97884 46162 83747 98082 76748 6888 56295 11012 56106 77146 4426 49154 28685 20379 38325 64034 14623 35686 5876 90953 35370 37586 33553 69843 89281 66929 80974 54415 69441 53216 5520 42948 5379 13810 53464 2214 77285 56489 73729 97595 55398 56988 12041 61182 68164 98170 6008 21286 45312 65687 10905 612 96693 28443 50691 14807 30353 50585 43261 10716 87423 81331 29769 63361 43044 89512 89680 17818 91701 11696 8981 51983 79321 20727 48562 76839 89037 7618 82847 55146 18330 53809 25075 49655 22493 62635 66131 20125 50524 57075 60542 18414 11417 39452 84320 63375 61854 75676 85115 71799 62831 15248 64898 54696 35009 64596 5615 46680 96615 90465 28277 12544 26724 46631 75529 10593 56930 32899 27497 92918 57722 88593 71141 3775 98047 76983 90416 83846 77400 72072 28294 6470 22037 12102 13760 78477 96905 59602 22004 24548 84990 28568 80176 94700 6259 10298 16445 40545 25808 24178 4263 95770 66535 55322 55032 17530 47650 69238 5506 40996 30128 1146 36649 55988 99161 5861 23574 40737 97038 86341 85467 93735 96157 61395 32690 49600 66469 81214 65913 69102 97760 68519 2616 21716 39334 25325 53666 62012 92890 85330 28794 19721 39122 202 63480 62460 9427 99734 17991 861 13338 56358 48994 92684 84998 34258 60787 96380 18459 51488 63011 6699 38110 92452 70177 90825 13017 17701 54604 92203 62239 4749 65097 36836 8680 56286 18952 11819 85855 677 10793 90609 35814 89161 73424 30650 38639 4665 97379 60284 28792 27799 69182 12231 63038 88664 29331 49881 49218 9978 82818 31295 19024 79087 1989 70736 80219 95409 97814 86674 73690 13294 67991 5499 79943 24332 46267 37029 94705 97027 4711 8769 77882 38005 67927 54613 98635 33086 14553 9437 28779 17945 34970 52657 46941 45406 61888 55159 68806 32751 51798 16454 6736 35967 24380 42727 38361 2167 67205 32557 94882 86856 12081 27827 60915 24999 89530 30428 73245 72656 9638 65303 35128 69282 9100 35269 58956 49320 39186 62762 61985 52146 72476 93273 24753 59274 87142 7683 71683 79048 2186 59620 93911 1673 53209 82983 96355 26758 69183 99255 97282 47668 41450 98872 74693 48231 14341 86773 6503 65418 51601 29137 62941 69501 84197 37455 8398 6539 36690 98434 30392 41620 73138 94789 14102 20548 59345 22624 75877 48852 25637 14830 44871 969 3347 93173 74767 70833 92028 97483 80148 64814 2374 81891 25069 39403 39708 86650 82848 12899 65744 25753 94205 61633 16645 85867 32457 2812 78213 77524 5612 77409 48309 97157 81646 73578 10064 7483 28808 47440 28657 64840 60852 9669 54724 3767 68466 32211 27135 29450 79944 52056 24564 12257 33866 84317 18328 64269 25159 70210 44587 32416 53478 43973 54914 11524 37980 61143 78936 89828 39497 36630 84447 99296 995 61850 37664 61309 55147 18233 85937 85413 60800 47394 91403 61769 17899 73750 81974 59141 17993 28426 64087 19308 44623 81403 48156 47754 5969 35332 23817 36430 27731 98313 12681 24952 98950 35144 22451 40228 81361 59570 7342 16136 88157 49562 98738 5397 10487 68314 53131 20638 57736 96770 13544 45581 95352 96336 68019 24702 63242 33272 58280 88723 48486 48430 91618 93855 92796 84387 63036 1144 83528 16473 39694 42750 14659 33118 47096 97639 95541 863 90765 46310 33804 3676 38264 96497 45797 95325 16343 65180 98897 47124 54890 31795 33913 44624 66871 73342 73958 20889 47266 48866 35868 89719 88349 55943 88178 14563 42405 18356 30226 67931 67923 90035 1751 6518 54586 38939 47476 65210 83376 13052 4143 36484 923 10881 74305 84062 89711 54252 74449 79181 52714 30783 99735 96726 20681 78573 7048 83231 43716 91433 75961 15305 79258 47627 1682 96840 44650 64297 41312 65449 67001 47383 36074 20478 76068 95137 55178 66082 4668 55187 15233 33857 98490 51093 29146 96454 41193 45931 22496 53577 58681 97556 92371 95472 13738 59378 7152 44829 29453 82576 64824 68817 60455 19568 91394 41952 76383 42323 65363 27719 12388 42579 64358 19622 63497 64724 36434 45693 67418 2922 87671 34952 14917 75098 50161 86809 48367 76544 95654 2508 86101 64039 63930 52327 40015 52754 15094 49891 27706 12449 76338 37467 88701 4068 83550 78579 82526 21257 83389 11645 95228 56363 25205 90627 41913 89386 44562 77370 24279 17694 72459 60051 81168 89407 73928 93542 81866 65981 92086 62428 88534 91440 69333 94409 19083 46097 3721 9479 49937 22794 24444 38594 15772 17745 26773 19230 32191 30297 70776 45113 83311 97415 6114 45625 90746 64388 24293 30676 45178 42868 18312 88298 56665 65477 50363 89041 77525 80021 36124 24762 12273 41487 91380 72917 4226 86545 76209 36082 30161 20723 96033 66087 22861 97671 53520 95478 46197 28710 50526 97806 115 49503 40314 8772 45333 81625 77071 65520 2197 17134 6229 63242 37767 43673 46728 32001 68676 67180 46915 86817 74009 55319 91417 50305 58518 4128 71603 41838 50340 29506 98710 65793 85576 53807 11270 33627 54835 2782 22964 49208 79228 8 31641 78950 39750 93738 13052 25629 74137 42123 64915 16889 64338 68229 88153 82907 37308 51869 23952 34746 17270 65725 16706 98880 12770 26459 64748 74707 5389 71775 36057 1183 60134 30163 94785 66849 59706 63266 95225 82781 93381 85584 30082 14936 43446 53090 16394 47730 31064 28351 75270 71828 39466 97859 58582 85270 8957 83720 83590 52731 33112 9286 1412 75131 62952 14634 26714 3671 67576 24869 76059 39126 68475 40692 77030 20418 25977 72363 19470 17798 19463 15725 52214 19945 58978 76373 89099 13426 51068 39828 79018 53401 63014 3812 31853 43693 64481 77031 4785 85393 16340 14611 31680 32974 85272 67192 71887 32126 84679 31097 44491 32568 60277 23623 29436 76440 63801 40764 6075 25965 76122 57015 91930 13662 16608 50045 60732 75614 38430 23675 25864 34841 65271 48087 16186 36406 45600 81121 12028 18588 73927 15742 7850 94877 13439 82613 5742 36759 77915 95234 64367 52572 27252 8862 4031 66655 5851 32958 53970 58904 80496 47135 83112 24060 5541 44109 58908 58126 47349 90265 72521 45128 60862 18926 49413 42471 89198 33185 66646 63067 32104 42167 87999 50081 69776 76996 35067 46473 12403 23311 17483 62805 58948 79811 62230 8489 27516 15816 20144 80354 17327 28026 55768 409 30403 50871 43463 67008 91260 26335 99186 14337 18639 34238 35248 56042 86426 85872 76476 86273 43565 75321 68164 24690 30928 65 23693 24597 15597 14663 60921 94013 43609 80059 1559 81231 21029 79199 65942 11751 29789 27356 3696 50817 12010 7597 3998 1642 59878 23009 44676 25852 92998 29858 91949 92216 65954 59514 82330 9267 19500 5488 47739 84648 51207 62728 17554 19025 95397 72735 76533 21291 24701 13574 28434 95728 98258 725 75141 77561 89736 80107 93807 88733 53320 92069 82894 17186 7859 52610 87014 46403 91326 14983 25295 24049 15016 927 76022 63486 96457 32228 56441 5541 38174 34222 43285 24682 28783 71411 75955 63245 7483 56624 18856 88362 20012 91932 17873 8013 57938 23861 29091 38306 72973 22995 14333 14629 28021 39969 16838 47038 50370 45005 26501 92177 21441 96756 34663 64978 16521 29726 36547 92403 20659 67373 4169 31921 52213 59890 16988 2693 7492 9774 52966 15833 85163 94847 66092 77448 85721 55598 54391 37775 71758 9593 88096 16129 33728 66233 99901 59371 24333 80169 79822 31409 62379 5767 20028 89163 43007 75337 15159 34202 3173 34726 2804 98502 7223 63537 37490 26757 20891 99522 40246 35646 5095 92569 81703 56342 23241 40113 20516 86867 15491 6583 29888 99539 142 98027 1599 4449 30078 45458 98176 12518 66416 28673 32822 20584 4205 7061 91689 5560 14856 49748 62439 79065 80563 14575 28430 84060 18283 4001 8316 28917 22988 70365 29279 35716 94046 55095 48569 65256 87452 26226 46645 78153 1403 53556 69160 30143 44143 84891 87861 55417 69356 73800 28300 49984 37923 62565 26472 82202 39349 54603 82320 64683 72528 7799 68143 76511 33108 48773 94269 99827 12858 23094 4691 32165 87429 74811 21267 81713 51946 18380 62235 46828 12405 27984 76499 18389 92012 4480 52692 8795 58736 3387 60117 4862 14004 89526 24557 42788 76347 35695 42488 90093 72671 6715 73608 1378 57849 49863 53188 69986 89963 36455 62543 19771 35740 3004 42239 93219 72502 80127 74971 91613 89631 72069 21491 73077 90008 60146 52516 92917 27516 85872 51026 74219 84503 62113 78129 70411 1313 60826 54688 57060 53567 89639 89995 79368 64106 65945 32541 23968 25808 49353 58156 5756 31210 77731 48089 33211 75114 58204 12790 41762 73552 55331 26051 37707 45111 73529 97748 58639 39028 44404 13817 97405 84707 75512 3413 44632 46778 70751 59793 9218 60034 11429 42238 76392 28493 45893 82051 79432 84165 88035 5140 16261 68436 55643 89949 71674 49821 89687 10565 5730 8537 53082 75707 74413 75068 29573 71708 10685 34526 85575 84263 68507 16502 56719 59149 70648 54562 47797 44520 70236 97910 1870 93727 95215 20259 50470 46658 76133 9551 24168 10776 64582 62644 44865 49434 59608 38990 35911 79302 73204 16993 92461 20704 79827 58606 59402 23067 46224 78906 78443 22907 42271 96361 82158 48807 62800 10478 20833 52253 89711 44933 6508 67700 44655 70976 49955 60136 4445 28089 91550 62062 14716 40231 77173 18445 43929 76813 49548 63256 17640 44595 18630 64971 91801 96958 93304 36840 59109 61740 93874 19502 41805 82878 28769 58268 9263 61091 23068 89395 41009 43529 54668 19691 30903 19261 82286 87222 49600 12706 48295 58795 23275 1777 30839 46348 32961 87259 50219 27208 80335 12433 61287 3814 44909 16382 98655 38180 64903 5877 81566 5797 63944 12771 5066 44206 30096 57633 61489 67362 68197 15569 20708 17971 34483 31130 9523 27922 99182 76947 32666 70953 89487 55691 32789 94640 82351 51347 45982 63384 33189 55930 58201 43799 65658 65323 89265 64005 88259 49406 48868 97175 3350 25372 38436 49186 79159 34564 24496 12930 1644 28506 77963 2889 32319 13119 87159 49477 54643 16530 89985 42932 50272 87776 68213 80558 49007 88893 10152 40500 86196 37296 99637 2633 97782 41443 27966 45474 69390 13098 500 91515 89680 7650 5168 45682 76422 81592 40582 68571 92459 99290 93972 90395 5436 5822 54904 2329 63686 64227 52388 52218 56399 21822 75112 52914 47081 16906 27406 85281 43890 2348 42724 11573 25828 5002 88705 93507 79644 83993 7806 12955 69143 39076 68336 19998 8193 52258 10824 55161 1552 84920 48256 72684 72700 78990 85990 74668 1698 60761 42706 10979 35266 35656 36972 65162 73966 75139 50210 7134 61444 7144 44686 90660 53935 76928 99396 73906 83361 39610 57928 10553 35847 1572 23206 42271 25095 96142 12633 30308 43115 8362 65992 29709 48951 98805 5930 69547 78218 31020 14946 99156 86786 46180 52202 97175 40716 71759 76480 83767 97357 86438 7997 33102 12232 81618 27047 96787 76829 2769 27317 67872 18976 51068 87787 15037 5494 57764 5640 31285 93010 50523 80988 83953 82722 82089 60752 25446 87011 20172 10153 68593 87100 27415 97562 50815 66486 86649 79402 87104 38914 31060 32484 48002 7613 51398 70974 44899 8349 75497 60993 98792 30080 37530 39704 86077 82071 36670 55642 71449 46581 24938 27866 82498 61968 58001 20934 45693 53572 96499 41102 76976 48100 79857 52680 13286 8910 37478 98085 54997 77773 21368 42746 84785 21288 86879 59011 4605 44348 28123 75584 65938 13792 30007 30098 49579 84387 63157 63953 30108 32128 22596 68636 54578 85810 55588 34482 28207 91556 13278 96130 30042 98019 53542 80599 87119 83140 52375 94603 41333 58052 59707 14327 30477 84628 45928 88370 16590 50589 49068 3724 70992 73426 86536 32719 33380 88598 22472 41420 37144 3753 33560 43002 72187 98633 4876 86449 32897 53086 56807 57037 3015 73609 34969 47918 15816 15492 20876 62487 76483 32903 44201 34366 82213 59814 8060 69885 51997 74440 4406 74444 75324 60584 96939 68335 2768 65484 25175 82291 76118 32685 53213 5137 11528 28565 8446 80662 81558 25585 53796 6867 28784 59482 93754 55536 29063 1537 78635 83898 24829 45599 82648 26233 1025 85457 49270 30400 59949 25981 80263 73750 83901 61521 61698 37437 57526 16553 74925 46369 76562 59172 63812 38890 13117 28644 12444 90345 10384 35082 79958 81487 45930 87593 43742 56038 85006 16468 19641 51437 5153 21468 61093 87699 7492 2019 10403 14362 81320 15824 96778 22194 50341 91772 16645 22462 40984 85089 93355 12623 3335 54255 70082 50785 75930 62286 73889 22326 61420 57210 68498 30350 52872 28613 43250 68489 87429 17267 268 3111 43308 81575 32472 9070 15343 17694 8664 61633 93517 23575 58577 49824 96406 6354 22583 22231 8025 34832 68297 82705 29260 41948 43730 79322 97153 23156 34897 88287 84151 48726 84708 96728 74617 23716 89384 77719 8617 57553 31781 62175 35557 7544 53215 41284 36954 54135 82343 16833 29260 19423 74532 57375 35469 46408 22336 64357 90442 77756 56763 97260 63205 58407 52082 61658 3903 3807 85286 32111 42391 74572 31770 21531 59093 25974 27729 81505 47191 12875 12839 48941 90080 61189 37874 62921 19514 59103 79693 93263 47739 94175 19370 44480 55755 27066 41784 75788 44098 16318 96036 75091 76288 27437 36677 71547 5394 91134 37439 59290 21609 47000 37809 72067 25506 60594 56690 46929 80280 43183 32923 4724 11645 18367 97781 95416 53038 74604 23645 13745 13055 62166 66237 26286 86329 69879 2408 88092 7905 78065 80200 10673 50393 62021 74504 17365 3485 60648 35098 18684 32467 47737 52553 38848 96970 7613 42427 88476 49900 75512 8832 79014 59373 7688 23582 15398 73148 43024 22918 76675 17876 75051 23685 68975 47949 41442 21950 33571 9748 60934 93786 21974 84013 6930 80374 19335 2378 79700 331 23037 73533 57569 79500 97876 16218 37949 32759 48478 97228 17555 75443 8554 80672 64078 94195 77173 39110 89147 88025 2198 23633 11174 32542 77558 44151 34357 3490 25638 24632 42568 29145 20312 80025 18035 40250 41992 57909 43875 28176 28665 48498 649 26793 26388 56874 27327 92725 79774 67738 88071 54407 62622 87113 3143 7288 49693 85628 83599 35531 18278 82705 43332 27814 21280 54803 24690 59313 79941 30429 59924 70092 86845 91470 497 25546 4848 54896 5131 55790 14764 86393 88702 65524 72952 75811 38650 83918 23493 10769 76104 60401 35995 13252 45526 76899 40091 40142 87898 56564 46070 27 31522 79518 75309 52993 6189 41345 33988 79035 36764 97437 45450 40487 85723 88284 26980 21824 87112 36350 36045 20835 33331 39897 77843 27478 50715 29826 22763 35854 84326 85433 97852 19971 75441 71626 71708 14444 23581 22888 6103 78066 76880 85754 89866 9493 61175 85454 65222 54839 23025 31124 53159 67440 93459 62359 63160 27026 16858 47967 59015 74515 97714 93863 13145 31663 72170 21942 63320 49933 56038 69256 48287 80364 91506 8089 86538 27318 42404 6468 45241 14702 75159 48803 44038 82705 44038 14764 97928 79896 97243 68480 52584 89370 59140 3057 23887 54139 62177 32589 44492 9863 5218 87401 31773 83044 85998 39302 59826 65341 65889 15707 16974 78197 9265 14846 95876 51549 40160 67964 73758 82218 29274 20811 64986 48313 37273 37384 79110 78066 31609 52585 17388 32477 62474 77836 18484 39587 76479 71567 88887 20604 88849 60501 63410 64824 31314 58522 51789 23306 86426 39154 69313 74695 71419 72002 27156 52115 16677 52397 79636 91623 69456 91614 40615 6602 29942 11652 96965 86277 51446 99777 87675 96463 76421 90289 29233 86474 55042 95721 69904 52231 47141 60558 47630 7188 85549 50527 43445 46612 95917 62537 91041 92159 74955 59092 48358 9402 65028 96844 79973 74894 85185 93286 27710 80126 88086 87921 92247 16433 78667 18319 19852 5601 45437 68984 45913 91873 82050 9618 68844 12459 37304 9560 65936 7539 11912 83806 88846 91631 55777 6819 64316 94113 76459 15221 73230 39170 49790 8589 84142 25541 29430 2509 87470 72303 57092 30897 1847 30830 51511 95233 39691 92637 64444 42491 23050 5857 67088 85019 60480 82665 90933 31347 89871 80992 42048 35871 3636 91526 20100 69385 18847 33664 60121 87560 80877 8130 72634 57626 62850 55457 40128 40815 4930 47470 50890 16438 69605 59156 18602 9306 86691 27818 37480 72146 80882 62933 37185 79690 49514 93370 5937 51463 61363 60138 93861 97768 33047 45270 99294 53931 75760 51069 37669 61722 89536 7133 9761 64287 45115 63150 87312 32096 46467 87205 76105 71445 72229 3087 1955 83034 44373 81721 13450 36298 18240 64329 16313 71227 67555 5269 10016 35361 82762 87796 15262 79794 92241 35265 12027 55484 81086 68958 20439 52878 73772 17943 14486 22566 50321 21824 33111 38527 5434 6719 88066 92325 32318 59662 45023 35904 68058 90901 32199 85927 77844 9530 39808 83259 18859 34586 33776 27547 35962 20781 63483 57729 73590 70449 83320 78044 27058 16085 9947 27131 84354 94213 77505 86215 27464 56652 15940 94531 72100 76492 12171 55738 41927 12660 75310 37311 27322 15953 78741 64637 4599 79729 60861 6406 34810 79227 76336 57973 60901 60448 28487 30709 9964 90509 6277 88745 77904 40231 64666 16386 71894 151 55877 51018 39649 31685 410 46863 52815 62280 36461 6518 19697 91399 69064 97681 98403 56135 70992 51586 84816 42048 3741 12574 921 5466 21116 29634 66023 68042 43610 93640 91560 96096 78308 20228 10167 87587 33673 73661 43187 3223 88958 67100 63647 16287 40016 10418 91467 62877 28520 22113 3213 20964 15559 67377 73920 26638 50762 70875 19309 43222 17678 33886 84171 12128 72005 75113 9198 60368 94444 15849 88754 30800 18670 47666 41159 38443 58300 77908 99737 5026 47746 65647 27922 65925 41377 28685 34343 45191 80662 82994 82476 50577 38400 44647 29171 94838 86156 32924 55979 48792 10137 62361 32248 21672 31776 78445 43646 62722 78192 88787 36058 81526 34923 99022 30853 6530 48183 19902 8554 9353 1877 29865 3552 89101 66445 84648 11287 86643 63349 72110 54851 24214 7100 58466 34493 90557 52683 2231 81893 37639 46583 80706 5590 49896 70817 4142 8828 97243 6859 52433 64468 78238 89009 82714 56451 117 47068 27158 59217 38924 63959 28452 1049 17755 79987 58399 23176 48623 54638 59521 36325 49762 60199 91622 40572 90096 62966 44005 30748 19526 85233 77065 20281 77965 23549 39008 93660 68198 5308 56485 73713 23455 57841 8682 50044 17388 51651 72226 74382 6113 8504 85918 12297 7759 31289 33084 66700 91639 13789 54655 63994 71077 11637 29674 92752 96387 36393 98999 82387 58988 68258 32994 25169 83402 49160 46480 21422 60268 64052 85314 17998 82069 4428 90650 34231 15987 78594 79261 60816 28139 62100 60458 52454 33433 20750 61849 64630 86078 38774 23983 79340 79758 44884 21251 5463 95201 93991 74265 54222 58144 45280 97423 36432 85456 93566 46516 59236 56433 89172 9159 42602 93465 87552 4003 34063 25648 87339 60237 28557 35056 94339 69787 84716 45314 22598 64941 49230 38564 24100 44750 33804 30442 62859 85079 60888 54546 59203 16579 7700 97633 20627 60998 42469 54034 73932 96793 76168 86646 59154 22560 10618 32036 63725 52369 48277 94961 91162 8157 50076 94246 2031 97834 13160 90985 9319 42716 35845 90598 79036 30399 78851 88766 34837 74646 56840 30466 23781 68971 61390 60644 60145 98472 28664 65176 37442 64256 34141 12554 34165 44557 46491 63231 81936 11729 89188 13471 16453 84823 91649 30260 7729 71703 75154 57432 9670 86758 70402 10551 8034 82481 7818 41165 42079 54200 42487 20335 25945 40375 94879 22314 97394 98045 10741 81496 66863 80836 90896 12903 16149 33379 48714 62210 18914 67813 53757 31817 72907 71644 76279 67064 93462 44017 66223 47663 55787 80330 97636 84283 99146 53177 27772 27675 40149 34054 69361 79035 97145 89016 34973 60339 92477 62903 45070 28912 20729 28412 4923 23540 85137 7549 9823 66503 86624 53830 9445 43098 69979 83623 49925 59629 18009 73485 9537 96447 37323 41076 99752 13206 47723 42169 15814 87229 76694 6515 24535 87524 94404 9507 98164 28615 93857 8351 84303 27546 95602 38723 71615 3582 97078 17518 81949 96671 57576 69868 46057 6158 21879 80688 78355 65995 45571 5901 5725 1664 49152 1173 53160 18866 77027 97860 62149 86052 7436 31078 20200 17626 71199 21390 49568 39729 59654 24778 67036 31449 34719 22669 71331 50544 3566 62501 882 70833 79852 44863 48254 84068 29579 29343 97114 76658 57914 36213 92941 99779 32636 35009 39195 36301 17767 60665 12442 3685 96672 67067 20495 40851 77781 42237 6178 20611 8690 57231 16412 74802 79393 81749 29886 90223 25260 6199 11176 62333 62604 30302 6921 64552 3043 96188 35390 48903 32960 67510 60836 60104 57574 47214 79293 54745 40761 86668 73231 23641 8516 9080 37491 70992 26125 15860 25805 51479 40310 2829 54866 89359 96674 84676 48450 25490 17720 25973 3737 26413 68572 62932 74634 41648 21695 19283 33226 42122 95695 73292 86983 43517 21504 6731 74797 29070 97837 63430 656 78153 23860 29313 60752 81477 71695 73491 55655 19558 10235 32568 692 17483 14817 83105 53777 92929 42397 33726 6642 12766 59894 73752 56097 18764 67424 1231 99207 48710 47132 34554 70069 41492 31179 85266 73776 47798 3788 89593 73015 51247 49084 5214 60922 55613 3609 70077 18152 6455 80281 90069 60017 99477 33086 38224 27388 71435 65703 73431 83773 48285 58529 19012 20738 77020 63512 44542 27483 32659 31950 56657 91621 2425 5583 56239 39080 17108 61034 25068 71294 76351 5377 26820 8095 42085 11907 19558 78990 43323 84539 30660 18739 12331 99428 42604 2622 35934 21811 2228 21648 98053 54427 61655 52560 4081 1587 73382 22603 51027 28581 78576 48700 80350 23484 85248 15869 42765 17389 14308 98608 80962 13263 21497 94994 64005 16008 66805 79290 78715 78401 47924 8075 34632 98903 39470 23860 8993 3216 43821 46936 65524 70138 70751 3145 28936 70450 13620 95763 42170 17932 33478 97978 9218 13210 83006 70002 84159 62431 88600 55498 45907 98128 99183 73402 38026 7322 71045 72432 31531 58518 44139 52598 80201 14838 26953 80741 49840 61134 66212 7309 37016 32018 51575 20764 14601 72616 22702 74816 97036 44983 67812 94405 80445 92264 87407 73180 28999 26769 90078 49996 53820 70048 91743 65222 30201 29457 59739 52826 41523 19898 34364 18939 47569 61332 61344 5642 75179 13898 44565 95942 84551 95383 54382 44130 41241 45419 91924 45636 83721 45395 91344 2655 68178 19254 69333 53571 99592 31451 94932 65645 46555 29680 4940 5259 93641 29694 92366 64435 9687 46742 23183 21174 97729 17030 22794 21077 36266 55423 74479 4739 43145 88638 71283 46078 76155 69029 64106 197 78702 33074 48252 5430 87996 54743 52417 64369 71443 51265 72763 18805 78254 84499 75818 87484 41144 78586 64974 13547 32988 78009 19638 88273 65845 45697 83947 87737 76725 96115 26245 71618 89492 45503 23300 37552 45576 64053 41797 33655 53853 16519 68417 906 64177 75757 99161 99460 79306 53447 16770 61063 27472 65523 16126 97019 55405 29877 11937 43342 62225 62433 38896 20249 23906 17256 36272 79657 80884 75493 92177 26070 64925 96915 82444 69211 42139 46982 52075 19470 37368 45189 50223 54891 4099 77526 97026 73432 32176 30141 3414 76991 88661 30082 54942 57564 43126 11165 7247 121 61053 6487 80650 22563 47593 54781 49307 322 49954 2127 12328 71230 89467 31864 19477 7831 81201 13729 72199 47459 34784 26930 71146 46125 87868 52606 89543 4913 4809 24375 82232 14469 51430 59046 76038 32207 23574 4162 26676 67145 64009 43047 72578 22856 60434 70893 21463 31091 95622 91840 31873 42533 16493 70583 22856 73370 83115 33119 39386 49509 72978 54341 71877 48050 25477 45883 64405 75165 91411 11512 33211 83845 26107 72608 58271 57345 321 38723 47004 14182 57435 56356 2021 53737 18065 62876 46535 80190 30974 22953 73272 66600 70418 52112 16599 87747 91484 97964 74213 84079 30757 15220 89179 96237 88228 34508 64755 57738 27146 52455 51534 61404 1978 42697 73058 53231 95966 45430 39834 63748 13078 22609 34099 62192 67141 22293 61303 94112 20136 82371 28807 23373 80355 22743 33079 41889 6222 30679 36489 40166 49775 60807 96057 95352 95225 92251 26795 97950 63174 46206 22635 8498 13424 61048 2289 59485 55294 74746 29472 42832 34102 3333 75918 15330 82521 85382 11169 91895 71120 33386 85441 55260 95942 60451 62960 38475 47042 10547 65919 14751 43325 1745 74538 53667 21848 90794 36241 24721 20445 25006 55774 25079 87473 96555 73654 74383 45386 52514 18929 7589 12606 2157 33607 97238 14095 21704 71309 91131 77862 64757 18920 47509 46682 38529 81322 93908 34978 6502 79495 25574 68033 42239 44433 74771 99110 63107 15101 74601 92715 45050 16860 71807 64846 68957 37231 67967 18086 3688 34265 14425 19869 26549 52597 90007 14117 38416 68923 18354 95799 74708 64027 85910 50640 20473 15318 59505 13641 37826 84168 26145 49679 22280 86238 38531 60767 6244 45642 3653 4696 77680 81234 48739 47653 62672 80263 28449 42703 53539 27796 16906 49406 55169 79402 67941 74840 75896 50017 42285 88112 87622 90099 71677 96897 89360 54318 58934 28181 61299 36379 54180 63443 23821 45113 75605 77982 95972 2881 25049 80510 80232 95784 29102 89080 20249 82127 6039 83891 85503 42187 14166 52821 38795 16287 73958 70793 36052 65925 66180 57910 30128 68210 68069 48938 15582 73316 24164 69861 27706 67531 78811 29593 69419 80424 37293 1784 15842 11563 9299 85927 95249 3775 27355 37667 59036 88578 44786 71126 32373 61485 43849 93454 52471 34299 37277 82609 37224 47754 11666 66148 39408 6040 25198 99334 86579 39081 98172 99453 32049 81445 30279 44228 97898 55601 80471 48206 15599 38461 31580 71163 29444 43408 65404 76958 12436 8964 34611 24623 72094 77791 23562 8593 19320 55773 66833 47047 67753 11527 64610 408 50361 83277 33043 71357 76728 41548 45898 56487 61437 15912 80164 46321 14749 66970 37237 81114 52478 35865 99326 20821 39458 69151 9918 35096 21509 76514 4918 58452 88952 51730 64989 61943 62864 14961 31995 1070 55682 29630 20836 58374 81407 29487 13489 76083 21021 7388 66070 67566 82766 3089 20792 36457 91180 69137 97016 86850 79398 58543 73728 36131 36731 71521 3459 43195 81940 55728 49551 28689 9027 87350 34216 20812 82803 70016 28913 77204 55400 19316 91009 57002 58162 44029 93576 42604 7179 79668 80700 30195 89942 67847 58246 63240 80350 91128 9368 57188 45305 85506 15933 17393 66339 25471 38614 69169 72287 7270 24041 41272 3233 73574 63530 2853 71618 78545 5171 64497 31967 50373 64659 37514 33279 26099 50014 71842 33627 10018 79316 34647 87013 94214 35135 65509 44349 15233 43162 29145 89450 96327 50468 44968 11121 70797 8326 9016 48567 70981 58708 37741 7963 53311 93841 24161 20591 994 61856 17906 25187 32702 16544 21367 67077 97901 76637 87789 16067 19831 42350 51098 86512 38395 84158 38317 79306 65796 54275 92437 57508 56071 35848 51228 50467 56746 46287 91693 49847 12227 48463 20451 74207 29395 54586 12615 29204 87693 11336 79235 56185 32056 34369 30651 50512 66621 31477 33592 95252 69741 46731 39421 14112 87073 17383 57515 21756 19433 84976 57436 9866 6495 39714 88069 58624 94851 65705 96455 99349 73736 55461 61652 81194 68046 99184 87065 58538 87968 45966 29360 42362 70076 3091 41220 72032 71258 41879 1172 82044 45342 67576 88663 80079 74485 71306 18515 94020 19166 42876 98523 32696 61946 81943 88952 12852 20144 3269 69446 72999 94485 63536 94698 80259 51598 37635 53570 75031 24058 99951 71766 34714 90911 98984 18607 74860 31526 33977 59392 92856 11292 14297 76542 54387 19079 6284 67790 53772 27206 76988 7882 9310 21940 7271 36491 47631 85189 39958 99498 26698 20602 42107 84451 52605 33438 47961 63533 47784 50991 41805 75272 32269 84770 36818 88680 18018 44958 86583 59437 97760 85292 89273 79387 15657 19123 55251 96471 69210 78621 94095 34138 96037 38459 5504 10358 41099 4380 98927 26467 63960 80754 49542 75120 16192 63898 49093 52058 59291 33999 99323 84674 88429 66363 30123 386 22466 21625 66720 43628 90762 72246 78601 42755 58982 566 81448 92737 96706 4814 72514 49162 51441 87038 66948 42139 15811 56901 11775 54324 39095 12691 15342 30178 20272 57046 37847 61215 27520 7662 87838 18880 7123 57568 38977 81474 84606 84252 8309 56840 53170 51833 93095 7920 64417 53312 59361 5350 42643 21381 5776 14829 88511 90494 55366 17299 98527 99086 52422 56949 39262 95192 93709 6907 51119 96994 13900 1853 74950 51397 15988 80326 22055 30493 39700 68899 20048 76519 52512 22575 7241 97661 77947 45336 56464 43418 6711 56322 82899 9972 70388 84113 92213 75992 61907 70744 6690 29090 90163 77183 58257 49209 8742 89017 76822 21276 79709 97042 27266 95313 9325 1368 77741 28511 19147 18252 35175 98406 78265 93279 7532 80394 7545 88951 47535 97896 19745 29074 42388 17558 23032 43356 49187 53530 37180 95634 6766 56800 46029 11509 89946 83588 84830 26198 41496 87816 59157 39692 39699 94423 55150 43734 59712 96210 98163 6371 4951 70389 45278 23711 84441 76552 93428 73005 66733 766 10884 13878 78621 53368 90006 30975 53329 68162 40430 32955 91474 17552 69734 53478 81486 682 26337 11552 91894 62069 44930 58020 7185 35999 2653 82702 20877 36706 4747 83120 10842 8016 1689 59861 15545 65439 93354 32748 30275 34508 21275 10202 75572 62156 995 88360 97295 73330 81183 40005 56405 49429 81645 79912 18346 79365 22155 71249 18681 97673 78081 49255 97645 52629 85933 58677 37229 21323 10238 24946 51512 98843 82523 23005 7999 50172 95381 60808 33768 29080 26168 98585 51907 3349 77219 60365 42380 60476 27075 64292 58443 53429 61365 1572 87923 74969 71172 38782 70178 98348 1460 67984 76900 43737 80078 55673 95359 55752 50541 41465 74068 90978 1636 53482 22367 17542 79541 70274 95001 81283 79474 87970 65879 81882 24468 94541 73766 76240 49148 6522 15667 17288 43896 33597 36111 1248 60146 24570 6753 10583 764 20917 53555 34157 12941 33689 44190 3739 72645 92251 77936 9682 30516 31114 24667 33653 94735 80291 75210 98439 49451 78898 13320 79227 45634 58716 41148 97509 74784 74060 14387 60487 70535 75558 3711 7236 85142 98679 22150 60669 16014 28340 92522 60441 34143 55746 12501 70771 16866 65917 23881 77988 7916 38678 23048 47559 63838 3667 26565 42227 64187 11887 97115 2359 80090 77177 71397 93392 14718 53091 20602 19985 66773 85512 9721 94087 56634 36798 72166 86067 37527 84067 89234 93883 52499 86229 46591 17725 4555 44489 89946 9392 32154 44933 78128 75284 55278 6462 41257 25824 41319 53247 15465 49116 24250 48893 49904 74105 38258 64137 8346 95772 80253 33017 95763 75548 45652 5617 16833 5999 87903 64362 34951 53415 90841 35838 69459 39028 89167 22633 60698 94422 96354 83421 45935 29343 372 56871 13948 84806 1755 35682 2697 38206 73325 83847 40551 98869 58197 64446 30985 76871 19505 54797 80868 46900 24068 77630 77554 2934 49514 20449 56857 66303 69668 99236 27907 31082 68856 51892 20716 76241 80220 63638 80279 95015 3376 89380 59619 55527 90855 63227 85348 23981 54445 78076 54398 89653 97881 32527 6768 61558 12549 10777 27553 95941 57934 15138 78822 56850 7121 99589 35783 15049 9893 62782 90955 61987 3743 70412 84867 50071 17199 60882 89496 67243 21957 68432 8244 92709 19868 36284 75584 72740 29972 32104 37003 10560 59112 24944 65988 47166 22582 27122 89925 523 80228 27710 57214 51670 71602 93592 78420 16176 91782 70593 86921 70328 97428 43677 21265 36806 56555 28096 72740 19049 86914 42486 39499 60350 35677 63261 8167 21293 13027 75906 69072 91362 97720 29356 19543 849 61074 96802 75728 75411 44516 70392 38069 43117 46559 52957 73005 27479 1154 46708 95013 90112 2063 70379 14290 74534 83114 54232 45262 43444 79614 68100 80342 24221 57490 65547 62658 92989 19594 29518 76124 1512 99973 81849 19846 94664 26183 67455 31730 59471 2580 66307 29000 24276 41448 55961 78609 39224 24519 44540 12077 60895 8774 91359 27682 30755 77460 71311 69455 36092 185 64696 61429 79707 66584 84241 4924 13698 27905 31191 38825 85435 99437 99277 58179 77943 17249 26039 74561 44601 27535 51027 1668 15366 74674 43818 39681 8 32651 83273 58280 8381 77137 30229 57859 73597 22503 79727 92 71518 34368 60650 41765 18769 13032 45499 61798 86294 41108 53070 76436 18827 48223 28552 15590 87221 6465 96469 40355 96859 40512 13883 64929 59622 16211 63777 88195 69441 83439 73176 4698 41997 42737 43991 52185 23273 86175 79497 64193 38196 56458 8500 98605 18817 8501 82081 44841 54883 82175 83392 69045 16966 92326 82808 95189 16938 46291 15053 73979 94037 61938 92786 56036 90289 42591 75155 51833 5547 18792 21717 51455 55789 34708 59428 83803 31122 15614 50567 59438 99863 46705 59396 64818 16029 13365 82323 9079 197 50915 79282 27896 38438 9819 36555 68507 68829 4177 60451 67752 76214 64357 1368 96686 60690 31817 85230 71094 27557 61624 9093 39303 34427 41616 45494 4431 27249 75412 57098 89342 96684 55860 57166 40121 55045 86773 91009 6736 75695 79915 69210 77982 27553 10726 55801 6218 74562 40031 68898 83274 59603 56182 64539 36375 84918 50241 15775 39395 43421 24407 91588 25769 67173 84656 912 11467 41785 19028 89259 936 18406 62591 64540 91101 51595 30720 22072 79529 14924 61658 11194 25882 18249 73996 12775 73742 31640 19835 5291 13434 16432 8123 75950 16490 72731 91684 6339 45349 85462 13317 53292 52580 63074 11429 72866 11837 60420 96246 52850 21299 29552 50806 93663 36849 94112 54994 22341 15744 99387 64939 21184 48738 27556 63794 84811 64541 69820 18817 15215 48553 44110 62099 56538 78181 72325 10845 61237 92608 87828 55325 84213 98246 70220 73361 53741 98853 90722 13396 10775 87980 93922 5949 22193 57225 85693 16550 24104 49283 52915 58234 92460 40831 40364 23499 57208 46606 47302 74082 32175 57680 14466 90421 63391 43761 28816 83939 97201 37466 43002 72276 44653 40396 32459 99858 26890 21092 75908 90867 25534 32623 78665 52212 64990 47410 705 73858 71272 92463 79428 7241 49382 55049 4543 8746 37627 32595 45019 53151 87609 73522 2914 38884 4041 93161 71472 27509 68811 38078 8819 60071 58341 81916 18073 22264 62045 18736 67457 21398 55092 99807 49396 99373 72121 9137 1200 61899 86807 58093 66819 23401 87549 40878 31651 63637 93756 58255 96639 55535 8847 49260 90091 69685 27138 17490 48061 88806 46824 51329 45808 53091 32447 72919 63694 64226 81656 29163 55659 51867 99176 37703 14599 39182 27966 90057 24250 21860 55754 65898 85232 42052 18126 61609 69508 28248 67055 39574 70012 60889 88721 451 45644 92004 8962 2317 75160 5577 22484 8746 44852 4879 69318 60033 55209 17133 29070 23470 26142 54281 31033 22092 75592 27588 631 61803 74555 7543 7357 67169 25947 63942 16002 40777 74130 22831 57882 88521 90820 62995 20992 5428 53629 92300 31176 69499 38319 94303 12210 96853 69402 73276 97850 46109 8058 17996 98375 95942 994 14377 21675 412 99624 1688 62877 61266 59434 79131 79845 80019 12051 73 28079 98000 49655 69069 60 48690 62814 4836 45040 33251 20813 26759 95658 16811 91515 6548 17706 73390 8417 4482 53461 20872 92901 39496 29226 9839 44983 81058 41916 9021 29524 59417 2557 57990 21646 55652 52886 3861 51455 28659 74669 75565 583 23723 70267 77832 91209 48231 79918 52600 6681 33536 91220 1415 59320 83374 49090 29152 59722 77467 67817 70245 34539 63232 95306 66024 58380 48754 27916 30568 67698 23294 88876 66994 23945 94597 85114 652 42572 31754 54425 56396 15170 80013 69431 72732 79707 74686 76415 98713 53791 41203 92123 55670 66579 99915 47941 2714 23672 42216 36220 49810 90423 50979 47173 20566 71929 45245 85935 48519 1237 87140 51275 80138 32509 92206 94554 73719 32780 26067 30932 83669 40107 3507 60160 34091 37418 19016 82687 54247 37515 24449 34603 52637 99166 60464 13177 22998 62624 18485 23067 86811 90834 9567 55406 88482 58141 826 36531 95188 51583 97049 81260 77499 79734 11378 80081 65274 92623 57289 97300 43969 78645 51942 47836 29785 74951 1131 65243 65651 15905 90712 68682 36021 79385 60163 20933 94868 86028 85895 33149 58799 70267 78376 27377 97407 73497 13779 42911 14104 24988 29223 94237 41399 47785 65357 92442 5251 12385 78513 75847 7538 8952 27726 36845 63846 84492 74905 90975 39451 17425 44013 16421 86257 22677 70263 7465 39460 1760 18928 26669 37674 81036 50748 79421 22531 96947 96273 58998 63147 1877 85375 64098 47124 56391 41049 98621 74097 51578 66568 27813 37021 35146 52305 1380 47725 20415 33954 22200 50648 6673 5957 41783 83032 50287 84861 2100 74589 60300 2362 27416 98718 32229 84854 72783 90435 39242 68528 34787 14272 21636 57546 20857 55328 89764 6753 58412 13096 20903 19981 61309 85 36377 11823 33495 71353 96726 77077 883 45259 469 80571 10701 28023 77255 78151 22771 5182 82374 9523 17433 26834 75615 45299 98812 45978 98750 13235 81422 20060 88518 52368 40127 16454 10945 6195 24660 5025 40423 72101 55685 47865 83320 45105 29418 22496 79425 86867 25609 83691 3149 72497 51210 15810 6432 39831 76666 28091 74148 34330 65244 647 38604 74038 84995 90691 96664 93590 63529 48188 28434 15439 59944 31198 52642 68021 17664 98983 23538 73371 11536 70528 53708 92484 73299 7862 36961 4116 65006 75498 82860 8484 6757 6040 84980 21990 15885 7635 82666 44820 23427 74613 75717 78827 79748 55455 51815 33075 90453 64774 47081 86521 24921 38007 13417 67234 91488 70434 56392 59612 92593 68380 87149 77972 73732 3870 27223 91626 1295 69894 62850 19833 81444 86721 77245 23009 67247 69873 78903 21532 60939 65347 95480 48784 50867 40615 49776 40739 51775 12541 81485 35866 78381 73209 51189 20531 917 42853 98349 56293 48542 65948 55862 3529 83175 64462 43262 45119 15213 25476 19966 23655 18490 94520 28302 74229 68250 46643 52644 93890 23292 14383 41613 93799 67301 19409 17823 22479 47274 26868 43868 74430 13602 43305 45289 80397 43885 2594 79571 72966 84043 8527 17702 4232 2107 83241 26010 10226 27493 55469 3097 11868 77183 23308 93209 89883 16360 85864 8568 24086 30116 23411 51954 48701 53057 9906 16568 88839 13969 78667 38324 49587 12084 99825 5240 34322 90008 32331 3260 48381 24011 62803 88860 78780 88208 43367 25084 68413 46471 71609 2838 43707 58289 97428 75960 20951 1658 34779 82439 48205 70845 9948 36201 60473 82645 82061 76560 75435 83139 29973 46582 49973 59803 87451 54366 67410 33915 25440 15293 13709 38881 75439 52802 7827 47740 36648 8317 93258 18929 13253 44365 40257 14396 24605 6182 96607 67080 52442 38278 46279 82734 36093 14364 728 49221 9529 89730 25148 52304 49070 39373 55150 68044 56679 62243 21348 45517 29043 24185 42303 48133 85043 62823 82994 68869 63261 32235 50863 54183 42673 97160 90560 99373 28059 21836 83644 23642 22161 17451 8674 10401 71397 71089 46140 5182 68362 35680 22456 5245 10566 38480 52765 99563 72872 51587 11977 1506 2824 67279 39518 62106 86652 31150 35747 28697 74565 34699 59649 13907 16469 31358 69285 22652 14636 39197 71987 31427 86430 88992 59388 50843 66126 64501 12524 32026 995 15475 81235 53831 42380 33667 68051 25637 50974 17135 15065 8541 41431 43516 16100 74973 25241 64491 47964 20486 24016 34828 48023 95757 82418 66239 48335 94264 40647 56386 71895 47498 21011 81819 58251 12762 34707 28808 45779 54932 18406 31777 87409 86694 91341 10184 61874 76860 36824 27546 58149 68401 765 51603 94537 25257 64075 34035 14521 97375 51271 20588 59235 57899 8357 50151 29156 44334 60711 49752 24301 64398 4817 60210 68779 75116 62792 87333 57046 97429 76227 78143 84708 67208 34720 12162 34978 61448 42184 9354 54233 15772 70042 96642 23623 4077 7228 66848 99531 93495 86159 96950 90510 50205 11358 37807 72756 70223 5067 24542 33549 30705 68962 619 85250 24861 9706 55183 75609 44455 98112 85721 38390 9473 76628 27059 46990 62141 13860 76582 31771 92884 29549 72590 35674 26164 10608 53318 11633 65311 87026 92294 51808 44841 10611 62506 35637 12051 55337 84854 43528 16276 70 26075 92981 61692 5816 237 54047 89434 77915 22732 97388 5085 14951 92540 11066 36241 35828 71713 28153 87326 49617 38376 72416 56735 75321 54303 42986 30706 65975 7526 50347 49980 18578 4747 56010 94004 3149 70047 52916 67728 72246 31839 47866 24532 18944 33931 41474 46092 44697 75394 89660 36418 94099 30250 13321 32738 87376 53571 13962 13619 90505 37160 53092 22544 43948 65779 71925 3657 36628 64392 83146 83730 60019 33984 67299 96379 7127 48474 71972 91400 22003 48933 62734 50479 82500 88718 77603 74299 81937 82395 85372 38811 77122 48123 82444 1849 77209 99888 28925 55807 17215 43285 91178 92456 24853 22664 56018 74310 79428 3090 94882 19853 31099 31535 18411 81885 30268 52993 90110 96216 51527 73493 83037 13398 60263 82211 26685 96142 16137 1657 82380 32065 47146 31821 28512 6792 87989 35183 44373 12505 65272 55808 94772 79221 48400 66791 255 47319 17803 14018 67922 42424 17558 13496 55119 82465 49723 38823 93814 21277 37792 43243 54886 93450 71675 29683 26070 69510 75878 81645 50973 32763 44118 79592 47704 66345 93425 13502 24489 95647 60808 54145 45510 13595 71317 75591 57991 99591 727 36675 42345 9033 51548 99141 50295 72649 18692 54419 88629 26940 60280 96060 59587 87781 60806 74081 79409 4851 40352 80069 70293 35932 27604 35178 47510 64239 53854 64319 34975 12072 74981 22025 91684 80071 1394 31890 38725 31962 68572 77712 89437 15850 86567 72491 147 66065 84475 54826 70396 17438 80774 21767 20563 32910 37776 50858 95309 93634 14785 24103 16227 62507 34498 23348 39892 71435 60340 33087 593 38501 9179 61457 5896 11466 6482 91009 6392 38232 91623 28304 85249 52056 33978 20654 44534 76222 4054 32395 27731 9516 8883 89783 99916 20697 14647 21187 8901 96931 61162 76267 18181 51564 39280 14671 91434 82323 73304 80512 68272 38006 85383 31914 19610 50172 55221 69252 11108 88342 13616 51531 10411 15720 56589 51026 36399 91234 49617 87357 96755 29435 65282 88404 50035 3441 12378 15050 31908 48101 31530 51934 24417 40262 66173 50978 19805 51506 99105 19383 48919 1899 50240 2954 24195 86949 54449 27429 67900 92739 11673 37189 3707 47412 60555 29830 13025 99383 58797 75759 23999 90857 18665 38546 96792 9978 61345 90081 31556 34886 56479 23488 98974 65019 86524 34296 8858 708 82186 66353 91321 21441 30036 50931 87486 51561 98449 52448 93405 63806 6071 72572 17479 75154 75391 50874 58714 46299 45634 27479 52747 69359 9655 6698 17382 26791 70253 30791 55463 50673 40595 96664 45584 15770 83594 11413 60990 79487 14902 70856 72036 10822 2151 86921 29933 57146 10363 29535 15002 74295 66096 5594 17281 96819 68780 73540 71940 29452 44501 47677 43591 58891 868 61515 20649 81961 71960 16105 27372 50486 55338 52814 64346 6967 60149 73332 86171 62858 29070 68904 45775 372 49611 85130 62313 9672 26169 82966 7682 91726 16166 34792 45744 30222 7783 24597 32899 83603 22610 8830 69839 78180 87443 5039 74334 10941 92319 99500 99029 84873 62410 28457 77081 69682 40880 20760 93309 26443 11848 88803 18585 46418 42147 46799 54572 42613 43397 61069 40892 17265 34021 95365 80084 50823 51474 11647 35495 71835 69851 13364 25109 3667 60137 60294 68437 76938 12259 75151 87747 61061 31646 29119 24556 63385 42186 66225 75238 82123 89609 91190 74570 57464 50394 42953 89752 43786 61410 4564 52779 14576 72865 27678 36473 51311 92717 8414 83279 2427 34000 5157 71393 36612 34738 33918 62842 39600 92459 89181 61728 60396 99294 74907 21725 77978 93189 60992 92722 34075 52246 9963 95078 87737 15401 60607 13673 24271 8654 4713 43910 65949 80177 1643 29873 7363 99106 76915 53716 37061 95735 80538 76244 58872 26959 67168 63985 96107 25116 9702 67254 36227 77582 8439 97428 71088 2136 20640 8740 18463 9682 51837 48866 99204 1632 22739 89782 55888 35477 88708 17156 62643 37759 47565 48846 99320 45160 61652 69253 79119 17504 11693 56414 50134 39634 12490 33845 2124 51419 39441 2025 81105 48899 46405 90886 10265 71725 87862 97023 32183 43308 4461 5218 87505 10408 19392 21663 21208 45078 89353 64494 49219 99811 15079 66376 13047 35138 97933 10356 54011 7536 53399 30873 15482 1010 7894 93043 69064 80596 55567 19166 99345 44772 93988 37688 70889 15892 79755 61221 11871 39542 95735 84699 8476 22419 48540 7443 94491 13059 97705 45700 5508 72737 35680 40133 84797 97908 73239 52178 16430 85051 66842 35958 58472 82022 90101 34699 94615 76313 66612 85901 37699 52703 12107 82303 6039 98935 76296 81273 94802 11329 24901 31383 54829 39899 61030 57936 33734 65406 52156 37102 98171 39412 17382 88051 18240 61323 87698 52358 8489 41311 76593 58276 16588 98369 33881 27275 43157 24857 51047 58389 31102 69686 66735 98605 6612 76102 84569 16309 46803 40610 94370 93832 1362 96857 98077 61711 89861 64326 92315 7295 12655 25980 15361 26959 12778 95610 18129 76238 85517 13614 70957 84185 55648 60203 81941 1768 43248 34157 30816 9518 51871 94536 49196 78972 35377 31546 46156 64157 46457 23855 48056 40497 80384 49755 36727 91917 6679 31983 27692 36838 74612 24453 25684 1580 89139 68502 73463 65881 47767 17313 98248 5032 64647 49239 15794 57324 22553 32804 63585 78431 73497 72195 72809 59226 49206 38595 27063 54241 44811 48374 24882 67220 94760 5649 36421 73079 90214 56803 99699 31768 79248 14870 17984 92004 82726 66233 33351 5401 71961 24411 26717 61758 17584 56882 22116 84662 1482 3483 46219 46509 17623 8247 9599 88887 79866 36744 26886 19671 50944 48507 70011 56625 72788 93667 29193 29889 38502 79833 29785 16010 54636 11034 53467 61866 27988 19387 67094 17184 47462 70008 61830 71513 74966 48483 1651 53991 56855 13974 68480 17298 62305 36940 93343 7903 17597 2556 42316 83845 69546 73142 85847 29616 47019 16550 35968 66585 65481 48783 410 99832 76948 56966 48084 40349 1674 59264 85262 16986 21068 22605 656 19331 96073 45528 57798 93371 12838 63001 39891 70612 96238 50417 52687 22066 46045 14910 53975 99779 63923 17979 12034 79076 48576 39805 97948 52740 38672 51150 5582 94681 82214 67362 88890 34756 34407 27272 91780 70259 40770 83539 93792 41766 61292 88574 11418 74758 44289 49870 51683 62007 7281 1501 45064 68045 1637 1972 95363 28055 98532 46750 27342 84279 20697 9584 16755 96869 19306 5449 33360 14873 70861 11763 71245 33740 84992 62476 29724 8176 9723 48752 42139 47866 27170 4643 57979 12539 43107 54289 36045 59813 17178 8318 61260 92684 65792 33820 24230 48564 33401 42675 28672 99282 83797 85988 28504 94572 72382 81683 41386 34853 25849 70648 92279 80629 26706 38614 84238 90019 34286 94392 96254 61038 17302 76468 27435 32800 1582 44207 1385 24336 51600 51496 14541 75349 49168 79884 65136 21601 70679 51489 25417 13726 77719 11725 17763 52127 13429 94295 3439 36233 24380 94566 88237 57214 64781 25717 6484 33059 7698 17918 29240 30760 29906 85144 24042 44645 29078 62570 63705 30730 30730 21458 33164 18173 95832 86263 93397 97483 21077 35686 97711 79437 90307 45474 6461 16459 81614 73917 40804 90815 72148 14914 30516 70504 39521 22148 9661 36693 73879 45513 39957 97530 39226 42390 7878 12027 22887 1375 8137 63498 70762 1215 27702 9611 49085 78322 55395 5942 45536 13409 17911 7763 9199 15629 3757 3894 14112 37951 14466 54831 48938 8069 33 86102 96519 79679 92394 83952 34168 58498 43848 92195 16405 55573 7009 46757 19991 14488 95152 17541 10804 36714 53480 81954 71084 56721 156 72196 44167 90273 46211 71498 18320 97588 82058 66547 50032 69864 24483 84467 18619 43808 12388 72897 54809 84782 62684 36794 47483 60404 11847 55564 32125 74242 36023 48158 39570 74091 71324 20462 55765 92766 68176 14159 30496 88017 58182 79054 17992 85077 58388 15847 66406 70077 80737 37426 78064 38869 32490 79285 80668 76514 5202 31353 3196 66998 69420 60638 90922 59368 16247 64730 13498 5214 28209 99075 75581 71389 32845 27197 29065 22779 56146 96406 81227 92616 45102 16100 41642 13294 59490 4250 47117 66969 89336 99468 89635 80728 47176 38200 30596 58264 72547 36630 60926 41227 66695 63488 1629 95140 94423 37195 74073 90418 76848 94043 94088 48987 69778 99054 46938 23503 93727 39639 75596 50409 88241 82556 47275 84490 80339 63777 33789 18498 68273 87082 8311 51607 69998 39067 29919 95317 68681 46340 42796 85811 29923 41958 88564 35317 48611 35978 85660 55651 20206 39383 16660 42113 54513 81725 36323 4561 78764 68574 38584 28766 70115 65217 38166 96516 92901 26404 12336 25302 10876 35040 97825 59152 19549 7846 29917 5775 95981 19102 95087 22221 8792 36698 86978 60792 5882 76484 42693 65211 97696 18682 31787 66789 70448 49045 74039 3019 53806 57311 29946 21123 71344 58332 10995 47162 69242 92534 22361 18304 95423 76973 10711 87577 78998 90046 2801 26959 53782 62989 29513 83270 79228 82491 52832 64924 2955 13629 32000 60577 83449 75469 26924 63686 83355 36395 55181 61330 87086 40464 56920 89137 10413 93606 76090 86922 13530 80025 93427 76001 78662 61196 50828 37754 63252 3507 89602 70589 47930 50415 65351 32457 3693 86257 77979 37975 6492 77879 83775 60507 58468 41794 22718 14154 34832 17741 29078 66284 74935 64046 74982 80892 44097 99682 91147 75983 58751 4349 21212 45505 79832 79685 37756 41172 57760 51825 30461 93029 6645 50350 51749 61910 90253 7012 59193 9390 52309 99312 39850 22860 26303 15638 519 8678 75882 49501 27538 19643 55420 15244 29822 51238 3728 43043 74367 87759 63125 66439 98193 39336 76711 74748 4496 89043 12966 16439 77188 26890 25954 98080 13514 13909 21399 5157 72393 23665 28090 50871 2814 14328 56117 77215 35364 98236 28300 41176 61629 15793 23018 72328 67251 3361 37582 85575 49886 42322 2794 9573 53111 923 74395 36470 70962 89312 20304 15271 19645 99578 47881 33095 88345 79655 1451 59063 77757 82979 83743 20538 26326 38719 10115 50849 23178 85740 8527 65886 87482 58694 75600 40980 30000 85472 96858 75555 88583 53375 19652 87988 57391 41634 23372 83566 93091 70139 70090 50621 67895 5319 32187 67917 57779 35373 70524 91068 51146 4258 75854 39506 44201 66047 29089 88488 33678 80723 92985 13637 86213 96145 25408 52361 80693 64733 85115 44407 59711 75437 68189 1693 41666 70499 54340 45735 78130 40111 21504 92589 39039 31834 55364 78264 74520 431 65 39898 5682 40419 47419 97155 4012 58277 9390 88093 51704 42552 98514 88646 51153 54782 79962 70825 53093 40099 58993 47024 44822 25252 62926 28166 86282 67938 64912 24462 14478 54930 96997 73300 94216 86947 17052 32736 7925 51792 96811 7829 86388 31514 20900 48136 62112 61665 94390 94784 74735 2513 19898 40319 95103 132 47919 58960 42595 73485 23084 9392 82654 83993 55927 59084 89771 82999 84425 69007 2453 63062 28246 94001 48083 84441 54434 4459 68149 24579 51323 30616 73060 53234 84658 74353 56242 55092 19329 65166 25911 31291 94774 99676 58424 99025 4363 97991 5724 45701 9003 50594 80901 65458 50784 23197 30147 2209 98971 52181 80112 69660 86026 99453 22641 79302 13285 66422 53616 70669 77692 75241 40059 552 94780 72402 45532 36284 3595 89589 90095 90023 43434 36485 98616 97728 34516 83116 80927 93346 56878 87516 27815 57977 70998 403 89513 77345 99087 86473 88353 76077 85407 39720 19098 54452 90213 94475 20736 27297 15148 74736 37376 47752 71133 99462 42311 40665 52587 70501 49084 95451 58926 69707 59818 2262 90480 58717 83665 40788 21386 30161 45589 55491 3471 50800 19640 94180 94984 82225 34524 63086 95503 67858 45492 58495 20405 78159 75760 58817 18005 90783 27364 28381 17094 11723 32884 56902 19332 66523 349 46247 50203 10357 97122 33144 52450 95234 59377 90237 85184 52438 6345 49238 82898 67670 59590 12998 37772 90182 97564 17270 55976 33994 58037 40483 3727 65489 36586 7729 4248 76038 63104 6226 33706 70263 54864 52908 75985 27038 47501 91468 1928 75541 51177 52912 69480 62277 50680 27797 69554 1279 69238 71857 79750 13452 42998 27281 14390 2640 79106 71211 80560 33854 74749 90308 27502 55731 48763 74011 13697 39309 99763 36737 88893 57663 74457 81077 96741 84879 22494 1705 76078 44654 19027 65029 80987 95939 70280 57148 66015 1236 45375 93243 39857 35581 18418 80683 52165 55218 29253 826 78196 25499 49483 13617 82907 18725 23641 81661 69512 40660 560 70717 37967 14985 78893 3895 36831 51249 19752 64830 49969 82129 74604 96703 19027 4067 25874 60220 48397 74816 88379 72612 20374 74545 31476 19427 59126 65430 67973 37391 33465 90037 65273 68484 46560 11721 78282 35240 61490 38482 8478 79694 94280 24288 82946 37653 81517 73547 62915 8680 59331 57356 11171 64350 57280 94060 86883 29854 38604 70505 13512 67198 26477 42947 88590 49406 89260 5727 69697 67147 50694 87471 67595 18722 61749 45609 10156 8785 39792 93197 76531 38025 56790 8786 68943 18662 337 12496 21091 3772 71225 40653 48024 86283 16123 93716 14557 24345 11438 44518 69110 79641 56476 60389 36964 79548 70487 81360 86811 95840 47998 52712 36593 17208 27437 25242 40635 71 8842 83049 79882 55730 62153 27028 47970 50071 9274 66026 84459 35975 40101 77589 69373 71892 71130 20343 59479 95523 43681 38874 1868 89945 78330 41678 19096 65572 1539 11124 82271 38885 41608 16198 30120 20428 19481 21591 767 62203 89694 12873 93413 42656 60680 80069 24266 18521 38302 41196 35252 3820 57461 87998 83119 62462 42806 18262 65906 87890 54175 99401 5975 20582 5950 39580 37356 11972 26058 69965 24163 55732 17303 73711 76962 37500 59049 21021 59576 75169 68179 95631 42969 11734 40916 6626 55197 52629 11975 47238 45256 50776 48336 12232 39998 95705 22706 2929 99193 51526 93167 1873 77686 71059 12192 10561 35679 87959 82215 32437 25458 84768 78351 71089 97296 23135 20303 81985 44154 60899 48050 64636 90198 54682 85975 50210 96379 5904 72449 32134 16549 62486 96654 26568 67283 74465 98083 89888 32657 11404 94321 38378 67716 54662 69539 75242 90233 50951 58024 42618 94163 91830 99707 27531 11159 54901 16452 16097 55096 16749 12836 92222 83084 91469 59446 40841 82561 91022 52954 41062 44486 89645 73727 52410 49446 97769 60706 88430 17098 29991 24426 10279 36565 47935 24568 86559 46489 24266 15508 91382 53326 41209 79758 64125 22773 73390 4156 8759 95571 96296 27305 92086 76846 96319 41416 83608 25500 24335 88531 43669 25575 7784 94343 717 32171 24254 78335 1212 61812 8488 89726 84363 5860 33968 42460 9642 60957 90680 44466 60756 45955 66083 84221 45915 92549 77308 23385 59243 88200 13542 12664 48421 13289 75526 24583 39374 81585 15699 33252 89184 63303 15568 15050 67681 99611 88439 61159 82387 8033 31739 79224 28912 96738 91174 93831 70075 14766 45597 84154 24690 7052 91419 23269 55342 85258 78854 7911 58617 96688 33997 49524 48267 61680 97744 18749 10069 64610 47065 47133 27542 20896 70611 67925 89214 75159 21815 79534 84778 48053 78883 2058 16508 8853 16788 38353 22536 9544 86998 7944 47297 95920 88117 78958 79207 6570 8852 45481 49034 56147 26026 74593 57401 12000 41817 98295 78197 31647 98267 53551 80182 82367 53392 3705 12838 56419 58563 7436 55035 35031 96282 98032 85876 72227 14644 30801 17320 30852 14700 84014 4638 76512 79400 42322 4172 6163 37954 93960 75440 69713 67053 36840 73819 22085 65662 45287 40449 87401 58003 86488 66422 47662 16287 79601 42404 75128 13370 37582 28893 73160 2234 98316 79337 65582 77909 96354 54081 77678 82480 91926 43001 47576 43405 44921 7640 77635 44323 61216 83539 67480 52355 39898 46839 88969 20276 44327 49592 34297 87884 50503 61187 12466 82716 81668 49186 7638 43702 98053 17041 83063 8514 26493 25554 95530 79996 54108 66740 55329 97065 72532 2043 4306 51199 30202 55835 19503 13827 10461 54904 12053 63122 41228 44055 31370 71988 45376 38842 42773 79496 53431 76821 25168 5992 87654 98884 91357 33306 49166 47063 4617 38919 68920 50888 72764 34313 53500 66390 21274 88788 74627 49532 18354 51780 48229 45724 72770 29012 51538 87022 84434 20049 49835 38015 73638 8482 42862 34336 98731 18035 64914 78896 88282 99780 39825 68619 4611 59043 85218 62213 74472 10926 72809 35423 73812 64982 86502 10095 22487 40946 52125 28116 31855 8575 88088 44141 84201 72416 57308 92557 93118 15051 22088 98525 57668 26215 27290 93332 19273 77454 43274 69637 62082 2314 72379 78523 82917 78255 81073 73098 78755 90449 19306 11678 89040 64333 85480 10153 85196 40215 49359 37776 42168 39518 76944 38115 97674 50941 3471 4798 93569 15304 34537 31081 46264 82646 32572 94434 14351 31304 15013 72417 39155 4843 4021 71001 51522 9191 45637 59445 26309 97122 44004 72320 49933 12043 98186 93516 19904 34360 53371 14592 45027 12623 2767 11557 53301 82487 39529 85106 74037 79646 67332 90145 13742 9804 78634 44340 12910 65261 57965 87318 27117 13537 39662 46002 35947 97853 80670 15261 53296 87305 92290 53387 92882 63994 99743 18153 99695 49845 76829 12180 65385 64396 25083 72289 39392 39976 93139 27559 95687 29010 90554 16608 86850 83496 54230 80369 20959 81174 42595 47348 87901 33840 82304 38935 97858 3884 35457 31606 57901 35139 70479 91398 5463 90382 78019 10828 11883 48162 60077 83544 4122 89782 91935 77254 75164 15027 6594 68491 5325 67952 77948 27465 15040 71515 3737 41244 40707 92170 55685 41022 45127 23979 15141 81690 78184 23858 43586 15717 17516 20018 9298 12531 97749 43365 54128 29395 23713 29022 32052 88147 25964 24245 90035 76908 32015 13585 20778 87187 23610 21125 50585 50728 99957 45472 4330 48160 76136 39408 14294 39365 18097 90592 89129 65300 56792 61565 66447 70050 28876 34280 31708 58263 40350 16437 41668 80887 57128 67091 83742 19254 58036 70326 95291 56420 88967 31662 67453 37025 26167 98439 45567 92426 50149 21086 32230 11274 1720 17448 51265 84655 70034 50270 51768 55371 26445 26767 6044 30989 99741 96215 93357 1571 25886 8914 13357 21538 90484 8628 82695 10225 40493 17686 17963 68577 22792 89529 17598 80616 50893 99032 41579 66047 43028 82591 27425 67950 74819 10223 70342 57920 85770 36524 74058 44806 52365 77572 24354 27407 34289 95892 40933 97882 78383 20504 40731 55109 62955 87400 35305 25683 38767 36029 41298 11846 21761 7083 59754 78527 60517 98572 77219 23914 49725 62540 80439 92222 896 64187 73964 75279 51323 67245 36496 25589 88020 83185 1227 61489 25436 34955 18393 69334 19383 6186 95011 10801 43296 48878 27751 36267 92632 84220 49722 82098 52094 14249 34495 85687 35760 52531 36955 59576 82524 51381 72155 52703 80000 1824 46121 58703 29357 88398 97375 56900 16805 56368 91536 20640 5117 83973 55411 68340 33996 61272 49174 73136 67524 41451 56485 1992 60700 34041 67899 99 19959 11147 30794 50151 84838 99917 60382 49166 72553 33383 82874 54803 81313 72452 77637 47457 73423 72549 29689 2127 6018 75366 86916 60387 80573 15589 77352 16496 58459 11558 61170 43319 89889 73196 17255 95174 99487 36127 85273 8800 58518 99054 92506 96601 36997 83030 96342 61849 95328 55439 99419 1206 65096 31376 14029 65672 81379 62804 76819 13051 86886 590 9788 18143 62035 3315 71427 7180 1518 79898 69491 63195 79142 46684 9275 86360 81448 36317 75206 44616 33208 8235 88473 68258 91565 21058 63401 8282 41 95666 74132 72962 34008 96691 8405 55417 6679 89556 83873 82358 46147 90853 77625 54135 63603 55148 32629 91381 79308 56090 87367 94706 64935 15460 73569 79335 33373 96627 65179 17249 5844 22682 23130 52889 62473 31923 73283 70768 98482 80495 5175 80173 34845 67674 87174 53767 19330 61470 51410 52483 81068 79317 85735 38526 43883 94490 23864 90405 43531 6012 81102 38932 7497 8065 40418 6714 14930 4394 81774 79206 71188 34008 32123 45780 36083 23048 55947 43112 3285 53661 39898 74844 64856 2201 31940 97992 35889 77151 30228 56016 15918 59649 15752 29013 71241 2781 72371 39916 1078 40338 98242 32122 22773 94571 42506 33567 59862 47020 25020 99707 64860 18203 90657 12295 17149 5336 24680 58166 47695 24983 59802 45966 56992 10108 98062 75248 87705 52726 44640 37667 46788 62658 75824 73446 13115 15676 27401 10669 14523 90058 73671 44829 72548 71400 25927 39888 66372 37592 62684 22152 99791 31884 89157 14323 92221 73296 39939 43795 9644 49181 93197 90944 88557 67694 87450 54153 19638 37690 78407 86114 15996 65361 50041 88329 94332 71546 28805 24359 27536 22689 9838 94208 13573 63963 83467 97079 62195 83025 93433 96639 99878 25924 64328 27901 67427 84081 362 47291 46297 29305 74007 3727 98125 73350 81864 659 86543 75559 76282 90263 98110 16425 15229 88287 56856 29551 35551 69555 93028 96510 51388 54921 96105 50808 68794 1740 60596 36741 1184 96038 11694 72157 67620 81157 65603 90179 3665 53422 5649 90870 90364 72506 42062 77441 33551 12990 1640 16266 36 92386 4707 60466 27246 57393 97035 77798 1971 7134 6597 16905 67923 49460 96108 3687 15986 10289 74992 69585 48238 91637 72796 60118 79210 94606 62745 25943 90298 83569 86796 29625 58047 5551 66663 40448 34671 92666 42003 83692 12745 73337 83551 34994 13464 48847 18686 96429 13503 51754 19546 76960 12345 99048 53619 31494 51565 37231 96254 32954 77670 13349 16143 30535 84995 31139 20771 41232 60934 19189 83704 40108 48657 91373 73608 48005 30045 2044 350 48213 42651 0 46165 74178 45377 99200 29524 12978 73365 97211 68280 97730 74444 27003 13487 2897 92271 72474 62218 77751 20288 29428 26556 87027 89267 14287 45171 76499 74974 20907 46007 19694 30529 72748 70807 77837 52076 65902 50100 3471 14055 74092 73716 52344 24994 84628 82393 90894 66294 85304 60334 82259 94361 41114 12274 74570 36126 32895 37561 30507 26874 31516 66794 46743 35208 16647 63653 74955 95038 20855 64778 21656 49695 95534 53051 4720 45058 18853 56037 56893 99978 18572 37244 1218 47978 59442 97705 46606 32559 78293 33813 90727 81351 13326 34396 57077 46118 67541 34827 32953 28406 2895 47548 61301 74864 48316 26974 29855 79541 9211 77588 79393 32570 38925 84687 9568 89275 99767 67222 74771 80120 46053 88546 17853 97968 61161 77805 50070 5904 71487 96616 79775 60866 18870 47946 18272 11610 64 34088 78790 94883 43658 96170 74048 65573 82992 4299 64199 71801 33959 14908 37391 77599 12042 42794 35065 92353 97754 39669 29011 82255 31903 71379 55501 55213 97098 59671 93481 24061 1951 58109 68485 57381 6301 80012 83457 56850 17968 58781 31129 79668 71190 11226 78486 92863 67416 6547 9961 93892 53510 24138 48786 66418 84875 92303 65089 25984 16151 7129 96262 32602 83285 95938 587 80715 57699 22009 25797 22166 22004 2008 61909 29972 28811 23578 50149 92966 37670 22516 40267 69263 96539 73118 4052 24799 73887 41679 68288 49248 93190 8072 34509 80294 49742 56368 96246 96844 8793 4287 34457 97636 94951 99482 33576 42633 10076 78205 30438 44546 38452 54174 22394 51098 39706 16664 87570 54067 2866 11038 90262 4428 56429 82225 92979 26468 30106 27690 65167 8974 79606 26632 5176 32823 34216 37410 9566 82460 61771 11218 20009 85941 7373 10254 76686 69499 97475 16560 13713 1607 54770 79679 39843 48239 83891 84577 75537 94761 68576 86729 48890 71206 56261 52720 19577 99030 63902 69376 52339 11438 74272 4936 89582 72712 76529 22367 46999 39465 39828 81476 87568 87420 17491 69331 47509 36026 13692 2584 63684 27053 33546 28565 39648 85385 70758 26006 7011 37148 95092 22659 61108 13888 18887 28920 21548 44723 64626 99928 34826 83926 16246 69213 4513 2673 36062 57973 9544 60682 51263 13103 37604 21451 13768 96846 65753 5072 17241 90477 64625 29445 78388 27190 50580 78318 99648 5676 36272 17570 75943 88655 41568 78330 18667 20135 20094 29312 8784 2298 26059 25627 70488 17970 74231 3419 97270 51389 29578 76019 45663 2496 37608 12389 62897 97948 94679 55023 48572 87949 92586 55940 74750 62722 26265 52236 24350 59335 23571 84717 96449 73050 94323 9029 44350 21711 57188 77280 43972 65987 21831 73570 92145 29638 45178 43335 90829 5200 11319 15649 64858 46279 52868 777 12618 85998 45559 38990 93371 60344 74772 69572 72847 99682 25175 35811 74000 59088 56383 2846 36751 90105 16385 20522 7466 33826 7606 62218 44965 84252 2089 78080 25863 42076 87809 24164 16381 67367 96300 47878 27012 3357 96017 45876 36644 11707 83872 78920 16516 4371 99860 40412 91076 70447 37745 28554 26119 42443 8669 73607 34180 70397 9419 23210 45354 60350 93219 36249 57242 86484 73904 8637 24890 97302 88259 31967 79591 85131 3148 3350 48283 947 84921 19297 2960 32245 87725 35385 69090 72324 33053 96309 58774 63333 54637 64007 17816 34228 84197 16465 49418 63364 11966 30979 5082 99646 42618 15465 7816 3802 85649 17556 55257 21889 90429 14615 65038 49669 16905 76608 56296 27101 90960 19264 68271 95219 14351 48862 90242 83936 13674 48875 94665 3571 194 97737 6244 52690 66223 87683 18576 57215 98073 51387 65058 22119 72853 43166 68967 25835 38 76701 55609 86716 66472 35060 8191 58724 30266 83051 36083 70152 67594 55057 17013 81552 74963 41693 85736 35323 68657 7011 83314 69271 84389 76660 59765 31866 18637 18759 13003 78708 90725 61830 13765 35495 77836 93339 64891 30166 78261 38972 87239 54643 2371 51892 82974 90267 88172 3013 51079 53288 37358 37373 253 56226 95619 32834 44770 19471 36083 26717 91286 44640 30001 26197 60724 25913 2781 21751 5999 91044 13012 67174 37902 45058 54097 23479 40980 27778 78264 61095 59166 8758 54057 54256 76043 54205 9338 15534 36154 86580 37644 88120 25343 79470 7780 61470 24626 81243 75666 42144 38314 97667 37381 53765 6735 7308 54271 51833 27445 51051 63691 35158 7967 65574 92108 29072 94514 23782 49958 30566 63198 26160 37496 17537 27751 58826 65540 71122 16578 89116 92914 30050 70611 98767 88958 24942 19385 65124 39209 2436 7745 54604 88074 93899 98328 73888 39731 95446 58835 41092 45119 37311 26027 44136 70038 87873 74604 95171 78602 56311 44049 1061 68930 79009 66528 844 83509 17831 10181 70260 98322 28255 99559 42494 62801 35499 84575 71483 24698 40572 44987 6033 25611 6518 82882 55188 84411 11523 10710 25728 60217 96558 88650 8348 70505 22580 90748 27072 81151 90690 30928 43121 10163 52337 25845 12922 26138 57588 59523 17877 55094 34280 4964 40216 89318 70789 87489 14078 83182 83834 28756 50005 44080 86018 52989 47232 38165 80975 33825 69725 50127 30812 41134 5406 14856 44451 53484 5305 35285 13444 66078 76700 68253 64847 77295 36560 60819 7541 73287 41103 64231 63417 21595 90912 16767 25339 18376 40702 66678 61175 87165 68504 99849 24502 45944 11849 66665 11306 97341 91361 76933 56096 9717 59532 40783 85690 62451 98454 79504 86695 81629 29063 12973 14052 99100 98533 6878 54569 84677 90912 68147 19226 44422 9233 68084 46160 87879 79774 34159 42363 45366 6042 48208 88200 94743 43830 37103 32607 73019 21087 78405 78505 39902 62777 49405 87299 54974 31864 77826 44434 55733 70656 52318 1261 23102 52592 84017 86244 54123 50705 92184 12613 49085 54638 457 69830 24026 64645 13333 36625 7434 69860 43422 26704 69758 17939 72111 3996 52044 96981 29515 49022 3454 66177 62259 49150 76916 53503 84446 28226 60807 57702 31303 18622 88049 60170 79748 40379 30684 29139 83916 11728 118 47858 58350 88645 83221 60691 45112 68039 65032 9551 56123 97284 41987 88945 95020 70496 56262 45095 18453 65958 52594 15823 43730 29792 6193 85672 37157 559 7609 53025 18429 1698 32348 96671 24395 59446 59264 9969 68543 36479 53826 53676 46976 50090 12748 35936 86278 2628 9746 53515 12468 10043 41612 18613 16428 44789 66947 17222 63760 2952 31998 83972 2130 65379 52060 60852 9874 27859 39543 67369 63892 42295 10594 21391 32417 71298 81489 29754 14818 15528 62125 72658 21683 6821 69149 52102 72318 13382 97522 72621 64718 69941 56902 63422 94273 59704 22263 15924 79236 71503 49778 87311 45968 63267 42443 58583 49796 49956 91414 83481 80096 16412 18533 23405 34648 31533 29217 75298 54594 99563 51201 25056 97884 71092 89375 65358 93108 74810 92682 17697 48646 66643 51536 57347 62171 22284 14899 91230 50212 61404 55888 91099 66659 2433 51596 86715 76122 22526 67892 82858 93705 21474 66253 11852 99550 41658 22642 22988 72669 46270 62658 90314 71855 64484 41801 30866 31146 85978 59903 1418 26747 46024 44773 42770 72193 15910 65376 33440 78813 13672 37275 82178 61881 62391 93768 11066 29512 28312 54216 27295 22730 75963 23029 46423 43141 23341 68050 78309 10742 15549 92033 78623 99002 20856 15301 85703 63095 5374 44178 87370 86301 84999 62442 69928 28004 44071 90871 55168 29602 84071 97124 38309 58340 87247 58477 37527 20332 65427 8363 26977 10224 77504 38448 65475 31339 56066 72898 6850 99705 21524 11701 37700 52911 79506 13209 53705 89349 5477 85548 43359 55026 15252 84345 88463 89540 61925 34773 61305 30164 44211 76444 79444 92814 44307 13683 95126 38352 93514 75875 8521 22479 65160 41894 94732 3183 61941 59641 1944 28935 28086 74659 53204 12564 23555 50572 79346 42671 95273 43075 68234 77929 79576 1782 88678 42540 51368 74432 52547 28221 14233 26472 10391 91128 24429 9095 61712 63845 78650 20857 29714 33449 25875 80609 32090 31123 22494 58849 66806 90192 78673 56834 19085 85402 48622 96624 79256 6734 41772 40611 91080 78692 4738 84196 7664 26117 40445 79399 5794 30970 32961 51520 10167 35555 91337 9698 74285 72901 56736 36873 73598 58826 9333 55662 12083 19576 1233 49333 6646 69640 16422 72209 79414 60096 97648 59058 66168 93472 81236 58472 11221 57218 98622 36627 88987 89748 53713 17871 46869 68834 64476 40197 31145 78927 55283 57504 63652 60717 60676 75670 15607 79523 45551 11087 29803 81367 41933 88506 58305 60720 75989 75654 76191 51880 58827 36903 49859 85602 78082 81131 77700 43082 34788 49240 96938 31672 11646 24819 93559 93234 75356 77715 68656 94381 20767 63554 42823 8411 4552 66359 9499 45569 79770 89695 60161 97356 2784 7606 51965 83204 73343 15252 2394 59007 2622 80254 99868 77051 77158 72478 71046 20581 66303 27750 20458 66013 90637 12396 37849 97084 82502 75545 92381 24622 56555 45689 89508 77236 627 45100 99258 39113 80578 37206 27318 92068 65080 97556 99906 66902 40391 58286 4998 11264 59588 42455 26673 33346 49600 34983 7639 96860 64331 41125 63803 15836 61858 55752 74678 62029 66277 77276 91381 54635 23917 85217 31734 37924 41057 31951 30034 92541 78509 36330 91206 40748 50893 24921 26182 84109 88449 4010 8779 97929 39703 22517 52493 7690 50314 87510 2857 7466 18044 40337 10603 38160 54532 66701 61891 83326 33096 37344 12978 57480 95498 86247 36651 73237 59437 24718 9047 89244 83910 59951 52486 97638 68298 94212 2891 96960 65773 64218 27092 70830 48405 30282 10319 17643 7977 6006 91241 38730 52248 95230 39806 47017 98562 94289 52518 88417 49956 96589 9321 33710 1845 22857 10125 97464 34577 5023 37460 15956 83461 32177 41280 63663 62561 22677 44280 14231 11135 18294 20360 31460 25729 33878 84465 35745 42587 88303 37749 94440 14191 10004 96632 78206 587 888 65822 71570 14889 45682 31893 60447 31441 70780 90467 94394 72171 17057 9416 36438 92185 2094 25718 96325 67706 50674 93840 84219 42173 77908 82868 87019 32835 8592 5171 41819 43574 7389 81200 96960 81582 412 26475 50126 5026 8798 86325 90048 2329 79289 8451 60396 62823 62773 91199 49108 29718 25906 90637 40037 29133 36841 1880 20403 7775 42549 796 92154 96197 15826 13549 65351 40924 80894 64058 28819 10149 657 46502 381 62383 27459 66385 82038 8256 93389 28272 12764 81665 84326 60632 69204 67832 12046 68931 31665 47135 6448 69926 15232 41999 31927 65048 52351 50421 86104 76702 16280 11936 564 7127 2580 49798 57953 98697 36373 33567 32046 26633 25293 2812 64212 85005 83543 17305 90604 10687 56152 28772 57974 23623 37895 54012 71809 87419 5075 64308 56180 64215 2340 18499 43458 34409 27525 95508 58798 36160 64499 99600 61453 50885 65650 35854 87319 13795 26426 94847 68524 43959 55515 3831 13495 24774 77359 14539 25336 28316 94111 82697 179 4144 45944 74269 11949 15144 4878 94376 33071 86802 88574 77878 15868 33523 37092 68497 83351 61599 44808 47071 96738 30651 42551 91869 12705 67432 26153 46546 33155 65704 82748 14980 35151 16791 34705 18011 99221 88900 38003 52032 29366 69075 69470 96467 55430 5905 52909 11115 22959 68904 90139 71789 19652 84551 92519 79194 75382 40506 87823 72444 89805 37346 47308 11628 59098 11288 77682 58719 52615 63553 48376 17978 20013 2290 14798 35611 45205 77883 41729 98162 39393 94564 99386 67082 44766 62342 1843 40108 58358 15491 4940 27915 6261 56647 30226 23688 50602 72709 65977 77639 53671 7838 16042 70433 32487 55292 24718 52866 53792 13453 5729 22143 10588 88353 43519 3522 16028 65062 70044 96177 77201 75949 60785 27222 44732 30316 56303 22798 94449 41802 34621 9666 3885 58106 61518 24229 78901 58393 47398 22428 92017 38513 3694 72161 48388 6957 5149 23312 24112 10553 34947 86673 97938 2376 66575 1008 70256 57818 95937 49656 37383 68375 31082 35789 34927 67811 51194 71923 17942 78105 8485 1853 86835 88727 70285 2224 30874 39721 89744 833 38584 20233 47736 49715 85381 13451 82807 70599 4022 53485 42273 71822 47834 94151 41869 27676 95405 69265 67553 68293 36122 96196 23639 49456 88869 17113 29524 92548 97919 81384 31986 90137 75588 77633 84155 94602 65406 53829 44249 47460 79723 29766 68566 13800 61118 24933 60995 59198 9849 42485 85866 9407 78632 43577 1706 19027 48065 31243 38559 54315 61247 4204 92214 27159 28411 36693 9765 21592 64164 69532 56928 28053 58775 86216 55437 32009 45353 32888 9918 32473 55008 65880 63095 63121 72148 96699 98118 92612 16933 84343 30939 90717 82566 77231 81302 34775 45315 32530 97799 48981 14834 99084 93792 17323 32079 29305 32377 12637 77231 7974 34506 90497 36580 90326 31279 4003 91128 51818 48010 63360 73865 25224 30784 80304 14887 18406 73534 92992 23009 26837 64840 80458 78706 79064 18675 20805 20458 52290 70237 94035 91271 38796 47114 62243 46468 81681 67180 86147 86927 30018 80776 73041 20530 27729 94755 79259 41663 67991 89797 14013 66277 93363 14447 73297 44161 4123 7092 18808 44390 43135 33939 80599 67008 63436 61231 86896 69940 52233 80386 42181 67064 96518 70023 71642 6158 5940 6095 83923 36667 8082 84559 52224 30643 26909 67276 40761 79480 21731 55554 84670 21705 67783 98103 99942 41387 23900 70478 7738 30614 86859 13468 16678 6496 94845 278 66792 18463 83462 98916 51533 78956 41467 40365 28911 78537 8655 24414 74460 33550 28964 60238 86608 48906 5834 89443 25070 9621 80697 90418 53678 80945 87776 73114 73976 47305 28522 29863 84870 37721 18069 83547 29815 50474 73128 27314 35737 73538 29368 98355 61199 84907 96149 95045 9374 84028 58406 19969 32320 34117 91321 87515 76877 7653 32979 8757 81145 75483 38172 5636 84901 96317 348 21192 92947 82015 48405 52676 36129 10545 98777 18452 92497 89816 94294 56123 47381 30656 2181 98696 46289 35376 64397 86411 57137 76462 81989 79962 79797 3053 42155 33005 23374 38660 28831 59484 10824 20789 17508 54066 97837 77431 46604 66613 28416 26555 87138 56127 17532 48928 13416 72167 19454 34726 6418 14353 9223 90014 28792 15429 37225 87977 46203 77788 66669 72678 71540 67881 56116 92642 14557 17072 21964 1404 86674 13015 69543 33047 87381 55051 46397 74012 85636 51117 33395 67420 15199 89167 98218 58255 47895 98681 72105 47679 51207 32651 25838 43956 57373 32117 96590 97643 60775 41631 25111 17536 9084 19099 89254 27626 45375 22183 36171 67131 95292 5884 3317 60872 33579 53257 16993 27401 66887 22345 23639 32971 59557 95016 96480 71691 86108 88145 73908 24183 34049 33643 15639 6202 1983 1865 80676 10922 64287 44557 6112 1358 14641 26779 94935 99756 3960 42261 1746 45510 87508 4091 63870 76452 49088 98354 22960 52332 97835 27985 17290 58550 11816 26859 57969 17548 28843 44102 9443 25965 9569 43065 3179 53698 5258 37273 57599 714 87724 26094 132 57477 68647 78941 12994 26188 65303 34550 10985 90576 74615 78899 37415 85198 47878 95690 90130 8641 39486 82405 88850 73536 94320 83641 90083 20490 82124 11443 74367 62821 56639 16538 92981 8817 26913 42985 44428 76750 48348 80927 5444 64491 6988 3996 28836 19635 25359 47780 35185 16020 99141 44236 31472 89467 52245 72789 52477 57869 67948 41757 34149 18876 16451 67120 97337 59928 5933 16154 47194 16491 9626 65368 65189 39179 89758 96901 46909 20673 93989 94843 3544 41939 94049 29156 85959 73447 36268 81746 66721 31570 68973 23147 15642 51101 16636 58072 56562 10587 88415 23006 97354 50891 37215 16723 64492 2247 50703 74342 47085 69599 56878 4062 26875 84042 27762 85014 94253 82173 69628 59516 3262 81193 50949 94840 96867 95551 68223 53297 88994 82001 28625 34879 84556 22873 51393 5344 42962 22612 35566 31993 3041 75527 40823 7251 52938 29457 54102 94859 17163 67028 70784 33154 98163 20155 14951 59715 32067 50267 50863 42234 12478 91230 85310 71319 65490 82092 73039 53189 60272 63511 3537 94537 65388 14780 15758 5337 81108 75852 42411 42497 80439 42287 8280 15020 28726 55604 28875 87525 9587 21523 58639 36326 67833 83949 3403 34844 14083 49183 29332 64403 70248 67414 5690 65960 83483 7521 53087 77091 81668 70038 53519 4832 2862 93171 89899 87611 53792 82696 43480 18118 49850 6797 34101 66523 5004 81519 69298 81457 74014 93727 9041 73252 77843 6808 48649 5359 25697 88518 81058 38736 3928 50629 59662 52842 18926 62955 70244 66411 30433 52265 1061 10472 5216 41617 93057 18666 79792 64648 65179 32847 51582 61262 73976 89282 33282 44755 835 1915 68513 92357 90601 80717 65456 26970 22317 65458 73999 92059 44863 86108 55009 79628 77165 85002 70171 76212 32313 10121 58511 88164 75821 25633 69333 18868 54313 83713 4172 34165 15246 77736 53106 42358 68971 87005 99167 2696 39616 16563 87147 64903 83843 67157 41670 64931 14782 87587 71005 92272 80866 12256 17720 3771 12759 41323 11301 44343 75843 23888 78948 58418 80317 51872 94776 58584 3436 85028 14115 24246 27205 63047 95635 68926 72046 94296 87073 32973 53634 6052 38289 70810 7503 1727 41810 6755 55407 10727 35653 80804 90078 3060 210 66536 46838 20507 18455 73461 7819 61899 36610 81569 35002 62876 56556 42670 11130 23823 65629 70326 91690 75774 316 27783 60018 10556 85007 14651 57156 1739 32319 22775 12037 70851 97237 21331 64740 97117 45314 86348 39218 3961 43188 34714 96708 6176 19787 97057 34270 60767 5724 1368 42581 67886 66110 94362 63043 17700 31257 85917 54566 35248 70343 55335 89017 72038 45824 98511 57699 24250 41730 96825 2943 3345 69131 11255 26034 94160 61135 59784 38216 20421 198 62454 78559 66179 29914 29785 1191 72895 81800 63097 55815 22208 61903 22595 75292 31655 70032 57558 31440 56051 8315 27095 91942 72828 30109 14437 50562 48171 77752 17574 5311 63217 29683 17 55934 41496 17039 89901 38226 62311 67105 26812 85467 7096 92809 65417 87312 72920 60290 25332 66930 99596 27112 64408 40222 56066 84280 91678 10887 38842 30856 77747 48778 55439 47265 79831 57925 82045 89131 40897 77686 79277 96991 51824 65663 48120 521 86486 11959 12434 10368 87807 734 97652 24468 82344 83249 34521 36203 5228 99 54433 60474 96958 83445 10148 56413 44927 66992 4152 62787 18445 45614 7595 24471 48695 65667 45956 98429 49909 87076 57696 74151 41117 84125 51074 9185 4220 26208 47952 32676 76847 90030 72850 36556 33621 79778 76777 1424 18814 77296 10181 77492 57067 972 87069 48867 3028 6788 3486 9367 36235 63257 23155 35841 39505 92916 32073 46138 82821 35484 55188 46156 46564 64226 22202 67288 20870 87408 95375 77586 84207 12044 54268 68939 82501 1030 3569 42505 82762 19540 56552 89235 60938 19829 99384 1224 78040 28224 91050 7603 86168 66500 13986 10368 81296 89160 60610 616 46325 64446 96514 23551 98686 77980 21206 99681 91447 6351 17152 70716 79875 24555 95782 55979 4905 23597 32926 4666 72617 90675 36879 11761 25676 75563 11556 42929 84149 75240 4981 7558 84565 95565 80867 45072 65186 79441 15242 15994 61766 53468 36556 80242 49121 50868 43903 39020 45765 62426 99460 16299 11834 14622 5843 4911 76452 5558 82939 75992 25306 15074 40525 25047 6611 37081 55739 19960 14797 38010 89694 75352 58366 54898 75857 13932 57547 34261 39740 45051 98350 92859 19060 39020 67254 53149 25645 35778 3144 33848 44419 56938 25914 48111 66420 4382 47884 82111 94907 12848 20982 68751 28308 51603 76538 96744 80936 54829 75087 65669 51470 87397 29145 39270 64625 85829 65250 32010 75327 33376 74215 13505 91165 8120 37560 21672 18982 74125 79946 98571 1827 93076 91593 85047 98998 49863 77975 22995 52700 63648 69590 79349 92730 37287 3896 30937 25403 35302 50803 74369 39666 13177 4890 62586 75146 55129 39402 10674 46103 6759 8802 17858 40686 84390 55595 91685 10322 35528 99459 35022 51082 18470 55958 40072 14183 19969 89806 77714 70046 93915 78456 10888 50475 28025 32226 95536 43635 77633 77990 53527 68529 55480 4680 37521 40869 40393 17632 59235 33976 74224 59639 87095 31212 45824 1975 58304 64044 66577 45598 23225 73973 83586 50073 40525 2994 98700 74903 12155 4900 37699 8529 55870 42067 82794 95963 18999 10043 75420 53255 66330 14485 90042 55548 17840 15277 19836 27688 79411 32161 99816 57617 10153 20691 56483 78421 86122 80557 46626 59540 79570 66486 30539 64823 64572 9892 48611 34099 63398 74838 79012 94741 63043 98396 83614 56268 12642 41363 2150 2542 6929 58493 95831 71281 30039 68618 47031 55149 92774 47438 63565 68652 78452 5519 19849 26684 12790 28055 60685 39822 88414 65267 97857 19449 76556 61680 45504 8980 54285 9636 31679 72040 41824 2893 49324 83402 8454 74513 39285 25390 17058 74217 73826 19431 46794 67583 77155 23799 12168 62855 29181 27419 56413 65852 32433 86285 17886 52912 76653 28767 78179 47368 89694 47752 52386 67080 37682 15125 26024 53643 38627 66799 51656 56466 63155 99524 20265 7860 73779 48935 36776 90818 82410 72533 38526 74841 5739 47063 43029 69817 69780 95797 13700 35208 75422 30446 27653 80431 89196 32771 18735 79349 32590 93941 18100 14066 14656 7173 96852 39933 15651 67890 81264 85456 48528 21667 6265 44994 82346 44763 49488 14473 86042 12772 8267 44762 93434 62687 72808 92015 74317 63142 68336 44396 339 67128 15844 43129 95824 52807 81320 92127 63956 89682 63751 40485 68867 75503 40669 47488 87857 13862 33334 20635 85804 30721 94138 60738 54595 27297 77963 72577 85050 98404 56809 87891 14596 15831 11647 16352 37418 27470 72288 81483 81871 85385 20482 34176 48109 25280 69376 23749 86415 49849 36305 44280 56728 66252 94537 78994 70448 79453 1439 62689 50298 59145 77601 14163 82553 7323 17228 2700 98385 14446 15705 71376 39243 56712 57170 5512 58144 31708 17389 70490 53805 57864 48471 46009 60244 92738 54180 25498 73076 3362 73002 72744 44905 94753 8398 45720 14202 76155 50908 90798 13181 73291 90069 54043 20505 19936 82121 56237 45472 83262 67765 82918 65619 23475 98926 65262 99003 8593 67135 20393 31534 96207 27132 63002 59801 35688 8147 37550 32308 79490 62168 88581 18968 9822 20573 45318 18210 92181 75392 43486 8467 75609 48059 47815 3784 24328 65330 70266 20858 80889 55722 27319 24899 42990 46523 22158 80642 40295 62977 40608 4773 7126 67874 39047 60854 80989 80520 16793 72552 12257 94243 25464 23764 53085 95130 2392 75072 67682 96011 2349 73071 74857 23000 5652 66698 13047 80351 1404 23793 49010 26804 57638 43893 60658 77335 41841 39080 59505 42753 4899 28664 95207 8362 53003 13406 18480 11640 65927 52330 32959 88005 51338 47913 69507 16676 7334 4088 98334 44717 92755 12075 6008 5460 43192 63810 96415 83942 71142 95494 61695 23705 72982 35709 50663 51779 35212 81349 84761 29990 80160 60887 12032 9183 34095 92184 84477 60081 77094 5406 82872 11793 20285 31221 27307 21127 38916 56828 96969 50606 70752 7277 91601 95712 84259 70069 24861 19565 14208 14979 11399 35233 88297 75546 38350 56822 7549 17847 67074 45017 95253 66027 14569 29423 25155 55188 61727 10916 79689 14743 41006 55859 69113 23779 36004 75140 67428 86653 19059 93717 98698 36813 83513 93311 69658 8873 83348 44139 63250 9243 34163 24759 43469 56570 47540 36856 7879 63322 87379 6258 55206 89000 34778 41529 69681 18495 70233 73769 19010 16554 8472 98436 75564 42635 48870 60973 87029 22109 83952 22673 83444 80675 21380 83499 95705 40966 18593 65794 94457 9408 89081 57112 52691 37645 44526 97530 61649 67251 39182 84236 25662 81253 83751 65817 79761 97899 62764 51791 9140 1316 5888 2830 2788 90258 64555 40725 24892 26354 60577 50471 18443 62396 23186 69660 78032 77727 92859 90303 26951 97489 10891 52228 61121 14736 85061 81089 82612 67682 13674 92909 48541 55182 91543 13467 36392 48220 22416 41035 66397 63646 92866 72577 58322 5436 31692 65935 47998 14241 60662 43674 80382 45169 49365 2142 9773 52201 80770 1409 29317 34473 99642 50338 54750 93864 71103 21611 57916 94595 78694 96266 16708 44482 68316 44419 94060 83082 50270 90129 51614 80814 77547 48240 42637 61890 52641 57696 69892 70475 31271 39291 28963 51855 3544 40200 22169 68731 23288 75406 60950 98110 51722 869 44904 23794 35594 53701 4194 40636 59797 3451 58671 21076 45104 80398 67922 36342 42717 91378 36525 50371 16878 42741 21755 32577 16841 71276 19670 78994 40367 74448 45799 96474 66131 87683 55384 45023 77533 53557 17456 34967 89803 75626 69276 5550 94298 86250 99362 53526 38979 52060 37236 17245 17575 45728 40975 64056 87635 93159 69913 25202 91067 49881 27259 99520 85456 55321 1758 32011 22456 91941 57248 29334 57355 65042 73861 55096 27203 11168 19977 7073 38113 91287 18796 60540 2803 21739 11834 97689 93714 52920 16656 26753 74124 10824 41991 81262 53832 71212 50563 14227 14306 15842 99265 47821 71900 79215 25902 79951 26974 97414 75148 86775 6095 90448 56763 11360 37835 49348 1737 45899 85939 48725 88605 99969 54841 65604 27572 82454 26762 31348 67204 81698 5715 16840 72754 55059 27324 9618 77664 54157 37953 27082 34849 52604 78348 29860 63807 85388 21070 75430 34661 86312 66430 52922 44969 82906 70980 81513 83334 92370 77741 99921 38832 69311 59217 72680 50868 78165 54708 50655 66388 24972 12963 22240 40248 46610 78898 46038 22597 26630 46837 11106 14932 60076 31773 46936 72428 44234 14032 37139 47031 93204 8379 89885 39028 40574 16176 64103 31387 58110 45283 44190 75463 72216 88763 94243 28204 67190 63926 93792 96630 39461 37168 98379 30721 90030 22825 18535 19740 5862 62713 23023 20270 89472 15702 74864 75218 29488 25203 53515 46314 40423 42871 61715 64541 47258 61124 61902 51144 47559 1017 49090 33123 36644 42956 24554 89891 93674 67199 49541 55748 91442 96092 42809 63595 67583 45378 73035 5582 82451 97607 94273 15445 81004 60003 41094 72508 1659 30899 11870 10727 87124 18683 13393 19080 53437 15538 95432 78520 96155 88696 23965 23588 99351 2758 31723 17608 48811 8416 70037 16278 95868 13247 55389 23384 62050 11907 57450 76049 26796 63220 95766 35303 43140 76994 82925 60236 67642 55618 19350 93674 70542 30098 8548 67051 53311 1562 96648 75673 74219 92879 60983 18759 66636 97597 35332 85873 89985 7213 61625 53404 46923 2538 49260 5313 86765 42273 6708 83986 96966 40374 91811 21699 75728 57326 23283 33725 6820 59454 94893 75434 5836 23750 61800 11727 33483 5258 95558 33814 34195 19501 53259 16731 13071 27047 45266 22671 20038 4967 26814 6921 1227 82358 65871 97191 379 77812 52061 40103 37934 89135 77421 5950 59625 46857 99541 95854 39635 39506 74472 96922 29044 83378 24856 21906 85574 50576 25594 72881 35525 94518 27374 17769 8418 17615 19924 31819 505 90292 15621 85165 16014 10979 61855 44930 1924 97621 89954 95887 78377 31164 46225 30619 96389 13580 46119 67665 2328 38765 40093 5293 19339 67511 61521 86574 57891 63029 57915 25846 34382 71477 24367 75881 66088 85464 92565 31483 90763 71370 38720 2609 86489 63486 71068 74428 2311 66334 86979 52288 7206 85763 83714 71986 33617 66973 27389 22792 47500 55363 91741 23090 96610 19083 32312 66299 59416 11242 72208 91271 49585 67042 59632 51111 15224 5256 47977 24140 36155 63494 91869 56181 80618 3393 24362 90274 59453 73709 57717 56118 75322 95306 4965 67766 25430 8567 52012 63031 86781 50088 80264 68512 15699 4637 25909 75445 10053 3467 61399 86951 29429 79686 61584 27268 64959 81745 23044 56650 74487 36163 6499 4124 90644 79892 96572 78308 29120 90266 24897 12628 80404 31479 83644 60091 33144 44511 1432 77039 28299 42366 73860 869 10213 62091 36694 38207 84250 43026 83141 93245 66974 67305 18189 38590 13401 97277 58916 89862 39379 28741 83009 67666 63505 64759 21136 90362 30350 39406 93703 43759 28552 28413 66030 18784 43944 60671 98064 5714 60950 17903 60552 74891 28315 48267 79123 31459 66383 5927 26561 87188 89418 46538 45734 46709 76693 38682 79840 47370 73388 87634 3529 30502 35932 67019 60920 32448 97849 79180 32350 44359 95182 3571 27377 31235 18152 13567 76627 28523 28606 76225 43365 44488 50656 5471 55523 32253 73450 87520 89198 41741 5154 7722 76704 88189 807 54784 99815 17190 3347 92358 47089 73964 34401 40536 4849 47300 61282 28038 445 16787 29782 85363 80481 38529 909 38555 53291 50058 29325 51807 94187 25114 74690 90846 96277 59385 56577 16711 74863 65610 89175 65548 64154 87070 12368 43535 47309 68929 99395 93212 16417 50495 98690 116 59351 60266 40685 49309 30237 89225 41789 29505 97109 21356 75477 6904 18074 75748 17233 73733 78647 5737 97747 13597 13731 20671 2855 79855 76964 95693 53930 97563 44485 66432 56797 35838 81575 16967 60368 67389 54939 86948 23481 72810 13335 82520 15101 81318 50530 25440 28507 64340 44469 46800 50414 6229 96748 2822 20605 2850 46092 41164 85972 95797 30850 61373 72181 90655 39917 50642 90570 89077 24816 91127 43514 62132 3080 16733 84368 59375 12541 27029 31688 64354 46174 99482 22475 92438 74955 93336 92346 9022 6688 26875 63762 61738 43586 52540 38634 21160 18400 21476 18724 76365 52114 5818 41300 18451 28777 16249 65029 80724 75191 63426 20300 71099 11280 54682 39943 89662 65729 13465 1391 91000 59683 67186 40369 43289 41142 40894 14208 26678 89440 80243 40047 45101 81831 58908 921 23625 63297 72614 25300 58190 42524 47678 63794 83268 59944 99340 49086 1094 9635 30268 28790 25408 32256 85048 91379 95793 88882 46700 86715 53107 18305 59705 2336 9837 32771 89506 1807 3759 81498 28135 20269 17702 53850 77926 45418 60657 21373 23918 63544 30512 19047 47394 77609 4915 65762 83317 82406 86781 23066 56967 62393 47717 45539 57076 37496 21815 2768 31701 47118 26905 21179 92540 39651 4541 46198 72496 41770 347 15961 21768 56395 24459 97873 98671 11825 69217 39176 32339 35088 4432 84310 60699 43808 36509 64811 70358 63640 11895 59409 16753 91878 47402 16210 95421 40179 22265 63150 67082 30737 65115 87589 86818 62094 78886 91959 16567 68949 78585 57121 80953 83739 81399 91184 9595 16112 48389 38411 50592 22937 28810 15625 87326 46524 7261 18970 53146 9031 77547 21292 66253 72105 94248 33931 93546 96119 93944 82393 59661 32624 3408 68528 17147 42675 18644 30177 19301 58107 99457 87277 79026 83550 49808 70052 51666 26158 80277 85805 36590 42550 69824 93418 84121 5938 31269 49112 10619 346 85224 89295 56866 53877 49075 62044 65212 77039 42475 99088 58948 43055 64783 12512 44974 53445 66036 7746 29114 13801 82460 47780 48016 38785 75927 91684 9227 8889 34759 37626 52659 64223 53042 73095 43497 29893 83608 83777 22419 84244 45889 45950 86784 6410 89604 90200 96908 70960 63194 68319 57396 78801 84912 56858 13804 14597 2483 64348 30076 32378 9703 95588 37063 4162 5703 45006 75673 7934 41367 6790 78747 99575 92419 3516 39044 18789 28393 94189 24242 90118 88607 55961 41497 24708 89951 82272 25621 34229 16354 94401 72718 62502 62186 67171 41948 60090 48268 1283 62449 62302 25283 30252 22750 62676 83017 30660 17846 59336 85111 51034 1727 24259 17451 80478 96417 96708 9055 10272 47955 6037 33641 87590 98936 50167 64467 56410 47734 55743 63921 37105 97731 71251 24852 60410 72337 64298 26769 21827 60846 57818 58342 55317 66845 14184 4793 59493 99451 95225 61253 81358 15258 27828 83839 63821 51365 15686 52605 48586 72105 19167 90542 76375 3370 74728 46612 36503 4505 59846 74193 43132 59599 86125 687 6562 55341 50510 55757 92125 61379 79783 49314 75932 85878 88098 28791 48192 29214 89112 9838 55294 25107 47381 61705 17610 20360 34656 52431 22293 60877 43689 35655 70077 29534 89362 73651 68655 77008 33357 7829 25595 85004 71602 65141 25956 30510 84940 87269 28254 27117 76615 77032 97189 52613 46124 71640 89521 10807 90898 73901 54848 53474 83066 85659 6071 49898 42478 15471 38736 940 15370 51643 42549 55843 47181 21823 61501 73683 24870 19380 6833 47337 90438 74275 16648 90625 71384 46992 40124 20806 8554 31007 27577 47087 80981 42996 2964 39553 85837 2771 86151 50021 49174 69990 51634 18620 8674 48064 90542 36735 56059 89668 18213 55665 75660 17919 30394 18040 26874 17599 53106 20114 89002 61111 67741 47176 57580 71555 26422 6726 9343 68241 11377 78311 18321 94460 58619 31237 73003 43354 21316 59254 29323 18671 37990 79535 76870 85057 11693 87947 26858 84691 83865 87526 37251 55765 80606 10789 11367 59083 85267 37497 82266 13954 26670 72308 4453 59102 9004 68487 48484 37622 23887 21891 60065 50105 34211 48376 3385 63259 75194 44604 30472 86866 91430 76567 44348 70800 31116 68244 21418 65079 94079 82934 63373 2420 12829 57741 31025 84907 39396 61149 40934 81734 58369 14620 82125 91569 26398 62349 82201 21862 84594 73509 94165 15566 10788 72825 5439 3397 52282 90387 26046 49624 2533 26224 20096 84920 85943 75135 40451 24210 86092 94758 32535 16380 57453 25439 69414 55842 90195 58479 31945 85450 29377 95260 56507 96266 18259 60885 90452 54909 44792 21728 58547 68971 41085 94382 94396 5126 49201 4985 17206 17350 49451 77296 26996 38025 35578 61803 62696 2371 38223 23279 42868 62421 16379 55649 72788 60146 2377 37268 3139 12039 34362 67311 98023 76980 75491 51260 82322 23769 85895 73982 34945 80153 67563 35769 40472 16155 96918 42562 94932 13891 13581 31847 6543 27087 97569 32199 23872 52404 71575 3204 89920 37364 48458 24715 33643 24359 60588 6999 91186 38507 34109 34685 90781 65245 3342 52992 54065 28429 82794 94500 58015 29264 74507 47841 66598 58440 37240 11484 25399 13244 66666 70727 41413 2710 54163 96589 78116 9180 96431 50642 83846 55249 44633 96641 18872 47146 63745 32003 68208 41116 26429 84109 69353 44131 26430 33701 4668 73426 5942 48245 28908 57486 86557 20583 97206 65796 45370 90633 18135 47244 15616 34325 20406 30424 84864 69684 46850 42500 21278 99631 43728 24324 30610 76772 41075 78872 37808 98528 17327 59627 78139 7497 88608 714 54121 7596 50997 41532 60502 80344 94250 75853 54325 12450 78993 80499 80436 11596 54107 13263 52709 10024 84156 23057 8708 39509 96965 21137 43205 7558 68767 31379 40926 43369 73885 10100 15968 79233 33915 10528 2835 927 80719 59153 25406 77267 13159 32249 8233 28639 10640 38218 32333 5231 75379 92128 35168 86253 44171 29455 74306 19321 34046 17352 71431 77052 27136 20473 28404 85641 61109 24711 4153 58966 60510 44428 78354 49161 68305 88753 18771 71906 36704 78836 72963 5941 74090 78650 33553 66880 9222 6747 79700 34912 35927 32037 38336 90106 97190 1809 99883 94026 36125 39325 95128 57029 34211 89283 94886 25337 86873 26135 9693 19495 83868 28821 37763 59319 50836 89076 80530 79021 54434 69301 41690 3588 56568 61567 47074 9291 53254 34842 96152 28105 18932 57248 77128 98254 61173 9709 36570 97559 8390 10255 64149 40639 73100 53266 72915 99085 62675 13412 76347 40400 17304 85613 75917 42011 15727 55084 87856 41639 54255 72051 94237 55167 60622 17234 4845 77569 85683 33111 74737 71400 90190 61860 93932 28105 36993 83366 68207 66800 80809 71279 52622 76287 8554 13817 75725 92626 59605 39773 10612 69628 41580 67156 68615 78070 99604 90568 49287 42641 85045 17500 88864 76984 64881 73161 13781 87751 67241 33876 74623 46449 20041 17345 88588 42261 41733 42660 38395 85544 60481 55493 80357 92015 58346 52834 67506 98401 786 90501 1994 70118 36116 39430 73665 15792 7352 73323 50434 55652 56074 54503 42491 58160 85725 39515 12658 46662 94254 62741 90714 92231 11358 33948 99389 47341 41324 52724 53307 12176 89884 14928 77798 13676 78783 47146 45123 96639 13999 56064 76919 93510 75492 47509 72910 17655 29420 12499 31568 85372 99724 1084 23190 61116 86726 95641 71124 51821 58588 10584 34069 77616 41031 28085 86497 89689 98857 99929 60690 45394 10330 71270 81401 6453 13359 25520 28721 40994 43650 40809 63616 58895 6948 98932 31171 1066 11539 43554 50289 51460 62590 63746 74685 21335 82695 43121 55656 9101 74161 86946 36182 2 87562 45423 95664 89752 87865 2107 51710 17760 28996 82426 83966 49829 93989 14853 23963 71576 50571 96350 93053 61982 72950 36922 82715 10214 61707 71048 89439 44920 42316 94490 44245 61967 65219 78950 4346 59141 65561 22547 70297 48367 48001 60013 77568 55193 23486 84774 37659 14623 88597 78419 70739 56517 22713 75298 5388 82111 30119 98304 73672 1949 44100 64865 68588 21416 22360 85749 35661 94452 10550 92650 95991 37086 9840 93653 71869 75606 89824 3924 72058 35123 19422 9337 83765 74666 56205 241 42444 94707 18835 31309 3800 18745 6376 19838 11838 96013 43283 18023 30219 28308 65850 40347 83162 26772 71548 34428 59026 74259 49763 75879 91591 23035 56851 19567 57121 66792 24371 71604 44251 58659 21709 25601 9359 27522 45259 65992 8199 70121 97753 8806 44759 44171 86979 91032 44452 11963 32447 35183 68724 11984 61365 23269 40779 48085 39406 97612 28810 2922 64249 69618 87496 77762 74841 54404 1009 86612 19241 33576 93920 37069 25666 46377 45828 31655 88520 42261 88604 24330 30909 14056 49416 13278 361 49697 71278 80209 42880 21274 21875 90659 24408 44002 74060 32411 11876 34184 64230 46302 71021 54843 69740 81901 47091 61819 84985 90170 12586 44666 5716 23724 56958 72679 9429 52166 42941 53291 11340 28074 55079 67592 83900 2318 6754 42159 18236 14323 36717 99935 25441 26036 10905 12508 68078 7037 16366 47711 8634 89997 98967 38686 42587 2278 18733 83813 28547 55851 33821 25288 33631 65359 18820 16273 19516 10309 80291 16269 62456 42279 6790 89021 31443 81751 6139 22564 49609 67930 47842 12755 13238 13492 93960 83868 79729 34765 45660 13305 19894 96732 87974 28322 1826 53510 75982 82054 69273 624 6818 58339 9703 2707 75644 89498 79122 78217 25682 48315 27303 19309 52076 31007 7947 47161 75245 42011 46946 83223 49624 86767 10557 86404 66058 1873 2048 29364 9699 22497 22319 78231 69241 97881 1781 20565 75248 50836 77292 58288 30698 48892 62042 50819 46873 13182 7058 97732 94736 55047 96882 97120 90592 81640 84558 34196 47756 34759 84187 2634 38768 93792 79154 62797 14925 55307 94644 57101 61798 30345 52619 48731 20034 84530 53085 56715 23877 54546 81244 36821 53503 45521 6182 16534 73357 52388 98190 13023 25493 86077 71102 64117 44403 97971 24482 43762 30118 65733 50726 19287 82449 83028 91457 93237 79895 44649 1217 78464 49170 66544 39063 57342 95613 78487 30923 11439 64088 18592 20975 80146 13175 3301 69580 81120 31689 61180 34449 14366 79013 83132 35490 79736 69212 25928 49789 46249 21693 14287 20828 65521 58340 3555 38251 18263 56188 53517 85056 50172 91175 67540 11156 18313 88578 11510 75588 9648 8506 13522 4368 84 20058 99109 97429 65579 41565 52106 7978 35100 46389 75099 37450 77820 42070 53164 49379 58868 74025 92616 10803 54870 20043 64498 10472 89136 60119 63098 63822 29345 38026 57703 31447 26566 57327 81202 39089 5321 30645 6245 15321 10826 86018 31806 30275 76221 1235 85213 71695 67494 52547 81377 4961 38278 31669 25306 42196 83526 68696 65652 14404 44911 78883 13075 39726 83332 59184 82826 53440 59725 9132 20727 70283 64663 11634 7487 57433 57335 55035 85607 71925 91943 81165 22783 90781 48181 81795 35801 52035 25698 60844 13205 31148 77319 18230 47232 60097 70915 66216 63620 12396 85966 49694 39811 26981 98678 60231 49450 24962 15826 25926 52901 91916 44766 64486 79728 92347 39124 31558 7212 91222 78846 95653 65225 9431 14029 14950 39553 82828 82888 57745 9315 19628 4330 27661 36121 92809 25767 2686 43101 47267 28731 635 91100 45302 52031 20103 14315 38876 45432 61375 38865 26083 84241 73572 78222 66311 89933 10726 13515 64643 86001 77831 85773 76383 79578 40242 62601 90741 75072 21142 83939 51114 88101 48058 50784 99897 67020 64905 82987 73067 23640 55879 62116 96527 46609 79936 60350 56107 3656 53066 16931 32880 54194 73743 52571 55736 84242 3934 62797 13661 56858 23865 86090 64396 36321 59419 48750 57112 90043 66310 16768 30453 98727 79301 60018 38221 14944 78012 17555 75625 6166 56884 36262 23062 78129 48993 17354 60599 77394 26096 37760 36893 81428 45012 81763 41069 8293 66869 83703 71984 45000 23369 91357 20218 45330 73390 57433 1534 39614 50173 10104 49411 56603 74859 42105 78222 10809 34857 98633 48225 3892 81482 11327 86285 17533 38152 16768 23876 35224 27672 55077 58183 70218 56784 3220 42233 85329 7 591 42718 57741 27331 54332 98135 94301 97388 15543 36308 73751 46825 34628 67628 48864 96824 29403 8373 72083 55128 68318 33220 77927 54503 99138 1875 18427 33950 18132 93165 87889 56856 99369 34582 93038 34096 26797 96243 69682 73834 46398 4487 52297 37113 40044 75492 96154 9034 32678 28270 77790 17103 62616 19074 40070 91288 24711 44731 44599 25594 47789 23508 84349 73782 58548 50358 70645 30713 99264 4049 313 78609 76910 76267 12685 35945 55225 71547 2657 98718 62377 82103 23057 96893 30585 95107 78239 39138 3215 37403 89035 15582 71933 87344 85706 58646 75475 91977 69923 68321 71572 77524 96393 99770 9827 71572 98671 3277 99074 94052 29064 39059 73352 49600 18479 7706 63732 88615 42149 61741 39937 45903 98148 21574 16685 27946 38361 88143 56208 8324 26631 51056 36317 24812 66088 61356 36503 17762 25594 84653 82559 56595 62019 50805 9170 36968 32223 23157 23182 46152 14154 19041 40317 43359 58604 69870 57561 36891 38395 92883 60137 75686 95068 3395 22968 53801 27096 70156 17331 67760 6716 41711 74609 2047 65063 29490 35001 56685 38561 11277 7240 79659 24536 34664 61014 80444 8126 96206 37042 35795 21712 87361 14492 65553 96072 58731 25914 15454 90447 75171 98091 43334 42706 4277 84444 53685 22054 65915 72946 34658 85272 80189 56406 84727 38850 35701 80295 33995 34150 33176 62228 14474 67543 59034 27007 71957 84649 16187 29199 84398 96190 2086 62376 46500 3262 16659 18013 64811 82017 92027 42362 22517 37920 24145 3258 66820 89641 11149 72455 95279 48077 56331 41039 31519 21583 72633 96024 40702 28130 19962 9254 80885 68280 33195 68506 98840 4653 52304 7510 22508 24683 68357 91465 10181 22613 18743 28549 49149 63658 77197 52804 72886 48274 52239 77748 62420 31716 67456 17799 9549 21548 91737 24709 65790 81262 39107 88953 13504 97241 17911 85792 14221 78730 13817 6646 45618 72628 7325 54925 8671 54584 89589 28438 81014 60676 5814 38522 6568 83020 63694 36543 50992 16849 5653 9043 88617 43932 53593 12009 24795 64790 43709 39375 13368 65074 52808 81760 93648 96747 62097 68163 77085 46119 69830 27989 52131 66644 5302 63100 26447 91590 83508 25542 51514 58574 86155 60715 76282 33133 52295 70586 46134 11082 86589 71023 93606 17989 91952 90319 86302 94347 18081 14949 64145 95112 97959 71466 77322 24120 96401 24591 37353 25721 70723 97608 73230 18296 484 22111 8644 67498 55225 17820 23270 5087 97326 91367 76410 53430 75441 58426 37223 19958 50471 8489 10005 79847 68048 33281 28447 15279 64730 75435 47093 38390 97820 2841 34868 97813 61780 54306 502 64382 61386 95490 11232 9576 38576 98619 43527 2937 78206 36217 75167 35917 60870 44709 55147 79727 79998 62073 78449 48598 2796 34058 30085 49958 88628 37065 30200 52339 3787 20708 37622 10962 64089 33844 43902 54542 33793 15834 57724 79808 74185 80171 56611 68240 39174 5998 70997 16090 97900 20473 66475 79790 57022 59506 46228 15061 77271 91999 57187 8230 63494 46500 62692 95453 52365 345 82648 11826 36275 25911 41806 71068 30145 66128 2080 93099 68588 67301 33653 32456 63326 18335 45748 27758 86250 6079 58006 20690 60486 88555 5324 2454 98270 96604 6915 36238 22924 44029 43002 85375 17000 28419 41633 35453 732 86294 52882 64568 14221 711 61928 56657 33382 6660 58549 81502 21421 82758 22590 18162 1053 22930 22761 4545 40169 94369 51068 22385 99920 16611 30934 38231 65628 29190 55143 88517 69477 4349 15579 70913 3312 49159 33003 49012 64965 27444 50306 36873 88598 38130 47475 87611 52515 15351 62612 95791 99084 73089 27880 83530 13041 67811 55600 15871 75661 54040 42414 4402 94347 49980 70428 5496 33617 29529 44267 32641 99009 11776 82228 81579 84362 85943 24704 29727 82017 95526 39502 20980 67432 60232 78817 90825 78015 6139 18093 98956 1273 72930 11965 39623 37345 8233 13978 6642 43245 35728 54995 30786 32272 70522 5365 80084 45566 96107 58912 50463 17358 42887 83357 36750 37497 14324 983 54332 41511 2446 23763 30787 80516 71893 20745 62739 74752 98994 91750 90334 97265 35910 62182 16285 8561 16308 78771 48865 52876 20835 92069 66545 11979 25799 29072 67937 35574 54520 25155 88615 56758 44315 45967 80745 86856 36277 94445 45811 17120 96275 42250 48114 44750 56888 48898 39641 83950 15141 84526 43166 97861 25920 84325 18526 78412 86536 6140 59841 23842 38395 57455 54917 5907 51570 77096 98079 47673 96849 63123 85496 23310 38228 55545 37907 32424 92402 62858 99364 50899 9993 31992 81829 71228 78091 70547 62219 36017 82509 23155 7668 20909 63987 88439 32354 68946 15662 46068 51128 78702 29969 21525 97034 24231 30592 40574 80345 31413 25999 60655 98085 35771 95725 75301 68870 85739 89519 64307 51041 47558 22183 61196 5475 32420 22389 10750 20162 66139 47439 41403 87459 18719 4649 71143 87497 72044 17172 27117 16601 9694 94282 37492 57931 97253 19721 68507 39209 38769 52795 66483 28995 66298 82638 82367 27966 6835 46028 7510 37099 54749 93068 49367 79179 65946 91459 21164 4858 91108 35712 8203 2951 77304 41248 33219 86769 19032 50267 67114 80462 459 96454 13086 30529 15524 41434 88999 31767 16465 4062 45349 51441 11388 27818 30706 83318 71238 53409 36499 39503 46326 75032 69624 86758 15402 82257 62730 39029 72338 24673 15889 11931 56911 7180 91975 31399 16778 51402 94463 6743 31103 30855 14162 93815 1147 97850 16430 50885 5442 6871 26978 93062 21193 92199 18140 58246 65882 16581 40242 72367 59982 50901 54413 70333 76705 80045 4646 79422 73774 63246 29782 5715 47704 9036 33342 94103 36092 62128 67763 23276 82229 59787 74838 89081 73461 79329 31867 90683 8027 46168 85260 90573 99122 22526 79475 29578 85661 87085 71266 792 15634 42096 32195 13755 99377 2235 79843 32127 28295 49709 97450 22480 88240 82596 85909 44877 48306 21980 38898 91169 52453 12660 16992 94986 16605 25242 27312 67621 43846 13304 44219 77953 87706 68634 54850 44758 25182 21033 90021 1194 26579 30084 42576 71116 14189 91468 43910 92630 8845 81067 80884 40573 74791 87988 33507 55414 26384 99581 92043 89849 22670 1378 38923 50198 99456 96541 72877 72382 76370 57420 76252 34435 33536 74170 15080 29319 73196 63991 31522 45989 20646 89593 18820 73626 32509 30915 43834 56487 81988 13745 47293 27150 26563 25795 84712 35698 16228 75452 44757 2355 51003 31915 6734 38393 93576 63355 21653 30229 67489 45619 92456 58866 87259 39078 58101 22066 66537 87568 18469 90748 98587 44589 96038 60552 37870 48804 54607 59771 40915 18596 69511 30904 53662 63669 5244 1582 91776 57507 74264 18653 71395 96493 48956 40668 9167 22910 67230 21541 33965 77833 78470 5410 73925 21225 72858 22741 38685 28527 6520 98436 96857 64647 43942 88050 65803 56349 36702 1937 3466 1681 78446 19741 6374 58617 83875 59583 96657 33956 50886 73940 15421 50119 36519 25028 86128 57137 71928 33308 89397 29245 90452 96985 28058 51650 93838 67895 99714 38669 18496 2006 99411 15341 10048 32889 56702 98545 42634 9060 17694 69661 32323 8684 23806 93031 3865 47544 476 76049 79352 54204 49996 44385 41385 28032 15993 60307 13097 74661 68967 33302 38967 56515 5331 7268 5484 36393 15876 48200 68562 90475 124 89354 34160 49447 71226 97059 58048 14659 37900 2750 99001 72714 77758 29704 63751 27353 20785 36074 91473 30952 63772 35908 46051 98302 58385 54723 38398 55136 99971 28076 7839 65614 50773 86063 63944 82165 59637 59997 34876 78852 60256 26875 47640 81813 24775 12945 12324 84279 40403 4160 59312 6331 67754 47622 19838 95007 17477 51051 24198 44937 56904 72163 52927 81564 86187 1685 91628 14447 96838 80178 68172 38645 71717 48297 26521 4495 83576 89454 58283 77621 51869 8555 17817 18381 54898 75774 42320 91371 74212 50148 46720 88911 78095 59768 64118 30027 49192 7559 3227 70143 24644 54904 73706 84814 67096 12307 39387 84308 66506 65266 46888 59853 88140 66071 57530 54394 44499 57665 35882 16945 17154 44308 94491 34150 63427 21009 87959 1832 48326 58478 23567 89508 75595 99902 87908 90684 69887 94455 6522 19515 56150 68167 64145 87221 61754 9751 98919 81943 76814 23992 58837 24377 58671 92036 59021 43712 57402 17345 50653 77918 14351 96460 72856 18288 39659 32872 79385 72496 27927 40121 55689 61844 13097 58295 58712 84973 23178 32322 30578 4492 1880 38756 10187 47416 37425 36346 72353 51560 52711 76563 25562 47104 29028 53784 44992 75356 14049 26458 54435 88825 14014 95120 70552 64185 9260 87034 9659 31433 81767 74206 39432 83307 12682 26171 70281 19010 72468 43884 60949 90647 37958 60540 33094 78848 30510 71527 84604 19885 62177 37207 96601 32101 21727 84271 54550 48817 2037 23186 43893 8933 91698 16413 97627 39158 71064 31317 46483 58614 56965 66195 55228 61651 45663 71161 49243 32493 63744 40548 79610 6753 15910 18539 36424 57999 23134 88797 87731 5943 49200 28963 95081 439 31910 5476 21121 50626 53510 64852 69437 42478 68606 86068 86592 42476 30610 69207 26429 18738 90410 39184 23227 13209 42854 91290 18390 22975 90626 83006 11662 62317 91946 75827 24618 33369 61588 92806 95048 29372 73800 70165 52376 93055 93039 77761 53083 5037 51754 22359 49870 92830 74896 78758 41547 22913 24348 19989 79820 62260 88242 37736 17256 24983 61550 67938 43054 44811 32321 23752 55685 23483 30180 74281 20616 774 55833 87926 8751 80634 58414 78535 31745 73302 83737 99238 25845 80902 40158 82700 41443 30249 70040 96079 32382 75997 49362 60560 79752 72123 97740 21315 18957 88730 4487 87032 18353 50875 80899 51140 27627 66905 52415 10143 11546 81833 27039 73592 26335 83407 77843 69003 68839 42187 60818 11930 54689 63806 7647 15314 75666 70466 19529 19588 48749 26894 81969 1096 30839 37121 12464 81923 37064 27336 16412 60798 89342 68288 42639 50325 90063 86283 26673 95758 1574 23958 67066 81886 81691 45091 12677 67543 50110 90811 87229 85194 49226 75709 15911 83660 12922 46666 59360 85792 45238 42187 44619 4284 95711 74566 58350 22301 79440 35407 45715 11595 92176 61162 84719 63496 15269 93708 37424 12261 41790 38909 73786 56139 95996 23623 37135 81204 48012 52181 35668 25349 38041 9804 1717 62252 13270 70511 39868 12625 22321 87227 31152 33271 85221 79952 68506 4437 98819 55804 92565 36663 74009 97445 22 18783 38266 83555 83774 55142 4729 5960 12343 19276 42297 61662 50994 63854 69413 48195 58929 54307 47945 33343 49535 36728 36760 96192 29824 88937 93914 90178 68536 69597 46101 97141 7687 28598 45413 63103 68964 20476 56243 59681 1580 8539 10458 80913 33076 88547 91739 34396 99148 79416 52895 17097 6381 57956 67333 26574 14406 1915 75465 32189 49280 43948 13617 94432 12577 58305 38899 61897 48057 61212 90701 33731 49375 39868 39709 26559 21304 1007 93521 10632 65692 86625 32965 4431 33238 37696 70641 44777 88759 47243 27757 13820 62774 79786 54704 26529 5927 49557 64078 74849 78232 97215 41672 87088 84096 24636 53612 65456 9898 68378 68062 32408 78972 81769 94979 69096 81532 44840 12181 23366 16127 44899 65685 38455 60738 2120 99321 84171 81127 14689 79362 61754 26954 54813 41085 12518 68647 25176 96050 76810 188 31328 27686 82168 36068 96257 80953 95307 39958 99692 63641 61580 98516 79064 75916 56925 95582 53047 66899 15707 57498 33370 57699 73957 19597 72850 86481 19890 66386 5756 68834 43204 16734 5564 17853 77351 78447 33976 16426 28928 95066 20615 89165 79137 63643 33311 65500 26519 1972 94034 12668 97897 2458 77372 21575 82190 44955 34397 86322 51722 70679 55978 59982 41460 25561 42704 33088 57669 10390 73724 9839 4130 65767 17078 50756 41582 2405 43669 52298 26856 89076 4472 92424 78134 48449 12745 66800 37105 87642 79013 28749 52836 72005 18869 43276 52630 81668 39256 76940 16621 62476 79646 31734 95899 11531 27820 51092 58819 58704 71535 97851 22561 90302 88938 24007 39374 85783 38164 5791 46313 62417 71881 8295 77257 38973 28571 78191 16164 6457 86835 51015 86822 70301 18310 58442 71139 62941 56271 83070 44850 61650 90540 32374 16945 36727 52319 3385 1735 95125 79773 56187 86701 59883 96951 74153 73107 84897 97737 58001 70477 32093 31993 14978 99885 58486 83043 59000 88224 17538 57789 34491 67715 46585 65766 34152 13844 6924 64510 70314 82298 90180 98808 8451 95418 3731 25392 30424 11218 87523 21406 39874 52358 65284 28129 74458 85145 79036 32463 36835 92459 58498 80158 99292 91325 4634 14544 58258 79247 70453 46590 9057 26287 24612 21203 57690 64282 50915 8205 94662 36837 6 50410 9259 15270 63064 95268 43530 95253 6139 12681 81819 82617 27626 36232 20350 30469 74820 90040 90189 98928 60537 26689 16641 73538 69882 45950 51631 27908 33949 67322 3670 97140 25786 2720 3728 12647 24015 87168 37210 6083 23356 4355 44250 38569 53044 45344 94140 48064 95524 96661 3846 4349 52637 24807 64697 89274 91327 31117 25630 54753 65472 92335 92747 56680 39342 67951 453 44022 79285 45452 45877 41795 10818 99045 56283 98399 10299 99417 51462 74723 57627 99995 30739 46737 11143 59789 66728 50175 17456 34132 73812 62959 19561 15682 37741 44999 67063 88746 13946 23914 21406 50069 83688 12757 13488 28304 58275 61734 64890 2750 64646 56362 64153 19413 48327 25696 2020 21666 14828 30829 78984 29347 62455 30679 7514 84120 67953 31656 9074 30033 26927 21066 53351 623 2658 35815 51727 91137 12504 60995 6109 56482 37553 89568 64766 43046 82676 67276 84297 73686 26074 96469 52561 27578 6541 13703 13348 42371 64942 79908 40743 12120 15223 14264 83360 79645 73579 82998 89699 75847 24447 32465 58423 63991 12192 91713 59143 20665 73662 90868 60 56567 77646 38244 88369 35880 10371 74354 54800 92215 19289 42430 46995 6888 49079 93345 21230 32438 43238 61736 40388 74565 31533 18255 98296 68411 74466 75224 52956 16519 7797 95425 99091 90235 70902 5116 72376 70834 23018 96135 69429 81902 9971 86512 93064 979 91923 35869 76097 20194 51374 26535 11275 12502 2387 55955 28690 88952 5344 15283 32007 2695 77211 32439 89022 55646 41927 74480 3928 33655 32370 36961 47601 79580 86119 11636 10602 63284 76207 9252 59454 39866 94785 84338 80594 72557 70899 7225 17362 4028 20749 40838 42126 10272 56941 94148 1220 9165 8762 67569 40203 67905 75784 67136 3094 83869 12265 21931 55360 1630 70953 23266 87475 68629 61085 15789 69307 22778 63048 58012 85864 17635 10248 34428 99056 1252 34910 53009 56596 37633 97952 49788 64388 78607 66252 92307 99782 60098 79076 59868 87932 51585 23221 41503 5356 62511 35440 30389 75900 24292 35549 75220 8802 36113 37112 81507 20276 83451 81772 63228 23809 33990 88333 51804 7705 74896 90246 34515 95615 51971 2858 35559 15712 27592 81803 56989 15115 79205 8549 45700 48296 12436 19581 71697 7799 48095 55401 53869 6250 49101 27202 74684 42386 37080 34241 64816 27446 9202 6410 85229 32899 22328 15269 49646 81539 60204 96224 48516 57024 29706 3834 67249 27087 59937 24655 2414 30371 71155 45701 82822 27072 1343 3311 38861 74113 20250 32682 60156 69186 25778 65095 32101 33846 8942 92724 20292 11676 49953 86251 15763 72840 2150 67390 20874 46798 68988 72858 17959 50670 2514 37860 44937 13219 6243 38639 17196 24650 12380 77543 76038 76723 11274 58037 69403 83214 44746 32066 99223 47691 35478 78477 15575 33573 33149 14237 4389 28321 98208 40011 76138 7784 21909 50149 21695 2452 99294 50096 38156 97818 30006 10573 8039 90834 23541 11671 19175 97268 43398 21248 46471 94094 96233 85502 5818 51034 76031 23209 49731 84763 20463 83086 34549 40308 20521 40721 85027 58047 7268 23540 98577 41160 16456 59569 37158 64699 17066 75167 64804 27837 53337 66209 60670 43854 10199 35211 83668 35627 56389 58820 12229 9143 62371 21147 95311 9585 47024 40783 47769 18528 59328 80229 12935 77923 62839 57954 6262 64112 36781 75610 30782 20529 91516 93487 21998 54928 128 80749 5386 26141 16198 25402 83600 50613 99203 56129 9675 24926 53937 51584 58850 4050 89512 15474 77878 4128 74141 67279 67618 14597 94110 18196 66779 83404 32641 28853 14124 55835 55282 69963 16546 97490 31054 22778 76989 98603 17576 71066 41103 72466 99074 67775 63 74413 46190 30833 10027 88093 30766 88798 60052 51592 33590 88575 2816 83425 3464 89303 53471 48270 45887 98722 44836 74849 83691 45595 94472 80838 5877 73865 17873 87100 15966 82440 94030 41952 5902 26520 58692 83678 31756 69096 78432 8003 99927 56696 69883 39019 27821 61588 81188 72941 9978 61748 51217 78204 79597 43163 57597 69171 88048 47772 75807 46926 43392 70392 10793 46308 1738 64553 48862 73323 38073 35333 95162 14718 69069 31042 90127 11101 23119 44188 1395 50774 27526 45365 98949 84314 19991 78398 59852 75553 41821 24178 75394 47871 1661 83666 50352 20287 42415 96690 97394 91904 39568 52326 84846 71359 20611 48299 8772 49993 88412 63865 21276 33684 43517 46509 76398 67265 8339 25137 91031 20695 51350 89360 39279 72154 47861 1109 96219 18637 12370 77526 96850 37241 64753 80330 9573 33737 90776 45239 63878 76611 75738 69917 59223 68081 45731 36712 3448 55131 16828 65822 78652 99176 19690 84537 34132 75657 34821 49318 2367 5870 93123 63509 69799 79505 93461 88853 75771 93661 7656 34166 96434 93273 21602 429 81465 97148 73271 21532 23253 78222 31092 15806 65620 13005 5498 65883 33591 28796 56381 45051 25353 81335 17516 1068 35271 56588 29626 10986 63542 48109 35866 53078 92299 3879 45226 5806 87778 71010 67608 52674 1459 61726 37514 40928 98343 80828 95646 71388 90259 15546 11347 66740 46443 38518 62725 11905 96193 11974 13396 37666 14131 54435 89983 11691 41910 7929 51249 40449 61001 59829 95973 41734 7923 14425 71854 17029 78554 96472 16985 50066 63239 55731 5101 47925 60861 16820 49398 4074 58985 88545 39418 21887 43058 26996 47326 70890 35020 20684 74937 27063 78377 63347 66972 71794 90287 66246 99231 15104 69393 32610 38844 57167 5938 23196 20527 60655 47782 63743 21563 40036 16294 70353 83611 51261 85799 78729 72974 73013 76959 18094 15717 37898 37801 95660 29258 45520 30347 27322 61116 63898 54755 1210 71051 57940 42560 87518 65620 78602 76996 98792 24203 62987 34696 5203 76157 45737 23904 36204 35424 96708 63906 52257 42355 3260 58324 78731 85882 43242 86261 11374 25116 68463 11233 35807 66619 44290 39845 42593 64565 20978 64464 1199 48898 90755 85527 32939 72647 88147 86103 34749 65800 76337 19794 43717 50729 37221 15730 94296 2090 15756 28158 92952 454 1621 16975 13634 90940 92308 56099 73219 5394 42281 70115 70377 77238 18202 88896 29831 1507 73155 68936 16586 27105 52932 22259 61608 3775 97507 99000 72775 14862 77238 34550 42909 57428 48355 23769 85863 58392 9542 75611 35033 22312 74912 96989 60567 71393 84592 10826 23699 87813 76985 51360 4431 64271 68144 18383 43074 84734 55113 60994 52320 92122 25238 52702 71950 63247 14335 52003 61111 82872 25615 1189 15300 31134 66540 30001 4060 52140 6820 46182 35633 72117 7130 8881 76894 87394 91253 49098 78337 77214 55605 9094 77183 76164 49444 50993 84772 70825 34016 39307 61750 4472 12005 77214 30560 54028 66922 8258 3424 21106 5764 50492 47818 72299 45554 48114 21884 8851 14821 68657 33657 53147 57819 77187 59028 24021 84896 62939 88576 35184 63127 22429 34594 37521 24533 51999 62432 65003 36277 98497 68269 99722 10902 99246 40732 90346 9013 98080 95621 71639 98142 94255 16577 21941 65771 91780 87413 68383 49419 39702 31727 63718 68000 47383 21782 48143 42637 80475 98396 61467 39059 45591 54058 43577 79907 49718 69479 40552 5494 95569 22527 24573 69626 77696 13806 16683 42526 15583 47910 60845 26950 82529 32493 12108 83563 18226 1355 51211 99001 14291 30897 69679 74568 41948 43666 27486 23488 49648 20841 10954 28734 94848 48865 35759 54183 57697 91554 32724 79745 34437 32279 68756 40750 60478 65751 44333 69498 60692 57832 23623 76528 85527 79333 20033 42418 77673 4515 9564 95909 47688 33379 16525 85168 36408 61176 24382 85787 44068 59661 48248 34388 69249 10352 16273 93735 65604 69218 94887 83472 85338 8333 8615 25 79789 35761 26158 44759 79173 21633 37168 60783 99435 76267 78400 95239 9624 16902 70330 60824 47679 272 34807 2934 31574 80094 48478 44808 12759 84845 72994 59104 42327 56984 64358 19265 99858 82004 32000 30920 43816 739 53680 80076 66914 88976 78242 68942 63073 92839 33258 64871 36995 15083 79222 40060 25686 24516 22285 70342 85258 47169 2097 47666 52538 93182 28609 56018 16015 36397 43093 35086 91752 19762 49011 89516 17142 92270 40629 48094 85694 88966 9411 91644 75609 89601 79165 68763 16576 73518 50569 28539 90437 70873 77123 79501 27825 60232 66255 26674 79759 96959 95798 73433 74243 21465 54709 73014 86685 32099 65103 96751 67753 46435 6990 68350 88616 69305 477 29604 51356 53990 40137 24554 77247 83673 29993 50952 44730 67805 6422 42984 6831 19830 99328 55514 80041 86662 22537 93821 80753 72752 93781 27307 73140 30086 85113 50307 34158 73602 79213 62820 49777 88121 6153 22975 89125 30339 56538 55966 38370 3578 77857 21582 43290 50637 77980 63646 63459 25649 88481 20007 6007 69329 42714 27260 32462 83249 91212 56794 62868 25702 4203 49246 10069 13200 6732 61389 41634 87233 10407 38896 86322 91215 93916 14832 1525 5396 96898 79479 81044 94661 59816 57038 38925 95926 70159 26239 37998 8209 13047 93122 6931 81893 87699 47032 50749 54273 85400 35410 37913 37855 37870 89509 25206 28186 98932 15351 9915 79150 39480 9938 12917 187 36354 44094 45648 99897 87639 87392 96494 8019 28722 74995 13564 60464 6396 14762 5765 60998 92662 16204 78507 63271 48484 85918 34131 91604 18343 46672 8984 64595 36668 96030 98132 7164 66740 32568 80440 65568 36832 97039 78554 58312 81346 1927 18120 95507 90011 80312 56771 24783 67153 67515 4457 60551 40166 33155 12801 97028 91195 29243 64694 13299 78874 11804 1727 55778 43082 83175 50011 5268 30582 96899 11661 94104 70045 28265 71522 42214 85264 6676 9652 46050 60333 39942 7802 92030 73792 5828 4749 29693 37130 89455 23817 17912 36062 60038 57611 74585 31624 7812 19634 74258 51669 4553 98767 34691 85872 64755 99736 66745 73618 58824 45734 93842 35518 77169 98820 33949 10244 57604 61728 76635 97627 81863 81216 97356 48234 49281 55492 93815 8073 39465 58370 91945 44029 46059 96087 71283 13164 4867 4634 83534 10819 94799 62561 5457 98703 5612 15923 60466 80454 88873 82615 42498 72329 98953 19736 27281 44218 13819 6523 39101 54934 67315 64605 65061 77010 81354 71735 9002 35949 41158 2411 91566 91295 60323 7835 72164 11900 75239 82123 44001 87088 45785 88419 56012 72054 86302 65607 81645 56494 25566 66880 1918 11293 68688 58609 77594 37469 65420 32861 42117 11184 26955 99920 20504 6242 87075 57182 39623 75093 55826 4780 76833 3345 82021 5362 36935 11607 68427 87355 16897 8976 28814 69022 47528 35316 73576 25560 30285 49139 58522 77132 11526 22389 78925 64842 54672 79567 90437 38507 21417 95909 39108 61967 90461 75405 8941 60833 91872 49095 81398 71906 71935 18170 86197 15302 66824 81528 84647 76142 44611 46868 35950 1482 13198 26328 9539 31928 48208 61274 47612 97351 81300 49606 92408 75822 25718 84249 51374 25526 60990 67008 25549 73391 91384 99524 68340 68821 49343 40906 58733 85684 19060 68765 44054 24797 75056 61231 12392 76831 25668 23473 68604 60293 75329 34115 49270 81413 92549 49131 55058 98394 29766 57314 11990 49176 87799 86427 48312 40330 84258 48272 33154 4709 15411 89286 51366 98373 48561 51211 65244 20798 97419 93044 29663 60435 70484 12881 19912 65279 94345 74766 7627 71083 35284 86984 55572 7598 7908 984 79289 27544 19216 58311 8022 89674 24327 93551 26552 67005 29132 15481 81198 49117 72065 52301 5311 70391 73885 84672 97228 93383 30874 57731 37007 12886 23734 34716 65662 16868 42246 27856 69143 96809 82903 54625 51306 69788 77221 45540 19868 90859 32059 53811 45306 98487 16023 291 79809 29375 3971 54975 37184 24311 47717 67402 2708 19396 76798 90549 13023 86871 52987 14455 54635 95538 16242 85533 65617 94588 10518 29408 43528 81712 15874 23721 9324 14037 15311 83693 69905 38335 28808 9443 40193 30837 66703 29079 29549 74537 16696 81373 51965 93907 61856 43005 83048 96437 73199 78032 45395 83862 22753 92095 72783 51104 14183 64009 64015 92256 44773 50408 73070 99085 17450 46809 1731 39562 52084 57096 81647 25653 15468 85144 90944 23915 12070 10290 96837 24892 11891 28990 78131 87925 86235 89936 87451 44808 28705 21638 224 37732 87832 46501 76685 99156 9159 88434 12564 57250 55553 97589 93870 42679 99907 70862 29221 15107 64171 26539 59609 77100 77397 31591 76663 11861 55749 17768 64897 55380 45946 58531 83254 55001 67472 34755 67948 90671 37808 89800 70653 14986 49033 578 1591 55881 79340 34781 69618 60177 26261 28207 24734 73261 2184 1308 55614 61737 18536 52978 16712 34624 16739 17785 29877 67603 93946 40630 25945 81540 75553 27084 98892 62318 12443 74308 71440 51621 18713 89889 79108 17788 73730 5147 20870 63265 20109 32368 89393 61548 51573 18788 3482 63268 27793 22888 80597 93884 36876 51184 53528 18937 25730 19669 2537 71009 42579 21272 30844 79553 97305 63912 77479 60747 76630 23709 22313 29851 96789 30930 58529 36162 69270 98089 87613 15646 16158 26632 16901 16037 30346 22671 6750 40288 20206 51773 36819 71293 38158 26598 27682 18156 85348 92157 24465 62146 85786 68096 66785 12476 53484 6454 65113 79149 30215 27913 52638 11672 10585 42013 52836 77521 6880 22559 84628 93927 63962 50418 75435 11857 9955 26728 58342 16897 98406 77655 18506 58606 77362 44180 2019 51939 88809 3498 4987 42453 71246 60968 95415 13164 89441 5063 78424 71589 88487 59663 24107 17037 72224 72537 93336 74000 68381 16347 34158 89573 35195 5330 47539 43589 50099 75350 41305 60842 86311 52972 45430 14452 82507 87608 44199 80592 60409 81452 81109 35062 67525 37032 88739 93242 31956 66720 76853 95697 94107 96709 70175 65770 48731 12398 45225 61372 56843 10406 5900 83238 23510 73268 5179 10028 99943 98143 69535 52931 55169 36687 89778 74479 51086 6870 72851 18647 23595 33856 44914 38597 68380 75108 72552 29949 17138 27659 57727 43857 81950 1651 69376 15786 54090 92827 69392 93977 34260 36908 69324 19224 78629 57417 73274 37139 6403 24494 50727 31773 5352 52637 13714 20664 15301 17886 22540 20910 27434 48601 24139 64899 27847 93039 17988 86218 1965 6496 64761 64600 68807 30806 49791 60060 67066 22648 51053 6474 82771 13336 85389 80710 64707 58011 22440 48799 81435 16909 58341 69999 46231 85166 5140 63126 92932 22760 26005 89826 33209 49130 52644 45529 11457 87318 51441 82966 74011 13990 54732 50133 97356 64168 43654 42288 75526 75570 10687 79855 53305 76866 95974 95354 28715 9192 11147 52533 28406 39426 66486 20353 465 26729 12163 47149 34230 94882 39107 44043 5881 23197 12193 25687 22022 24243 21061 19966 95810 89198 6753 599 20412 20644 23616 48603 82655 41708 23 8286 31913 98687 91492 72445 97546 78939 53809 72232 56411 98565 51395 2739 95740 14093 68988 66485 85620 54390 28390 42115 62943 19106 4219 74014 28281 85273 26308 82404 68986 22576 91190 56988 63948 71544 79241 85238 42199 13469 71480 90847 17495 61321 25102 23255 84461 80003 98453 15925 68003 23378 4616 88856 64820 8722 68777 99333 17219 78873 32051 30700 16501 45014 29013 86332 72574 67777 87208 59676 75349 80247 62211 34016 67551 11285 83352 79642 21407 32745 70799 19605 12242 71762 62093 15190 61867 99892 46355 76304 88536 35866 495 84286 15309 42423 98321 23356 79271 53021 33841 87994 64220 99614 36509 70426 41237 30327 87568 2235 19582 94037 10885 77601 61663 53004 39782 26224 38214 60695 97580 69249 77431 19003 17539 46334 43480 68193 39910 42765 5287 22073 18761 5446 20533 5565 44797 71784 84357 28260 73173 4778 53671 42814 41696 73038 27550 68208 56339 14537 41582 94284 23665 36096 39408 80162 27797 86021 598 46947 51324 51482 96496 5803 20932 40278 9806 67100 52852 36468 29111 4436 68990 36723 84919 91449 40858 85265 31659 918 8476 35439 33027 3697 46058 16845 38048 50455 72811 81611 45204 58011 62915 63124 16765 95961 38228 13633 9700 42838 45937 8676 38736 68886 61718 3398 91638 62807 39251 9325 25980 69617 38269 35164 78261 62125 22732 8198 43119 5327 29997 67700 53829 19584 29098 18903 76673 56966 57620 21051 73879 52190 3783 66436 13976 18203 32247 41921 53886 11970 80546 78619 3640 96355 30098 20646 10153 87471 58068 77549 64079 52173 50764 34799 95176 91851 83374 54806 53895 63108 13857 23246 37626 51425 67288 41641 53577 37124 55269 40356 66389 28885 25664 10322 65268 57859 96580 83397 99530 12791 41257 66253 46540 44203 66539 86461 8980 74848 31882 42718 87984 6490 34007 64852 90835 68689 97007 55793 9155 31763 64894 70454 5293 65936 16460 44419 8172 17477 90482 4972 21259 44737 84478 14059 28472 64692 57227 38759 3520 61940 40751 75084 39635 6668 53229 17757 19786 39701 44885 38048 13775 21185 10872 48210 73684 33403 77403 75712 9003 95676 24417 7414 27802 12169 67892 16722 60614 11585 29442 85053 71799 62224 11244 72937 87702 79028 84238 15577 37996 43332 79041 92923 4217 41174 47539 11081 26300 68146 19945 21554 62267 80251 63301 14156 54765 39656 11472 62816 97155 15931 63577 2327 96242 54440 68996 88247 21781 80035 4944 44944 46074 59553 3148 85178 30217 72846 32168 23333 67749 38256 56135 28398 2429 29395 39111 90986 77983 13869 79565 29530 88131 18516 34186 24720 10108 1210 86404 8147 48389 85207 45402 42651 67510 87012 25089 22524 23705 99960 60754 4559 83826 81707 98361 35202 4867 1588 15398 69240 6999 3012 79149 63311 49529 99287 70207 17722 49121 30144 52821 97702 95671 37560 508 31987 8708 54559 13726 62353 14989 7584 3387 27981 18299 51335 48842 14002 69994 5968 14609 78176 47489 70966 97787 30669 136 27641 98836 95208 31057 49365 23016 21202 10977 95357 85404 92360 27992 47654 56102 56993 61459 28796 24552 24979 25118 43455 56724 51853 59750 43752 5092 79325 90040 3946 89206 19085 84323 36516 55466 59681 6875 94377 93222 8393 71659 21629 98925 48415 27142 91266 52071 26876 8355 13871 12233 34182 57448 46230 14678 66146 68143 55636 43818 78564 81257 60067 224 48723 74902 28270 7124 17852 25522 87881 92012 18597 33083 91273 47686 29789 48879 68527 58321 52715 53571 14334 43522 40550 452 63713 41334 45590 23238 31639 65131 25886 56774 1523 64896 50313 49012 5129 43470 9962 79647 32793 34821 61219 83846 91010 54182 7300 48054 33423 8238 35061 25294 67579 25087 79460 61956 32066 3980 62774 15679 77048 26635 49040 23308 42892 40301 65297 75044 37392 69788 9880 6270 73708 41689 9317 74399 16847 68122 21964 2706 81611 753 40656 15287 12010 30676 25182 99146 57140 28710 96864 43680 32444 37496 63534 34684 42216 17347 23674 94548 46878 23579 32877 33657 17490 41486 246 9450 41508 65489 21853 43977 26245 46855 45320 40490 38609 39187 48038 82331 94695 86717 53351 11625 42749 85243 69765 53666 31204 75239 36720 80404 78621 19899 48670 84699 13115 24589 28709 12527 5989 5423 14460 87585 40195 64237 47117 40815 71483 14510 55593 73818 97260 37168 98647 79909 10482 398 7270 58896 30218 79669 13625 58390 40552 84246 92416 46983 98499 21415 93984 4576 38709 89583 79619 68855 59860 89791 20387 93639 53450 17874 6141 11760 76359 7745 30837 67059 51805 80137 38722 92682 22640 23808 7509 81357 48881 70270 63219 96174 58209 10453 54130 36076 5108 11415 35142 94 8638 42751 82145 75282 73310 32419 68859 38023 89105 38756 66919 5762 83668 9894 26987 2996 51657 102 11863 53507 5608 31170 41502 71926 92795 11290 53220 74008 97034 84030 14898 46425 59244 56766 18317 23521 53314 89591 56737 67482 47733 95994 24881 25664 80135 73420 31953 68464 89553 8112 6471 77331 81167 11834 57438 8545 20364 25706 31443 78460 22724 69269 69871 37754 43546 68743 55313 17610 52973 31669 15114 97012 64773 97206 17992 4567 71352 26438 42299 91914 71612 76725 17792 74394 99921 82035 13704 60134 29550 98969 34359 2557 97210 69043 55363 78076 60557 84312 78873 84869 81259 63403 13708 21932 76545 11413 38462 73926 30296 11656 80688 94771 6122 10972 72318 87234 92410 2379 20264 71312 45331 86873 74227 42235 29536 16646 64262 16817 90596 84533 10230 18774 30314 77748 52529 38765 31934 84031 6958 67177 87332 16596 84004 34755 43964 47864 47294 84860 81826 14823 39657 31 31028 16210 16695 19707 94039 13648 44684 32223 31692 14361 42855 55438 75794 33684 45225 98814 66469 61910 49659 17723 99932 62833 86749 82942 28456 40305 75327 10013 97717 77369 59060 9978 39435 86131 42705 60020 81207 60507 5030 25819 14904 69252 89014 57905 91276 26831 93584 82073 62103 88827 33592 24385 36945 13404 33225 42981 71829 56917 24852 58702 70258 72889 86310 17818 85063 70198 41222 94028 80499 42412 54747 50603 37938 39493 239 54800 22281 51633 10546 60562 83280 36405 83682 74235 67877 69217 44348 19275 96680 39029 3299 52671 57216 55778 14116 60581 97108 64606 45879 27224 72003 96749 26211 98276 95831 24170 54026 96708 13526 88566 38019 1476 52310 2960 29235 99877 75187 96094 89398 65363 79659 38168 47235 17013 9469 14383 58880 86422 40779 97253 87145 52127 51164 42672 93757 30352 28085 90533 40159 11823 14262 10168 31348 39561 86639 27997 16157 54808 89406 66275 7382 34263 25575 22556 6296 17605 10081 87388 55064 3234 36656 61787 8018 78794 26173 12123 82129 86513 98542 51907 39454 14055 80718 9148 30133 8773 57388 34366 12053 53764 93069 17253 33110 40838 35542 90568 68880 82954 32798 48181 79079 64479 36381 2327 24536 29701 6323 80064 70783 6305 64369 44957 54537 62941 15298 24900 23505 57160 33210 81403 58719 78111 7649 76536 75435 59309 67283 95994 84988 31741 40335 57292 70278 24438 31506 1290 99424 76443 72851 78711 55151 71954 63194 32998 19327 86189 99468 26593 76179 82846 2343 27829 71368 29864 86054 8101 14255 40681 33394 55672 75788 38501 25898 64518 19622 56763 47129 66883 33467 98231 31215 17121 65476 52531 9428 13031 17788 77980 28382 38729 15275 19722 41365 76619 39449 64686 84839 19926 70422 62559 13472 98357 63373 64337 15261 85756 29683 78018 33636 27689 62108 40669 88258 23585 10887 36524 91181 79870 81951 59259 35101 77178 55677 91126 13959 2209 18163 19753 3007 46600 83815 64274 66120 80201 60649 26406 44045 1156 49454 83645 40114 71421 79579 66792 8860 75262 16110 14837 43734 12486 88557 53214 89379 95440 72035 59802 62880 98279 30230 73666 47876 60386 77096 13316 84302 77965 34042 63469 94731 55159 1643 44389 18401 83526 45856 41543 56077 6083 13195 70594 82483 58352 79520 41908 8210 5254 54461 25164 63651 50459 92584 88628 12468 58121 66768 57415 73515 32551 4574 48999 32959 78548 73266 67486 26066 97371 15488 35225 5300 6000 83679 45945 29601 43128 95335 35071 31524 81415 88201 56140 23590 55778 93514 13980 68878 74787 1569 23975 10021 41710 48924 80712 96069 6678 73503 29847 60977 80624 48926 71218 60744 69026 51483 83595 24054 62314 16195 59746 35479 16222 15501 31424 10980 83846 28846 76176 58931 81248 21157 21121 10322 6051 49616 4687 84325 43938 21675 4695 19227 11518 86795 17162 65180 44563 17347 58100 99941 34039 65850 58542 31726 18010 86205 84537 3236 3301 24441 8205 39596 49662 83061 38792 22759 92584 98465 76498 94111 56801 13178 32653 84802 2783 55412 55754 78494 38899 22962 51032 16076 85601 19516 54654 37685 97072 24774 34364 20433 72740 26955 95911 49864 4805 47176 44704 96419 69666 10329 16847 14376 73259 35138 96544 8553 33786 6289 10445 15485 55925 45856 57802 25777 88708 73282 28761 86523 41012 77883 748 70322 65122 87803 21172 41022 89867 25388 53440 45609 27221 87425 34061 60016 42087 15216 61177 18691 92076 95529 53012 50081 56554 98003 47332 63224 92345 83226 60985 27740 45321 24041 6228 5783 88450 1615 92405 68678 68947 18356 33361 47496 63499 67105 74361 77990 71717 64842 17360 94351 88187 44445 47700 36400 83622 63417 90763 48807 76351 98975 90963 4154 96575 56274 97741 84029 47072 54951 66911 83389 92864 26370 76427 79054 7381 97346 86736 68038 45389 11823 19416 76013 79575 17987 24270 87314 8912 92609 25141 44029 27157 49756 9837 97153 39956 70326 68113 22651 68060 17602 62141 4376 42826 89025 58004 27251 57810 68366 93590 35629 67271 31585 93897 27648 6356 26831 90144 49135 33850 83941 81672 43020 78882 6808 95851 92182 42896 76974 35370 56314 76010 21757 68786 64981 38526 48982 55509 19215 54454 1831 65593 7960 34186 15585 73264 42691 64812 66158 74014 56392 39977 41742 55859 63468 10260 12862 71206 48698 22967 83593 56784 82605 61012 71212 16804 40436 5092 4653 64515 22624 27353 14985 73411 31876 65948 93156 12609 12383 73211 11071 850 72926 85143 95876 16989 45292 80700 19111 79495 93254 38697 61767 90337 7656 48462 11395 61983 13256 70930 9927 13359 96269 63528 67405 91671 72925 90606 4433 43336 81790 6733 71780 91958 67229 33353 80015 11146 29326 79270 4407 29079 86666 25459 96362 99818 77002 960 57592 61171 38382 85876 73162 48537 73589 38724 19558 45950 42043 92750 307 77783 96152 44804 51535 69480 24386 48346 91319 45718 12963 35873 74 7400 25995 44510 82033 8879 47262 20607 3903 15711 27698 64948 13115 97750 48370 94657 71662 22433 76942 81954 21844 14264 3076 94858 94824 94126 17589 28158 96172 6109 14750 16499 6515 2724 58962 99873 15599 48427 42581 93763 70187 19530 29548 25517 15863 81639 43327 48212 40439 39913 18277 14371 69760 55547 23871 5207 64894 74467 71116 23830 40902 97903 66359 83176 66041 82926 954 54170 54563 67185 45190 65720 34403 46851 72154 67008 24304 96812 46436 24595 31205 41225 71834 23777 69691 3403 80977 33848 61656 2518 385 65217 61748 35799 89458 13194 85361 42081 41817 83313 65112 69541 12388 31815 60614 19178 44197 74089 71472 22434 23498 23755 7846 65017 11946 26837 34326 3962 12154 84760 25082 5861 85726 31615 95702 96559 37874 9203 33421 19819 70768 91307 74749 61897 12458 45812 26110 29839 36744 49303 98124 66413 78226 3669 5288 50854 13359 41637 42893 17089 23143 52245 42803 30617 44048 97444 69715 9069 84347 24124 24020 7563 77386 79995 93989 78466 71544 72921 54831 14035 34400 33489 53293 42396 33825 13862 90082 7594 42717 50341 67356 1994 40311 28461 2556 27290 39239 69984 92039 87136 22868 18497 85614 39548 95018 86338 91113 56807 63068 21320 61839 39891 75323 56046 58911 85744 65892 49867 29699 68423 58043 82873 6184 87833 87708 39496 39 33048 67983 13287 16876 55881 56488 91604 98599 98929 73086 61127 63099 44375 99115 91657 20713 40658 66746 85150 73681 6388 69209 78250 76335 16829 35758 60170 71659 73692 96795 25929 72179 34688 30197 6217 69471 13569 27204 6136 98470 71390 78524 57544 24174 27639 61629 41350 20980 26819 50728 45544 85819 17993 68122 62580 56913 752 67691 47670 65558 17612 36750 34058 54121 69083 60274 61397 23049 29409 48142 30572 69552 19741 40443 79499 66491 41432 33917 43870 19415 50772 60141 650 66950 35283 98694 29567 92997 626 75877 88493 32526 402 51210 14345 65870 48682 58430 52510 76550 48292 83592 79196 2867 78124 53338 59404 65087 6546 28288 50826 1118 31111 79272 34645 68534 37321 83922 93041 23 96769 56185 52445 67066 79468 17324 67832 76165 49889 19129 34362 24810 50081 1872 36070 69708 34081 82238 49717 24525 40014 84878 68780 71372 19031 92386 42125 35010 23710 79373 76884 47256 61730 84168 57033 60843 156 61617 7917 84828 10695 75733 58552 21421 34696 31132 23077 84740 51939 10338 35987 49417 40109 8764 13769 93517 93760 74808 20435 64977 59551 76410 21331 99917 27764 7888 51962 8668 69101 66436 73256 93512 78862 13461 16514 56419 63284 59117 77201 10984 9443 63782 85985 39378 43274 80514 94447 19157 42544 39834 86770 73871 48769 16450 81693 27212 92058 52953 5562 15763 86355 74394 5884 13544 58498 22948 43362 74982 97323 85906 83581 40051 87235 63903 13044 34806 90008 86407 57913 90602 23284 86468 28265 27988 35643 61299 7586 12087 21243 23171 26603 80235 18761 79516 6251 11464 27327 73741 75617 37719 1931 64627 72701 62070 38234 11319 51370 34595 78850 80196 25817 8282 65020 87927 33317 85148 52444 10335 74672 73550 6559 49467 88672 50744 22143 25540 65274 65671 77105 80636 24840 41010 53501 33001 82072 65809 25033 39797 35703 33306 54265 51056 68008 59085 28967 39781 51202 70732 8448 57274 58542 95899 30347 37339 71681 86505 36557 17202 72701 58553 10258 11962 62957 62534 31112 98694 3586 42076 66519 39449 29459 54360 89566 82426 64498 12393 67377 40014 70202 91109 35670 92937 15547 22244 58830 10348 44876 22836 64633 48166 5698 35829 86645 478 40156 28149 12629 14554 19454 58647 96015 42437 88592 4664 9529 1362 25155 75517 4252 50910 47945 17400 66491 87016 19503 25146 44910 70874 69998 37187 77591 51350 96175 36658 25505 79457 86943 96715 97850 89547 83996 16794 42371 41279 54570 61325 30681 16427 21228 99895 20216 14837 48597 32112 89533 61090 73535 58504 46651 16035 4083 80333 43954 34872 96054 79258 88962 47189 4910 64587 49761 56561 85888 95961 22571 61604 16120 5460 3425 93919 48157 43799 14377 52564 7949 56544 81100 99261 71571 79343 19370 82662 96451 91535 73288 45317 90011 4588 64634 17590 32063 18338 47688 42728 58804 20609 872 81934 62452 35500 70211 51174 839 20108 82018 45870 54117 29679 2855 4231 6675 34779 82818 49572 33473 5741 20355 77123 42170 96966 35894 7762 17935 22570 79682 78316 1812 40127 69030 91724 34417 75103 28152 18170 35008 41415 57741 32587 10772 65797 98481 21079 93753 64950 51997 14073 14582 33358 46960 74768 11489 80127 76329 54900 82171 41176 82215 61198 11072 43757 99207 28903 97921 4852 9046 64573 21978 36090 64561 92982 88218 42030 15946 5601 69915 23556 91782 79872 70565 68459 74091 31096 23000 72787 68683 62465 57642 42386 69495 76568 50708 82235 67289 2907 43457 67138 38641 95064 80371 79309 16424 26947 50282 67237 91187 67364 23083 6019 8527 91726 7153 54050 73617 99078 53612 95298 98325 98952 48172 54691 93601 26945 42888 98091 90917 33038 44804 44840 65926 74229 95394 12592 33877 66305 70874 48702 28426 97331 44745 72009 16472 49392 61985 33645 58420 45414 82465 97546 96729 80664 73243 26083 47597 27275 7889 30828 95331 19957 89799 89252 21951 56290 85231 62709 18927 88148 74159 62897 97359 7232 90890 36283 10757 72537 30449 62284 1459 51811 89893 59843 22066 26821 36734 77866 60957 72627 85487 90894 3671 18807 11401 13989 15169 18942 88682 22994 3739 71050 81629 4937 81190 60431 46460 1100 84486 6394 23189 88534 98075 81091 70549 6663 60169 97966 37887 78007 96607 20105 30642 9628 98948 42503 98878 45515 14251 4176 70640 63179 39400 5964 14140 40194 79633 79329 49559 2866 71838 11064 10483 16522 73023 34122 59789 13191 18581 83240 45467 56241 56488 60297 2128 49696 32682 95126 47749 2997 36175 40467 62788 21122 213 15631 86125 69384 2261 57082 59103 55573 93147 69670 65353 58272 52824 54395 43691 50729 55589 16380 87033 4198 31552 38925 73940 40756 17018 48887 90450 73488 6971 5487 64848 61202 5704 94371 42773 41646 18295 72918 25573 63542 24295 42415 52568 34573 53073 52421 2999 62056 33500 32646 70361 61821 96929 37910 22172 45692 75289 87985 79842 54692 42123 77081 68285 5249 54936 13116 36229 41723 17586 19899 61116 38837 51646 51170 37572 83374 71289 82302 74438 840 65904 81076 49401 67854 1148 9499 16585 95711 87338 77768 35602 13839 48973 68538 66605 65544 12621 55453 52689 22764 58837 67541 3781 42767 51540 72851 18670 29812 41533 44525 24960 55221 18610 76139 1929 63958 69495 74768 7416 10319 69922 21571 26532 97162 40457 47883 89681 76897 81767 42942 43293 83816 32342 17539 55384 80484 21263 33873 29625 79917 40708 72178 85694 39073 2686 9494 20377 89158 13329 17480 75753 82980 99681 67842 340 40836 13842 84238 70049 63838 85473 30047 82165 25687 52771 22269 17178 30800 80296 84003 87054 11942 14792 3578 38314 62337 59697 43767 29158 27661 95899 79432 73041 32275 76081 62813 5844 84714 13341 69467 10897 18861 15976 73269 97554 45182 35140 19500 68527 32186 44585 91704 5313 64310 31192 87870 77496 15293 51309 66697 49936 12741 19983 84776 18859 29332 40550 30297 60754 29108 70961 78925 93485 37926 46678 46724 89075 73557 87296 12154 58702 89472 19976 60177 65339 63512 10033 36301 40006 73638 63658 95909 34114 26769 71101 99087 54328 61926 61656 60430 15728 36364 34237 14530 9639 80394 64102 66526 16992 11958 43257 75992 1795 63391 36923 19032 48666 25341 70699 32351 77126 88293 16932 87157 12346 328 75284 2609 73876 1475 13205 47717 47347 12386 74700 90404 54290 80260 81919 4187 64845 92918 81641 21439 13782 36943 73143 37453 45573 34404 24712 3026 84746 53810 8392 10614 58959 8886 14560 20535 68879 48144 41498 56768 47875 72345 44845 1231 96220 33728 14621 41449 5542 51462 61083 84853 17253 41542 28354 71740 95872 53131 16361 32134 81670 49271 22840 86999 31455 83918 803 76391 56860 78340 37124 30450 3022 24135 34549 72129 2411 56456 63062 20021 27107 68608 72086 24176 44279 15756 45283 37164 83510 72309 7315 7496 81856 18852 73306 46908 94376 32371 1081 77001 7668 89163 50844 2310 64307 63175 64335 92894 85940 82701 4161 45734 58288 16762 1389 63248 39718 59414 40745 84504 54929 73493 88282 51946 28010 82362 48150 80290 84118 25508 61366 43415 61297 54591 9388 35536 38726 38672 44491 62686 8269 50255 32055 90905 67877 36323 94759 99597 41036 67536 79205 70246 36301 74431 88284 53194 57025 38732 26304 59468 88785 51661 18608 19831 20857 84467 33707 40105 67494 25695 23359 11516 99240 71091 5636 9979 27356 20346 95026 35335 85451 97920 24001 99587 16863 79462 145 83306 81117 25574 34165 64149 44312 7507 88479 94381 17591 75796 65224 5741 52896 71240 38540 42938 16815 11373 97951 38536 31751 94715 26424 64729 73138 35332 95077 18888 63943 19174 74522 69778 76472 6566 34943 36273 89159 53025 17185 95185 42982 54033 25782 9776 65781 3643 19803 95265 99010 58933 61786 37817 58807 31473 34002 78343 35371 81438 34464 92105 39067 17188 88176 40482 74373 48058 7216 836 60745 77950 937 45096 83238 5466 98551 64676 33022 63835 44283 75734 71698 97217 75696 98561 8446 9889 60760 21744 3710 58362 24221 36395 36251 61775 93478 45249 97068 14460 78226 36158 98546 61452 29375 28635 31068 12730 79447 72022 99320 59825 54933 11705 82558 24146 59775 38885 67160 38592 4567 64119 23401 57070 59209 51981 73290 37250 22820 20986 14422 14802 33320 60088 14043 62030 28111 79563 50901 66287 36669 92456 91089 48400 93599 79149 69609 98236 81255 25318 72736 50068 41000 59907 31965 50008 48011 2477 88723 55846 78530 94593 39210 58000 72837 66448 40572 58660 7534 62379 64933 77650 80996 35734 27004 6834 77797 72163 68698 3912 13224 32431 4906 12671 5290 26654 40543 48316 31579 19764 53235 17381 15596 83686 54950 35793 31699 74523 23908 71570 71025 23562 25293 31359 63751 20587 55548 13405 50574 72920 57196 99299 95479 84379 22014 1705 61759 38835 82252 29296 22113 63312 18911 10015 20974 35281 63527 90525 47684 97808 58760 85378 62175 27697 95308 44727 45304 14807 438 1626 59321 54890 18738 85999 10538 78993 98585 47622 19203 38741 62598 88750 57318 68730 86270 25379 70221 48320 83090 34202 47230 23918 90289 13204 11466 2442 62007 71726 19491 66886 13101 57320 43697 89826 76555 79236 68516 77458 64317 76654 25047 43864 91218 55206 9670 11202 85659 93605 59770 40586 82011 19501 37220 18328 27694 45081 12195 2178 47710 89172 3174 95417 76076 94137 19765 44572 33617 78298 36613 17108 61960 58282 91647 50715 25986 39631 54389 29188 38071 96926 7222 58117 73954 58708 19053 90945 65479 71058 10906 23395 10618 27804 44252 40309 93172 68761 3007 15425 59344 6702 37679 21123 62371 12238 97450 85817 93635 27823 13522 3864 28987 80566 51706 21780 91028 23007 63914 41984 27655 28430 94666 79620 67429 60613 88660 84975 65503 23565 40701 3315 99528 60098 61184 83654 9716 85843 89959 88078 18108 51044 72907 61138 35534 25706 96126 94940 85343 46216 20056 10241 82007 38262 37680 58896 26046 85058 17501 91290 4698 23879 51781 32460 17733 94877 97681 50312 88398 73652 38023 29906 42955 4176 85320 1687 17798 61789 97103 87077 83795 86541 1745 9953 25646 48020 52678 14877 77734 61522 14077 95359 31215 63423 46251 32820 74853 37481 78540 54035 59366 22213 80099 21316 99323 54429 15103 26569 86491 68069 9860 78895 25973 93443 30878 72183 41318 19467 49558 90522 91151 82041 5084 95807 92109 98080 34964 11817 17415 69571 77243 53593 42337 1343 56423 88278 81457 70000 19452 59624 90071 88262 45539 48597 38544 96389 2650 43286 84650 22817 94448 18620 17175 19376 59746 25136 87447 69812 98496 39737 8090 42942 10547 91144 66417 47199 9554 6454 59805 40592 98243 45167 3469 7753 9122 57530 90094 8397 32908 4914 27717 99666 40976 43223 27905 29794 91060 21626 31905 50606 18274 72514 47373 87252 79937 21871 57033 91679 76453 67537 78209 2752 72583 90773 40379 17736 25619 71708 88541 41058 59527 52635 19319 42036 36422 2697 14807 16958 90743 64748 49874 46502 56292 68039 90842 30418 26759 5097 71003 38806 61030 69584 69715 26915 75770 55827 69849 64360 47332 93143 83304 59922 37766 25399 85122 96920 78114 12504 35182 76046 79736 38235 54884 61431 8443 22814 29208 95943 68688 62762 27643 76857 6394 12709 21315 50972 43743 61115 36123 93317 52067 477 40999 9862 87204 81991 28631 50850 22971 31393 61613 75212 49917 66419 91456 83879 39856 84874 49505 88979 61544 95666 36699 54080 9511 84157 31304 62506 79501 68955 20502 40026 93343 17174 9881 34471 96371 66696 53032 43253 97381 32941 42111 60242 77173 55040 81134 71690 20971 95137 70226 47130 84033 95215 41715 32361 32957 89404 40334 76863 93516 56568 8478 9226 64362 42506 96870 24949 12066 58426 44615 5688 35101 37613 51725 41506 68853 34694 65523 62082 37524 68801 84295 75843 1455 10192 19843 2759 86116 56388 89592 1955 97918 66681 57134 24693 25025 59672 47281 69432 73639 77087 15488 88742 50848 11168 50398 95122 26451 30776 2746 41424 79775 8770 3042 2101 48680 98039 27127 45885 99938 72960 47593 50360 50375 94727 49410 14929 35419 65738 42971 50735 46011 7638 15624 86801 80907 23677 70918 34478 62456 10541 8025 22 83763 15149 46833 36151 38059 75386 93354 25651 99077 26413 68628 81538 38206 64821 97335 65977 64405 54058 14090 97280 9390 67471 49804 90303 9578 90839 84105 40801 92678 25248 68353 97618 79917 94173 61442 3318 57181 37815 33937 49360 42673 81519 91540 75836 49559 44295 55359 63853 34293 89576 8307 79073 78127 20198 93441 28058 31593 18098 15249 41961 39692 44483 4178 7306 7008 99167 87359 372 25438 97893 54340 81079 80871 18674 31993 36921 96622 26006 91115 87201 81784 36171 74917 54878 28227 1871 90475 33178 82856 56339 14311 31371 58074 76209 73806 45523 35603 82422 79143 10194 55267 36351 89374 20677 35662 57065 1254 8853 61128 66007 63615 41457 9751 91533 57016 92791 36213 75330 92318 55462 95651 68401 84436 57159 17858 56845 34788 79820 99496 44975 96066 9516 87320 67873 74998 70030 11067 14189 2952 87635 86414 35576 76376 51227 24465 21868 20728 3077 23659 5591 57099 48895 56258 57221 73329 50830 57924 55577 63093 40268 950 43313 95472 62846 67932 74684 24979 44679 93968 39719 88924 12852 89431 31597 51967 4708 46159 11278 70197 5631 54427 65865 66274 68973 55756 43575 33561 84586 17172 86688 41682 27838 24652 40059 49209 34549 46072 74437 87016 58417 91505 28736 81789 65247 61723 14192 31613 33582 214 44904 8161 64562 17827 29110 54974 30862 98731 69898 33353 94604 3283 79773 90246 14188 44360 24553 19770 24817 69438 99708 62930 5447 93172 85309 78564 39658 56260 78338 56483 15225 52717 65119 54986 39450 12342 30687 52653 81160 21133 25984 74370 81446 58003 55073 18607 1191 63741 53152 60616 40653 17400 50762 81291 69999 16707 40214 27854 56238 36166 80254 72331 5129 87100 83910 18043 48579 46105 63706 39759 46032 56880 93163 4288 82706 76897 85773 44597 59938 15150 55611 91539 92631 51316 85108 89684 29904 27972 25253 45268 15560 83723 79169 9670 78213 76513 22243 3969 92321 91853 14852 73634 83496 39883 33824 79913 12976 71644 58020 58728 90323 75671 68060 59148 1903 49019 36816 21541 66217 32689 13051 88098 34701 17937 29763 32745 66070 72256 61905 95556 90010 43659 26440 37063 65141 99700 98047 54403 8092 78409 84069 95819 89612 26939 89875 95465 85464 46022 94644 65422 16382 55985 42662 13283 28907 92890 77895 314 99280 3053 25697 90820 28403 31461 99337 52370 30350 63900 63568 86860 8667 46831 9386 32965 98241 13971 57125 95137 36954 14662 93247 67009 68771 22999 73861 87505 73371 47933 74590 74581 76759 67881 4390 86033 71946 78503 6640 9910 11421 89080 25874 47499 93695 52384 36288 82079 32668 35619 31659 52286 47487 24527 11015 44397 34482 34832 25973 488 49100 23010 44125 36290 26777 33411 84254 19587 49590 88245 69406 71605 3093 73535 45503 15766 68661 51088 60667 93028 76762 27523 91720 7221 55238 84746 98422 42722 89020 98035 77072 10899 77305 62054 53296 96358 93749 44668 82004 49921 36413 32893 91055 41640 9988 6162 69471 3948 31706 48018 69531 98071 71469 18456 66071 50236 69706 76581 70879 83190 64486 31371 30035 32404 12854 63560 40137 36151 99942 31363 54242 75377 16142 91513 22470 42961 76663 54741 35284 61903 72090 94699 53747 50028 93806 10363 22943 28601 55372 34665 70410 63048 1962 70565 61468 19199 67886 54367 82833 64300 82969 43698 76964 94454 72682 15432 62015 72803 16392 7613 84784 98073 96707 90933 1689 58688 26207 90283 87747 59753 19498 75124 82301 82817 42227 18773 46472 11471 18558 49387 50384 10467 69623 19611 62431 25043 65179 69718 66805 73850 56885 57424 78370 20375 88072 62688 86470 36946 89445 86984 95907 40696 52649 6713 18981 60737 73572 72808 3828 537 46640 7703 37406 89302 94147 72414 34995 35147 21512 9541 64644 23617 2449 89894 3607 47529 50554 45243 42315 29786 16509 97571 19089 98057 49968 65397 53365 68443 36140 91555 84681 79586 8961 72914 75744 66399 76577 60022 41030 87156 4856 52935 12178 84947 91352 7222 56442 99816 5961 70508 61244 75513 96584 93454 80848 45367 64354 82571 65398 48697 95043 28222 71793 55757 52771 52962 2191 73240 83611 87818 38630 52551 73264 57152 8104 37657 98603 3221 38249 32174 27197 17855 68463 81896 81590 75927 55240 4763 27823 69840 49196 4071 7133 46448 21074 79148 44122 36054 43500 23490 80216 85192 10536 2889 80514 65710 31354 60546 43605 90821 65496 57345 74915 41661 96816 89649 71893 9200 28906 10801 74855 2294 93971 90083 7268 60159 91142 70708 93246 51021 31055 2124 2779 84648 84540 84512 43435 30568 77064 53885 38374 57421 98823 34232 28758 34150 96842 49468 95854 55076 40734 7872 53594 4287 7892 77438 69059 24178 9115 57107 90363 67282 24394 37111 22537 93338 39184 85501 73896 91649 50032 19775 39449 38059 4700 97218 90329 84784 43005 96757 37102 43255 43187 33440 59661 7179 32844 67150 53282 16383 43666 64489 67687 3687 46666 58455 96188 5085 96913 44927 99380 21238 939 39384 18805 45538 20478 62406 36372 70927 96049 10672 5304 39381 9259 11514 56879 63471 2448 20432 80660 61481 89402 50032 59796 10899 38898 35271 75254 71112 98445 68662 93933 48609 30098 29515 81548 12126 52127 41076 14754 98332 55986 64573 71180 10529 39381 3601 94482 64888 49001 88605 67023 58546 52340 34381 92988 66411 19171 1695 89663 70916 58323 35357 69390 99689 75718 142 98223 15909 47178 1626 74892 1714 42662 38597 69396 63652 33030 75648 52547 88626 7185 34644 40325 42664 17748 76198 83696 47161 33926 47444 33238 16772 15614 84021 60514 13674 21508 15750 25323 29304 12195 83739 72836 94735 17512 35683 81550 32188 51562 50013 54825 89964 58972 58434 12919 16606 52490 88355 99891 1482 80178 55810 68378 35179 13775 73169 37221 79206 56205 65246 4932 45220 8135 84769 43753 10792 11691 8867 65263 51671 63318 56886 1570 10081 78694 90674 86698 28006 7672 89302 66464 73313 50601 34534 54152 59847 87836 45251 64774 54264 23255 14337 20123 5181 17203 29166 73910 44543 48225 49464 66781 70092 80658 12353 65264 91510 31133 47737 16532 86267 20456 94404 66588 52810 41975 96097 9054 13943 16373 73662 42262 74192 54664 24322 53984 87189 63371 99810 17540 2632 83986 45649 32448 76256 79785 37846 15527 53035 97577 62188 93236 28587 61279 72195 98517 19700 25084 73602 17350 245 41110 12681 86376 76586 30550 35665 66519 11286 71678 19162 25843 43004 65714 73415 89082 70548 61976 3023 10028 84911 52398 84514 70960 81775 54900 70152 20228 50807 37426 86135 46521 37709 79195 9130 21686 29067 78784 35938 40686 15137 48501 12328 15577 28354 96715 39780 85978 4261 28659 52151 29673 97803 80312 89959 37419 12230 72790 97784 24461 6020 67285 6150 71692 87308 89302 62025 60760 64287 21463 12437 26595 50132 65430 21108 14402 45680 8971 10368 53728 57678 21348 68151 45243 64813 48152 10937 60886 27936 44530 81334 20989 71244 50836 72280 12288 41775 85906 71876 11466 16953 24201 25665 43346 18659 20450 24431 29080 46736 32791 12978 28411 28958 99518 13696 70023 39801 62738 77681 87059 59691 35108 31417 55994 57726 82366 41984 98647 46360 74458 22064 7432 30915 61430 39929 58609 25110 40737 14456 83484 1737 22202 76486 37727 37338 76525 51465 75497 59167 96492 80088 34524 88148 57127 30754 49479 39573 21555 3487 71646 60333 73583 16742 36083 21630 80167 32209 15858 7642 53333 40563 51044 86362 54780 69624 59146 64286 75875 66425 96234 56202 56296 29778 38055 25112 62110 71128 8825 81159 16446 22190 81281 258 86819 1182 82779 82567 33699 97495 80568 6658 75536 74579 34545 8779 19451 83462 48457 89594 57904 37805 27000 18140 39059 59396 22245 21156 1119 42517 18433 66007 10943 24803 9487 24163 73600 1866 81640 77443 92810 22485 54959 72975 2435 42769 64912 27832 44676 38559 5903 89902 59310 80727 72585 7661 39242 66060 52544 42129 42946 32495 69125 36084 92224 67598 33467 71079 45160 53050 11350 39983 7044 12533 65410 47837 77113 4829 90050 66981 1597 23026 65882 88943 21360 85286 98873 35573 80658 37684 82113 3945 6786 32152 55216 85695 82846 45930 58608 15971 73438 42491 68734 57735 61810 96004 91687 36456 55473 29815 11813 17044 56808 25672 95335 75428 64862 54050 11424 15658 35502 2405 12449 31828 85846 41748 79701 62985 30479 15847 24429 70518 88773 88447 82719 6738 68040 35038 63733 36243 62814 17901 48286 37656 55489 79492 61227 12427 95181 59249 66313 68652 41725 90522 52967 52062 7239 72145 4743 38797 82791 47860 3706 9264 43180 11596 76152 95275 940 87602 48996 81939 25154 51212 82470 80346 68002 4291 15536 71827 75216 61919 79056 44311 455 77484 58400 87013 83148 88137 44470 92098 5191 45332 34416 25324 73886 98898 34934 87315 95537 97895 39572 7537 38450 71481 18697 68395 68398 4022 81365 24259 74226 31337 85536 9881 51348 42090 1411 64364 91494 16885 41708 22382 37009 97457 63950 36172 6000 86811 16427 5596 88682 94393 92214 3003 45576 71882 45439 5465 20498 92736 14017 11028 80286 68066 97982 85085 10235 63542 90755 84551 42023 46322 23326 70215 4410 60796 56893 17470 15996 45266 66114 71953 72696 54519 45687 1585 26739 37003 72842 51488 19757 85843 2194 30664 92258 55718 89809 6317 33563 90377 40159 98317 99208 75585 21740 84824 72575 69042 234 80424 89968 34771 67831 64861 33890 64383 5052 34441 4896 90308 37253 35945 18042 8845 73343 77401 74177 28426 91831 56964 35877 5730 55443 23802 77682 2327 60604 45091 83642 13055 73739 19679 79424 96532 59934 28790 89259 44174 27633 77031 26899 86700 59639 80816 61303 73840 19269 13138 44317 63082 79484 6397 30658 10095 56892 73082 53492 43577 78142 57003 17867 72102 82874 53718 34194 2486 55902 22129 49911 782 59217 15669 33112 80696 76696 3442 35160 49627 32374 55618 28324 38702 50353 16219 5472 78342 98624 12144 30860 22061 98978 53789 29493 65863 39615 28060 65442 7125 44595 66089 56332 89487 89594 92173 47991 19739 60733 63688 8634 62927 8456 4214 24717 20652 57834 96649 85278 77629 79977 25012 24790 61331 37602 89723 27610 89191 96089 18690 41460 97909 35835 13215 4318 8245 1809 24645 71225 49435 31501 70457 28565 88225 18408 65683 12288 69560 12860 28551 96142 71900 85729 3896 52091 45382 18371 19897 66481 14523 58352 94731 80958 13254 90987 74193 80249 57493 8973 6902 15694 15077 16492 57538 31879 7522 49593 79427 77475 19911 13054 31599 77275 32956 26478 12363 21674 62911 93853 2334 74546 40090 91330 87020 86285 34987 95624 23352 38670 94295 8575 83193 1824 5897 62565 50679 32751 7090 71488 85298 20675 51244 51624 64580 16533 49569 22329 53097 84337 66565 59479 96276 77745 49138 50863 99923 99257 91753 1371 22184 27102 69934 98554 27557 8983 87455 9410 56118 99546 49173 89755 72763 47519 86019 1419 45204 65987 59756 14211 97148 49355 99086 94729 71561 9824 34756 48555 76653 52729 16043 86634 67591 46246 92172 31839 97249 29126 82692 93675 43943 61310 10303 6380 93112 31890 65466 93939 64324 44949 84766 68883 62180 65196 70727 46233 75161 39433 89689 85531 46927 94144 10261 66409 88351 89174 68088 29724 8777 64365 28726 5041 15385 31068 59141 77341 65966 2456 75659 69087 16230 74920 27314 69287 89744 48160 52076 84212 76086 19358 58936 90450 22440 14320 27097 45649 29536 3893 11226 16753 48781 64013 4390 68709 13946 56818 66324 78049 40778 7875 77547 20626 94961 97877 72156 21939 76689 58187 67897 46704 3991 44597 60999 73791 5975 57485 26317 16145 43163 55056 60432 67670 1060 71265 12125 66246 43654 8701 51749 55766 15278 8788 38027 31138 83837 32150 25228 47968 48893 24668 17116 72175 52958 47638 58454 50358 53616 73617 36068 60636 64879 9182 67041 41819 57090 29841 14841 28313 55552 12749 93492 78134 33801 3976 56686 33483 16494 50900 2784 28542 24947 56798 26441 35131 14347 16387 3525 4196 91847 24527 97374 27296 74670 24555 87830 8357 41202 50064 63905 62426 75179 45569 61817 6303 23215 65134 92382 45265 26490 76598 9051 5674 11172 48651 95395 94415 63324 73385 56308 94393 38367 67874 76547 43002 97447 66637 57360 85552 89292 30264 86312 16919 49342 98179 52126 68870 2863 46269 68950 19992 53702 13874 91638 46358 15815 55338 6568 61047 61708 14892 52810 32706 14668 13246 13180 62872 52565 68534 81882 31508 47484 20661 66808 46912 43834 16919 9634 20966 26079 78195 78313 52416 1826 16020 85503 73300 98054 15695 69805 95906 30954 1451 33962 90849 79993 71662 98805 89686 96956 59452 21911 83646 69014 68629 84404 67481 79480 75583 91190 59016 22782 24342 54617 25924 37982 26034 64009 56537 39686 14134 70834 21427 47036 57752 41613 62828 12381 79923 48318 87415 82621 75069 37163 87770 14460 50415 10635 92544 66731 84249 69802 17070 82169 14993 8479 11698 92996 68245 79929 52640 71748 71452 4044 98942 25803 76181 4313 76611 9690 27584 84922 63964 58136 70750 36960 45252 12379 43099 37354 19916 20321 78282 37905 89600 82709 78159 20482 33414 79213 36265 60405 25896 32313 24366 85754 89214 41285 82988 57052 46749 68018 91902 75707 42408 70869 75697 19281 6597 43187 83349 11792 20195 64746 29189 17940 38401 64320 50754 65668 83281 68375 65245 10500 84234 45352 19739 31869 73568 49888 56710 82440 87780 35190 28162 85075 83170 20398 44364 59784 18612 91219 51945 84308 14284 49830 26798 83260 71592 94675 33050 69454 42465 91294 91326 66074 40123 95311 17020 24336 88196 62532 27654 95623 47211 28680 36288 76230 78815 99746 33628 26469 18454 78007 5644 49846 46236 86643 80327 48762 59143 48873 46901 91232 13593 32768 69365 23180 16326 55569 63264 49042 34047 8272 41043 1537 89878 88098 30340 26082 58472 1597 32329 82544 1224 53665 30911 25736 75436 3050 26251 98193 76875 19111 59019 25709 73196 28281 75076 46614 98725 26695 50624 84218 76035 44530 54885 94268 70816 10330 773 98457 82166 40662 78299 32035 32636 33935 81602 86840 93377 89612 66286 33301 34846 44994 580 26106 41731 59014 40818 54264 85779 99952 92866 39032 3058 13394 58358 91623 20717 1924 39334 69182 52506 49018 59319 24994 8758 88994 13132 48539 27344 44138 38477 51044 13390 84676 99905 1068 30404 83313 46265 85930 34275 21672 31925 62482 86739 58380 35922 83266 94563 61653 57769 19967 77969 80947 48630 34609 47203 74713 61936 93051 37866 70531 54126 29240 26911 99855 79977 47900 12898 20301 48543 97399 6612 88460 97378 26246 23264 86642 32222 35113 86078 16327 90275 16364 30663 38800 62931 66799 23839 77975 75556 97214 60163 14840 91328 82288 77076 24130 12061 29004 46479 46303 17892 1314 34108 10960 11465 54181 25267 29741 55078 26837 14558 58682 98752 64793 4425 79661 87737 68105 57654 55023 15006 34393 46050 66877 67940 89369 66517 94991 33484 85841 56384 87357 55636 44580 97814 88145 13915 29582 86954 59628 70788 14720 81220 48897 74581 33515 54019 72455 36464 94970 35630 64952 74079 41133 63607 20988 25382 50403 64759 90237 27201 13602 17658 58091 64065 30004 65880 66772 43296 36157 60395 49995 8767 33601 5700 2265 21773 78700 98990 62710 18873 8079 14855 20489 99701 85729 38282 45015 28857 22056 52754 71439 6228 73267 71192 93045 71902 71919 3158 76641 45904 68700 53838 90833 47642 70471 71090 16628 91542 63047 34396 68650 9553 37035 40592 87591 84047 29510 9049 42748 60021 13161 16815 15018 26125 61478 26272 6974 26826 80298 31437 42245 70781 19439 78695 39427 61505 92962 68161 50492 71531 36008 6940 82378 95322 33247 20057 28566 41536 95289 39770 5137 38023 33019 44422 91145 67017 50608 31304 49418 43447 6842 56983 5786 60377 16733 17540 26117 93137 70483 41500 59373 58720 44229 17129 66287 46844 65528 95467 52195 63857 71645 14212 64979 91563 81014 31190 2204 94307 84599 12024 45456 58070 64993 36166 57409 742 7547 28215 17081 16798 32871 5763 97616 61224 42907 98515 97042 40674 48170 91574 80891 7984 65197 28176 84919 10507 4792 2482 57185 30604 77761 90022 8196 72585 11340 66776 18894 81282 22983 71112 52758 23342 3185 60261 56510 13036 59202 16818 97785 2183 96403 44883 27274 71620 60642 70080 56535 96276 53189 77544 64274 10992 42692 34543 46684 40655 36231 64854 39534 47781 71463 28169 77966 65155 82178 62282 65091 22936 1748 25961 5932 68215 17890 70328 19089 95365 95299 50013 17203 20932 30446 49620 38901 13299 41029 19127 30460 60023 60627 7792 88140 42209 52786 81430 3802 8490 47120 18102 46441 43309 37251 84815 31453 40873 59758 75393 48229 98781 6460 12665 87555 45783 87858 13242 12566 57376 2632 26511 22265 68737 5456 83071 7082 68996 89373 83289 39788 25302 18346 60093 92343 91512 91166 71738 4760 11403 9317 42383 25332 20479 63213 56479 38677 87900 98368 16310 211 39100 71539 74265 35255 23115 61122 40393 55573 6244 5268 27014 21270 18420 91958 90074 61008 47147 48636 44136 63241 12326 94852 31459 2859 88434 66099 49799 83028 25126 2395 34792 38196 98649 74824 47774 15994 95502 10139 98740 60332 19461 57615 95751 65759 41541 47802 64704 44978 16445 9295 98229 70636 66759 45439 71518 99735 26554 1417 69269 21846 22687 49450 87638 1212 19946 30349 27303 98266 64805 51798 28559 37078 69254 46085 \ No newline at end of file From ff86508b8f00038a9060a49d3f6671c7fa7ba0ce Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 30 Jun 2023 23:59:45 +0200 Subject: [PATCH 327/815] Removed leftover debug code --- module/mempool/herocache/backdata/heropool/pool_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 4ae03e2ccd6..0f8fbaf06ee 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -381,10 +381,6 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject for id, indexInThePool := range insertedEntities { flowIndentifier, _, ownerIdActual := pool.Get(indexInThePool) require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") - tmp := ownerIdToIndex[ownerIdActual] - if entities[tmp].Identifier != id { - fmt.Print(" ") - } require.Equal(t, entities[ownerIdToIndex[ownerIdActual]].Identifier, id, "Pool contains an entity with an unexpected owner id") } } From 85db7359333466ec455468cecec8a5cf458caf80 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Sun, 2 Jul 2023 15:51:19 +0200 Subject: [PATCH 328/815] Improved comments --- module/mempool/herocache/backdata/heropool/pool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 32e0245493e..24fca543cf6 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -95,7 +95,7 @@ func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Log return l } -// setDefaultNodeLinkValues sets nodes prev and next to InvalidIndex for all cached entity poolEntities. +// setDefaultNodeLinkValues sets nodes prev and next to InvalidIndex for all cached entities in poolEntities. func (p *Pool) setDefaultNodeLinkValues() { for i := 0; i < len(p.poolEntities); i++ { p.poolEntities[i].node.next = InvalidIndex @@ -316,7 +316,7 @@ func (p *Pool) getStateFromType(stateType StateType) *state { func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { var s *state = p.getStateFromType(stateType) if s.size == 0 { - panic("Removing an entity from the empty list") + panic("Removing an entity from an empty list") } if s.size == 1 { // here set to InvalidIndex From f9a1e686aa06dc593529aaba74f69ceccbef9ef9 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 14:16:20 +0200 Subject: [PATCH 329/815] Refactored test --- .../herocache/backdata/heropool/pool.go | 22 +- .../herocache/backdata/heropool/pool_test.go | 204 ++++++------------ 2 files changed, 89 insertions(+), 137 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 24fca543cf6..499c24b8924 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,6 +1,7 @@ package heropool import ( + "fmt" "math" "github.com/rs/zerolog" @@ -314,7 +315,16 @@ func (p *Pool) getStateFromType(stateType StateType) *state { // utility method that removes an entity from one of the states. // NOTE: a removed entity has to be added to another state. func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { - var s *state = p.getStateFromType(stateType) + var s *state = nil + switch stateType { + case stateFree: + s = &p.free + case stateUsed: + s = &p.used + default: + panic(fmt.Sprintf("unknown state type: %s", stateType)) + } + if s.size == 0 { panic("Removing an entity from an empty list") } @@ -353,7 +363,15 @@ func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { // appends an entity to the tail of the state or creates a first element. // NOTE: entity should not be in any list before this method is applied func (p *Pool) appendEntity(stateType StateType, entityIndex EIndex) { - var s *state = p.getStateFromType(stateType) + var s *state = nil + switch stateType { + case stateFree: + s = &p.free + case stateUsed: + s = &p.used + default: + panic(fmt.Sprintf("unknown state type: %s", stateType)) + } if s.size == 0 { s.head = entityIndex diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 0f8fbaf06ee..e8a95fa949e 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -1,12 +1,10 @@ package heropool import ( - "errors" "fmt" - "io/ioutil" - "strconv" - "strings" + "math/rand" "testing" + "time" "github.com/stretchr/testify/require" @@ -14,22 +12,6 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// A type of operation to perform on a pool. -type OperationType int - -const ( - // Add - interpreteded as a command to add an element to a pool. - Add OperationType = iota - // Remove - interpreteded as a command to remove an element from a pool. - Remove -) - -// OperationAndIndex contains an operation to be perfomed on an entity stored under the specified index of an array of entities. -type OperationAndIndex struct { - operation OperationType - index uint -} - // TestStoreAndRetrieval_BelowLimit checks health of heroPool for storing and retrieval scenarios that // do not involve ejection. // The test involves cases for testing the pool below its limit, and also up to its limit. However, it never gets beyond @@ -235,153 +217,105 @@ func TestInvalidateEntity(t *testing.T) { } } -// a helper function that converts an operation encoded as 0 or 1 to Add or Remove. -func stringToOperationType(s string) (OperationType, error) { - switch s { - case "0": - return Add, nil - case "1": - return Remove, nil - default: - return Add, errors.New("Unknown operation") - } -} - -// reads random number used as ownerId and operation to perform on flow enitites. -func readAndParseData() ([]uint, []OperationAndIndex, error) { - fileRandomNumbers := "pool_test_random_numbers.txt" - contentNumbers, err := ioutil.ReadFile(fileRandomNumbers) - - if err != nil { - return nil, nil, err - } - - ownerRandomNumbersStr := strings.Fields(string(contentNumbers)) - ownerRandomNumbersUint := make([]uint, len(ownerRandomNumbersStr), len(ownerRandomNumbersStr)) - for i, numberStr := range ownerRandomNumbersStr { - numberInt, err := strconv.Atoi(numberStr) - if err != nil { - return nil, nil, err - } - ownerRandomNumbersUint[i] = uint(numberInt) - } - - fileOperations := "pool_test_operations.txt" - content_operations, err := ioutil.ReadFile(fileOperations) - - operations := strings.Fields(string(content_operations)) - operationsOnEntity := make([]OperationAndIndex, len(operations), len(operations)) - for i, operation := range operations { - operationAndIndexSplit := strings.Split(operation, ",") - indexEntity, err := strconv.Atoi(operationAndIndexSplit[1]) - if err != nil { - return nil, nil, err - } - operation, err := stringToOperationType(operationAndIndexSplit[0]) - if err != nil { - return nil, nil, err - } - operationsOnEntity[i] = OperationAndIndex{operation: operation, index: uint(indexEntity)} - } - return ownerRandomNumbersUint, operationsOnEntity, nil -} - // TestAddAndRemoveEntities checks health of heroPool for scenario where entitites are stored and removed in a predetermined order. // LRUEjection, NoEjection and RandomEjection are tested. RandomEjection doesn't allow to provide a final state of the pool to check. func TestAddAndRemoveEntities(t *testing.T) { - ownerIds, operationsAndIndex, err := readAndParseData() - require.Nil(t, err) - for _, tc := range []struct { - limit uint32 // capacity of the pool - entityCount uint32 // total entities to be stored - ejectionMode EjectionMode // ejection mode - ownerIds []uint - operationsIndexes []OperationAndIndex // operation to perform on an entity stored under the given index, - // where index ranges from 0 to the entityCount + limit uint32 // capacity of the pool + entityCount uint32 // total entities to be stored + ejectionMode EjectionMode // ejection mode + numberOfOperations int + probabilityOfAdding float32 }{ { - limit: 500, - entityCount: 1000, - ejectionMode: LRUEjection, - ownerIds: ownerIds, - operationsIndexes: operationsAndIndex[0:1000], + limit: 500, + entityCount: 1000, + ejectionMode: LRUEjection, + numberOfOperations: 1000, + probabilityOfAdding: 0.8, }, { - limit: 500, - entityCount: 1000, - ejectionMode: NoEjection, - ownerIds: ownerIds, - operationsIndexes: operationsAndIndex[0:1000], + limit: 500, + entityCount: 1000, + ejectionMode: NoEjection, + numberOfOperations: 1000, + probabilityOfAdding: 0.8, }, { - limit: 500, - entityCount: 1000, - ejectionMode: RandomEjection, - ownerIds: ownerIds, - operationsIndexes: operationsAndIndex[0:1000], + limit: 500, + entityCount: 1000, + ejectionMode: RandomEjection, + numberOfOperations: 1000, + probabilityOfAdding: 0.8, }, } { t.Run(fmt.Sprintf("%d-limit-%d-entities", tc.limit, tc.entityCount), func(t *testing.T) { - testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.ownerIds, tc.operationsIndexes) + testAddRemoveEntities(t, tc.limit, tc.entityCount, tc.ejectionMode, tc.numberOfOperations, tc.probabilityOfAdding) }) } } -// testAddRemoveEntities allows to add or remove entities in an order given by operationsIndexes. Each OperationAndIndex consists of an operaton to perform -// on an entity stored under a corresponding index. Index range from 0 to entityCount. -func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, ownerIds []uint, operationsAndIndexes []OperationAndIndex) { +// Indexes contains ... +type Indexes struct { + indexInPool EIndex + indexInEntitiesArr int +} + +func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { + //create and log the seed. + rand.Seed(time.Now().UnixNano()) pool := NewHeroPool(limit, ejectionMode) entities := unittest.EntityListFixture(uint(entityCount)) - // this map maintains entities currently stored in the pool - var insertedEntities map[flow.Identifier]EIndex - insertedEntities = make(map[flow.Identifier]EIndex) - - // this map maps a random owner id to an entity index in an array of entities - var ownerIdToIndex map[uint64]uint - ownerIdToIndex = make(map[uint64]uint) + // an array of random owner Ids + ownerIds := make([]uint64, entityCount, entityCount) + // generate ownerId to index in the entities arrray. for i := 0; i < int(entityCount); i++ { - ownerIdToIndex[uint64(ownerIds[i])] = uint(i) + ownerIds[i] = rand.Uint64() } - - for _, operationAndIndex := range operationsAndIndexes { - - operation := operationAndIndex.operation - entityIndex := operationAndIndex.index - - switch operation { - case Add: - _, found := insertedEntities[entities[entityIndex].ID()] + // this map maintains entities currently stored in the pool + var addedEntities map[flow.Identifier]Indexes + addedEntities = make(map[flow.Identifier]Indexes) + + for i := 0; i < numberOfOperations; i++ { + // choose between Add and Remove with a probability of 0.8 and 0.2 respectively + if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { + // adding an entity + entityToAdd := rand.Intn(int(entityCount)) + // check that entity is not already inserted + _, found := addedEntities[entities[entityToAdd].ID()] if !found { - indexInThePool, _, ejectedEntity := pool.Add(entities[entityIndex].ID(), entities[entityIndex], uint64(ownerIds[entityIndex])) + indexInThePool, _, ejectedEntity := pool.Add(entities[entityToAdd].ID(), entities[entityToAdd], uint64(ownerIds[entityToAdd])) if indexInThePool != InvalidIndex { - insertedEntities[entities[entityIndex].ID()] = indexInThePool + addedEntities[entities[entityToAdd].ID()] = Indexes{indexInThePool, entityToAdd} } if ejectedEntity != nil { - delete(insertedEntities, ejectedEntity.ID()) + delete(addedEntities, ejectedEntity.ID()) } } - case Remove: - indexInPool, found := insertedEntities[entities[entityIndex].ID()] - if found { - removedEntity := pool.Remove(indexInPool) - require.Equal(t, entities[entityIndex].ID(), removedEntity.ID(), "Removed wrong entity") - delete(insertedEntities, entities[entityIndex].ID()) + + } else { + // randomly select an index of an entity to remove + entityToRemove := rand.Intn(len(addedEntities)) + i := 0 + var indexInPoolToRemove EIndex = 0 + var indexInEntitiesArray EIndex = 0 + for _, v := range addedEntities { + if i == entityToRemove { + indexInPoolToRemove = v.indexInPool + indexInEntitiesArray = EIndex(v.indexInEntitiesArr) + break + } + i++ } - default: - require.True(t, false, "Unknown pool operation") - } - checkEachEntityIsInFreeOrUsedState(t, pool) - } - require.Equal(t, uint32(len(insertedEntities)), pool.Size(), "Pool size doesn't correspond to a number of inserted entities") - for id, indexInThePool := range insertedEntities { - flowIndentifier, _, ownerIdActual := pool.Get(indexInThePool) - require.Equal(t, flowIndentifier, id, "Pool contains an unexpected entity") - require.Equal(t, entities[ownerIdToIndex[ownerIdActual]].Identifier, id, "Pool contains an entity with an unexpected owner id") + // Remove the selected entity from the pool + removedEntity := pool.Remove(indexInPoolToRemove) + require.Equal(t, entities[indexInEntitiesArray].ID(), removedEntity.ID(), "Removed wrong entity") + delete(addedEntities, entities[indexInEntitiesArray].ID()) + } } } From 2543fc44ec50ef114639058809c6e593aebeaee7 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 15:28:17 +0200 Subject: [PATCH 330/815] Added check in remove entity --- .../mempool/herocache/backdata/heropool/pool.go | 6 ++++-- .../herocache/backdata/heropool/pool_test.go | 15 +++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 499c24b8924..955515828da 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -274,8 +274,10 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // removing its corresponding linked-list node from the used linked list, and appending // it to the tail of the free list. It also removes the entity that the invalidated node is presenting. func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { - poolEntity := p.poolEntities[sliceIndex] - invalidatedEntity := poolEntity.entity + invalidatedEntity := p.poolEntities[sliceIndex].entity + if invalidatedEntity == nil { + panic(fmt.Sprintf("removing an entity from an empty slot with an index : %d", sliceIndex)) + } p.removeEntity(stateUsed, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index e8a95fa949e..b954e693181 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -255,32 +255,35 @@ func TestAddAndRemoveEntities(t *testing.T) { } } -// Indexes contains ... +// Indexes contains an index in the pool of an inserted entity and an index of this entity +// in the array of entities created by the EntityListFixture. type Indexes struct { indexInPool EIndex indexInEntitiesArr int } func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { - //create and log the seed. - rand.Seed(time.Now().UnixNano()) + // create and log the seed. + testSeed := time.Now().UnixNano() + rand.Seed(testSeed) + fmt.Println("Seed used for this testAddRemoveEntities execution", testSeed) pool := NewHeroPool(limit, ejectionMode) entities := unittest.EntityListFixture(uint(entityCount)) - // an array of random owner Ids + // an array of random owner Ids. ownerIds := make([]uint64, entityCount, entityCount) // generate ownerId to index in the entities arrray. for i := 0; i < int(entityCount); i++ { ownerIds[i] = rand.Uint64() } - // this map maintains entities currently stored in the pool + // this map maintains entities currently stored in the pool. var addedEntities map[flow.Identifier]Indexes addedEntities = make(map[flow.Identifier]Indexes) for i := 0; i < numberOfOperations; i++ { - // choose between Add and Remove with a probability of 0.8 and 0.2 respectively + // choose between Add and Remove with a probability of 0.8 and 0.2 respectively. if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { // adding an entity entityToAdd := rand.Intn(int(entityCount)) From 5952770193a4e3a6f85b7dfbde292b5bd94877f8 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 15:51:55 +0200 Subject: [PATCH 331/815] Removed getStateFromType --- module/mempool/herocache/backdata/heropool/pool.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 955515828da..841932293c5 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -300,20 +300,6 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { return true } -// a helper method that allows to get an adress fo the state form the state type. -func (p *Pool) getStateFromType(stateType StateType) *state { - var s *state = nil - switch stateType { - case stateFree: - s = &p.free - case stateUsed: - s = &p.used - default: - panic("Unknown state type") - } - return s -} - // utility method that removes an entity from one of the states. // NOTE: a removed entity has to be added to another state. func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { From 9c8ea50aea09427f29e22c9e1a692b1d0fad480d Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 15:53:32 +0200 Subject: [PATCH 332/815] Removed asset test files --- .../mempool/herocache/backdata/heropool/pool_test_operations.txt | 1 - .../herocache/backdata/heropool/pool_test_random_numbers.txt | 1 - 2 files changed, 2 deletions(-) delete mode 100644 module/mempool/herocache/backdata/heropool/pool_test_operations.txt delete mode 100644 module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt diff --git a/module/mempool/herocache/backdata/heropool/pool_test_operations.txt b/module/mempool/herocache/backdata/heropool/pool_test_operations.txt deleted file mode 100644 index 9d583e742cc..00000000000 --- a/module/mempool/herocache/backdata/heropool/pool_test_operations.txt +++ /dev/null @@ -1 +0,0 @@ -1,777 0,587 0,711 0,184 1,932 0,254 1,980 1,613 1,868 0,632 0,402 0,665 1,494 1,171 0,84 0,257 0,608 1,793 0,883 0,131 1,706 1,647 0,430 0,530 1,819 1,674 0,135 0,420 0,944 1,399 1,551 0,372 0,62 0,676 1,608 1,123 0,321 0,221 0,379 0,660 1,398 1,188 0,842 1,119 0,637 0,315 1,750 1,678 1,791 1,346 0,879 0,499 1,862 0,413 0,338 1,477 1,998 0,897 0,187 1,693 0,578 0,490 0,606 0,105 1,936 0,848 1,742 1,621 0,109 1,226 1,728 0,76 1,908 1,754 0,432 0,560 1,710 1,486 0,123 0,45 0,888 1,862 0,518 0,40 0,795 1,576 0,979 1,511 0,617 1,379 0,155 0,991 0,982 1,864 0,201 1,99 1,909 1,459 0,414 1,525 0,3 1,122 1,752 1,646 1,307 0,937 0,910 1,531 0,124 0,141 0,374 0,741 0,587 0,346 1,726 1,21 0,590 1,133 0,172 0,69 0,133 1,652 0,905 0,103 0,294 1,991 0,166 1,301 0,37 1,739 1,763 1,289 0,529 0,13 0,612 1,537 0,31 1,867 0,280 0,920 1,609 0,399 0,754 0,472 1,19 1,322 1,251 1,354 1,903 0,412 1,497 0,288 0,940 1,684 0,589 1,911 0,728 0,439 0,791 1,193 1,454 0,789 1,866 0,970 1,862 0,66 0,754 1,233 0,751 0,962 1,844 1,947 0,224 0,919 0,6 0,626 0,436 0,911 0,252 1,99 0,413 1,869 1,917 0,872 0,952 1,350 1,773 0,185 0,133 1,197 0,672 0,641 1,752 0,126 0,430 1,675 1,89 1,141 0,899 0,101 0,891 1,304 0,704 0,243 1,914 0,846 0,975 1,656 0,11 0,657 1,364 0,160 1,736 1,88 0,839 0,908 1,731 1,566 0,530 0,826 0,439 1,272 0,918 1,819 0,721 1,359 0,765 0,661 1,685 1,107 1,644 0,527 1,240 1,488 1,702 1,485 1,575 0,857 0,707 0,749 1,340 0,446 1,611 0,16 0,220 1,567 0,131 0,460 0,864 0,343 0,670 0,223 1,369 1,388 0,792 0,107 1,259 1,961 0,904 0,670 0,494 0,377 0,395 0,724 1,313 1,109 1,167 1,531 0,528 1,443 1,884 0,525 0,872 1,260 1,526 1,466 0,908 0,664 1,682 0,832 0,925 1,483 1,263 1,576 1,818 0,622 1,577 0,50 0,251 1,825 1,610 1,943 1,218 1,227 0,799 1,491 0,644 1,772 1,818 1,799 1,308 1,751 0,573 1,15 1,622 1,486 0,549 1,924 0,782 0,366 1,641 0,490 0,362 1,943 0,872 0,421 1,577 1,967 0,101 0,667 0,528 0,269 1,542 1,380 0,957 0,218 1,673 0,188 0,835 0,556 0,544 1,987 0,96 0,966 0,197 0,884 1,917 1,908 0,677 1,280 1,753 1,79 1,558 1,11 1,246 0,626 1,235 0,203 0,747 0,173 1,772 0,507 0,92 0,896 1,945 0,921 0,106 1,957 0,240 0,748 0,840 1,354 1,630 1,318 0,842 0,787 0,230 1,884 1,125 0,807 0,934 1,124 1,55 1,323 1,158 1,208 0,197 0,756 1,425 0,716 0,541 0,454 0,550 1,641 0,318 0,336 1,547 0,142 0,57 1,184 0,218 0,112 1,286 0,308 1,344 0,894 1,643 0,242 0,190 1,687 0,198 1,160 0,309 1,207 0,609 0,149 1,258 1,730 1,152 0,643 1,76 1,264 1,688 1,812 0,53 1,287 1,601 0,60 1,582 0,59 1,18 0,190 1,915 0,421 0,267 0,487 1,947 0,976 1,893 0,84 1,420 1,622 0,191 0,324 0,618 1,627 0,287 0,301 0,656 0,292 0,197 0,69 1,356 0,963 0,437 1,726 1,932 0,771 1,538 1,683 0,116 1,586 0,476 0,52 0,901 0,884 1,953 1,262 1,947 1,716 0,600 0,25 0,849 1,160 1,594 0,297 0,110 1,354 0,164 1,158 0,885 0,134 0,301 1,388 1,834 1,721 1,433 1,496 1,338 1,765 1,289 0,466 1,922 0,594 1,668 1,915 1,587 0,881 1,857 0,685 1,627 1,690 0,248 0,732 1,849 1,955 0,40 1,485 1,346 0,898 1,341 1,614 0,222 1,478 1,248 1,122 0,86 1,492 0,397 0,274 1,1 0,970 1,752 0,711 1,866 0,221 1,544 0,523 0,808 1,462 1,130 0,215 1,71 1,490 0,750 1,760 0,515 1,134 0,225 0,460 0,725 0,876 0,878 1,664 0,79 1,150 1,342 1,487 0,24 0,31 1,869 1,264 1,437 1,332 0,261 1,541 1,38 1,746 0,179 1,475 1,929 1,72 1,570 0,889 0,919 0,923 0,567 1,872 1,341 0,207 0,15 1,901 1,155 0,283 0,849 0,238 0,126 0,784 0,638 1,688 0,543 1,4 1,332 1,965 1,949 0,270 0,739 1,717 0,37 1,984 1,700 1,144 1,679 0,966 1,629 1,556 0,621 0,280 1,423 1,439 1,929 1,142 0,54 1,228 1,97 0,705 0,628 1,275 0,571 0,963 1,252 1,329 0,990 0,105 1,864 0,12 0,912 0,794 1,679 0,258 0,487 0,857 0,335 0,573 0,57 0,384 0,869 0,824 1,149 1,928 0,554 1,572 1,617 1,828 1,910 0,28 0,279 0,375 1,187 0,635 1,832 1,46 1,279 0,934 0,341 0,630 1,600 0,676 0,311 1,199 1,343 1,398 0,43 1,447 0,526 1,479 1,638 0,870 0,772 0,58 1,310 0,88 1,209 1,470 0,868 0,511 1,156 0,681 0,249 1,766 1,491 1,292 0,498 0,207 1,454 1,249 1,589 0,950 0,813 1,169 0,339 0,236 1,368 0,621 1,537 0,741 0,937 1,404 1,992 1,17 0,517 0,830 0,131 0,171 0,247 1,650 0,825 1,918 0,230 0,806 0,145 0,529 0,726 1,480 0,992 0,99 0,671 0,50 1,991 0,623 1,181 1,848 0,679 0,131 1,413 0,529 1,186 0,388 0,806 1,630 1,69 0,367 1,327 1,980 0,5 1,118 1,625 0,844 0,537 0,888 1,673 1,935 1,582 0,212 1,61 1,246 0,979 1,487 1,393 1,862 1,291 1,127 0,848 0,573 0,278 1,573 0,894 1,255 0,575 1,943 0,689 1,985 0,194 0,299 1,599 1,908 0,760 0,996 0,741 1,977 0,956 1,394 0,165 0,778 0,622 0,724 0,866 0,452 1,759 0,566 0,622 1,690 1,385 0,820 1,748 1,828 1,977 0,384 0,119 0,371 1,545 1,537 1,510 1,381 0,594 1,95 0,979 0,262 0,777 1,928 1,919 0,914 0,370 0,164 1,78 0,961 0,633 0,687 1,939 0,160 0,644 0,128 0,350 1,400 1,643 1,399 0,605 1,688 1,233 0,116 1,999 1,717 0,101 1,508 1,54 1,704 1,578 0,91 0,761 1,14 0,303 0,539 1,692 1,548 1,238 1,217 1,88 1,844 1,257 1,911 0,919 1,632 1,189 1,297 0,627 1,126 0,162 1,700 1,662 0,205 0,128 1,993 0,196 0,454 1,307 1,686 1,382 1,682 0,849 0,445 1,374 1,13 0,495 1,885 0,81 0,42 0,583 0,750 0,374 0,991 0,239 1,841 1,281 1,160 0,415 1,821 1,216 1,124 1,298 0,461 1,58 0,949 1,729 1,383 1,343 0,285 0,146 1,248 0,220 1,248 1,193 0,185 0,807 0,69 0,450 1,773 1,179 1,703 1,554 1,721 0,448 0,697 1,789 1,26 1,747 0,527 0,974 1,230 1,648 0,851 1,337 1,845 1,207 0,588 0,128 1,638 1,265 1,398 1,217 0,634 1,155 0,389 0,529 0,502 0,360 1,45 1,308 0,63 1,64 1,843 1,815 1,202 1,641 1,22 0,342 1,973 1,385 1,865 0,703 1,884 1,95 1,282 1,28 1,781 0,745 1,141 0,17 0,512 0,199 0,421 1,21 0,291 1,833 1,333 0,882 1,949 0,196 0,322 0,283 0,483 0,952 0,591 1,850 1,917 1,641 1,348 1,131 0,628 1,320 0,748 1,770 0,884 0,852 1,880 0,767 0,675 0,138 1,419 0,127 1,843 1,779 0,958 1,575 0,585 0,986 0,839 0,260 0,807 1,608 0,575 0,876 0,290 1,167 0,443 0,596 1,809 0,623 0,675 1,406 1,656 0,507 1,846 1,418 0,636 1,350 0,539 1,125 0,744 1,931 1,479 1,697 1,352 0,717 0,352 1,973 1,191 1,883 1,377 0,745 1,385 0,884 1,243 1,772 1,558 1,124 1,867 0,218 0,750 1,715 1,284 0,901 1,545 1,461 0,230 0,615 0,592 0,774 1,814 0,950 0,418 1,562 0,436 0,327 1,547 0,417 1,566 0,784 1,555 1,465 1,12 1,437 0,367 1,515 1,378 0,564 1,393 1,865 1,72 0,650 1,447 0,565 1,749 0,683 1,99 0,918 1,544 1,849 0,732 0,228 1,203 1,256 1,634 0,29 1,703 1,858 0,381 1,938 0,547 1,551 0,21 0,552 1,41 0,632 1,959 1,563 1,705 1,379 0,698 0,152 0,430 0,217 0,272 0,325 1,331 1,458 0,231 0,867 1,997 0,813 0,630 0,519 0,744 0,527 1,984 0,792 1,695 1,591 0,323 0,285 0,479 1,763 0,807 1,245 1,503 1,603 1,773 1,647 1,321 0,19 0,295 0,767 1,799 0,510 1,935 0,739 0,395 1,532 0,902 0,631 0,295 0,544 0,922 0,362 1,56 0,581 1,80 1,738 0,243 0,351 0,74 1,200 0,254 0,828 0,831 1,860 1,743 1,273 1,734 1,202 0,604 0,257 1,459 1,378 0,887 1,421 0,858 0,538 1,521 1,849 1,271 0,979 0,701 1,110 1,149 0,853 0,595 0,177 1,449 1,354 0,583 0,878 1,254 0,621 1,152 1,158 0,558 1,752 0,718 0,26 1,674 1,172 0,143 0,644 1,681 0,546 1,393 1,458 1,823 0,677 0,498 1,969 1,377 1,923 0,485 1,786 1,19 1,682 1,118 0,200 0,879 1,111 0,336 1,207 1,857 1,823 0,847 1,880 0,388 0,953 0,371 0,448 1,474 0,828 1,330 1,512 1,604 1,306 1,284 1,858 1,556 0,338 0,380 1,249 0,995 0,417 0,118 0,603 1,255 0,221 0,984 1,222 1,573 1,221 0,979 0,280 0,425 1,486 1,710 1,465 0,675 1,745 1,912 1,578 0,968 1,218 1,630 0,131 0,400 0,831 0,429 0,888 1,998 0,924 1,563 1,325 1,913 0,135 1,548 0,56 1,125 0,93 1,89 0,465 1,492 0,779 1,240 0,503 1,475 0,730 0,399 1,81 0,715 1,874 0,243 1,248 1,682 1,872 0,701 1,847 0,509 1,108 0,768 0,722 1,152 1,992 0,923 0,147 0,218 0,160 1,144 1,545 0,537 0,381 0,735 1,162 1,663 0,844 0,533 1,507 0,182 1,472 0,279 0,369 0,961 1,464 0,540 0,119 0,689 1,875 1,240 1,520 1,881 1,983 0,708 0,889 1,905 0,138 0,163 0,396 0,137 0,857 1,709 1,275 0,579 1,103 1,781 0,294 1,595 0,35 1,394 1,294 1,787 0,296 1,595 1,392 0,740 0,442 0,98 1,373 1,323 1,315 1,332 0,605 1,933 1,326 1,973 1,893 1,698 1,966 0,928 0,786 1,361 1,683 1,412 1,137 1,655 1,704 1,708 1,972 1,850 1,287 1,266 0,119 0,819 1,952 1,342 1,243 0,978 0,49 1,100 0,631 0,522 1,173 1,677 0,778 0,567 0,436 0,830 0,849 0,581 0,998 0,176 0,965 1,545 1,454 0,343 0,456 1,603 1,566 1,228 0,889 0,704 0,143 0,763 1,300 1,232 0,403 0,633 1,850 1,66 0,892 0,114 1,445 1,710 0,172 0,188 0,247 1,110 1,989 1,310 1,299 0,902 1,446 0,298 0,180 0,823 0,620 1,696 0,182 0,15 0,44 1,467 1,506 1,737 0,473 1,994 1,229 1,739 0,145 0,847 1,133 0,836 1,15 0,797 1,689 1,843 0,115 0,210 1,652 0,887 0,366 0,989 1,342 0,592 0,152 1,662 1,18 1,270 0,757 0,902 0,763 0,62 1,940 1,743 1,142 1,42 1,5 0,781 0,230 1,131 0,751 1,177 1,786 1,820 0,309 1,564 0,719 0,13 0,815 0,451 1,510 0,77 0,844 1,860 1,373 0,470 0,453 1,747 1,942 1,703 0,673 0,945 1,552 0,111 0,433 1,610 0,63 0,386 1,836 1,765 1,246 1,935 1,576 0,946 0,463 0,608 1,679 1,811 1,308 1,658 0,160 0,172 1,828 0,954 1,818 1,568 1,103 0,546 1,283 1,386 0,249 1,834 0,613 0,478 1,564 1,342 0,400 0,3 1,635 1,382 1,798 0,42 1,38 0,776 1,228 0,905 0,309 1,230 1,312 1,582 1,189 0,665 1,524 0,768 1,421 0,696 1,422 1,293 0,421 0,109 0,888 1,265 0,779 0,139 1,164 1,321 0,367 1,344 0,886 0,212 1,361 0,558 1,845 0,836 0,840 0,821 1,573 1,9 0,607 0,924 1,439 0,993 0,818 0,966 1,42 1,806 1,339 0,622 1,871 1,639 0,891 1,697 1,255 1,68 0,423 1,852 1,539 0,425 1,182 1,473 0,323 1,855 1,277 1,25 1,726 0,653 0,803 0,841 1,972 0,275 0,967 0,301 0,436 0,596 1,415 0,304 0,937 0,727 1,123 1,708 1,349 0,738 1,129 1,569 0,520 1,599 1,311 0,504 0,999 1,731 0,844 1,585 1,809 0,449 0,99 0,5 1,271 0,812 1,82 1,313 0,746 1,577 1,465 0,384 1,183 1,568 0,68 0,373 1,959 0,177 0,910 0,852 0,268 1,828 0,590 0,578 0,525 0,793 0,993 0,814 0,784 1,41 0,811 1,176 1,687 0,171 1,209 1,470 1,262 0,212 0,979 0,567 0,804 0,794 0,109 1,343 0,874 1,811 1,779 1,788 0,362 0,244 1,473 0,450 0,315 0,734 0,121 0,81 1,425 1,760 1,882 1,621 1,757 1,404 0,759 0,228 1,590 1,201 0,107 0,979 0,656 1,376 0,553 1,878 0,238 1,962 1,934 0,238 0,879 0,817 0,488 1,446 1,920 1,902 0,168 1,190 1,143 1,139 0,83 0,509 1,313 1,135 1,362 1,338 0,96 1,12 0,138 0,511 0,752 1,794 1,62 0,875 1,35 1,149 1,162 0,992 1,168 1,373 1,515 1,634 1,343 0,250 0,472 0,68 0,720 1,160 0,686 1,698 1,865 1,861 0,161 1,749 1,992 1,810 0,107 1,97 0,510 0,271 0,853 0,446 1,367 1,660 1,624 1,524 0,954 0,332 0,526 0,305 0,179 1,115 0,972 1,271 0,118 1,68 0,164 1,269 0,697 1,370 0,862 1,420 0,692 0,732 0,877 1,297 0,369 0,693 0,510 1,984 1,733 1,282 1,902 0,737 1,551 1,233 1,111 1,367 1,774 1,247 1,922 1,724 0,33 1,460 0,859 1,574 0,910 0,290 0,292 1,132 1,122 1,957 0,258 1,257 0,847 1,435 0,845 0,731 1,387 0,967 1,579 0,285 1,347 0,631 1,546 1,215 0,391 1,762 1,485 0,489 0,483 0,887 0,469 1,993 0,657 1,827 1,564 0,867 1,88 0,979 1,846 1,711 1,671 1,752 1,451 1,461 1,102 1,742 1,9 1,736 0,685 1,984 0,487 1,784 1,33 0,274 0,555 1,856 1,462 0,235 0,786 1,397 1,441 1,84 0,11 1,151 1,457 0,450 1,942 1,94 1,447 1,90 1,345 1,557 1,699 0,403 0,320 1,108 1,400 1,359 0,368 0,109 1,846 0,583 1,555 0,282 0,150 1,833 0,885 1,44 1,36 0,98 0,839 0,279 0,683 0,798 1,945 1,898 1,904 0,376 0,619 0,665 1,832 0,930 1,451 0,253 0,647 0,332 1,347 1,453 0,993 1,150 0,196 1,904 1,119 1,84 0,81 0,413 1,596 0,995 0,918 1,415 0,963 0,180 0,765 1,100 1,708 0,257 1,801 0,442 0,840 0,150 1,128 1,965 1,748 0,625 1,478 0,683 0,676 0,55 0,291 0,304 1,230 1,396 0,278 0,45 1,951 1,126 0,762 1,90 0,430 0,31 0,236 1,458 0,75 0,901 1,117 0,78 0,907 0,585 1,139 0,438 1,702 1,866 1,510 0,233 1,710 1,620 0,935 0,870 1,481 0,673 1,690 1,455 0,884 0,211 1,203 0,908 0,312 1,383 0,595 0,88 1,601 0,157 1,901 0,449 1,315 0,407 1,372 0,974 1,433 1,781 0,689 0,718 0,253 1,445 1,956 1,157 0,340 1,17 1,83 1,883 1,669 1,415 1,272 1,130 0,739 0,880 1,818 0,201 1,295 1,262 0,344 0,438 1,372 1,824 0,534 0,385 0,795 0,333 0,430 0,382 1,45 1,857 1,435 0,553 0,170 1,160 0,907 1,949 1,657 0,938 0,117 1,773 0,497 0,807 0,322 0,22 1,913 0,458 1,599 0,623 1,602 1,774 0,846 1,995 0,772 0,959 1,43 1,244 1,32 1,460 0,395 1,644 0,640 0,819 0,656 1,983 1,371 1,661 0,480 1,295 1,480 0,604 0,384 0,861 0,342 0,401 0,449 0,837 0,60 1,932 1,528 0,950 1,358 1,302 1,633 1,331 0,371 1,3 0,221 1,860 1,72 1,420 1,281 1,89 1,108 0,566 0,407 1,343 1,531 1,232 0,961 0,298 0,739 0,76 1,529 0,869 0,70 0,192 1,178 1,663 1,918 1,292 0,36 1,980 0,820 0,943 0,654 0,883 0,690 0,769 1,402 0,768 0,635 1,675 0,709 0,326 1,679 1,324 1,940 0,163 1,760 0,989 1,892 0,771 1,930 0,811 1,880 0,583 0,935 1,164 1,954 1,200 0,481 0,705 1,853 0,455 0,70 0,166 1,851 0,570 1,237 0,117 0,891 0,208 0,741 0,259 0,360 0,300 0,965 0,776 1,465 0,840 1,208 1,961 0,856 1,601 1,856 0,322 0,18 0,132 1,440 1,80 0,872 1,291 0,468 0,483 1,536 0,208 0,21 0,976 1,992 1,238 0,410 1,851 0,952 0,28 1,154 0,107 0,625 0,517 1,679 0,335 0,621 0,258 0,707 0,399 1,85 0,621 0,489 1,491 0,988 1,810 1,558 0,275 0,371 1,183 1,113 0,495 0,365 0,302 0,950 0,289 1,955 0,715 1,653 0,596 0,207 1,765 1,602 1,888 0,728 1,561 0,898 1,875 0,918 1,493 1,772 1,364 1,858 0,744 0,128 0,663 1,90 0,120 0,627 1,938 0,984 1,787 0,988 1,954 1,311 0,839 1,140 1,834 0,944 0,949 0,865 1,49 0,402 0,898 0,738 1,558 1,748 0,110 0,793 0,250 1,109 1,919 1,730 0,44 0,989 0,644 0,703 1,245 1,222 0,26 1,105 0,864 0,123 1,564 0,850 1,342 0,155 1,349 1,999 0,123 0,935 1,422 0,870 1,279 1,189 0,462 1,369 1,237 1,718 1,974 1,224 0,29 1,493 0,398 0,690 0,262 1,861 0,846 0,159 1,339 1,983 1,722 1,705 0,589 1,462 0,666 0,535 1,68 0,753 1,630 1,591 0,874 0,57 1,811 1,940 0,938 0,541 0,362 1,593 0,857 1,375 0,519 1,646 0,999 0,732 0,304 1,337 1,48 0,709 0,646 1,668 0,332 1,142 0,535 0,839 0,721 0,113 1,456 1,788 1,95 0,154 1,59 0,543 0,635 1,30 0,838 0,27 0,919 1,7 1,637 1,816 1,509 0,867 1,167 1,356 0,435 1,884 1,554 0,618 0,243 1,336 0,587 0,568 1,848 1,169 0,102 1,963 0,632 0,985 0,325 0,497 1,612 1,504 1,230 0,784 1,352 0,565 1,25 1,690 1,459 0,830 1,838 1,914 0,699 1,973 1,417 1,845 0,751 1,907 0,936 0,152 1,607 0,798 1,84 1,118 1,351 0,299 0,247 0,745 0,455 0,325 1,287 1,271 0,620 0,446 1,929 1,360 0,426 1,912 0,487 0,869 0,706 1,531 0,661 0,574 0,484 0,256 1,899 1,868 0,996 0,5 1,332 0,583 1,979 1,121 0,25 1,333 1,952 1,361 1,534 0,142 0,119 0,411 0,87 1,643 1,126 1,13 1,104 0,790 0,955 0,343 0,504 0,803 0,668 0,621 0,551 0,752 1,873 1,185 1,374 1,212 0,678 1,995 1,313 0,78 0,768 1,93 0,45 1,47 0,721 1,200 0,379 1,723 1,483 0,610 1,309 0,355 0,41 0,844 1,781 0,538 0,565 1,397 1,586 0,339 1,112 1,922 0,909 0,738 0,16 1,192 1,415 1,935 1,705 0,585 1,73 0,451 1,211 1,29 1,151 0,984 1,268 0,704 0,507 1,78 0,174 1,394 1,823 1,818 0,185 0,423 0,252 0,494 0,952 1,790 0,1 1,561 0,707 0,373 1,445 1,666 0,141 1,882 1,611 0,250 0,143 1,325 0,291 0,252 0,915 1,750 1,155 0,704 1,887 1,710 0,950 0,651 1,692 0,983 1,756 1,354 0,701 1,950 0,301 1,502 1,356 1,373 0,117 1,209 1,459 0,827 1,928 1,621 1,41 0,239 0,132 1,653 1,508 0,548 0,438 0,345 1,513 1,798 1,715 1,941 1,285 1,388 1,178 1,862 1,548 0,554 1,421 1,456 0,393 0,935 1,332 1,148 0,509 0,124 0,649 1,588 0,396 0,30 1,738 0,496 1,505 0,805 0,913 0,168 0,1 0,620 0,326 1,720 0,133 0,720 0,520 0,746 1,992 1,571 1,638 0,232 1,617 0,369 0,105 1,558 1,789 0,395 1,813 1,522 1,156 0,208 1,615 0,735 1,178 1,417 0,491 1,712 0,485 0,112 1,454 0,654 0,476 1,906 0,376 0,442 1,12 0,240 0,657 1,146 1,781 1,581 1,445 1,446 1,746 0,976 1,786 0,572 0,229 1,246 0,940 1,478 1,531 0,285 1,742 1,123 1,477 0,180 1,539 1,554 0,896 0,241 0,626 0,867 0,267 1,494 0,0 1,508 1,948 0,747 1,294 1,247 0,503 0,668 0,636 1,250 1,168 1,337 0,22 1,534 0,989 0,143 0,392 0,619 1,128 0,412 0,398 1,840 1,265 1,135 1,670 0,604 0,222 1,168 1,806 0,714 0,607 0,136 0,764 1,551 1,69 1,394 0,320 1,420 1,514 0,543 0,904 0,696 1,396 0,563 1,271 0,267 0,846 0,752 0,512 0,464 1,38 0,512 1,64 1,968 1,760 0,736 1,626 1,120 0,865 1,243 0,161 0,765 0,245 1,618 0,949 1,252 1,832 0,853 0,565 0,984 1,369 0,200 1,95 0,96 0,530 0,111 1,653 0,111 1,511 1,389 1,444 0,347 0,911 1,145 1,85 0,615 0,564 1,476 1,337 1,140 1,309 0,236 0,686 1,951 1,327 1,524 1,49 1,534 1,314 1,262 0,716 1,524 1,709 1,905 0,470 1,959 1,794 0,684 1,649 1,354 0,720 0,948 0,84 1,779 0,19 1,884 0,658 0,267 1,579 0,646 1,406 1,794 0,888 0,597 0,5 0,640 0,6 0,367 0,55 1,596 1,498 1,485 1,263 1,605 1,826 1,852 1,989 0,613 1,161 0,845 1,28 1,854 0,331 1,718 0,972 1,605 0,615 1,944 0,200 1,71 0,584 1,389 0,974 0,999 0,116 0,234 1,190 1,933 0,25 1,3 0,215 1,331 0,556 1,948 1,570 0,955 0,775 0,984 1,199 0,126 0,401 1,708 1,459 1,362 0,290 1,174 0,527 0,261 0,52 1,232 0,990 1,354 1,652 0,568 0,567 0,723 1,158 1,410 1,974 1,885 0,132 0,837 0,906 0,891 0,269 0,257 1,179 1,560 1,623 0,115 1,222 0,750 0,38 0,119 0,120 0,958 0,245 0,607 1,435 0,365 1,986 1,300 1,736 1,9 0,494 0,253 1,326 0,68 0,644 0,674 0,566 0,726 0,277 0,785 0,898 0,846 1,453 0,328 1,440 1,851 0,913 1,583 1,435 0,288 1,450 0,441 1,674 0,403 0,147 1,124 0,361 1,709 1,476 1,158 1,563 0,416 0,44 1,938 0,576 0,191 1,405 1,908 1,291 0,967 0,860 0,382 1,47 0,352 1,11 1,303 1,414 0,497 0,542 1,240 1,801 0,558 1,148 0,61 1,654 1,725 1,900 1,94 1,607 1,56 1,303 1,497 0,871 1,806 1,904 0,741 0,761 1,749 1,48 1,451 1,991 1,695 1,860 0,406 1,847 0,302 0,382 0,915 0,431 1,72 1,411 0,352 0,184 0,733 1,326 1,964 0,663 1,81 0,636 1,117 0,923 0,84 0,217 1,24 0,348 0,94 1,66 1,153 0,294 0,621 1,452 1,769 0,230 0,172 1,983 1,173 0,975 1,411 0,889 0,944 1,298 1,975 0,893 0,115 1,7 1,967 0,817 0,641 1,680 0,545 1,542 1,482 1,612 1,600 0,254 0,393 0,161 1,684 0,182 1,656 1,48 0,837 0,800 1,248 1,387 0,640 1,292 1,928 1,250 1,128 0,947 0,457 1,457 1,406 0,52 0,388 0,942 1,794 0,837 1,198 0,510 1,836 0,896 0,22 0,66 0,173 1,458 1,764 0,310 0,112 1,762 0,962 1,836 0,748 1,187 0,487 0,945 1,844 0,696 1,547 0,160 0,881 1,840 1,136 0,132 1,431 0,560 0,256 0,471 1,273 0,223 1,745 0,176 0,306 1,362 0,649 0,844 1,140 0,967 0,931 1,549 1,240 0,882 0,543 1,710 1,747 0,175 0,369 0,76 1,0 1,764 0,806 0,392 1,986 1,191 1,966 1,269 0,419 0,84 0,456 1,344 1,118 0,24 0,701 0,779 1,830 1,85 1,520 0,620 0,242 0,873 0,977 1,891 1,472 1,116 1,376 0,427 0,937 1,214 0,16 1,584 1,11 1,659 1,53 1,602 1,240 1,331 1,582 0,965 1,383 0,741 0,797 1,135 0,37 0,726 1,862 0,378 0,545 0,543 1,8 1,24 0,772 0,831 1,663 0,487 1,669 0,500 0,45 0,662 0,170 0,476 1,790 0,363 0,458 0,15 1,327 0,371 1,801 0,613 0,267 1,899 1,215 0,116 1,653 0,634 0,652 1,831 1,777 0,862 1,557 0,549 0,757 1,893 0,847 0,302 0,410 0,214 1,431 1,521 1,783 0,159 1,335 1,897 0,641 1,929 0,503 1,44 0,691 0,11 0,419 0,272 0,767 0,759 1,127 1,532 1,353 1,41 0,524 1,273 0,469 1,240 0,267 1,737 0,849 1,910 0,189 1,448 0,485 1,265 0,419 0,887 0,34 1,540 1,727 0,344 1,938 1,14 1,409 0,468 1,390 1,108 0,365 0,717 0,545 1,247 0,572 0,522 1,791 0,495 1,290 0,510 1,627 0,30 1,667 1,600 0,670 1,445 1,296 1,991 0,545 1,717 1,242 0,206 0,990 0,528 0,566 0,898 0,104 1,145 1,703 1,992 0,70 0,538 1,514 1,999 1,269 1,581 0,191 1,337 0,653 0,897 0,603 1,533 0,480 1,95 1,955 0,587 1,701 1,304 0,209 1,635 0,452 1,151 1,996 1,910 0,439 0,805 0,918 0,321 1,43 1,353 1,561 1,293 1,862 0,742 0,164 1,503 1,965 1,911 1,93 0,638 0,872 0,314 1,90 1,983 1,639 0,468 1,789 1,439 1,174 1,739 1,530 1,913 1,870 0,951 1,198 0,159 1,749 0,510 0,301 0,206 0,862 1,625 1,590 1,570 0,243 1,721 1,341 1,996 0,722 1,131 1,796 0,850 1,233 0,235 1,317 0,283 1,411 1,285 0,515 0,938 1,419 1,369 0,39 1,682 1,958 1,942 0,697 0,779 1,758 0,878 1,382 1,129 0,395 0,743 1,829 1,456 1,535 0,478 0,959 0,598 1,120 1,616 0,431 1,440 1,578 1,715 0,364 1,516 1,851 1,664 1,772 0,830 0,438 0,659 0,391 1,647 1,797 0,869 1,121 0,968 1,890 1,43 1,711 0,729 1,670 0,186 1,454 1,791 0,968 1,10 0,755 1,152 1,64 1,514 0,404 1,532 0,479 1,255 0,686 0,582 0,210 0,465 1,621 1,472 0,930 1,708 1,473 0,279 1,596 0,184 0,287 1,102 0,17 0,991 1,332 0,103 0,267 0,717 0,586 1,88 1,656 0,677 1,694 0,911 0,23 1,948 0,828 1,146 0,76 0,856 0,344 1,506 0,258 0,694 1,587 1,13 1,646 0,225 1,934 0,355 0,705 1,152 1,180 0,571 1,789 0,308 0,926 0,665 0,614 1,661 1,894 1,699 1,700 0,455 0,384 0,134 0,860 0,78 0,135 0,851 1,39 1,838 1,114 1,904 0,143 0,679 1,292 0,395 0,237 1,401 1,91 1,605 1,112 0,601 0,44 1,985 0,13 1,281 1,514 0,533 1,516 0,327 1,537 1,266 0,772 1,979 1,121 1,826 0,377 1,419 1,913 1,679 1,209 0,917 1,630 1,468 0,51 1,221 0,836 1,245 0,533 0,430 1,943 0,41 1,133 1,798 1,782 1,921 0,296 0,884 1,901 1,998 1,54 0,322 0,227 0,926 1,716 1,459 1,151 1,776 1,979 0,455 0,948 1,255 1,645 1,321 1,963 0,294 1,139 1,959 1,478 0,296 1,146 0,905 1,214 0,728 1,5 0,191 1,330 0,11 1,231 1,980 1,980 0,598 1,813 0,340 1,146 0,857 0,252 0,743 0,256 0,914 1,156 0,861 1,59 1,112 0,457 1,943 1,789 1,431 1,634 0,553 1,552 1,944 0,6 0,125 1,625 1,443 1,760 0,645 1,542 0,857 0,210 1,827 1,900 1,908 0,492 1,618 1,448 0,734 1,235 1,452 1,900 1,251 0,421 0,756 1,806 1,342 1,64 1,885 0,381 0,928 0,549 0,271 0,599 1,412 0,936 0,4 1,982 1,22 0,630 0,248 1,466 0,332 0,152 0,951 0,994 1,666 0,401 0,734 0,52 0,921 0,925 1,98 0,563 1,998 0,240 1,714 1,653 0,616 0,653 0,864 0,742 0,333 0,335 0,424 0,852 1,59 0,339 0,806 1,107 0,499 1,748 1,367 0,510 1,528 1,224 1,791 1,59 0,510 0,142 0,363 0,916 1,470 0,844 1,354 0,7 1,397 0,101 0,36 0,160 0,336 1,314 0,859 1,612 1,158 1,268 1,390 1,625 0,307 0,278 1,927 0,628 0,913 0,44 0,558 1,151 1,655 1,503 1,796 1,526 0,313 0,971 1,253 0,658 1,567 1,919 0,153 0,482 0,335 0,371 0,792 1,784 0,449 0,266 1,534 0,512 0,118 1,76 0,360 0,916 0,1 1,266 0,558 0,128 0,775 0,21 0,907 1,941 0,62 1,349 0,604 0,765 0,64 0,474 1,774 0,193 0,772 0,64 0,779 0,327 0,670 0,939 0,316 1,146 0,516 1,672 0,868 1,104 0,249 0,702 1,452 0,443 1,588 1,256 1,904 1,870 0,708 1,114 0,745 0,374 1,451 0,708 0,166 0,383 0,490 1,124 0,681 1,230 0,677 1,94 0,163 1,618 0,993 0,201 0,784 0,624 1,721 0,174 1,234 1,421 1,556 0,357 1,317 1,971 1,567 0,142 0,917 1,116 1,198 0,743 1,648 0,804 0,451 0,827 0,173 0,428 0,117 1,629 1,773 1,182 0,866 0,789 0,510 1,30 0,481 1,115 0,271 1,298 0,17 1,746 0,38 0,794 0,99 1,481 0,199 0,971 1,895 0,392 1,672 1,878 1,525 1,484 0,602 1,340 1,348 0,484 0,289 1,841 0,154 1,168 0,580 1,218 1,404 1,169 1,701 0,258 1,365 0,543 0,165 0,621 1,401 1,281 0,425 1,37 1,801 1,434 0,170 1,320 0,988 1,901 1,881 0,965 1,258 0,225 1,301 0,361 0,380 0,294 1,557 1,502 1,737 1,279 0,776 0,830 0,712 0,363 0,587 0,272 0,687 1,816 1,615 0,115 0,982 1,214 0,270 0,637 1,439 0,483 1,232 0,146 0,675 0,214 1,866 1,165 1,357 1,855 1,98 1,487 1,677 0,536 1,336 1,877 1,12 0,472 1,502 1,626 0,314 1,807 0,516 0,691 1,616 0,121 1,561 0,112 0,499 0,875 0,911 0,705 1,538 1,174 0,661 1,34 1,601 0,155 1,751 0,939 0,51 1,82 1,262 0,766 1,549 0,173 0,186 0,11 0,407 0,761 1,758 0,990 1,122 1,232 1,181 0,890 1,234 1,402 1,905 1,646 1,526 0,374 0,470 1,14 1,613 1,556 1,838 1,244 1,766 0,941 1,290 0,296 1,648 0,84 0,658 1,974 0,795 0,743 1,630 0,309 0,183 0,241 1,402 1,139 1,227 1,798 1,159 0,888 1,640 0,148 1,637 1,434 1,580 0,662 1,385 0,670 0,724 0,217 0,436 1,461 1,272 1,232 0,80 0,695 0,789 1,90 1,418 1,377 0,99 0,112 0,271 0,671 0,813 1,174 1,520 1,417 1,962 0,122 1,571 1,436 0,542 1,23 0,723 1,306 0,818 1,422 0,876 1,253 1,74 1,795 0,981 1,970 1,357 1,246 0,417 1,444 0,321 0,166 1,741 0,280 1,456 1,416 1,573 1,815 0,308 1,748 0,231 1,66 0,340 0,866 1,602 1,692 0,501 1,195 1,329 0,86 1,742 0,424 0,702 1,352 0,185 0,828 1,740 0,591 0,228 1,159 0,574 1,244 0,384 1,513 0,430 0,135 0,462 1,535 1,468 1,842 0,894 1,116 1,808 1,75 1,664 1,373 0,664 1,647 0,700 0,599 0,819 0,698 1,57 1,532 1,532 1,348 0,84 1,424 1,713 1,71 1,846 1,101 0,626 1,207 1,421 0,789 1,468 1,651 1,281 1,176 1,574 1,183 0,109 1,945 0,157 1,911 0,256 0,360 0,210 1,643 0,759 1,626 0,470 1,864 0,850 1,569 0,563 1,761 0,688 1,34 1,899 1,349 1,217 1,606 1,548 0,777 0,744 0,605 0,18 0,688 0,279 0,724 1,857 1,677 0,51 1,36 0,101 0,462 1,117 0,300 0,741 0,989 0,363 0,215 0,271 0,584 1,185 1,558 1,483 1,634 0,733 0,752 0,586 0,28 0,27 1,853 0,605 0,385 1,88 1,781 1,458 0,557 1,898 0,58 1,127 0,586 0,418 0,208 0,998 0,79 1,113 0,19 0,491 1,21 0,734 0,317 1,229 1,540 1,487 0,883 0,969 0,592 1,919 1,718 1,118 1,739 1,249 0,623 1,935 1,128 0,529 0,107 0,704 0,663 1,592 0,960 1,471 1,517 1,860 1,108 0,656 0,517 1,671 1,392 0,204 0,578 0,594 1,669 1,398 1,965 1,106 1,356 1,828 1,62 1,767 0,893 1,156 0,450 0,955 1,370 1,896 0,96 1,335 0,833 1,660 1,575 0,212 1,247 1,602 1,969 1,844 0,339 1,926 1,526 0,626 1,272 1,14 1,735 0,182 0,471 0,582 1,295 0,526 0,22 1,311 1,143 1,960 0,294 0,843 0,776 0,330 1,591 1,833 1,742 0,106 1,499 0,865 1,687 1,671 0,462 1,30 0,465 0,388 0,746 1,164 0,305 1,773 0,216 0,225 1,724 1,486 0,819 0,848 0,978 0,173 0,101 1,550 1,67 1,57 1,559 1,936 0,612 0,508 0,794 1,160 1,459 0,197 0,598 1,47 0,41 1,691 0,301 1,236 0,255 0,832 0,876 1,747 0,397 0,969 0,979 0,160 0,593 1,853 0,283 1,372 0,423 1,924 1,823 1,848 0,16 0,601 0,502 0,680 0,262 0,457 0,560 1,962 1,873 0,915 1,324 0,602 0,349 1,719 0,619 1,477 0,265 0,403 1,425 0,70 0,891 0,827 0,514 1,182 1,240 1,550 1,847 0,114 1,875 1,753 1,130 0,134 0,270 1,986 1,670 1,935 1,136 1,697 1,648 1,879 0,545 0,397 0,904 0,602 1,487 1,29 1,981 1,683 0,684 0,407 0,453 0,444 0,766 0,486 1,80 0,252 1,708 1,223 0,790 1,315 1,758 0,230 0,517 1,487 1,702 0,692 1,79 1,618 1,631 0,618 0,213 0,308 1,645 0,925 1,378 1,868 1,958 0,874 1,186 1,20 0,538 1,60 0,878 0,427 1,869 0,201 1,482 0,518 1,922 1,557 1,345 0,258 1,932 1,748 1,748 0,77 0,710 0,683 0,243 0,676 1,971 1,900 1,520 0,678 0,263 0,452 1,847 1,176 0,57 0,572 0,309 0,657 1,81 0,896 0,508 1,913 1,674 1,682 1,215 1,359 0,780 1,302 1,147 0,656 1,103 0,201 1,754 0,717 0,969 1,999 1,502 1,370 0,353 0,375 1,713 0,146 0,711 1,188 0,389 1,50 1,467 0,6 0,373 1,573 0,700 1,549 1,251 0,384 1,839 1,896 0,500 0,985 0,512 1,172 1,638 0,395 0,398 0,363 1,214 0,536 1,513 0,651 1,68 0,616 0,558 1,36 0,441 1,858 1,447 0,421 1,449 1,199 1,264 0,644 0,101 1,416 1,302 1,50 1,638 1,733 1,746 1,651 0,95 1,752 1,641 0,988 1,960 0,783 1,530 0,951 0,152 1,738 0,587 1,573 0,808 1,49 0,225 1,17 1,564 0,727 0,410 1,84 0,218 0,279 1,410 1,513 0,314 1,580 0,970 0,721 1,365 1,912 1,906 1,403 0,616 1,52 0,147 1,33 1,868 1,742 1,211 1,607 0,138 0,772 1,335 1,835 0,333 1,421 1,772 0,729 1,581 0,655 1,347 1,378 0,344 1,252 0,572 0,18 1,785 0,237 0,246 1,698 0,771 0,194 1,843 0,250 1,181 0,164 0,621 0,943 1,876 1,395 0,976 0,555 0,726 1,764 1,404 0,651 0,880 1,508 0,890 0,897 1,60 1,693 0,867 0,838 0,936 0,813 1,797 1,400 1,156 0,320 1,363 0,688 0,138 0,887 1,782 0,784 0,86 0,471 0,223 1,193 1,761 1,283 0,592 0,771 0,516 1,852 0,324 0,12 0,107 1,860 0,162 0,273 0,80 1,816 1,496 1,519 1,738 0,926 1,429 0,918 1,120 0,488 1,693 0,969 1,846 0,249 0,33 0,938 1,740 0,374 1,958 1,113 1,557 0,151 1,250 1,465 0,503 0,809 1,366 1,243 1,308 0,185 1,184 1,549 0,527 1,819 0,961 1,300 0,886 1,736 1,510 0,573 0,294 0,743 1,306 0,344 0,968 1,943 0,241 0,414 0,638 1,913 0,639 0,79 1,41 1,482 0,216 1,645 1,22 1,684 0,102 1,389 1,820 1,495 0,512 0,157 0,622 1,988 0,841 0,829 0,17 0,37 0,774 0,72 0,114 1,972 0,147 0,544 1,490 1,835 0,636 0,750 1,314 1,495 1,370 0,487 1,564 1,584 0,843 0,955 1,639 1,676 0,227 0,422 0,960 0,14 0,487 0,823 1,584 1,187 0,64 1,698 1,615 1,830 0,254 1,826 1,912 1,740 0,628 1,592 1,499 1,462 0,288 1,765 1,493 0,811 1,391 1,155 0,482 1,831 1,830 0,406 1,511 1,160 1,532 1,834 1,348 0,752 1,389 1,531 1,751 1,751 1,592 1,133 1,294 1,821 1,821 0,62 1,882 0,269 0,479 0,961 0,672 1,979 0,579 1,707 0,775 0,128 0,310 0,569 0,49 1,99 1,614 0,90 0,952 1,12 1,248 1,589 0,637 0,872 0,812 1,742 0,847 1,786 1,75 1,582 0,1 1,362 0,169 1,299 0,460 1,808 1,989 1,248 0,618 0,173 1,618 0,807 1,473 1,205 0,706 0,404 0,5 1,333 0,744 0,744 0,131 0,665 1,295 0,835 1,352 1,308 0,193 1,939 0,654 0,814 1,654 1,774 0,349 1,477 1,219 1,848 0,944 1,513 1,754 0,242 0,73 1,833 1,313 0,329 1,969 1,839 0,359 1,754 1,935 1,898 0,326 1,517 0,624 0,168 0,0 1,343 1,964 0,310 0,418 1,963 1,872 1,21 1,928 0,575 0,65 0,12 0,402 1,330 0,178 1,85 1,157 0,210 1,973 1,277 0,351 1,364 1,760 1,655 0,649 0,761 1,610 0,289 0,986 1,325 0,809 1,360 1,511 0,914 1,931 0,324 0,685 0,870 0,588 1,496 0,93 1,515 0,779 1,979 1,7 1,419 0,937 0,52 1,154 1,27 0,174 1,781 1,840 1,485 0,359 0,592 1,594 1,18 1,725 0,162 0,512 1,986 1,249 0,831 0,673 1,335 1,36 1,264 1,46 1,193 1,551 0,697 0,676 0,6 0,720 0,720 0,873 0,49 0,703 1,81 1,18 1,939 0,620 0,848 1,656 0,594 0,843 1,897 1,404 0,900 1,160 1,893 0,778 1,757 1,305 0,84 1,943 1,4 0,628 1,334 1,282 1,443 0,932 0,695 1,137 0,65 0,310 0,211 1,45 1,842 0,665 0,144 1,75 0,857 0,85 0,983 1,87 1,634 1,349 0,43 1,713 0,187 0,617 0,696 0,593 0,292 0,687 0,191 0,130 1,338 0,621 1,537 0,410 1,15 1,610 0,372 1,186 1,69 0,820 0,580 1,777 1,336 1,222 0,132 0,82 0,803 0,308 1,563 0,628 1,487 1,705 1,542 0,434 1,878 1,32 1,921 1,957 1,934 1,533 0,278 1,128 0,463 0,253 0,340 1,224 1,242 1,995 0,319 0,934 1,519 0,919 1,532 1,393 0,221 0,493 0,82 1,464 1,649 1,367 0,64 0,611 1,686 0,856 0,906 1,821 1,593 1,858 1,947 0,54 0,716 0,612 0,981 1,392 1,102 0,863 1,623 1,178 1,957 0,402 0,383 1,659 0,239 1,78 1,586 0,588 0,197 0,273 1,329 0,59 1,496 1,81 0,230 0,443 1,923 0,485 1,684 0,90 1,951 0,509 1,178 1,241 1,822 0,530 1,416 0,505 1,234 1,800 0,178 1,759 1,522 1,193 0,515 0,486 1,386 1,851 0,324 1,111 1,700 0,501 1,933 0,834 0,477 0,39 0,847 0,410 1,625 1,636 1,490 1,72 0,132 1,137 1,175 0,561 1,237 0,143 0,801 1,792 0,775 0,983 1,105 1,243 0,254 0,129 1,606 1,226 1,201 0,594 0,439 0,730 0,609 1,561 1,458 1,850 1,501 0,512 1,27 0,746 0,860 0,111 1,586 0,920 1,372 1,704 1,939 0,692 0,573 0,18 1,742 0,693 0,681 0,350 0,274 0,547 1,765 1,320 0,466 1,738 1,661 1,373 0,396 0,779 1,153 1,885 1,122 1,174 1,204 1,659 0,357 0,119 0,564 0,906 1,792 0,39 0,158 1,882 1,724 0,914 0,145 1,66 1,352 0,722 1,940 1,384 1,311 0,76 1,130 0,318 1,892 0,931 0,829 1,622 0,574 1,462 1,827 0,916 0,822 0,313 0,460 1,198 1,246 0,879 0,393 1,84 1,846 1,57 0,574 1,803 1,554 0,759 1,15 0,668 0,315 1,270 0,870 1,741 1,857 0,741 1,545 1,405 1,603 0,398 0,615 1,411 1,268 0,509 0,701 1,333 0,630 0,746 1,276 1,694 0,962 1,52 0,902 1,524 1,885 0,738 1,133 0,975 1,703 0,960 1,398 1,92 0,915 0,659 0,52 0,425 1,325 1,947 1,119 1,76 1,816 1,245 1,654 0,55 1,393 1,205 1,327 0,432 1,849 0,572 0,489 0,303 0,25 0,22 0,991 0,5 1,602 1,428 1,118 1,279 1,392 0,785 0,863 1,159 1,571 0,36 1,291 1,392 1,446 0,904 0,556 1,548 0,46 0,55 0,509 1,466 1,914 0,825 1,263 1,320 0,222 1,45 1,310 0,982 1,996 0,760 1,601 1,109 0,971 0,102 1,14 0,634 0,109 1,70 1,45 1,354 0,696 0,33 1,140 0,628 0,608 1,378 1,600 1,32 0,220 0,755 1,963 1,930 1,890 1,426 0,250 1,561 0,774 1,528 1,406 1,511 1,781 0,699 1,495 0,422 1,845 0,597 0,756 1,391 1,275 0,616 1,672 0,754 0,196 0,642 1,754 1,421 0,528 0,773 0,572 0,41 1,712 0,396 1,465 1,834 1,31 0,37 1,829 0,338 0,48 0,254 0,746 0,851 0,119 0,319 0,931 1,535 1,599 0,72 0,991 1,994 1,915 0,176 0,361 0,667 0,781 1,473 1,901 1,23 0,573 0,477 0,260 0,489 1,744 0,364 0,950 0,897 1,359 0,902 1,984 0,899 0,770 0,275 0,618 1,428 1,55 1,721 1,239 0,698 1,960 1,415 0,635 1,910 0,582 0,447 1,274 1,840 1,905 1,674 0,970 1,49 0,451 0,212 1,630 1,681 1,179 1,672 1,692 1,905 1,192 1,117 1,125 0,434 1,447 1,122 0,390 1,90 1,715 0,620 0,309 0,402 0,761 0,836 0,125 0,292 0,898 1,313 1,294 0,742 1,630 1,34 1,369 0,506 1,70 0,556 0,798 1,529 1,177 0,83 1,348 0,955 1,932 0,534 1,538 0,70 1,960 1,167 1,258 0,543 1,244 0,908 0,266 1,388 1,937 1,608 0,499 0,939 0,982 1,244 1,924 0,986 1,906 1,385 0,20 0,961 0,385 0,700 1,899 0,106 0,881 0,407 1,422 0,673 0,687 0,460 0,664 0,813 1,992 1,520 0,486 1,568 0,340 0,35 1,638 1,130 0,721 1,503 0,214 1,982 1,996 1,302 0,584 1,218 0,668 0,568 1,861 1,110 1,905 0,361 1,740 1,297 0,629 1,223 0,863 1,276 1,148 1,362 1,801 1,478 0,968 0,828 1,88 0,557 1,974 0,998 0,7 0,125 0,559 0,550 1,496 1,122 1,276 0,604 1,202 1,897 0,839 0,509 0,734 0,809 1,703 1,793 0,326 0,397 0,253 0,631 0,456 1,269 1,199 0,614 1,797 1,569 0,510 0,711 0,625 0,119 0,399 1,515 1,927 1,93 0,907 1,884 0,119 0,426 0,159 0,863 0,992 0,310 0,829 0,30 0,429 0,185 1,496 1,605 1,912 0,123 1,163 0,89 1,471 1,954 0,438 0,797 1,808 1,117 0,955 0,745 1,214 0,236 0,298 0,331 0,227 0,385 1,287 1,497 0,736 0,713 0,777 0,630 1,349 0,435 1,976 0,943 0,176 1,396 1,419 0,708 1,320 1,694 1,206 1,966 0,827 0,712 0,588 0,849 0,672 0,968 0,772 0,79 1,972 1,434 1,203 1,548 0,122 0,950 1,532 0,740 1,662 1,859 1,767 0,470 1,183 1,448 1,604 0,727 0,871 1,685 0,928 1,638 1,321 1,434 1,818 0,449 0,616 0,272 1,233 0,599 1,807 0,964 1,529 0,898 1,342 0,925 0,95 1,132 0,718 1,857 1,644 0,933 0,316 0,734 1,371 1,262 0,959 1,480 1,851 1,20 1,721 0,416 1,847 1,383 1,230 0,346 1,323 0,396 0,399 0,611 0,127 1,237 0,878 0,4 0,375 1,583 0,24 1,856 1,251 1,0 1,199 0,852 1,954 0,153 0,342 1,425 0,501 0,613 0,239 0,45 1,925 1,878 0,935 1,32 1,693 0,999 0,821 0,42 0,588 0,645 0,19 1,409 0,806 1,996 1,503 1,943 1,782 0,669 1,663 1,127 0,405 1,971 1,623 1,311 0,799 1,439 0,508 0,660 0,114 0,682 0,495 1,788 1,660 1,198 0,874 1,0 0,969 0,502 1,268 0,217 0,412 1,933 0,175 0,255 1,367 1,112 0,360 1,601 1,483 1,669 0,279 1,372 1,279 0,739 0,607 1,899 0,770 0,672 0,96 0,971 1,638 0,369 0,383 0,527 0,299 1,185 0,501 0,326 1,602 0,310 1,561 1,579 1,771 0,792 0,395 0,338 0,29 0,252 1,882 1,553 1,786 0,68 0,445 0,705 0,132 0,55 0,964 1,392 1,730 0,777 1,966 1,135 0,599 0,544 0,276 0,237 1,978 1,405 0,847 1,421 0,49 0,882 1,328 0,576 0,505 0,250 0,19 0,477 1,527 1,94 0,734 1,800 1,762 1,150 0,690 0,322 0,223 0,800 0,303 0,860 0,369 1,480 0,238 0,57 0,528 0,875 0,156 0,794 1,32 1,461 1,58 0,982 1,283 1,454 0,946 0,329 1,383 0,778 1,884 0,300 0,750 0,7 0,380 1,723 0,329 1,640 1,348 1,916 0,755 0,646 1,30 0,184 1,377 0,350 0,709 0,978 1,171 1,966 0,502 1,438 1,839 1,56 0,260 1,54 1,271 1,357 1,9 1,264 0,231 1,890 1,686 1,15 1,349 0,698 0,34 0,4 0,882 1,843 0,849 1,138 1,920 1,390 1,7 0,761 1,88 0,98 0,337 0,287 0,991 0,267 1,661 0,5 0,64 0,936 1,521 1,682 0,908 1,532 0,524 1,366 1,278 1,79 0,427 1,17 1,526 0,200 0,676 0,534 0,424 0,483 0,60 1,450 1,763 0,18 1,949 1,321 1,54 1,605 1,828 1,1 1,466 1,384 1,808 1,68 1,112 1,188 0,77 0,1 1,678 1,320 0,548 0,917 1,598 1,352 1,837 1,48 1,193 0,748 1,453 0,347 0,156 1,969 1,664 1,525 1,813 0,651 1,975 1,216 1,281 1,920 0,214 0,600 0,743 0,226 0,824 1,21 1,610 0,928 0,435 1,457 1,630 0,252 1,645 0,869 1,890 1,429 0,516 0,194 0,114 0,680 0,966 0,890 0,770 0,125 0,427 1,3 0,608 1,537 0,644 0,920 0,499 0,950 1,812 0,905 0,695 1,458 1,669 1,431 1,629 0,298 1,958 1,479 0,61 0,894 1,759 0,210 1,47 0,877 1,292 0,458 1,65 1,834 0,435 1,804 1,583 1,751 1,569 0,135 1,743 1,287 0,241 0,201 1,995 0,461 0,406 0,833 1,695 0,557 1,483 0,217 0,659 1,335 1,14 1,640 0,165 1,855 1,388 0,724 1,186 1,658 0,711 1,465 0,742 0,130 0,962 1,954 1,577 1,37 0,453 1,644 0,854 0,523 0,764 0,896 0,73 1,487 1,141 1,438 0,842 1,444 1,989 0,606 0,704 0,89 0,932 1,293 0,141 1,333 1,632 0,431 0,78 0,942 1,382 1,472 1,781 1,937 0,211 0,489 1,524 1,422 0,978 1,163 0,217 1,610 1,989 0,217 1,745 0,811 0,284 0,725 0,578 0,433 1,427 1,214 1,176 1,385 1,696 1,972 0,842 0,377 1,873 0,113 1,382 0,294 1,541 0,776 1,413 0,513 1,829 0,215 0,308 1,738 1,367 1,571 0,465 1,800 0,856 0,981 0,239 1,991 1,908 0,418 1,2 0,453 0,39 1,741 0,289 1,688 0,606 1,181 0,885 0,675 0,701 0,373 0,863 1,111 0,379 1,274 0,548 1,974 1,195 1,880 0,528 1,585 0,652 1,437 0,407 0,505 0,313 1,12 0,17 0,189 0,588 0,731 1,359 1,255 1,295 0,662 0,394 1,611 0,692 0,75 0,601 0,591 1,498 1,852 0,662 0,587 0,749 0,497 1,712 1,84 1,788 1,145 1,316 1,920 1,424 1,944 1,130 0,200 0,969 0,361 1,276 1,146 0,959 0,290 1,171 0,227 0,399 0,644 1,742 1,423 1,77 0,419 0,495 1,605 1,215 1,372 0,81 1,865 1,118 0,363 0,708 1,752 1,772 0,619 1,706 0,375 1,322 1,23 0,313 0,700 0,473 0,244 0,718 0,410 1,422 1,619 1,235 1,867 0,658 1,941 1,537 1,621 1,507 1,112 0,292 1,992 0,814 0,641 1,108 1,949 0,227 0,917 1,66 1,229 0,476 1,438 0,265 0,842 1,352 1,966 0,260 1,193 0,108 1,707 1,272 1,67 1,337 0,479 0,468 1,415 1,136 0,75 1,981 0,142 1,202 1,662 1,753 1,822 1,793 0,769 1,598 0,415 0,307 0,43 0,7 0,456 1,382 1,976 0,773 0,635 0,404 1,872 0,725 1,235 1,769 0,696 0,912 0,640 0,81 0,981 0,993 1,638 0,559 0,649 1,945 1,214 0,641 1,722 1,371 0,341 0,746 1,715 1,960 1,379 1,474 1,719 1,285 0,219 1,327 1,578 1,259 1,737 1,314 1,575 1,335 0,649 1,416 0,572 1,908 1,347 0,605 1,673 1,974 0,48 0,547 0,767 1,999 0,645 0,147 0,178 0,47 0,777 0,986 0,713 0,659 1,780 1,300 0,929 1,627 0,831 0,754 0,647 1,155 0,269 1,596 0,912 1,952 1,4 1,319 0,443 0,350 1,459 0,349 1,566 1,675 1,46 0,128 1,957 1,245 0,614 1,333 0,685 1,421 1,238 0,884 1,822 1,351 1,592 1,539 0,257 0,68 1,328 1,989 0,905 0,994 0,665 0,120 0,682 1,996 0,922 0,598 1,132 0,66 1,559 1,773 0,887 1,558 0,437 0,490 0,669 1,301 0,219 1,91 0,67 0,971 1,875 0,638 0,27 0,790 1,573 1,362 0,988 1,297 1,538 0,310 0,611 1,77 0,927 0,526 1,291 0,426 0,655 0,772 1,179 0,76 0,391 1,173 1,598 1,783 0,34 1,558 1,617 1,770 0,587 1,743 0,795 0,254 1,448 1,719 0,304 1,805 0,640 0,225 1,179 1,98 0,169 1,50 0,250 1,162 0,690 1,558 1,430 0,619 0,174 1,847 1,434 0,295 0,850 1,913 0,941 0,200 0,361 0,749 0,945 0,55 0,481 1,488 1,330 1,234 0,97 0,105 0,314 1,659 0,721 1,290 0,683 1,489 0,830 0,784 1,835 0,627 1,718 0,208 1,203 1,810 1,344 1,107 0,379 1,42 1,165 1,576 0,922 1,365 0,273 0,320 1,764 1,753 1,284 1,204 1,877 0,218 0,935 1,888 1,965 0,640 0,832 1,684 0,764 0,26 0,21 1,752 1,686 0,636 0,66 1,395 1,657 0,540 1,541 0,85 1,466 0,501 1,329 0,806 0,509 0,524 1,99 0,501 0,107 1,883 0,375 0,994 0,909 0,413 0,661 1,335 1,718 1,539 0,600 1,393 0,893 0,466 1,69 1,632 1,347 0,753 0,17 1,866 1,121 1,542 0,625 1,351 0,630 1,79 0,551 1,514 0,423 1,465 0,923 1,624 0,425 1,281 0,440 1,424 1,339 1,19 0,652 0,517 1,151 1,370 0,578 0,775 1,768 1,12 1,398 0,51 0,381 0,450 0,296 1,908 0,643 0,730 1,606 0,341 1,216 1,919 0,255 1,544 0,283 1,420 0,164 0,349 0,37 0,586 0,972 0,659 0,844 1,277 1,634 1,261 1,771 0,411 1,300 0,902 0,299 1,873 0,656 0,270 1,696 0,632 1,509 0,176 1,274 0,434 1,906 1,288 0,163 1,158 1,695 0,126 0,850 1,943 1,184 1,258 0,440 0,270 0,641 1,248 0,357 0,583 0,232 1,495 1,11 0,657 1,836 0,471 0,359 1,13 0,216 1,141 0,138 0,319 1,813 1,371 1,363 1,19 1,821 0,941 0,522 0,824 0,308 0,932 1,427 0,508 1,762 0,328 0,393 0,755 0,795 0,247 1,717 1,306 1,595 1,895 0,376 0,557 0,881 0,374 0,266 1,348 0,920 1,42 1,908 0,495 1,280 0,60 0,535 1,765 1,176 0,440 1,298 0,294 0,527 1,379 1,133 1,75 1,975 0,125 0,2 0,621 1,261 1,379 0,207 1,461 1,871 1,95 0,79 0,4 1,496 1,271 0,239 1,409 1,733 1,93 0,849 0,640 0,235 1,777 0,624 1,47 0,778 1,642 0,318 0,432 1,431 1,476 1,481 1,823 1,523 1,675 1,469 1,185 0,395 0,333 0,840 0,174 0,758 0,755 0,534 1,628 1,460 1,787 1,423 1,905 0,205 0,116 0,803 1,861 1,407 0,918 1,620 0,469 1,719 1,900 1,348 0,549 1,700 0,126 0,738 1,583 1,917 0,418 1,209 0,500 0,697 0,904 0,237 0,154 1,655 0,725 1,480 1,712 1,602 0,420 0,980 0,272 1,211 1,338 1,993 1,871 0,156 0,989 1,312 1,35 0,981 1,396 0,497 1,857 0,222 1,562 1,752 1,818 0,228 1,9 0,631 1,970 1,593 1,798 1,208 1,207 1,52 1,255 1,394 0,69 1,141 0,372 0,48 1,850 1,25 0,885 0,408 0,81 0,930 0,74 1,257 0,813 0,828 1,982 0,583 0,708 1,79 0,910 1,639 0,668 0,46 1,188 1,75 1,825 0,367 1,868 0,932 1,215 0,767 1,328 1,595 1,188 1,233 1,507 1,587 1,225 1,786 0,91 1,300 0,286 0,427 0,713 1,217 0,507 0,72 1,582 1,277 1,868 1,584 0,83 1,848 0,635 1,126 0,320 0,347 0,936 1,808 0,486 1,235 0,935 1,974 1,573 1,376 0,672 0,705 0,930 0,897 0,719 1,911 1,499 1,325 1,384 0,424 1,637 1,820 1,956 0,807 0,810 1,472 0,731 1,276 0,971 0,313 1,928 0,609 0,874 0,214 1,506 1,821 1,326 0,640 1,294 0,622 0,128 0,92 0,256 0,550 1,560 0,698 1,547 0,377 0,208 0,253 0,370 0,79 1,693 0,209 1,49 0,428 0,779 0,652 1,467 1,757 1,498 1,125 1,818 0,470 0,781 1,716 1,23 0,107 1,116 1,814 1,36 0,780 0,810 1,628 0,860 1,439 1,797 1,328 0,971 0,876 1,907 1,698 1,920 1,105 0,228 0,132 0,802 0,620 0,161 1,37 1,935 1,436 1,316 0,806 1,103 1,120 1,521 1,676 1,890 0,363 0,532 1,340 1,717 1,146 0,451 0,178 0,704 0,783 1,952 1,376 1,397 1,264 0,849 1,270 1,978 0,230 0,139 1,55 1,612 1,332 0,21 0,13 1,8 1,160 1,51 0,118 1,465 0,563 1,418 1,127 1,895 0,175 1,59 1,42 1,148 1,127 1,569 1,41 1,770 0,711 0,477 0,842 0,247 1,768 0,484 1,634 0,646 1,760 1,459 0,880 1,750 0,323 1,844 0,378 0,712 0,985 0,549 0,377 0,956 0,405 0,612 0,72 1,468 1,813 1,729 1,482 0,226 0,609 0,552 1,806 0,526 1,505 1,562 1,63 0,91 0,459 1,916 1,631 1,326 0,242 1,429 1,602 1,86 1,607 1,884 1,227 1,100 1,182 1,222 1,244 1,274 1,803 0,55 0,356 1,756 0,919 0,108 1,195 0,211 0,772 1,179 1,947 1,107 0,156 0,459 1,210 1,846 1,681 0,759 1,851 1,990 1,975 0,980 0,120 0,973 0,129 1,709 0,135 0,842 0,618 1,284 1,344 1,929 1,768 1,314 1,316 0,556 1,945 1,555 0,379 0,733 0,703 1,426 1,271 0,296 1,977 0,209 0,126 0,862 1,799 0,468 1,564 0,795 1,356 1,139 1,237 0,646 1,550 1,156 1,861 1,977 0,61 0,354 1,12 0,176 0,89 1,694 1,302 0,51 1,605 0,108 1,465 1,202 1,941 0,827 1,744 0,157 0,465 0,174 1,534 1,752 0,444 0,47 1,259 0,706 0,91 1,928 0,646 1,279 0,28 0,748 1,783 1,654 1,353 0,31 0,222 0,272 0,666 1,914 0,893 0,414 0,723 1,230 0,881 0,4 1,463 0,416 1,753 1,706 1,948 1,780 1,38 0,181 0,468 0,946 1,969 1,152 1,939 1,598 1,184 1,617 0,45 0,307 1,498 1,14 0,914 1,991 1,411 1,675 0,40 0,667 0,614 0,653 0,420 1,346 1,264 1,377 1,681 1,164 0,812 0,657 0,409 0,67 0,104 1,815 0,12 1,358 0,281 1,815 0,37 1,356 0,676 1,939 0,916 0,536 1,147 0,885 0,151 0,236 1,44 1,505 1,983 0,716 0,510 0,95 1,487 1,142 0,171 0,16 0,189 1,773 0,264 0,783 0,503 0,66 1,518 0,239 0,40 1,962 1,500 1,956 1,864 0,116 1,176 1,883 0,166 1,681 1,225 1,518 1,721 0,231 0,370 0,681 1,113 0,919 0,613 0,403 1,862 0,389 1,172 0,692 1,511 0,466 1,847 1,716 1,693 0,20 1,284 0,665 0,615 1,668 0,257 0,62 1,777 0,712 0,710 1,553 0,534 0,379 1,546 1,57 1,493 0,931 1,851 1,739 0,815 0,170 0,783 1,512 0,64 0,845 0,644 1,279 0,571 1,587 0,354 1,184 1,645 0,679 0,684 0,665 0,848 1,831 1,367 0,442 0,754 0,517 1,543 0,282 1,919 0,254 1,893 0,911 1,875 1,963 1,460 1,967 1,870 1,311 0,347 0,941 1,822 1,663 0,38 1,616 0,709 0,388 1,244 1,844 1,573 0,48 1,977 1,244 0,686 1,329 0,697 1,407 0,789 0,190 1,134 0,181 0,425 1,896 0,745 1,743 0,577 0,643 0,268 0,28 0,470 0,111 1,129 0,464 1,776 1,289 1,904 1,677 0,837 0,137 0,548 0,42 0,141 0,132 1,961 1,649 1,255 1,834 0,183 0,10 1,342 1,563 1,628 1,440 0,928 0,318 0,464 0,723 1,990 0,301 0,125 0,469 1,192 1,628 0,226 0,687 1,283 1,893 1,654 0,538 0,477 0,902 1,616 1,9 1,961 0,851 0,596 1,179 1,461 1,109 1,343 0,33 0,265 0,255 0,461 0,357 1,543 1,834 0,742 1,99 0,799 1,634 1,25 0,901 0,407 1,332 1,726 0,317 0,71 1,262 0,593 0,196 0,768 1,474 0,358 1,585 1,191 1,384 1,344 0,177 0,174 1,469 0,726 0,193 0,514 0,788 1,336 0,343 0,115 0,235 1,732 0,623 0,672 0,59 1,886 0,732 1,626 0,35 1,558 1,582 1,147 1,828 0,851 1,150 1,833 1,961 1,766 0,728 0,395 1,772 1,330 0,698 1,118 0,733 1,62 1,819 1,42 0,89 1,312 0,954 1,122 0,535 1,530 1,686 0,720 1,420 0,20 0,683 0,707 0,817 1,612 0,494 1,16 0,398 1,124 0,186 0,135 1,829 0,175 0,859 1,653 0,260 0,513 0,481 1,922 1,782 0,908 1,839 1,802 0,458 0,940 0,879 0,137 1,829 0,570 1,756 0,298 0,385 0,736 1,902 1,363 0,318 1,67 0,888 0,949 0,128 0,888 1,843 0,431 0,129 0,344 1,109 0,29 0,733 0,722 0,48 0,204 1,489 1,266 0,729 0,684 1,993 0,128 0,164 1,360 1,143 1,567 1,422 0,458 1,282 1,303 1,574 0,558 0,888 1,587 0,309 0,135 1,363 1,354 1,215 1,314 0,452 0,424 0,281 0,272 0,805 1,80 0,373 0,452 1,529 1,870 1,196 1,216 0,963 0,24 1,618 0,970 1,701 1,184 1,246 0,417 1,21 1,418 0,913 0,773 1,180 1,913 1,45 0,610 1,795 0,654 1,547 1,976 1,896 0,829 0,440 0,104 1,461 1,357 0,988 1,226 0,637 1,560 0,238 1,490 0,940 0,776 1,619 1,323 0,904 1,535 1,938 1,694 0,856 1,331 1,902 1,293 0,646 0,732 0,990 0,69 1,393 0,767 1,626 1,392 1,63 1,957 0,113 0,452 0,970 0,610 1,623 1,660 1,719 1,458 0,623 1,456 0,344 1,862 1,283 0,306 1,902 1,431 0,545 1,785 0,268 0,321 0,472 1,976 0,360 0,280 1,664 0,263 0,664 0,351 0,572 1,723 1,803 1,759 1,600 0,360 0,471 1,99 1,816 1,570 1,248 1,602 1,943 1,124 1,359 1,421 1,624 1,94 1,489 1,728 0,694 0,263 0,490 0,467 0,784 1,319 0,717 1,746 0,534 1,852 0,945 0,854 1,65 1,322 1,120 1,647 0,164 1,640 0,298 0,537 0,826 1,345 1,324 1,774 1,90 1,444 0,357 0,329 1,789 1,836 1,447 0,246 1,134 0,298 1,667 0,362 1,202 1,866 0,424 0,73 0,809 1,487 0,287 1,91 1,75 1,0 0,428 0,398 1,836 0,388 1,576 0,343 0,144 0,974 0,4 0,628 0,734 0,413 1,951 0,882 0,585 0,288 0,740 0,859 1,204 0,269 1,450 0,237 1,682 1,238 0,652 1,846 0,94 0,571 0,571 1,364 1,640 0,479 1,233 0,778 1,584 0,720 1,641 1,964 0,734 0,626 0,826 0,14 0,231 1,174 0,942 0,654 1,791 1,28 0,894 0,532 1,763 1,535 1,905 0,666 0,412 1,815 0,945 1,686 1,672 1,921 0,483 0,770 0,948 1,19 1,550 0,282 0,390 0,820 1,421 0,189 1,273 1,403 1,477 1,961 0,846 1,346 0,772 1,635 1,47 1,443 0,857 1,107 1,892 1,357 0,372 0,922 0,821 1,742 0,446 0,943 1,123 0,414 1,917 1,644 0,813 1,895 0,545 1,520 0,702 0,879 0,43 1,58 1,901 0,141 1,829 0,633 1,423 1,350 1,119 1,554 1,562 0,487 1,71 1,847 0,654 0,437 1,104 0,972 1,845 1,45 1,121 1,562 1,80 1,261 1,441 1,876 0,573 1,19 0,806 0,524 0,459 1,842 1,798 1,536 0,878 0,315 0,647 1,325 0,329 1,464 1,760 1,82 0,975 1,394 1,866 1,219 0,889 1,519 0,704 0,681 0,216 1,148 0,491 0,461 1,945 0,715 0,783 0,378 1,346 0,786 1,110 1,426 0,319 1,339 0,555 1,899 1,634 0,505 0,374 1,526 1,460 1,400 1,251 1,71 1,471 0,313 0,66 1,943 1,672 0,44 1,664 1,117 0,431 1,165 1,719 0,468 1,684 1,611 1,997 0,988 1,533 1,163 1,480 1,268 1,716 0,661 0,352 1,614 1,80 1,901 1,765 0,215 0,111 1,974 1,0 0,691 0,342 1,592 1,956 1,643 1,976 1,949 1,673 1,390 1,694 1,399 1,282 0,540 1,143 1,265 0,740 0,354 1,445 0,211 1,904 1,854 0,333 0,50 0,861 1,320 1,615 1,740 0,675 0,969 1,753 1,24 1,111 0,304 0,788 0,40 0,705 0,999 0,705 1,58 1,519 0,808 0,398 0,153 0,438 1,831 0,474 0,143 1,519 1,831 1,615 0,335 1,715 1,77 0,902 1,987 0,598 1,926 1,1 0,462 0,41 0,626 0,613 1,642 1,32 1,687 1,521 1,215 1,594 0,30 0,712 0,464 1,592 0,107 0,318 1,754 1,162 1,893 1,179 1,173 1,383 1,356 1,459 1,745 0,668 0,842 0,565 1,839 0,496 1,29 0,991 1,85 1,336 1,759 1,603 0,888 1,779 1,469 1,438 0,374 0,286 1,864 1,372 1,792 0,40 1,556 0,817 0,371 0,497 0,506 1,671 1,407 1,394 0,205 0,751 1,70 1,718 0,952 0,905 1,129 1,463 0,987 0,431 0,528 0,703 0,914 1,781 0,200 1,675 0,842 0,43 1,475 1,748 0,694 0,287 0,940 1,845 1,393 0,772 0,150 1,442 0,31 1,41 1,931 1,843 1,848 1,420 0,681 1,353 0,855 1,102 0,930 1,432 0,498 1,662 0,872 1,667 0,129 0,427 1,135 0,967 0,868 0,713 1,5 0,499 1,497 0,921 1,353 0,580 1,133 1,261 0,518 1,63 1,547 1,203 0,894 0,768 1,504 0,674 0,237 0,863 0,487 1,739 0,515 1,373 1,780 1,497 1,521 1,637 0,769 1,457 1,769 1,888 0,960 1,286 1,393 0,728 0,759 1,595 1,550 1,173 0,114 0,382 0,194 0,976 1,886 1,956 0,444 0,655 0,383 1,870 0,500 0,740 1,230 1,133 1,511 0,957 1,762 1,816 0,379 0,365 0,418 0,767 1,887 1,178 1,376 1,144 0,927 1,907 1,31 0,855 0,927 0,570 0,18 1,313 1,227 1,301 0,185 0,38 1,304 0,259 0,858 1,379 0,930 0,99 0,309 1,685 1,656 1,380 0,155 0,92 0,241 0,220 1,338 0,797 0,890 0,239 1,146 0,675 0,416 0,357 1,770 1,226 1,584 0,575 1,697 0,20 1,923 1,986 0,719 1,699 1,52 0,615 0,180 1,488 0,843 1,427 1,130 1,267 1,842 0,894 0,970 1,551 0,92 1,452 1,937 0,909 0,857 0,557 1,43 0,206 0,684 1,638 0,272 0,245 1,737 1,53 0,9 0,862 0,652 0,628 0,582 0,171 0,139 0,987 1,821 0,299 0,12 0,656 1,799 0,848 0,74 1,566 1,470 1,916 1,357 1,678 0,982 0,425 1,590 1,102 0,350 0,133 0,707 1,11 0,498 0,462 1,352 0,496 0,712 0,881 0,153 0,538 1,747 0,647 0,388 0,784 0,424 0,565 0,311 0,689 1,814 1,236 0,236 0,803 1,221 1,506 1,246 1,852 1,800 0,325 1,118 0,156 1,657 1,846 0,743 0,419 1,839 0,969 1,284 1,894 0,738 0,847 0,98 0,147 1,623 1,882 1,586 1,43 1,918 0,253 0,893 1,371 1,454 0,869 0,304 1,554 1,735 1,557 0,635 1,583 1,378 0,728 1,420 1,496 0,251 0,475 1,200 0,652 0,128 0,846 0,354 0,762 1,774 1,251 0,133 1,525 1,69 0,39 0,101 0,108 0,902 1,167 0,483 0,326 1,989 0,356 1,664 0,492 1,188 0,368 1,108 0,616 1,415 1,788 1,785 1,495 0,367 0,351 1,195 0,683 1,198 0,197 0,832 1,867 1,795 1,508 1,346 0,383 0,651 0,838 0,26 1,555 1,189 1,564 1,453 0,19 1,218 0,144 0,863 0,23 0,50 1,148 0,273 0,149 1,638 0,379 0,100 1,247 0,45 0,625 1,71 0,686 0,411 0,13 0,880 1,13 0,199 1,191 0,217 0,719 1,371 1,710 1,745 1,967 0,828 1,563 0,372 0,378 1,409 1,438 1,418 1,315 1,760 1,799 1,291 0,271 1,525 0,996 1,197 1,823 1,24 1,327 0,386 0,542 1,999 0,592 0,145 0,824 0,794 0,87 1,285 1,104 0,996 0,989 1,364 0,432 0,548 0,275 0,318 1,883 0,215 0,842 1,535 0,753 1,550 0,907 0,925 1,401 0,784 1,961 0,439 1,747 1,741 0,687 0,312 0,103 1,540 0,809 1,489 1,99 1,290 1,823 0,407 1,140 0,688 1,436 1,622 1,112 1,225 0,147 1,355 1,318 1,688 0,173 1,152 0,681 1,448 1,241 0,472 1,175 0,397 0,633 0,748 0,567 0,55 0,267 0,801 0,409 1,680 0,344 0,490 1,17 0,874 1,326 1,887 1,694 0,7 0,167 0,655 0,867 1,584 0,45 0,78 0,221 0,138 1,175 1,555 1,340 1,428 0,307 0,538 0,27 1,478 0,557 0,8 1,605 0,949 0,384 1,19 0,543 1,390 0,629 0,729 0,934 0,694 0,181 0,351 0,884 1,426 0,503 0,636 0,796 1,336 0,543 0,711 1,663 1,339 0,719 0,789 0,606 0,369 1,743 1,361 1,718 0,347 1,917 1,59 1,593 0,450 0,891 1,262 0,138 1,466 0,227 1,402 1,868 1,210 0,673 1,967 1,866 0,674 0,898 0,800 1,924 0,514 0,566 1,144 1,577 0,643 0,155 1,73 1,84 0,782 1,754 0,661 0,876 0,770 0,838 1,799 1,591 0,764 1,426 0,700 1,197 1,850 1,129 1,630 0,127 1,71 0,982 1,53 1,4 1,574 0,141 1,622 1,870 0,989 0,571 1,653 1,361 1,743 0,354 0,923 0,341 0,517 1,228 0,499 1,927 1,554 1,774 1,278 0,986 1,371 1,744 0,920 0,116 0,410 1,587 0,261 0,643 1,880 0,406 1,743 1,201 1,117 1,663 1,540 1,254 0,968 1,0 0,855 0,830 0,278 1,336 0,826 0,693 0,502 1,935 1,29 0,525 1,880 1,344 1,140 1,350 1,741 1,309 0,50 1,643 1,183 0,761 1,641 0,650 1,907 1,102 1,684 1,337 0,433 1,286 1,811 0,446 1,644 1,922 0,233 1,634 1,522 0,63 1,248 1,335 0,532 1,846 1,968 1,112 0,397 1,62 1,213 1,46 1,86 1,380 1,320 0,676 1,831 0,749 1,265 1,138 1,82 0,862 1,969 1,987 0,263 1,154 1,692 0,530 0,536 0,241 0,100 1,303 0,313 1,133 0,205 1,903 0,892 0,286 1,874 0,790 0,880 0,760 1,13 1,930 1,273 0,19 1,80 1,243 0,652 1,567 1,552 0,293 1,863 0,255 0,648 0,205 0,938 0,856 1,554 1,901 1,568 1,696 0,44 1,209 0,148 0,763 0,123 0,934 1,615 1,855 1,4 1,368 0,979 0,453 1,597 1,723 0,227 0,18 1,73 1,761 0,512 1,60 1,224 0,57 0,955 0,502 0,356 1,102 1,745 1,581 0,250 1,592 1,909 1,375 1,983 0,255 0,651 1,1 1,494 1,951 0,336 0,908 0,796 0,315 0,422 1,291 1,111 0,909 1,912 0,367 0,925 1,513 0,568 0,849 1,653 1,142 1,459 0,341 1,279 0,729 0,101 1,199 0,850 1,555 0,66 0,968 0,701 0,912 1,762 0,680 0,285 0,578 1,113 1,991 0,496 0,409 1,431 1,64 0,759 0,868 1,414 0,122 0,945 0,601 1,896 1,305 1,51 0,302 0,709 1,773 1,989 1,471 0,208 0,41 0,613 0,639 1,885 0,942 1,569 0,43 0,35 1,420 1,322 1,220 0,214 1,907 1,621 0,333 1,598 0,262 1,921 0,870 0,586 1,286 0,991 1,208 1,917 1,154 0,52 1,73 1,308 1,265 0,128 1,352 1,572 0,138 1,14 1,955 1,763 1,727 1,18 0,395 1,96 1,973 1,442 1,747 1,443 1,437 0,138 0,472 1,781 0,348 1,589 0,325 1,945 1,928 0,507 1,711 0,169 1,426 1,933 1,852 0,24 1,573 1,755 0,718 0,60 1,981 1,333 1,562 0,356 0,200 0,638 1,889 0,626 0,272 0,620 0,682 0,985 1,853 0,588 0,454 1,664 1,206 1,150 1,100 0,384 0,415 1,588 1,680 0,233 1,139 0,827 1,141 0,48 1,249 1,497 0,993 0,489 0,517 1,397 0,124 0,205 0,821 1,977 0,560 1,308 0,108 1,165 1,216 1,23 1,429 1,994 1,233 0,779 0,895 0,116 1,97 1,22 1,553 1,406 0,103 0,847 0,267 0,488 1,462 1,107 1,462 1,232 1,833 0,781 0,203 1,633 0,798 1,421 0,535 0,488 0,420 0,693 0,666 0,963 1,368 1,109 0,311 1,221 1,175 1,614 1,12 0,812 1,281 1,974 1,798 0,980 1,420 1,706 1,503 1,273 0,946 1,86 1,676 0,446 0,907 0,11 1,49 1,599 0,767 1,937 1,567 1,761 1,441 1,435 1,56 1,62 1,141 1,342 0,625 1,633 0,915 1,869 1,493 0,614 1,41 1,300 1,338 1,374 1,540 1,275 0,529 0,80 1,228 1,859 0,965 0,216 1,882 0,718 1,741 1,272 1,651 1,556 1,461 0,71 1,589 0,445 0,313 1,678 0,518 0,738 0,453 1,879 1,276 1,855 1,554 1,18 0,882 1,768 0,612 1,376 1,696 0,996 1,784 0,774 0,382 1,722 0,117 1,670 0,515 0,494 0,849 1,922 0,47 0,922 0,393 0,954 1,699 0,170 1,4 1,511 1,880 0,496 1,578 0,935 1,1 1,567 0,67 0,471 1,583 1,707 0,544 1,910 1,702 1,309 0,579 0,563 0,787 1,395 1,874 0,195 1,549 1,107 0,79 1,829 1,428 1,21 0,344 1,203 1,37 1,888 0,442 1,618 1,905 1,730 1,504 1,754 0,673 1,176 1,832 1,596 1,674 0,74 0,893 1,73 1,276 0,607 0,450 1,412 0,442 0,893 0,3 0,421 0,249 1,248 0,732 1,331 0,528 1,37 0,413 0,796 1,707 0,532 0,949 1,922 0,277 0,553 1,313 0,636 0,312 1,276 0,432 1,626 0,297 1,208 1,211 0,562 1,440 1,256 1,839 1,646 1,253 0,352 1,630 1,874 1,171 1,954 1,35 0,260 1,734 1,4 1,583 1,396 1,131 1,896 0,184 1,763 0,219 0,495 1,776 0,157 1,295 1,644 1,607 0,31 1,721 0,886 0,834 1,988 0,219 0,993 0,447 0,954 1,986 0,196 1,50 0,863 1,33 1,353 0,694 0,82 1,237 0,896 0,593 1,1 0,673 1,852 1,566 0,475 0,761 1,54 0,97 0,625 0,496 0,210 1,761 1,909 0,982 1,815 0,614 1,731 0,736 0,531 0,806 0,89 1,126 0,721 0,531 1,106 0,79 0,31 1,755 0,107 0,713 0,693 1,197 0,508 0,835 0,7 1,705 1,184 0,527 0,221 0,45 0,591 1,518 0,96 0,109 1,201 1,993 1,165 1,377 0,564 0,62 1,562 0,354 1,715 0,306 1,259 0,789 1,973 1,515 0,364 1,218 1,189 0,993 1,508 0,517 0,972 0,134 0,324 0,13 0,772 0,513 0,414 0,63 1,446 0,882 0,396 1,113 0,855 0,298 1,847 1,239 0,189 1,239 1,971 1,511 1,284 1,601 1,15 1,968 0,453 1,506 0,625 1,873 1,288 1,405 0,449 0,700 1,919 0,267 1,505 0,753 1,59 1,58 0,600 1,752 1,414 0,735 1,779 1,240 1,596 0,858 1,1 0,759 0,99 1,311 0,835 0,339 0,185 0,375 1,612 1,515 1,182 1,990 0,328 0,847 1,26 0,752 0,715 1,446 1,191 0,986 1,96 1,603 1,867 0,21 1,912 0,157 0,274 0,327 1,966 1,65 0,702 0,252 1,467 1,502 0,204 1,746 1,740 1,685 0,668 1,204 0,553 0,250 1,764 0,4 1,815 1,371 0,562 1,109 0,954 1,161 1,635 0,671 1,497 1,550 1,870 0,379 1,765 1,582 1,123 0,240 0,820 0,689 0,162 0,42 0,895 0,945 0,453 0,56 0,780 1,852 1,999 0,527 0,170 0,729 1,913 0,323 1,576 0,994 1,458 0,642 0,448 0,621 0,137 1,545 0,188 0,597 0,339 0,295 1,748 0,797 0,484 1,106 0,938 1,247 0,674 0,521 1,814 1,239 1,95 0,736 1,471 1,296 0,710 1,681 0,814 0,609 0,372 1,931 1,250 0,164 1,266 1,134 0,485 0,237 1,918 0,27 0,701 0,919 0,97 1,674 0,250 1,797 1,57 0,850 1,352 1,425 0,803 0,272 0,732 1,656 0,781 1,460 0,163 0,47 0,38 1,359 1,220 1,971 0,122 1,249 0,559 0,369 0,372 0,552 1,52 0,57 1,145 1,207 0,730 1,6 0,249 0,308 1,845 0,551 1,484 1,247 1,198 0,686 0,78 0,478 1,594 1,523 0,587 0,36 1,616 1,738 0,889 1,438 1,138 0,809 0,944 1,433 1,877 0,555 0,778 1,766 0,890 1,92 0,472 1,733 0,772 1,786 0,645 0,1 0,804 1,435 0,461 1,632 1,476 0,189 0,685 0,522 1,438 0,788 0,602 0,413 0,956 0,694 0,754 1,656 1,298 1,467 1,376 0,754 0,191 0,143 1,399 1,671 0,891 1,665 1,842 0,546 1,821 0,670 0,683 1,571 1,278 1,641 1,768 0,97 0,340 0,730 1,446 0,501 0,904 1,197 1,645 0,8 1,842 0,14 0,517 0,29 1,157 0,954 1,465 1,66 1,882 0,30 1,531 0,873 1,580 0,186 1,13 0,549 1,51 1,505 1,624 1,9 0,884 0,500 0,53 1,138 0,321 0,995 0,148 1,196 0,745 1,216 0,702 1,320 0,384 1,162 1,878 0,280 0,301 0,11 0,883 0,962 1,707 1,14 1,234 0,924 1,204 1,117 0,805 1,120 1,235 1,367 0,665 0,241 1,455 1,961 1,519 1,30 0,219 0,522 0,126 0,860 0,960 1,661 0,319 1,297 0,103 0,441 1,775 1,126 0,707 0,581 1,858 1,901 1,521 0,287 0,711 1,78 0,514 0,134 0,611 0,582 1,444 1,18 1,241 1,805 0,953 0,855 0,494 0,424 1,188 0,28 1,26 0,834 1,545 0,134 1,781 1,330 0,612 1,319 1,647 0,557 0,194 1,520 1,173 0,540 0,37 1,929 0,642 1,240 0,316 0,513 1,842 1,243 1,494 0,686 1,893 0,53 1,963 1,630 1,822 0,945 0,645 1,263 0,759 0,184 1,48 1,591 1,234 0,348 1,744 0,178 1,547 1,839 0,504 1,165 0,34 1,3 0,673 1,624 0,665 0,5 1,709 1,564 0,178 1,654 1,286 0,906 0,931 1,898 0,943 0,561 0,91 0,416 1,827 0,783 0,881 0,291 1,902 0,678 1,941 0,258 0,40 1,55 1,437 0,184 1,917 0,184 1,644 0,786 1,282 0,912 1,750 0,203 1,150 0,64 0,290 1,139 1,48 1,714 1,16 0,365 1,263 1,806 1,728 0,129 1,729 0,882 1,966 0,2 0,532 0,415 1,593 1,470 1,909 1,242 0,26 0,304 1,212 1,371 1,343 0,971 1,694 0,388 0,38 1,938 1,796 1,247 0,763 0,295 1,248 0,370 1,911 1,863 0,107 1,646 1,24 1,549 1,241 0,888 1,927 1,441 0,906 1,188 0,601 1,61 0,580 0,818 1,445 1,208 0,415 1,924 1,47 0,93 1,212 0,648 1,11 1,84 0,634 1,502 1,173 1,209 0,235 0,201 0,317 1,458 1,721 0,812 0,875 1,515 1,195 0,945 0,201 1,398 0,612 1,731 1,117 1,549 1,329 0,872 1,132 0,450 0,235 0,168 0,650 1,521 1,491 1,227 1,61 1,805 0,977 1,424 0,469 1,329 1,919 1,399 1,452 0,361 0,918 1,730 1,62 0,492 0,741 1,848 0,363 1,106 1,181 1,476 0,102 0,544 1,212 0,834 0,605 0,981 1,141 0,938 1,558 1,879 0,622 1,976 1,388 0,137 1,595 0,552 1,373 0,273 1,256 0,416 0,743 0,495 1,660 0,427 1,72 1,452 1,718 1,853 1,133 0,116 0,987 1,339 1,627 1,582 1,977 0,631 0,559 1,898 1,353 0,959 0,312 0,292 0,355 1,599 1,63 0,687 1,231 0,527 1,263 1,48 0,71 1,430 1,913 0,790 1,8 1,818 1,730 0,528 0,695 1,175 1,14 0,630 1,110 0,998 0,970 0,787 0,917 0,821 0,70 1,417 0,232 1,874 1,356 1,886 1,110 1,841 0,284 1,445 0,578 1,919 1,75 0,485 0,185 0,100 1,238 0,259 0,203 1,359 0,135 1,377 0,364 0,166 1,270 0,873 1,981 0,386 0,419 1,896 1,691 0,143 1,673 1,741 0,54 0,386 1,565 1,320 1,41 0,949 1,660 1,181 0,943 1,816 1,852 0,299 1,608 1,695 0,922 1,856 1,226 0,412 1,754 1,179 1,684 1,763 0,90 0,150 0,457 0,552 1,416 1,780 1,722 1,241 1,614 1,180 1,283 1,391 0,603 1,56 0,135 1,82 0,611 1,330 0,713 0,525 0,943 1,987 1,193 1,724 0,679 0,909 1,577 1,138 0,67 0,480 0,864 0,177 0,46 0,705 1,520 0,217 1,107 1,557 1,108 1,813 1,8 1,638 1,961 1,901 0,156 0,106 0,931 0,254 1,125 1,784 0,745 0,22 1,419 1,411 1,63 0,592 0,187 0,224 0,295 0,481 0,233 1,627 0,692 1,398 0,372 0,765 0,131 0,266 1,750 0,512 0,358 1,340 0,226 1,631 0,233 0,418 0,540 0,868 0,537 0,242 1,58 0,895 1,645 1,99 0,607 0,67 1,420 0,427 1,449 0,121 0,278 1,148 1,765 1,49 0,780 0,602 0,174 1,349 0,508 0,282 1,31 0,844 0,2 1,551 0,688 1,47 1,810 0,113 0,990 0,303 0,126 1,310 1,685 0,218 0,67 1,172 1,631 1,399 1,636 0,988 0,938 0,0 1,445 1,340 1,682 1,213 0,488 1,912 0,703 1,475 0,69 0,958 0,807 0,518 1,199 0,372 1,123 1,775 1,156 0,594 0,849 1,779 0,741 0,596 1,104 1,66 1,181 1,915 0,557 0,469 0,974 0,88 1,410 0,481 1,866 0,785 0,391 1,436 1,261 0,16 0,297 1,210 1,990 1,233 0,386 1,40 0,666 0,963 1,108 0,405 1,438 1,518 1,890 0,925 1,169 0,283 1,740 1,173 1,21 1,953 0,772 0,337 0,260 0,931 0,318 0,125 1,49 1,183 0,292 0,873 0,456 0,912 1,679 0,690 1,422 0,597 1,824 1,382 1,331 1,117 0,665 0,534 1,397 1,306 1,171 1,243 0,359 0,359 1,434 0,30 0,696 0,736 1,707 1,633 1,807 1,659 0,763 0,311 0,334 0,820 1,553 1,970 0,405 1,96 0,146 0,989 0,790 0,375 1,630 0,755 0,389 1,193 0,161 1,768 1,216 1,933 0,201 1,432 1,889 1,44 0,618 1,553 0,909 1,432 0,907 1,105 0,746 1,485 0,592 0,363 0,220 0,302 1,971 1,260 0,708 0,508 0,404 0,740 1,26 1,463 0,263 0,845 0,518 1,384 1,197 1,151 0,762 1,440 1,365 1,46 0,289 0,503 0,182 0,226 1,79 1,398 0,238 0,243 0,895 1,560 0,802 0,421 1,965 1,297 1,754 0,241 1,97 0,963 1,535 1,762 1,287 1,888 1,860 1,950 1,994 0,647 1,757 1,485 0,89 0,303 0,970 0,468 0,953 0,658 1,378 0,438 0,759 1,824 0,937 1,291 0,78 0,401 0,299 1,888 0,983 0,440 1,755 1,242 1,498 1,64 0,354 1,488 0,658 1,207 1,543 0,274 1,187 0,916 0,913 1,22 1,932 1,786 1,936 1,157 1,467 1,543 0,243 0,777 1,2 0,517 0,418 1,646 0,207 1,349 0,209 0,754 1,520 0,707 0,793 0,885 1,310 0,386 0,357 1,757 1,985 0,857 1,39 0,232 0,833 0,760 0,443 0,444 1,120 0,682 0,813 0,849 1,456 0,95 0,337 0,257 0,782 0,140 0,235 1,839 1,653 1,721 1,649 1,566 1,427 1,239 1,536 0,350 1,373 1,87 1,157 1,404 0,154 1,115 0,59 0,17 0,215 1,49 1,565 1,746 0,881 1,980 0,463 1,700 0,324 1,80 1,33 0,20 1,481 0,904 1,875 1,811 1,970 1,797 0,128 0,330 0,737 1,306 1,335 0,236 1,49 1,308 1,737 0,199 1,716 0,555 0,812 0,131 0,75 0,671 0,321 1,189 1,644 0,602 0,287 0,556 1,6 1,663 1,650 1,348 0,516 1,693 0,304 0,333 0,963 1,568 0,955 1,482 0,836 0,419 0,218 0,351 1,20 1,557 0,615 1,66 1,176 0,624 0,61 0,568 1,185 1,262 1,308 1,918 1,777 1,103 1,944 0,111 1,191 0,966 1,429 0,180 0,324 0,521 0,766 0,761 0,215 1,843 1,924 0,697 1,307 0,246 1,718 0,380 1,12 1,703 0,483 0,243 1,132 0,954 0,296 1,485 0,36 1,275 1,594 0,825 0,92 0,640 0,242 0,351 0,426 1,683 0,960 1,190 0,485 0,832 0,820 0,845 0,556 0,282 1,138 1,704 1,811 0,526 0,943 1,648 1,355 0,759 0,613 0,220 0,711 1,77 1,448 1,974 1,516 0,495 0,500 1,440 0,964 0,435 1,809 0,461 0,404 1,330 0,798 0,878 1,528 0,291 1,747 0,434 0,541 0,990 1,876 1,904 0,338 1,409 0,86 0,453 1,579 0,759 0,864 0,740 1,197 0,981 0,870 0,114 0,669 1,289 0,700 0,527 0,784 1,372 1,150 0,16 0,60 1,63 1,638 0,819 0,925 1,396 0,442 1,824 1,381 0,115 0,666 0,911 0,336 1,844 0,648 1,370 1,387 0,320 0,131 1,263 0,489 1,720 1,124 1,187 0,270 0,649 0,57 1,191 0,637 1,83 0,580 1,87 1,494 1,688 1,67 0,613 0,94 1,137 0,642 1,729 1,844 0,847 0,71 0,912 0,119 1,154 1,442 1,700 1,958 0,837 1,261 1,862 1,558 0,807 1,782 0,344 1,930 0,445 0,363 1,191 1,891 1,735 1,662 1,957 1,545 0,135 1,58 1,376 0,733 0,648 0,782 0,683 0,221 1,41 0,733 0,104 0,780 1,439 0,867 1,147 1,42 0,91 1,483 1,927 1,974 0,854 1,393 0,535 0,725 0,732 0,580 1,375 1,212 1,759 1,762 0,791 0,220 1,872 0,102 1,878 1,799 0,269 0,985 0,768 0,772 0,947 0,918 0,543 0,162 1,921 1,573 0,18 1,582 1,140 1,520 0,405 0,422 1,803 0,876 0,451 0,987 1,285 1,17 0,469 1,919 1,496 1,342 0,787 0,676 0,431 0,92 0,460 1,92 0,929 1,917 1,456 1,680 0,245 0,601 1,785 0,447 1,40 1,532 0,463 1,548 0,302 0,759 0,813 1,506 0,132 0,972 1,752 0,168 0,659 1,332 1,569 1,459 1,328 0,551 0,182 1,155 0,826 0,84 1,879 1,875 0,754 1,884 0,885 1,455 1,403 1,37 0,982 1,538 1,587 0,390 0,687 1,717 1,893 0,309 1,821 1,669 0,738 1,979 0,607 0,393 0,8 0,727 0,309 1,515 0,862 1,899 0,43 0,838 0,272 0,72 0,793 1,477 0,923 1,934 0,109 1,922 0,280 0,817 1,495 1,983 1,251 0,516 1,756 1,94 1,261 1,72 0,347 1,641 1,796 0,96 1,154 1,97 1,103 0,525 1,483 0,931 0,88 1,641 1,758 1,403 0,498 1,703 0,847 0,272 0,251 0,3 1,185 1,878 1,608 0,390 0,768 0,650 0,324 1,739 1,435 1,95 1,309 0,259 0,461 0,772 0,421 1,5 0,129 1,817 0,750 0,198 1,494 0,127 1,792 1,827 0,830 1,822 0,809 1,441 0,397 1,682 0,385 0,743 1,729 0,224 1,311 1,268 0,547 1,359 1,273 1,814 1,942 0,246 0,10 0,886 1,626 0,660 1,572 1,429 0,585 1,723 0,55 0,301 1,626 0,512 0,51 1,380 1,216 1,707 0,600 0,483 0,404 1,523 0,714 0,200 0,695 1,476 1,183 0,838 1,677 0,927 1,423 0,672 0,256 0,99 1,241 1,775 1,528 1,69 0,916 1,262 0,625 0,478 0,72 0,4 1,233 0,738 0,575 0,162 0,103 1,483 1,629 1,677 0,658 1,638 0,340 0,190 0,732 1,297 1,200 0,450 1,815 0,545 0,599 1,804 0,600 1,292 0,310 1,219 0,887 0,522 1,153 0,839 0,703 0,66 0,912 0,90 1,536 1,56 1,145 1,497 1,696 0,238 0,458 1,83 0,694 1,422 1,755 1,935 1,585 0,961 1,135 0,291 1,403 0,433 0,228 1,300 1,230 0,279 0,476 1,254 1,468 0,622 1,328 1,732 1,227 0,242 1,756 0,242 0,935 1,652 0,115 1,719 1,436 0,27 1,742 0,546 1,636 0,249 0,598 1,689 0,85 0,215 1,67 1,971 1,36 0,304 1,220 1,631 0,306 0,207 0,728 0,893 0,662 0,599 0,354 1,486 0,729 0,729 0,785 1,990 1,533 0,877 0,549 1,88 0,210 1,995 0,218 0,664 1,702 1,632 1,912 1,850 1,236 1,985 1,42 0,658 0,944 0,909 1,662 0,123 0,507 1,312 0,596 1,687 1,996 1,268 1,31 1,776 0,484 0,760 0,499 0,130 0,909 0,517 1,146 0,524 1,420 0,234 0,332 1,599 0,318 0,102 0,586 1,550 0,395 0,179 0,866 1,304 1,344 0,299 1,996 0,698 1,28 0,596 1,578 0,939 1,875 0,327 1,160 0,567 0,537 0,120 1,324 0,307 0,39 0,640 0,898 1,445 1,685 1,812 1,496 1,455 0,766 0,631 1,606 1,367 0,165 1,343 0,431 0,461 1,791 1,724 0,468 1,357 0,702 1,176 0,262 1,645 0,967 0,311 0,509 1,200 1,456 1,891 1,810 1,348 0,625 0,43 0,595 1,705 0,651 0,844 1,313 1,309 0,145 0,247 0,122 1,918 1,869 1,57 0,277 0,119 1,63 0,41 0,259 1,311 1,910 1,336 0,474 1,918 0,677 0,129 0,202 0,746 1,361 0,149 0,915 0,398 1,469 1,490 0,844 0,948 1,482 1,226 0,454 1,742 0,810 0,135 0,675 0,268 0,375 1,384 0,494 0,153 0,628 0,703 0,767 0,803 1,343 1,302 0,948 1,561 0,837 0,673 0,316 0,78 1,293 1,247 1,543 0,160 0,725 0,649 0,301 1,424 0,693 0,299 1,445 1,26 1,227 0,68 0,249 1,766 1,435 1,65 0,310 0,198 0,323 0,8 1,794 0,324 1,246 1,904 0,370 1,738 1,719 1,263 1,870 1,911 1,929 0,685 1,353 1,248 1,698 0,290 1,907 0,384 0,340 0,927 0,89 0,909 0,461 0,713 0,492 0,462 1,651 1,957 0,520 0,146 1,422 0,720 0,885 1,532 1,451 0,507 0,738 1,176 0,896 0,798 1,622 1,94 0,996 1,615 0,269 0,174 0,219 0,793 0,564 1,890 0,365 0,115 1,611 0,75 0,340 0,574 0,768 1,795 0,792 1,321 0,892 1,166 0,761 0,724 1,392 1,361 0,690 1,172 0,848 0,360 0,775 0,11 1,913 0,577 0,136 0,42 1,929 0,18 1,613 1,669 1,174 0,889 1,899 1,199 1,187 1,942 1,867 0,964 1,383 0,221 0,929 0,667 0,785 1,761 1,750 0,223 0,588 1,412 0,390 0,289 0,332 0,894 1,979 1,516 1,314 0,455 0,589 1,670 0,536 0,5 0,18 0,124 1,145 0,940 0,155 0,179 1,93 1,363 1,285 0,689 1,153 1,132 0,488 0,276 0,52 1,167 0,898 0,856 1,587 0,376 0,981 1,712 1,218 1,45 1,628 1,850 1,785 0,502 0,675 0,801 0,103 1,654 0,33 0,239 1,422 1,538 0,469 1,116 0,407 0,68 1,738 0,363 1,445 1,665 1,464 1,685 0,908 1,13 0,516 1,570 1,708 1,590 0,949 0,296 0,67 1,670 0,782 1,431 0,105 0,36 0,447 1,143 1,638 1,60 1,295 1,101 0,309 1,384 0,330 0,827 1,13 0,746 1,503 0,515 0,746 1,708 1,208 0,148 0,253 0,936 1,14 0,610 0,444 1,666 1,886 0,380 1,681 0,668 1,405 1,701 1,223 0,76 1,56 1,58 1,54 1,105 0,376 1,122 1,804 1,525 0,24 0,810 0,383 0,554 0,554 0,172 0,873 0,263 1,834 0,905 1,270 0,919 1,319 1,318 0,17 0,120 0,203 1,284 0,812 1,991 0,606 0,615 1,166 0,973 1,942 1,237 0,746 0,774 0,61 1,920 0,893 1,256 1,434 0,625 1,493 0,442 1,907 1,50 0,469 0,469 0,569 0,21 0,798 0,154 0,489 1,601 1,153 1,60 0,331 1,165 0,481 1,944 1,218 1,755 0,875 0,908 1,356 1,881 0,52 0,838 0,435 1,435 1,13 0,678 1,675 1,378 0,782 0,437 1,365 0,626 0,35 0,846 1,853 0,921 0,498 1,979 0,657 0,861 0,519 0,105 0,951 0,224 1,515 0,329 0,444 0,421 0,787 1,831 1,730 1,529 1,629 0,697 1,647 0,383 1,625 0,907 0,735 0,468 0,427 0,752 1,92 0,209 1,344 1,770 1,331 0,76 1,962 0,178 0,204 1,649 0,842 1,659 0,416 0,552 1,892 0,459 0,928 0,592 1,984 0,989 0,349 1,840 1,670 0,389 0,141 1,303 1,804 1,935 0,528 1,709 0,768 1,134 0,286 0,917 0,490 1,66 1,983 1,393 0,73 1,739 1,554 0,187 0,844 1,527 0,446 0,344 0,95 0,476 0,68 1,811 1,347 0,553 1,646 0,234 1,88 1,645 1,461 0,743 1,316 1,825 1,92 0,316 1,279 0,618 1,796 1,170 1,675 1,226 0,589 0,102 1,615 0,128 0,498 0,857 0,828 1,573 1,0 0,282 0,264 0,711 0,628 1,299 0,814 1,982 1,43 1,698 0,780 1,176 0,791 1,988 1,279 0,265 0,857 0,843 0,599 1,865 0,256 0,336 0,426 1,533 1,108 0,700 1,374 0,615 0,985 1,952 0,209 0,885 0,36 1,43 1,802 1,94 0,470 0,95 0,804 0,376 0,485 0,848 1,919 1,846 1,696 1,906 1,862 1,508 0,161 0,651 1,934 1,519 1,653 1,64 0,574 1,695 0,219 0,299 1,895 1,763 0,111 0,187 1,965 0,696 1,259 0,2 1,574 1,564 0,115 0,467 0,810 0,704 0,501 0,266 0,160 1,684 1,81 0,939 1,824 0,493 0,556 1,486 1,724 0,644 0,672 0,136 1,727 0,35 1,864 1,641 1,191 1,457 1,549 0,321 1,121 1,726 1,818 1,536 1,640 1,959 0,672 1,6 1,572 0,895 0,818 0,742 1,139 0,165 1,944 1,69 0,5 0,998 1,221 1,341 1,877 0,616 0,498 0,718 0,119 1,407 0,401 0,421 1,99 0,611 1,753 1,904 1,809 0,103 0,409 0,484 1,833 1,202 0,981 0,107 0,951 0,56 0,160 0,834 1,449 0,660 0,285 1,718 0,128 0,952 1,115 0,683 0,749 1,502 1,45 1,139 1,553 1,273 1,98 0,564 0,371 0,257 1,543 0,433 0,727 0,186 1,897 0,843 0,722 1,823 0,955 1,816 1,16 1,983 1,11 1,479 1,211 1,398 1,744 0,584 1,468 0,971 0,310 0,311 0,776 1,716 1,613 1,833 0,218 0,618 1,530 1,1 1,824 1,53 1,712 1,467 0,461 0,677 0,264 0,990 1,588 1,277 0,196 0,593 1,484 0,417 1,307 0,461 1,755 1,177 0,656 0,937 1,20 0,219 1,240 0,585 0,747 0,145 1,264 0,671 0,832 1,952 1,306 1,252 0,878 0,38 0,314 1,502 1,192 1,827 1,735 0,889 0,76 0,312 1,692 0,712 1,152 0,893 1,117 1,992 1,373 0,616 0,740 1,196 1,416 0,305 0,576 0,535 0,463 0,134 0,923 1,486 0,514 0,36 1,635 0,280 1,95 1,551 1,880 0,102 0,74 0,625 1,149 0,947 0,955 1,739 1,521 0,720 1,227 1,426 1,593 1,184 0,747 0,295 1,382 0,463 0,198 0,424 1,62 0,127 0,603 0,148 1,980 1,738 1,833 0,199 0,709 1,857 1,545 1,876 1,170 0,766 0,35 1,268 0,673 0,531 1,49 0,627 0,352 0,714 0,701 0,779 0,796 0,994 0,410 1,225 1,450 1,964 0,484 0,400 1,928 1,970 0,286 0,331 1,741 0,760 0,329 0,557 1,59 0,305 1,277 1,910 0,859 0,498 1,774 1,420 0,215 0,610 1,355 0,295 1,260 1,191 1,251 0,224 0,716 0,865 1,222 1,141 0,970 0,862 0,552 1,870 1,287 1,548 1,67 1,170 0,921 0,343 1,695 1,215 0,587 1,60 1,521 0,69 1,782 1,864 1,594 0,820 0,18 1,185 0,143 1,452 1,790 1,576 1,593 1,922 0,12 0,255 1,502 1,434 0,234 0,94 1,251 0,484 0,857 0,730 1,883 0,735 0,436 0,120 1,709 0,60 1,119 0,535 0,891 0,278 1,945 1,542 0,981 1,285 0,344 1,674 0,704 0,791 1,682 1,564 0,67 0,437 1,310 0,810 1,371 0,367 0,638 0,601 1,623 1,624 0,898 0,914 1,730 0,167 1,530 1,454 1,857 1,919 1,801 0,454 1,314 1,769 1,112 0,705 0,783 1,473 1,977 1,616 1,880 1,446 0,830 0,107 1,553 0,613 1,517 1,490 0,580 0,337 0,29 1,41 1,661 0,909 0,881 1,189 0,980 1,885 1,48 0,600 0,440 1,702 1,593 0,550 1,632 1,657 1,679 1,712 1,37 0,988 0,695 0,574 1,566 0,792 1,267 1,109 0,718 1,43 1,922 1,668 1,679 1,321 1,190 1,279 1,632 0,962 0,872 0,862 0,534 1,77 1,2 0,626 1,185 1,528 1,567 0,248 1,924 1,785 1,424 1,706 1,309 0,809 1,411 0,124 0,72 1,364 1,645 0,9 0,655 0,557 0,509 1,13 1,664 1,705 0,133 1,161 1,33 1,258 0,644 1,820 0,470 1,672 0,867 0,455 0,194 1,102 0,333 1,325 1,825 0,512 0,61 1,899 1,979 0,832 1,237 1,680 1,538 1,982 1,718 0,293 1,670 0,935 1,382 0,237 0,130 1,32 0,631 1,483 0,521 0,233 1,526 0,211 1,435 0,314 1,399 0,212 0,86 0,362 0,491 1,982 0,996 0,927 1,641 1,590 0,619 0,758 1,318 1,790 0,218 0,863 1,335 0,456 0,442 1,882 0,836 0,767 0,355 1,271 0,881 0,763 1,940 1,81 0,484 0,638 0,568 1,262 1,402 1,653 0,399 1,595 1,47 1,191 0,752 0,74 0,80 1,862 0,98 1,396 0,418 1,45 0,697 1,28 0,67 1,877 1,923 1,233 0,21 1,866 0,450 1,799 1,744 0,159 0,783 1,766 0,637 0,975 1,324 0,371 0,257 0,154 1,577 1,566 0,474 0,723 1,791 0,423 0,614 1,134 1,173 0,222 1,879 1,215 1,34 0,196 0,959 1,608 0,614 0,34 1,238 1,694 0,494 0,98 0,989 1,433 1,269 0,392 0,428 1,972 0,403 1,858 1,566 0,207 0,32 0,57 1,855 0,72 0,47 1,745 0,483 1,982 1,651 0,827 1,847 1,286 0,687 1,61 1,849 1,467 1,890 1,337 0,742 0,914 1,582 0,583 1,770 0,853 0,67 1,752 1,549 1,487 1,302 1,787 0,485 1,614 1,643 1,941 1,911 1,689 1,430 1,813 1,292 1,380 1,306 1,669 0,215 0,514 1,578 0,783 1,467 1,438 1,47 0,322 1,42 0,33 0,770 0,16 1,908 0,613 0,932 1,116 1,582 0,966 1,805 0,359 0,48 1,466 0,644 0,725 0,257 1,308 1,914 0,960 1,406 0,295 0,925 0,683 0,337 0,243 1,347 0,947 1,9 0,547 1,244 0,812 0,920 0,988 0,276 1,226 0,40 0,218 1,81 0,363 1,453 0,641 0,282 1,874 1,852 1,272 1,938 0,360 1,516 0,789 0,82 0,39 1,932 1,428 1,489 0,303 0,391 1,46 1,591 1,671 0,438 0,587 1,653 1,499 0,923 0,308 1,482 0,578 1,338 0,778 1,670 1,539 0,316 1,772 0,238 1,938 0,279 0,548 1,535 0,947 1,904 0,101 1,685 1,855 1,363 0,947 1,850 1,732 0,239 0,450 1,792 1,413 0,780 0,960 0,246 0,298 0,860 0,993 1,210 0,263 1,642 0,569 0,614 0,73 1,23 1,839 0,791 0,995 0,85 1,369 1,452 1,545 0,324 0,281 1,930 1,99 0,136 0,597 0,700 1,334 1,305 0,977 1,196 0,852 0,729 1,776 0,484 1,68 0,891 0,412 1,278 0,829 1,89 1,607 0,932 0,981 0,935 0,841 0,71 1,392 0,777 0,605 0,76 1,319 0,175 0,415 1,919 1,253 0,843 1,553 1,725 1,255 1,296 1,182 1,398 1,348 0,603 0,462 0,497 0,119 1,89 0,602 1,620 0,179 0,684 0,109 0,586 1,217 1,887 1,494 0,512 0,160 0,442 1,697 0,185 1,209 1,173 1,55 0,882 1,928 1,913 1,385 1,948 0,863 1,452 0,630 0,876 0,225 0,207 1,623 1,902 1,693 1,865 1,952 1,683 1,297 0,983 0,365 0,496 1,992 1,537 1,937 1,980 0,682 1,997 1,922 1,587 1,653 1,339 1,285 1,408 1,602 0,104 0,594 1,195 1,733 1,474 0,518 0,310 1,399 0,305 1,531 0,602 1,458 0,149 0,695 1,719 1,5 1,419 1,221 0,106 1,341 0,283 1,984 0,171 1,351 1,374 1,944 1,347 0,274 1,818 1,973 1,969 1,227 0,76 0,267 1,586 1,102 0,733 1,635 0,167 1,324 0,948 1,59 0,258 1,580 0,800 1,127 1,475 0,745 0,762 1,459 1,991 1,503 0,588 0,285 1,687 1,228 0,602 1,666 0,965 1,5 0,562 1,276 0,884 0,870 1,373 0,442 1,127 1,173 0,418 1,905 1,32 0,716 0,902 0,960 1,135 0,827 1,737 0,886 1,965 0,130 0,558 0,81 1,658 1,860 0,750 0,537 1,676 1,990 1,54 1,251 1,551 0,584 1,337 0,746 1,55 1,301 1,792 0,639 1,740 1,190 1,62 0,748 0,495 1,211 0,904 0,798 1,295 0,116 1,492 0,868 1,646 1,597 1,798 1,566 1,204 0,931 1,665 1,856 1,720 1,975 1,638 1,724 0,56 1,952 1,494 0,33 1,824 1,707 0,770 0,933 1,450 0,466 1,255 1,281 1,176 1,666 1,238 0,855 1,432 1,289 0,770 0,789 0,792 1,162 0,115 1,332 1,67 0,217 0,222 0,18 1,975 1,688 0,242 0,546 0,714 0,638 1,960 1,627 1,72 1,635 0,599 0,601 0,139 1,434 0,476 1,682 1,973 0,959 0,435 0,726 0,303 1,513 1,423 1,653 0,762 0,542 0,864 1,183 0,681 1,392 0,260 0,336 1,854 0,395 1,798 1,584 0,664 0,57 0,717 0,93 0,924 0,528 1,938 1,389 0,902 1,723 1,943 1,188 0,75 1,235 1,295 1,492 1,2 0,795 0,487 1,871 1,77 0,330 0,737 0,458 1,451 0,956 0,251 0,849 1,67 0,596 1,739 0,263 0,883 0,874 0,227 0,295 0,976 1,601 1,718 1,246 0,389 1,172 1,686 1,196 0,18 1,320 0,624 1,44 0,849 0,152 0,964 1,589 1,753 0,936 1,507 0,719 0,259 1,629 1,942 0,856 1,126 0,902 1,250 1,238 0,189 1,114 1,704 1,53 0,926 0,571 0,259 1,753 0,317 1,793 1,977 0,436 0,706 1,474 1,572 1,228 0,319 0,501 0,384 1,681 0,825 0,742 0,422 1,941 0,257 0,105 1,109 0,974 0,297 0,143 0,269 1,265 1,458 1,609 1,137 0,348 0,656 1,495 1,96 1,45 1,628 0,228 0,586 1,301 0,681 0,747 1,962 1,206 0,870 1,152 0,773 0,519 1,600 1,294 0,297 0,307 1,808 1,823 0,591 1,965 1,729 1,839 0,557 1,124 0,32 1,197 1,783 0,548 0,135 0,916 0,496 0,913 0,527 0,172 1,791 1,962 0,903 0,526 1,474 1,59 1,632 0,702 1,450 1,87 0,661 0,216 1,530 0,382 0,444 0,351 1,499 0,88 0,329 1,503 0,548 1,186 1,122 1,660 0,862 0,796 1,82 0,507 1,923 1,818 1,582 0,346 0,634 1,614 1,507 0,888 1,693 1,512 0,158 1,961 0,73 0,956 1,825 0,963 1,413 1,422 1,249 0,984 0,733 1,547 0,78 0,839 0,865 0,7 1,894 1,70 0,103 1,991 1,183 0,986 0,890 1,734 0,935 0,10 0,329 1,153 1,584 0,252 0,135 1,905 1,570 1,885 0,112 1,605 1,779 0,758 0,910 1,449 1,771 0,804 0,484 1,875 1,109 1,432 1,312 1,5 1,182 0,769 1,206 1,297 1,208 1,2 0,226 0,641 0,526 0,145 1,249 0,178 0,685 0,932 0,259 1,569 1,704 0,972 0,406 1,815 1,519 0,181 1,201 1,229 1,490 1,604 0,459 1,33 1,78 1,383 0,792 1,916 1,829 1,992 1,604 1,894 0,665 0,198 1,933 0,769 0,44 0,694 0,441 1,595 1,799 1,8 0,525 0,246 0,353 0,975 1,637 0,520 0,14 0,670 1,760 1,201 0,977 1,767 0,837 1,480 0,770 1,809 0,287 1,717 0,316 0,463 0,716 0,147 0,765 1,794 1,878 1,763 0,708 1,204 0,711 1,298 0,918 1,42 1,786 1,257 1,249 0,750 1,748 1,9 1,285 0,427 1,78 1,369 0,603 1,776 1,487 1,383 1,312 0,900 0,436 1,666 1,196 1,225 0,974 1,409 0,811 0,675 0,43 1,481 0,325 1,572 0,137 1,968 1,344 0,468 0,499 1,602 1,326 1,154 1,731 1,57 1,586 1,700 0,640 1,400 0,542 1,125 0,14 0,893 0,643 1,143 0,16 0,280 0,733 0,580 0,745 1,505 1,277 1,444 0,458 0,449 1,140 0,491 0,624 0,949 0,968 0,449 0,907 0,252 1,146 0,115 1,246 0,527 1,672 1,644 0,881 1,131 0,375 1,419 1,767 0,518 1,815 1,242 1,689 0,365 0,21 0,681 1,423 1,923 0,152 1,200 1,860 0,661 1,387 0,191 1,346 1,751 1,128 0,967 1,398 1,830 1,305 0,390 1,299 0,207 0,796 1,551 1,118 1,85 1,687 1,694 0,735 0,378 0,493 1,524 0,999 1,615 0,695 0,545 0,644 0,928 1,988 1,38 0,386 0,179 1,427 1,298 1,67 0,468 1,716 1,719 1,467 1,869 0,360 0,760 1,228 0,607 1,567 0,995 0,677 0,902 1,841 0,685 0,469 1,992 1,520 0,643 1,414 0,172 0,943 1,775 1,175 1,124 0,685 1,75 1,172 1,688 1,881 1,926 0,298 0,995 1,730 0,391 0,429 1,392 0,194 1,312 0,701 1,335 1,224 1,491 1,278 1,760 0,355 1,976 0,102 0,312 1,685 1,445 1,708 0,297 0,42 0,829 1,887 0,741 0,569 0,145 0,801 1,477 0,25 0,198 0,342 1,400 1,451 0,236 1,112 0,887 0,56 1,234 1,245 0,588 0,680 1,923 1,936 1,64 0,233 0,561 0,443 0,735 1,374 0,587 1,408 0,22 0,140 1,684 1,24 1,38 1,233 1,612 0,317 0,103 0,762 1,908 1,826 1,670 1,599 1,821 1,731 0,323 0,252 1,532 1,783 0,200 1,439 0,103 1,656 1,670 0,969 1,985 1,529 0,562 1,236 1,781 0,278 1,672 0,807 1,8 0,534 0,416 0,647 0,983 1,879 0,328 0,761 0,242 0,299 0,756 1,701 1,653 1,315 0,38 1,975 1,437 0,140 0,145 1,826 0,912 1,594 0,899 0,750 0,382 0,493 0,784 1,960 1,697 1,933 0,681 0,656 1,908 0,313 1,449 0,804 1,290 0,329 1,40 0,773 1,188 0,361 1,987 0,18 1,237 0,688 0,561 0,421 1,673 1,229 0,616 1,963 0,385 0,307 1,450 0,471 1,610 0,634 1,640 1,880 1,861 0,603 0,153 0,176 0,440 0,868 0,590 0,657 0,654 0,180 0,271 1,143 0,762 1,813 0,706 0,482 0,216 1,814 0,216 1,927 0,894 1,623 1,621 1,48 1,397 1,985 0,276 1,548 0,533 0,604 0,675 1,265 1,682 0,844 0,963 1,349 0,394 1,773 1,881 0,1 0,247 0,865 0,433 0,797 0,537 1,192 0,701 1,748 0,308 0,811 1,492 1,764 1,297 0,591 0,280 1,431 0,229 1,545 0,835 0,712 1,573 0,164 0,420 1,422 0,735 0,9 1,223 0,701 1,652 0,645 0,935 1,855 0,274 1,527 0,926 0,992 1,960 1,647 0,424 1,244 0,779 0,860 1,481 1,99 0,993 0,498 1,241 0,180 0,880 0,917 1,828 0,943 1,225 0,482 0,506 1,294 1,734 1,205 0,816 1,624 0,237 1,345 1,428 1,406 1,681 1,92 1,855 0,542 0,83 1,612 0,895 0,472 0,176 1,75 1,687 0,632 1,603 1,673 1,89 1,16 1,657 0,661 0,415 0,848 0,752 0,30 0,27 0,718 1,228 1,977 1,341 0,711 0,738 1,636 0,136 0,440 0,509 0,331 0,377 0,784 1,352 1,788 0,377 1,762 1,14 0,366 1,432 1,559 0,46 1,244 1,18 1,288 0,901 1,457 0,931 0,924 0,679 1,939 1,194 1,878 0,55 1,861 0,902 1,902 0,415 0,9 1,617 0,840 1,892 0,92 0,74 1,768 1,881 1,344 1,770 0,502 1,172 0,436 1,70 0,420 0,818 0,373 0,341 1,639 1,285 0,420 0,479 0,907 1,361 1,651 1,971 0,416 1,706 0,97 1,876 1,27 1,42 1,124 1,569 1,124 1,579 1,708 0,801 0,325 1,234 1,984 1,168 1,512 0,12 1,801 1,816 1,365 1,463 1,853 1,53 1,156 1,395 0,866 1,733 1,878 0,312 0,317 0,960 1,846 1,767 1,84 1,526 0,287 0,268 0,133 0,204 1,756 1,987 0,703 0,133 0,360 0,131 1,512 1,946 1,14 1,673 1,207 1,514 1,379 0,154 1,545 1,942 1,672 1,758 0,222 1,403 1,741 0,676 1,216 1,1 1,402 0,240 1,927 1,877 1,112 0,974 1,759 1,535 1,599 1,688 0,442 1,148 0,216 1,787 0,367 0,353 1,783 0,102 1,519 0,466 1,515 0,536 1,251 1,571 1,654 0,409 0,684 1,855 0,278 1,883 1,296 1,184 1,591 0,624 0,279 0,859 1,795 1,201 0,343 0,85 1,228 0,767 1,724 0,831 1,222 0,237 1,938 0,193 0,871 1,387 0,552 1,319 1,777 1,402 1,313 1,36 1,679 0,323 1,738 1,416 0,294 0,296 1,46 1,341 0,93 0,179 0,283 1,665 1,472 0,489 1,447 1,907 0,563 1,651 1,288 0,350 0,224 1,672 1,385 0,38 0,954 0,119 0,913 1,817 0,197 1,909 0,948 1,760 0,440 0,23 1,195 1,466 1,37 0,728 0,255 1,70 0,386 1,485 0,46 1,153 0,291 1,366 1,208 0,468 1,885 0,984 0,496 0,409 1,176 1,984 1,445 0,204 0,185 0,463 0,3 0,44 0,575 0,842 1,824 1,495 1,897 1,202 1,73 0,869 1,595 1,30 0,374 1,964 1,785 0,831 0,653 0,456 1,947 0,245 0,831 1,338 1,45 1,184 1,303 0,478 1,262 0,422 1,137 1,801 0,998 1,646 0,599 0,787 1,584 0,479 0,285 0,706 0,960 1,305 0,166 1,380 1,736 0,687 1,61 1,236 0,214 0,46 0,993 0,495 1,229 0,285 0,401 0,911 0,94 0,151 0,810 1,359 1,425 0,394 1,311 0,241 0,192 0,469 1,245 0,474 0,958 1,55 1,995 0,210 1,78 1,944 1,707 0,591 1,465 0,822 0,962 1,398 0,81 0,224 1,59 0,274 1,485 1,472 1,569 1,175 1,86 1,598 0,955 1,87 0,317 0,347 0,308 0,977 1,165 0,697 1,422 1,198 0,341 1,148 0,587 1,405 0,591 1,822 0,380 0,342 0,827 0,920 1,94 1,432 0,321 1,420 1,40 0,415 0,592 0,327 0,942 1,178 0,399 0,928 0,846 0,229 0,814 0,519 0,599 1,34 0,763 0,418 0,449 1,516 1,187 1,680 1,186 0,711 0,323 1,867 1,775 0,734 0,97 1,812 1,214 0,461 0,596 1,676 0,102 1,905 1,536 0,677 1,543 0,331 1,44 0,253 0,934 1,300 1,567 0,610 1,694 0,299 0,886 0,599 1,103 0,541 1,649 1,51 0,492 0,562 0,579 1,428 1,123 0,101 1,383 0,222 1,392 1,165 0,652 0,128 1,664 1,391 1,911 1,568 1,882 0,394 1,851 0,590 1,591 1,877 0,66 1,490 1,985 0,529 1,222 1,222 0,986 0,790 0,994 0,145 1,111 0,927 0,139 1,631 1,326 1,510 1,339 1,545 1,646 1,172 0,123 0,176 0,731 0,964 1,148 1,499 0,484 0,577 0,202 0,94 1,800 0,81 0,427 0,452 1,124 0,455 0,859 1,937 1,6 0,272 1,891 1,413 0,337 0,799 1,49 0,333 1,529 1,916 1,469 0,41 0,36 0,205 1,932 1,48 1,809 1,591 0,499 0,532 0,540 0,499 0,418 0,670 0,518 1,481 0,421 0,487 1,885 1,125 0,919 1,476 1,895 0,989 0,566 1,342 0,644 0,552 1,754 1,987 1,808 1,853 0,350 1,218 0,262 0,201 0,190 1,189 1,843 0,307 0,793 0,169 0,45 1,866 1,473 1,138 0,296 0,503 1,138 0,66 0,791 1,582 0,938 0,90 1,57 1,590 1,863 1,515 0,513 0,536 1,612 1,828 1,893 1,421 1,979 1,964 0,166 0,320 0,754 1,686 0,627 1,271 0,864 0,197 0,142 0,385 0,714 1,188 0,351 0,892 0,803 0,397 1,555 0,544 1,713 1,294 0,325 0,604 1,675 0,431 1,311 0,935 0,948 1,196 1,539 0,213 1,162 0,209 1,770 0,28 0,878 0,128 0,874 1,519 1,498 1,229 0,923 0,813 0,163 1,79 1,192 0,183 0,58 0,503 1,925 0,930 1,235 1,326 0,337 0,754 0,139 0,276 0,859 1,658 1,367 0,997 0,629 1,522 0,784 0,455 0,765 0,902 0,13 1,29 1,192 1,851 0,86 1,804 0,513 1,118 0,10 1,50 1,657 1,803 1,954 1,124 1,277 1,280 0,862 1,37 0,33 1,136 0,756 1,781 1,143 1,617 0,619 1,919 0,130 1,806 1,206 0,84 0,960 1,644 1,215 1,632 1,602 1,404 1,380 0,245 0,774 1,807 1,217 1,359 0,122 1,883 1,715 0,612 1,184 1,237 1,866 1,431 1,490 0,159 1,151 1,71 1,234 1,676 1,94 1,140 1,529 1,852 1,948 0,304 0,350 1,399 1,481 1,306 0,172 1,302 1,641 1,95 1,863 1,80 0,866 1,567 1,941 0,241 1,838 0,757 0,742 1,600 0,324 0,564 0,92 1,769 0,45 0,178 0,371 0,671 0,64 1,734 0,335 1,735 1,437 0,227 0,465 1,564 0,91 0,380 0,708 0,934 0,584 1,29 0,329 1,822 0,264 0,839 1,19 1,613 1,516 0,162 1,766 0,425 0,119 0,647 1,729 1,928 1,943 1,421 1,270 1,333 1,467 0,870 0,837 0,237 1,568 1,566 0,608 1,701 1,588 0,793 1,140 0,189 0,803 0,439 0,922 1,123 1,578 1,783 1,324 1,399 1,972 0,300 1,90 1,945 0,168 1,46 0,281 1,213 0,762 0,747 1,733 1,343 1,422 1,608 1,997 0,50 0,686 0,367 0,515 0,180 0,275 0,99 0,598 1,219 0,531 1,957 1,749 0,178 0,1 1,225 0,367 1,132 0,125 1,288 0,642 0,945 0,476 1,92 1,270 1,9 0,69 1,948 1,98 1,294 0,536 1,756 1,341 0,775 1,336 1,543 1,996 1,594 1,264 1,782 0,153 1,694 1,60 0,53 0,973 0,144 1,67 1,795 1,782 0,150 1,172 0,430 1,80 1,544 1,143 1,755 0,758 0,998 1,79 0,789 1,740 1,857 1,953 1,615 0,563 1,904 1,45 1,469 1,554 1,957 0,823 0,618 0,667 0,921 0,457 0,456 0,640 0,148 1,398 0,347 0,165 1,502 1,848 1,61 1,102 0,433 0,355 1,442 1,475 0,4 1,834 0,470 1,565 0,503 0,673 1,198 1,629 1,921 1,389 1,922 0,288 0,851 1,817 1,422 0,670 0,392 0,837 1,303 0,642 0,668 0,641 1,820 0,504 1,825 0,989 1,917 0,479 1,620 1,531 1,932 0,469 0,503 1,715 1,167 0,156 0,522 1,24 0,235 0,598 1,26 1,844 1,720 1,33 1,350 1,84 0,46 1,486 1,129 1,629 1,414 0,294 0,500 0,191 1,8 0,644 1,806 1,336 0,47 0,704 1,105 0,917 0,480 1,222 1,746 1,751 0,514 0,881 0,616 1,674 1,373 0,627 0,827 0,704 0,447 0,444 0,615 1,356 1,620 0,447 0,290 1,531 0,400 0,18 0,924 1,816 0,3 1,723 1,531 1,685 1,645 0,274 0,324 0,604 1,231 1,826 1,573 0,855 1,722 0,25 0,210 1,340 1,977 1,299 1,735 1,749 0,976 1,632 1,501 0,671 0,347 0,925 1,395 0,77 0,472 0,291 0,638 0,181 0,443 0,783 1,683 1,37 0,419 1,639 1,301 0,912 0,550 0,684 0,442 1,277 1,404 1,73 0,998 1,550 0,116 1,181 1,363 0,153 0,830 1,261 0,838 0,235 0,302 1,608 1,402 1,916 1,633 1,215 1,755 1,718 1,341 0,526 0,318 1,550 0,829 1,841 1,70 1,902 1,438 0,117 0,567 0,269 0,67 1,895 1,263 0,364 1,663 1,985 0,451 1,321 1,879 0,664 0,370 1,105 1,344 0,173 0,912 0,602 0,707 0,344 1,445 1,757 1,723 1,512 1,629 1,818 1,281 1,967 1,407 1,842 1,961 1,501 0,341 1,989 0,905 0,415 1,397 0,190 1,738 1,473 1,114 0,555 1,733 0,839 1,354 1,847 0,661 1,724 1,82 1,122 0,155 0,433 0,214 1,114 0,115 1,960 1,42 1,92 0,377 1,284 1,443 0,474 0,814 0,668 1,362 0,225 0,271 0,774 1,665 0,262 1,917 0,269 0,413 1,655 0,51 0,536 0,332 1,379 1,377 0,295 1,24 1,313 0,845 0,8 0,338 1,966 0,38 0,430 0,8 0,400 0,426 1,767 1,529 1,975 0,704 1,724 0,290 0,511 0,415 1,414 0,193 1,451 1,171 1,250 0,825 1,356 1,301 1,342 1,509 0,59 0,36 1,984 0,753 1,554 1,395 0,174 1,591 0,995 0,802 1,591 1,11 0,782 0,889 0,950 0,626 0,422 1,47 1,388 1,708 1,134 1,156 0,771 0,736 1,31 0,296 0,361 1,703 0,664 1,565 1,250 0,582 0,539 1,838 1,594 0,911 0,85 0,887 0,142 0,778 0,653 0,22 0,938 1,610 1,15 0,947 1,157 1,766 0,930 0,504 1,30 0,988 0,753 0,426 0,271 1,350 0,227 0,22 1,206 1,890 0,622 1,338 0,977 1,533 0,743 0,63 0,354 0,222 1,976 1,979 0,861 1,288 0,386 0,310 0,472 0,168 1,207 0,477 1,843 0,719 1,842 0,262 1,73 1,38 1,644 1,477 1,793 0,99 0,177 1,997 0,75 0,2 0,753 1,717 1,749 0,646 0,412 0,951 0,521 0,529 1,630 0,946 1,125 1,278 1,567 1,535 1,26 0,978 0,352 0,629 0,804 1,435 0,314 0,922 1,518 0,935 0,202 0,307 0,435 1,450 0,791 1,229 0,613 1,379 0,241 1,104 1,364 0,848 1,772 0,457 1,872 0,716 1,385 0,616 0,725 0,83 0,349 1,535 0,517 0,598 1,136 1,916 1,900 1,547 1,880 0,660 0,293 0,625 0,917 1,442 0,122 1,576 1,246 0,189 1,880 1,668 0,242 0,519 0,510 1,972 1,173 0,122 0,128 0,115 0,63 1,608 0,408 0,332 1,893 0,542 0,856 1,536 1,878 0,5 0,962 0,700 1,986 0,947 0,77 0,414 1,221 1,522 1,659 0,463 1,517 0,995 0,151 0,438 0,530 0,243 1,835 0,747 0,774 1,894 1,255 0,794 0,701 0,155 1,390 1,624 1,652 1,425 1,994 0,347 0,411 1,361 0,176 1,355 0,754 0,399 0,490 0,425 1,545 1,889 1,908 0,953 1,606 1,100 1,677 0,491 1,571 0,882 1,371 1,226 0,972 1,336 0,996 1,84 0,826 1,299 1,507 0,269 0,636 1,900 0,465 0,438 0,272 1,478 1,112 0,643 1,79 0,760 1,550 0,712 0,884 1,293 1,430 1,717 0,659 0,907 1,335 0,401 1,791 0,420 1,996 0,596 1,966 0,934 0,723 1,22 0,970 0,255 1,495 1,768 0,683 0,633 1,955 0,876 0,178 0,795 1,648 0,296 0,635 1,637 0,350 1,230 0,905 1,957 1,306 1,416 1,396 1,30 0,132 0,71 1,61 1,77 0,65 0,196 0,476 0,167 1,309 1,168 0,188 1,300 1,284 1,498 0,61 1,619 1,518 0,419 1,392 0,906 1,312 1,95 1,791 1,532 1,257 0,401 1,8 1,839 1,971 0,124 0,216 1,526 0,200 0,140 1,913 0,189 0,40 1,546 1,174 1,281 1,830 0,476 0,250 1,53 0,962 0,163 0,503 0,973 0,111 1,894 0,843 0,563 1,266 1,859 1,56 0,898 0,743 0,92 1,688 0,651 1,869 0,980 1,402 0,408 1,932 1,38 0,647 1,262 0,369 1,255 0,361 0,311 1,384 1,168 0,683 1,554 0,785 1,306 1,957 0,308 0,882 1,452 1,899 1,396 1,225 1,779 1,662 1,64 0,105 0,170 0,160 0,958 1,851 0,563 1,912 0,545 0,714 0,387 1,999 1,753 1,199 0,22 0,767 1,384 0,325 0,936 0,887 0,307 0,80 0,217 1,112 0,417 1,724 0,674 1,581 0,840 1,816 1,964 1,853 0,534 0,447 1,155 0,598 1,74 0,10 1,769 1,919 1,84 1,305 0,974 0,25 1,976 0,253 0,285 1,803 0,405 1,163 0,842 0,530 0,492 0,254 0,351 0,884 1,379 0,225 1,148 1,689 1,51 0,24 0,557 1,458 1,694 0,550 1,988 0,254 0,43 1,560 1,120 0,780 0,618 1,951 1,817 1,326 1,653 0,559 1,522 1,39 0,869 1,229 1,529 0,805 0,642 0,413 1,445 0,103 0,396 0,807 0,958 0,831 1,835 1,791 0,925 1,712 1,755 0,61 1,292 0,231 0,609 1,358 1,458 0,172 1,215 1,330 1,972 0,53 0,151 0,484 0,320 0,669 0,111 0,30 0,980 0,90 1,361 1,662 1,513 0,156 1,493 0,831 0,908 1,827 1,969 0,200 0,326 0,822 0,486 0,21 0,399 1,101 1,454 0,518 1,642 1,124 0,452 1,60 1,585 0,621 1,953 0,275 0,498 1,463 1,226 0,969 1,842 1,538 1,535 0,778 1,331 0,829 0,401 1,386 0,583 0,676 1,417 1,471 1,390 1,616 0,166 1,130 1,70 1,787 1,877 0,855 1,33 0,685 0,411 0,495 0,274 0,574 1,615 0,105 0,756 1,148 0,135 0,476 1,781 0,146 0,951 0,485 0,997 1,865 1,906 1,462 1,96 1,910 1,664 0,95 0,145 0,947 0,308 0,108 1,998 1,274 1,877 0,45 1,243 0,666 0,950 0,482 0,311 0,945 1,513 0,746 0,562 0,524 0,509 1,647 1,120 1,223 1,906 1,331 1,88 0,277 0,319 0,473 1,203 0,105 0,566 0,317 0,874 1,95 1,981 0,686 1,31 1,79 1,54 1,971 0,63 1,386 0,351 1,409 0,402 0,675 0,108 1,241 1,342 1,34 0,830 0,421 1,427 1,947 1,577 1,873 1,618 0,107 1,593 1,279 1,896 0,74 0,994 1,910 0,70 1,338 0,92 1,675 1,451 0,738 0,293 0,666 1,662 1,503 0,460 1,228 0,838 1,124 0,165 1,111 0,365 1,274 1,812 1,670 1,590 0,937 1,377 1,440 1,920 1,446 0,1 0,489 1,580 0,483 1,287 1,689 1,37 1,91 0,351 1,78 1,764 1,857 1,627 1,753 0,464 1,536 1,343 0,301 0,220 0,722 1,158 0,93 1,802 1,355 1,361 1,40 1,4 0,33 0,769 0,643 0,35 0,943 1,616 0,705 0,993 1,572 1,807 0,260 1,234 0,570 1,396 1,467 0,963 1,606 0,221 1,288 1,975 1,634 0,884 1,884 0,375 0,863 1,302 0,391 1,326 1,227 1,951 0,839 1,285 0,995 1,322 0,323 1,424 0,279 0,138 0,649 1,467 1,610 1,398 1,406 1,762 0,941 1,323 1,422 0,631 0,663 1,99 0,929 0,303 0,979 0,404 1,451 0,385 1,793 1,147 1,252 0,913 1,562 1,528 0,860 1,341 0,396 0,473 0,669 1,250 1,927 1,457 0,680 1,973 1,962 0,985 1,698 1,319 0,117 1,367 1,489 0,581 1,602 1,297 0,722 0,705 0,755 0,656 0,822 1,729 0,279 0,148 1,219 0,757 1,807 0,251 0,528 0,227 0,48 1,340 1,24 0,835 1,3 1,834 0,531 0,827 0,672 0,887 0,745 1,573 1,604 0,710 1,209 0,972 1,12 0,426 0,90 1,158 0,402 1,955 1,311 0,958 1,546 1,326 1,439 1,211 1,903 0,194 0,77 0,440 1,729 0,322 1,304 0,418 1,487 0,512 0,322 0,951 0,307 1,18 0,25 0,986 1,332 0,81 1,989 1,854 0,22 1,561 1,656 0,488 0,985 0,14 1,752 1,258 1,456 0,249 1,404 1,207 0,58 0,130 0,546 0,339 0,813 0,167 1,132 0,665 0,555 1,201 0,988 0,770 1,339 0,910 1,882 1,68 0,218 1,947 0,888 0,832 1,167 1,310 1,833 1,266 0,955 1,128 1,752 0,654 0,37 0,438 0,22 1,690 1,632 1,632 0,158 0,657 0,18 1,29 0,224 0,473 1,318 0,84 0,329 0,268 1,706 1,467 1,228 1,117 0,245 1,974 1,667 0,314 0,389 0,912 1,816 1,18 1,784 0,331 0,828 1,103 0,935 0,778 0,723 0,629 0,798 1,144 0,876 1,485 1,765 0,46 1,79 0,283 1,411 1,884 0,58 1,908 0,560 0,7 0,681 0,171 1,593 0,665 0,577 0,327 0,241 0,916 1,175 0,1 1,137 1,574 1,554 0,716 0,7 0,597 1,863 1,946 0,688 1,908 0,194 1,496 1,176 0,111 1,895 0,261 0,788 0,972 0,633 0,295 0,690 0,601 0,759 1,787 1,6 1,708 1,279 1,604 0,899 1,162 1,652 1,747 1,723 1,32 1,945 0,390 1,685 0,716 1,970 1,990 0,557 0,109 1,690 1,671 0,575 0,931 0,175 0,731 1,574 1,553 1,301 0,628 1,697 0,195 0,145 1,472 1,818 0,508 0,464 1,900 1,96 0,781 1,96 1,452 0,266 0,265 1,183 1,916 1,513 1,64 1,94 0,872 1,230 0,431 1,888 1,205 0,112 1,425 1,212 0,685 1,319 1,672 1,999 1,734 0,831 0,69 1,906 1,649 0,132 1,615 0,0 0,434 1,659 0,138 0,87 1,769 1,87 0,955 1,79 1,649 1,748 1,379 0,638 1,32 1,984 1,976 1,860 0,780 1,635 1,693 1,339 1,707 1,626 0,649 0,528 1,927 0,941 1,765 1,352 1,508 1,576 1,62 0,761 0,588 1,677 0,880 0,944 1,194 1,227 1,672 1,847 1,64 1,727 1,91 1,860 1,727 1,11 0,372 0,150 1,159 0,468 1,766 0,46 0,688 0,582 0,747 1,968 0,397 0,975 1,630 0,937 0,829 1,677 1,590 1,646 0,987 0,650 0,473 0,708 0,207 1,534 1,793 1,529 0,613 0,329 1,251 0,277 0,886 1,695 1,68 0,393 0,945 0,533 0,964 1,510 1,591 0,517 1,223 1,783 1,813 1,296 1,616 0,434 0,860 1,118 1,118 1,836 1,791 0,942 0,1 1,700 1,566 1,182 1,42 0,220 0,971 1,494 1,153 0,48 1,120 0,534 1,420 1,905 1,766 0,216 1,913 0,515 0,132 1,809 0,487 1,998 1,707 0,989 0,304 1,758 1,295 1,830 0,235 1,824 1,400 0,613 1,830 1,198 0,619 0,44 0,419 1,390 1,178 1,578 1,808 0,574 0,225 1,165 0,702 0,758 0,101 1,191 0,374 1,426 0,560 1,158 0,597 0,623 1,354 1,447 0,934 1,134 0,630 1,249 1,419 1,398 0,978 1,653 0,457 0,499 1,924 0,62 1,302 1,268 0,39 0,567 0,606 1,612 1,410 0,240 1,509 1,498 0,99 0,861 1,600 0,754 0,796 1,612 0,400 1,109 1,821 1,839 0,623 0,268 1,720 0,144 1,39 0,288 1,734 1,815 0,796 1,316 1,148 1,580 1,51 1,785 1,819 1,204 1,613 1,345 1,816 1,329 0,97 1,902 1,233 1,878 0,605 0,625 0,612 1,253 1,127 0,52 0,668 1,112 1,802 1,672 1,977 1,591 1,495 0,888 0,51 1,762 1,246 1,825 0,805 0,704 1,895 0,899 0,658 0,956 0,806 1,169 0,608 1,611 0,866 1,799 0,657 1,596 1,279 0,455 1,572 0,822 0,622 0,529 1,78 1,952 1,30 0,766 1,219 0,898 0,42 1,405 1,616 1,98 0,717 1,754 0,515 0,848 1,585 1,689 1,401 0,873 0,964 1,841 0,946 1,825 0,928 1,300 0,278 1,234 1,475 0,290 0,520 1,174 0,39 0,77 0,21 0,144 0,981 1,633 0,93 0,43 0,708 0,444 1,479 0,428 0,153 0,418 1,707 1,209 1,128 0,693 1,410 0,265 1,96 0,597 1,878 1,799 1,289 1,62 0,997 0,27 1,286 0,114 1,163 1,528 0,529 0,827 1,792 0,593 1,310 0,192 0,190 0,971 0,447 1,13 0,372 1,834 0,658 1,869 1,637 1,393 1,676 0,590 1,436 1,428 1,25 1,55 0,419 1,884 0,306 1,402 1,212 0,286 1,38 1,790 1,683 1,205 1,898 1,626 0,850 1,787 0,565 1,625 0,102 1,584 1,38 1,757 0,781 1,610 1,155 0,99 0,511 0,328 1,860 0,343 0,761 0,666 0,794 0,516 0,469 1,39 1,297 0,668 0,180 0,216 0,522 0,707 1,631 1,766 1,834 1,780 1,453 1,715 0,565 0,88 0,499 1,982 0,513 0,988 1,401 0,268 0,92 1,717 1,250 0,555 1,692 1,531 0,591 0,888 0,995 1,218 1,830 0,343 1,367 0,631 1,163 1,118 1,660 1,890 1,982 0,121 0,87 1,4 0,202 1,742 0,869 1,691 0,163 0,895 0,390 0,627 1,317 0,393 0,680 1,108 0,507 0,91 1,126 1,986 1,843 1,828 1,54 0,918 0,267 1,752 1,844 0,758 0,34 1,161 1,277 0,640 0,58 0,95 0,985 1,778 0,180 0,620 1,429 0,554 0,720 0,532 1,744 0,205 0,964 1,610 0,158 0,847 0,126 1,893 0,316 1,480 1,59 0,883 0,952 1,345 1,667 0,310 0,953 0,458 0,161 0,350 0,275 1,326 0,2 0,738 1,738 1,195 1,372 1,345 1,547 1,675 0,684 1,949 0,693 0,460 0,772 0,952 1,934 1,696 0,863 0,669 1,531 1,883 1,659 0,88 0,73 0,876 0,551 0,991 1,316 1,622 1,801 1,401 1,454 1,757 0,599 0,397 1,648 0,449 0,138 1,318 0,289 1,98 0,882 0,47 0,481 0,605 1,621 1,937 0,626 1,187 0,70 1,241 1,865 1,830 1,132 1,95 0,96 1,702 0,693 0,514 1,892 1,458 0,208 0,376 1,578 1,904 0,967 0,429 1,423 0,258 1,19 0,240 0,692 0,505 1,332 0,370 1,189 1,325 1,199 1,430 1,898 1,671 0,607 1,325 0,665 0,943 1,999 1,523 0,646 1,706 1,160 1,173 1,412 1,81 0,490 0,94 1,565 1,106 1,288 1,921 1,334 0,406 1,544 1,536 1,422 0,317 1,654 1,826 0,454 1,321 1,220 0,287 1,92 1,239 0,888 0,463 0,469 0,315 0,247 1,943 1,142 1,386 0,598 0,291 1,759 0,605 0,834 0,692 1,267 0,669 0,801 0,512 1,935 1,208 0,730 0,165 0,954 0,378 1,741 0,512 1,194 0,630 1,492 0,188 0,291 0,736 0,50 0,823 0,7 0,895 0,278 0,657 1,330 0,458 0,554 1,302 1,599 0,742 0,741 0,857 0,901 1,751 1,871 0,153 0,288 0,365 1,457 1,862 0,965 1,640 1,741 0,45 0,609 0,808 0,689 1,822 0,91 0,111 1,271 0,666 0,162 1,55 1,974 0,358 0,138 0,895 1,28 0,82 1,738 1,129 1,498 1,97 1,270 0,477 0,959 0,533 1,571 1,571 1,608 1,746 1,319 1,827 1,211 1,181 1,485 0,505 0,949 0,255 1,143 0,911 1,995 1,351 1,735 0,653 1,352 1,647 0,281 1,20 1,884 1,579 0,238 1,99 1,670 1,186 0,669 1,509 0,63 1,879 0,311 1,101 1,219 1,9 1,753 1,128 0,123 0,787 0,463 0,719 1,207 0,838 0,143 1,230 1,67 1,751 0,525 0,614 0,182 0,506 1,971 0,482 0,480 1,841 0,572 0,369 0,816 1,871 0,792 0,910 0,843 0,405 1,13 1,968 0,782 1,574 1,653 1,437 0,590 1,673 1,744 1,445 0,596 0,578 1,93 1,89 0,479 1,372 1,58 1,393 0,121 0,587 1,197 1,706 1,871 0,796 0,119 0,153 0,587 1,39 0,704 0,230 0,588 0,755 1,893 0,912 1,428 1,614 1,244 0,146 1,104 0,460 1,742 0,178 0,853 1,820 0,655 0,841 1,11 0,24 1,631 1,196 1,497 0,22 0,712 0,633 1,57 1,506 1,46 0,538 0,872 1,500 0,173 1,379 1,355 1,639 0,131 1,258 1,288 0,681 1,379 1,720 0,575 0,742 0,876 0,17 0,500 0,691 1,634 1,464 1,775 1,803 0,162 1,284 1,824 1,418 1,983 1,628 0,802 0,315 0,293 0,336 0,167 1,939 1,892 0,391 0,628 1,402 1,756 0,15 0,563 1,690 0,733 0,762 0,150 0,823 1,151 1,525 1,27 0,514 1,262 0,512 0,823 1,575 0,907 0,959 0,394 1,290 1,731 0,165 1,245 0,115 1,801 1,824 1,323 1,687 1,835 0,400 1,92 1,653 1,478 0,506 1,254 0,418 1,423 1,930 0,355 1,823 0,652 0,52 1,743 0,265 1,261 0,322 1,404 1,595 0,243 0,557 1,853 0,369 1,265 0,886 1,672 0,213 1,487 0,734 1,929 1,560 1,689 0,271 1,56 1,210 0,742 0,791 1,673 1,184 1,607 1,470 1,459 1,106 1,84 0,392 1,994 1,14 1,532 1,299 1,0 0,325 0,458 1,319 1,9 1,606 0,362 0,849 1,149 1,211 1,287 0,350 1,933 1,19 1,736 0,309 1,712 1,52 0,584 0,226 0,546 0,543 1,419 1,74 1,946 1,519 0,510 1,582 0,987 1,37 0,466 1,364 0,677 1,619 0,108 0,957 0,306 1,492 0,63 1,606 1,616 0,17 0,585 1,301 1,773 0,976 1,977 1,375 0,235 0,340 1,292 0,852 0,35 1,495 0,230 0,462 0,97 1,109 1,124 0,384 0,327 0,720 1,182 1,939 1,577 0,657 0,359 0,341 0,888 0,120 1,34 0,31 1,455 0,632 1,6 0,160 1,974 1,563 0,283 1,138 0,366 1,347 0,615 0,334 0,81 0,314 1,96 1,870 0,735 0,996 0,865 0,213 0,989 0,308 0,233 0,808 0,138 1,587 1,465 0,767 0,838 0,670 1,626 1,48 1,61 0,504 1,678 1,776 0,4 0,33 1,19 0,710 0,871 1,572 1,342 1,257 1,279 0,271 1,815 1,543 0,730 0,893 1,418 1,771 1,394 1,508 1,136 0,689 0,520 0,353 1,965 0,651 0,466 0,572 1,770 1,202 1,205 0,974 1,442 0,897 1,801 1,167 0,426 0,174 0,772 0,724 0,896 1,128 0,153 1,7 0,510 1,609 1,969 0,801 1,78 0,507 0,409 0,346 1,713 0,920 0,86 0,333 0,670 1,379 0,977 1,307 0,829 1,254 1,830 0,188 0,481 0,473 1,634 0,501 0,652 1,622 0,390 0,308 0,921 1,504 0,8 1,721 0,628 0,594 0,473 0,532 1,803 1,528 0,942 1,817 1,913 1,899 1,821 0,677 1,467 0,379 0,805 1,832 0,812 0,592 0,24 1,886 0,293 1,447 0,200 1,327 1,263 1,222 1,388 0,539 0,348 1,112 1,881 1,547 0,379 1,142 0,203 0,782 1,247 1,81 1,170 1,948 0,790 0,650 0,114 1,469 0,312 0,514 1,294 1,778 0,164 0,487 0,628 1,710 1,256 1,767 0,220 1,44 1,677 1,837 0,351 1,106 1,781 0,405 0,563 0,223 0,161 0,763 0,745 0,69 0,942 0,267 0,882 1,193 1,230 0,317 0,909 0,109 0,299 1,833 1,651 1,415 1,829 1,588 0,36 0,738 0,565 1,864 0,755 1,411 0,779 1,399 1,752 0,34 0,275 1,245 1,400 1,941 0,463 1,547 0,986 0,53 0,355 0,999 0,586 0,553 0,357 0,390 0,868 1,831 1,958 1,843 1,698 0,119 0,42 0,183 1,246 0,922 1,614 1,874 0,483 1,187 1,899 0,264 1,86 1,238 1,369 0,547 0,182 1,605 0,886 1,840 0,845 1,212 0,966 0,822 0,836 1,460 1,841 1,646 1,17 1,608 0,682 0,352 1,607 0,70 0,5 1,895 0,179 0,327 0,467 1,735 0,169 0,943 0,583 1,199 1,595 1,702 0,128 1,203 1,558 1,940 1,734 1,409 1,980 0,375 0,117 0,616 0,687 1,254 1,345 1,164 1,465 0,863 0,694 1,966 0,378 0,451 0,698 0,260 1,561 0,630 1,342 1,746 0,789 1,345 1,605 1,422 0,347 1,708 0,461 0,830 0,921 1,891 1,727 0,918 1,580 1,698 1,528 0,251 0,524 1,433 0,111 0,697 1,905 1,541 0,533 1,46 0,254 1,289 1,18 0,204 0,660 0,276 0,654 1,102 0,170 0,605 0,216 1,765 1,983 0,831 0,464 1,290 0,850 1,751 0,927 0,891 0,503 0,859 1,237 1,115 1,795 1,684 0,633 1,267 1,974 1,77 1,665 0,547 0,494 0,301 1,572 1,238 1,268 0,963 1,7 0,181 0,430 0,924 0,824 1,850 0,300 1,663 1,821 1,590 0,999 0,11 0,161 0,369 1,929 0,928 1,255 0,67 0,774 1,293 1,668 1,534 1,998 0,927 0,981 1,97 0,663 0,947 1,252 1,471 1,520 1,697 1,558 1,549 1,661 1,437 0,591 0,353 0,605 0,105 0,963 0,59 0,672 1,828 0,616 1,998 1,111 1,709 1,985 0,414 0,232 1,194 0,648 0,192 1,832 0,758 1,906 0,449 0,385 0,250 1,488 0,244 1,471 1,283 1,609 0,176 1,708 0,687 1,140 0,422 0,835 1,817 0,328 0,73 1,555 0,115 1,568 0,970 0,736 0,819 0,799 1,793 1,940 1,448 0,930 0,516 1,904 1,482 1,108 1,983 0,477 1,729 0,770 0,944 0,515 1,438 0,988 0,483 1,721 0,755 1,778 0,951 0,190 0,362 1,558 0,995 1,854 1,2 0,348 1,300 1,393 0,715 0,548 1,832 1,760 0,710 0,881 1,53 0,97 1,372 0,55 1,574 1,248 1,550 0,496 0,833 1,109 1,637 0,265 0,896 0,598 0,669 0,937 0,241 0,857 1,486 0,527 0,459 1,403 1,489 0,675 1,128 1,752 0,309 1,257 1,12 0,424 1,594 1,51 0,413 0,270 1,982 0,32 0,370 1,872 1,486 0,697 0,611 1,304 1,134 1,90 0,154 1,746 1,893 1,457 1,821 0,795 1,66 1,261 0,426 1,708 1,260 0,320 0,9 0,878 0,281 1,62 1,925 1,407 0,20 0,604 1,445 0,537 0,134 0,335 1,789 0,432 1,276 0,798 1,832 1,104 0,237 1,569 1,765 0,356 0,866 0,525 0,247 0,163 1,466 0,402 1,650 1,747 1,137 1,56 0,149 0,774 1,779 1,499 1,89 1,98 0,625 0,38 1,171 1,442 0,285 0,605 1,14 1,76 0,344 0,728 1,141 1,65 1,244 0,49 0,879 1,694 0,948 1,624 0,335 0,149 1,466 0,239 1,435 1,235 1,85 0,36 0,415 0,895 1,428 1,990 1,714 0,764 1,614 0,874 1,120 0,31 1,765 1,38 0,592 1,852 1,325 0,137 0,411 0,930 0,427 0,639 1,376 0,862 1,230 0,850 0,230 0,810 1,80 1,2 0,632 0,979 1,978 0,984 0,294 0,758 1,55 1,480 0,14 0,145 0,198 1,96 0,196 0,215 1,243 0,266 1,740 0,771 0,154 0,972 0,373 1,82 1,381 0,608 0,806 0,605 0,379 1,634 1,340 1,784 0,151 1,247 0,611 0,893 0,938 1,262 0,468 0,454 0,601 1,89 0,992 1,158 1,37 0,564 1,183 1,255 0,119 1,337 1,882 0,304 1,818 0,183 0,703 0,849 1,383 1,697 1,234 1,298 1,221 1,845 0,807 1,608 0,856 0,173 0,409 1,482 0,521 1,247 0,101 0,785 0,471 0,211 1,75 0,88 1,267 0,971 0,722 1,819 0,162 0,836 1,480 0,424 1,327 1,240 1,501 1,701 0,326 1,254 1,194 1,762 1,825 0,891 0,842 0,138 0,928 0,574 1,701 0,80 1,320 1,15 0,572 0,759 0,751 1,59 1,865 0,410 0,890 1,756 0,472 1,456 0,623 0,780 1,561 0,111 0,60 1,159 0,217 0,586 1,456 0,438 1,113 0,706 0,509 0,741 1,460 1,754 0,547 1,894 0,143 0,522 0,550 0,620 1,798 0,778 0,791 0,138 1,881 1,57 1,671 0,709 1,813 0,736 1,426 0,13 1,731 1,931 0,131 1,761 0,186 0,807 0,375 1,709 1,411 0,757 1,956 1,992 1,537 1,879 1,826 0,215 0,531 1,889 1,106 0,868 0,330 1,981 0,107 1,83 0,772 0,689 1,932 1,707 1,196 1,171 1,725 1,618 1,980 1,54 1,92 0,202 1,114 1,700 1,475 1,141 1,351 1,687 1,562 0,94 0,152 1,42 0,374 1,946 0,575 0,983 1,186 1,866 1,557 0,587 0,667 1,551 1,54 0,646 1,313 1,466 1,688 1,471 0,219 0,779 0,903 0,797 1,666 0,207 0,714 0,987 0,951 0,284 1,455 1,974 1,8 0,826 1,474 0,986 1,935 0,508 1,968 0,437 1,703 1,834 0,958 0,722 1,254 0,935 0,489 0,463 0,741 1,923 1,483 1,590 1,455 1,389 0,198 1,275 0,967 0,318 1,116 1,767 1,701 0,172 1,659 1,824 0,283 1,654 0,113 0,351 1,503 1,272 1,886 0,764 0,834 1,299 1,855 0,298 0,364 1,612 1,645 0,939 1,667 1,287 1,909 1,477 0,733 1,728 1,51 0,878 0,893 1,498 1,800 0,9 0,531 1,271 0,964 0,772 0,757 1,670 1,397 0,43 0,459 0,873 0,556 1,503 1,272 1,540 1,11 1,971 0,708 1,448 0,633 0,793 1,521 0,774 0,207 0,453 1,945 1,82 0,647 0,801 1,955 1,369 0,109 0,811 0,787 0,111 1,531 1,108 0,353 0,505 1,158 1,222 1,43 0,925 0,564 0,483 1,125 0,517 0,865 1,806 1,404 0,268 1,586 0,14 1,257 1,236 0,395 0,198 0,894 1,289 1,32 0,205 0,193 0,27 0,863 1,359 1,629 1,879 1,847 1,122 1,138 0,463 0,994 0,846 1,908 1,581 0,38 1,307 1,627 1,610 1,412 1,207 1,929 1,121 1,959 0,408 1,336 1,307 1,907 0,683 1,298 0,869 1,121 1,66 0,769 1,193 0,735 0,299 1,954 1,809 1,726 1,150 1,981 1,61 0,13 1,479 1,681 1,682 0,335 0,291 0,681 0,137 1,966 0,868 0,847 0,432 0,52 1,461 1,395 0,158 1,561 1,54 1,204 1,448 0,390 1,135 1,612 0,44 1,466 0,84 1,24 0,798 1,70 1,391 0,72 1,870 0,728 0,477 1,273 1,833 0,702 1,81 1,433 0,553 0,793 1,421 0,193 1,950 1,186 1,267 1,691 1,451 1,422 1,231 0,545 0,439 1,798 1,338 0,120 1,455 1,196 1,741 0,834 0,298 0,422 0,191 1,127 0,205 0,838 0,780 0,193 0,738 1,854 1,626 1,151 0,584 1,341 1,260 1,438 1,545 0,382 1,308 1,933 0,912 0,291 0,42 0,564 0,516 1,161 1,40 1,944 1,641 1,8 0,468 0,508 0,764 0,77 1,215 0,164 1,432 0,259 0,789 0,906 1,858 1,222 0,709 0,252 0,275 1,29 1,888 0,484 0,342 1,473 1,362 1,367 0,492 1,854 0,507 1,841 0,405 1,201 1,404 1,282 0,358 1,809 0,13 1,230 1,811 1,502 1,699 1,316 1,762 1,274 1,824 1,145 1,657 0,601 0,340 0,483 0,624 1,345 0,740 1,858 1,3 1,892 0,881 1,437 0,562 1,545 0,354 1,44 1,802 1,797 1,392 1,565 1,154 0,440 1,424 1,429 0,631 1,76 1,376 1,456 1,2 1,298 0,598 0,86 1,854 1,911 0,714 1,419 0,907 0,570 0,196 1,755 1,533 0,196 1,164 0,932 0,599 1,252 1,559 1,265 0,54 0,178 1,17 0,603 1,248 0,231 1,819 0,468 0,728 1,134 1,331 1,871 1,801 1,577 0,933 0,979 0,10 0,785 1,199 0,532 0,141 0,946 0,714 1,7 0,233 1,692 0,714 1,596 0,700 0,613 1,594 0,364 0,760 0,303 0,757 1,885 0,225 1,225 0,755 0,450 0,564 1,250 0,595 1,430 1,576 0,411 1,789 0,119 0,46 1,518 1,740 0,760 1,607 0,883 0,613 0,995 0,881 0,336 1,859 1,402 1,443 1,907 0,342 1,768 1,932 0,760 1,623 0,756 0,895 0,350 1,582 0,268 1,629 1,581 0,658 1,768 0,760 0,911 0,585 1,971 0,68 0,223 1,56 0,660 0,522 1,729 1,985 0,828 0,208 1,126 0,712 0,399 0,283 0,98 0,460 1,424 0,132 1,600 1,456 1,194 1,389 0,286 1,26 1,788 0,450 0,372 1,853 1,67 0,927 1,386 1,567 0,441 0,837 1,261 0,542 0,570 1,264 1,500 0,470 1,559 0,583 1,989 1,241 1,2 0,783 0,274 1,129 1,421 0,802 0,280 1,32 1,241 1,243 0,186 0,175 0,35 0,284 1,190 0,893 1,532 1,132 1,355 1,653 0,240 0,824 1,163 0,348 0,744 0,970 1,737 0,607 1,619 0,508 0,515 0,774 0,437 0,597 0,967 1,185 1,326 0,0 0,131 1,812 0,4 0,414 0,669 1,589 0,322 0,299 0,717 1,251 1,946 0,179 0,393 1,485 1,57 0,660 1,657 0,650 1,759 0,997 1,537 1,761 1,850 0,258 1,468 0,704 0,186 1,772 0,92 1,12 0,81 1,565 1,558 1,274 1,881 1,153 0,103 1,202 1,389 0,450 1,228 0,101 1,310 1,995 0,454 1,646 1,237 0,613 0,819 1,42 1,491 0,789 1,482 0,666 1,238 1,638 1,566 0,549 0,849 0,272 1,12 0,422 1,702 1,588 1,463 1,290 0,171 1,524 1,666 0,866 0,51 0,77 0,471 1,773 0,936 0,515 0,133 0,39 0,512 0,567 0,57 0,800 1,284 0,442 1,260 1,827 1,628 1,574 1,225 1,226 1,85 0,883 0,649 1,117 0,638 0,961 0,457 0,230 1,236 1,608 1,273 1,184 0,195 0,631 1,531 0,138 1,58 1,385 1,870 0,628 0,114 0,803 1,949 1,509 0,347 1,581 0,445 0,651 1,516 0,206 1,29 0,499 1,183 0,147 1,676 0,554 0,427 0,479 1,778 0,240 1,14 1,389 1,70 1,306 1,488 1,37 0,815 1,490 1,236 1,521 0,639 1,312 0,254 1,207 0,250 1,378 1,792 1,699 0,74 1,757 1,459 1,975 0,453 0,298 0,826 1,955 1,800 1,980 1,538 0,472 1,122 1,850 1,846 0,324 1,999 0,30 0,623 0,140 0,66 1,51 1,185 0,25 1,521 1,943 0,93 1,768 1,905 1,2 0,31 0,296 0,801 0,847 0,215 0,638 1,449 0,239 0,674 1,962 1,286 1,795 0,35 1,850 1,377 1,313 1,458 1,338 0,556 0,150 1,261 1,782 1,466 1,602 1,425 0,812 1,917 0,270 1,706 1,24 1,267 0,803 1,413 1,396 0,257 1,59 1,329 1,537 0,573 0,142 1,6 1,884 0,97 1,659 0,21 0,449 0,751 1,909 0,259 1,662 0,876 0,131 0,993 1,778 1,613 0,809 1,24 1,597 0,673 1,368 1,132 1,169 0,246 1,375 0,573 1,129 0,598 1,824 1,90 1,304 1,287 0,665 0,35 0,452 0,696 0,206 0,734 0,329 0,395 0,742 0,806 0,836 1,867 1,241 0,295 0,759 1,833 0,925 0,729 0,617 0,921 0,393 1,436 1,432 0,684 0,845 1,871 1,873 1,491 1,340 0,256 0,836 1,822 1,500 1,968 0,4 0,400 1,589 0,221 0,609 1,417 1,636 0,790 1,181 1,941 0,485 1,871 1,293 0,572 0,995 0,992 1,903 0,495 0,497 1,212 1,603 1,384 0,421 1,725 1,428 1,154 0,838 1,584 1,448 1,29 1,138 0,379 1,888 0,381 0,511 1,338 0,869 1,804 0,185 1,62 1,193 0,355 0,599 0,908 0,106 0,924 1,879 0,627 1,607 0,876 1,784 1,855 1,23 1,592 1,686 0,736 0,750 0,655 1,142 1,302 0,354 1,258 0,4 0,551 0,369 1,963 1,566 1,796 0,636 0,474 1,671 1,683 0,870 0,735 1,290 1,885 0,194 1,940 0,854 1,330 0,741 0,187 0,878 0,337 1,983 1,627 1,838 0,8 0,537 0,44 0,30 0,79 1,637 1,547 0,515 1,371 0,692 0,819 1,117 0,315 1,921 0,303 1,937 1,355 1,363 0,719 1,162 0,49 0,45 1,133 0,135 0,107 0,691 1,677 1,115 0,386 0,899 1,151 0,97 1,221 0,631 1,32 1,540 0,504 1,338 0,184 0,373 1,887 0,325 0,248 1,739 0,891 1,972 1,333 1,305 1,950 0,201 0,539 1,162 0,222 0,537 0,383 0,959 0,789 0,419 1,29 0,454 0,426 1,819 1,218 0,146 0,986 1,314 1,159 0,478 0,569 0,820 1,919 1,327 1,363 0,617 0,276 0,317 0,994 0,576 0,924 0,364 0,496 0,974 0,903 1,592 1,394 0,671 0,988 0,975 0,912 1,950 1,676 1,141 1,859 1,689 1,689 1,22 1,532 0,302 1,941 0,589 1,250 0,438 0,966 1,316 1,419 0,697 0,966 0,38 0,161 0,966 0,942 0,59 1,2 1,332 0,61 1,704 0,803 0,158 1,474 0,791 1,53 1,998 0,342 0,901 1,534 1,522 1,27 0,962 0,569 1,393 0,653 1,420 0,122 0,858 1,917 0,300 1,298 1,684 0,976 0,528 0,108 0,399 1,439 1,902 1,847 1,306 1,682 0,297 1,830 0,158 1,782 1,713 0,699 1,536 1,558 0,674 0,883 1,458 0,188 1,861 1,788 1,7 1,472 0,283 1,431 1,48 0,91 0,398 1,737 1,748 1,328 1,419 0,620 0,652 1,304 0,506 0,351 0,632 1,346 0,889 1,723 1,586 1,939 0,260 1,826 0,870 0,815 1,848 0,812 0,248 1,914 1,691 0,621 1,642 1,357 1,315 0,275 1,524 0,795 0,407 0,513 0,750 1,526 0,882 0,630 1,273 0,323 0,958 1,841 0,306 1,587 1,519 1,335 1,542 0,546 1,671 0,559 1,485 1,41 1,489 0,959 1,157 0,828 1,145 1,513 1,62 0,753 1,287 1,888 0,722 1,829 1,415 0,521 1,948 1,496 0,264 0,31 1,620 0,672 0,50 1,822 1,810 0,86 0,864 1,334 0,259 0,121 1,913 1,951 0,890 0,161 1,420 0,10 1,433 0,186 0,714 0,369 1,850 1,556 1,880 0,323 1,406 0,356 0,458 0,896 1,155 1,867 1,921 0,320 1,165 0,895 1,67 1,546 1,442 1,574 1,678 0,597 0,709 0,978 1,817 1,41 0,618 1,557 1,527 1,93 0,913 1,462 0,783 0,740 1,957 1,576 0,628 1,549 1,867 1,449 1,155 0,280 1,796 0,218 0,347 1,42 0,759 1,120 1,644 0,22 0,787 0,198 0,173 0,586 0,667 1,309 0,928 0,386 1,983 1,561 1,113 0,839 1,262 0,51 1,4 1,523 0,115 1,126 0,284 1,205 0,107 1,908 0,306 0,160 0,815 1,624 0,503 1,710 0,107 0,717 1,361 0,566 0,199 0,45 0,845 0,334 0,318 0,31 0,20 0,807 0,333 0,580 1,423 0,985 1,968 1,434 0,625 1,586 0,303 0,131 0,951 1,242 1,346 1,357 1,797 0,408 1,303 0,741 0,426 1,69 0,928 1,777 1,47 0,452 0,761 0,600 0,777 1,757 0,976 1,49 0,398 0,992 0,619 0,375 1,600 0,767 0,13 0,108 0,363 1,869 1,853 0,909 1,248 0,60 0,348 0,416 1,376 0,223 1,592 0,393 0,134 1,896 1,222 0,573 1,984 1,246 0,488 0,528 0,845 0,624 0,939 0,446 1,826 0,152 0,25 0,937 0,707 1,252 0,75 0,975 0,183 0,5 1,91 0,433 1,735 1,231 0,552 0,700 0,152 0,746 1,78 1,505 0,230 1,647 1,353 0,709 0,60 0,565 1,179 1,884 1,842 1,445 1,568 0,906 0,897 1,932 0,728 0,810 0,447 1,955 0,867 1,903 1,101 1,997 1,345 1,682 1,614 1,409 0,492 1,40 0,174 1,559 1,3 1,320 0,867 0,396 0,7 1,777 1,797 0,568 1,232 1,832 1,933 1,571 0,39 1,988 1,366 0,800 0,154 1,887 0,795 0,412 0,258 0,74 0,346 1,566 1,90 1,682 0,861 1,958 0,919 0,930 1,897 1,376 1,363 1,474 1,453 0,615 1,783 0,371 0,905 0,615 0,220 0,67 0,662 1,603 0,360 1,141 1,555 1,710 1,423 0,13 0,241 1,539 0,413 0,8 0,199 1,3 0,253 1,408 0,329 1,394 0,364 1,962 1,522 1,614 0,356 1,639 1,919 1,170 1,474 1,399 0,279 0,72 0,211 0,392 1,63 1,948 0,979 1,485 1,450 0,779 1,348 0,239 0,315 1,378 0,932 0,873 1,604 1,37 0,800 0,916 0,588 1,802 0,264 0,85 0,181 0,711 1,144 0,957 1,7 0,356 1,918 0,400 0,460 1,83 0,149 1,297 0,993 0,804 1,222 0,504 0,706 0,254 1,295 1,239 1,267 0,318 1,408 1,631 1,162 0,697 0,45 1,235 0,908 0,448 1,700 0,903 1,368 0,344 0,315 0,267 0,8 0,762 0,335 1,851 1,513 1,977 0,683 0,226 0,711 0,598 1,119 1,43 0,665 0,624 0,713 1,342 1,880 0,702 0,434 1,832 1,226 0,724 0,458 0,238 1,315 0,76 0,890 1,794 1,120 1,118 0,243 0,847 1,674 0,144 0,814 1,138 0,366 0,994 0,398 1,450 0,876 1,326 1,224 1,662 1,199 0,435 0,722 0,485 1,462 1,876 0,622 0,968 0,814 1,409 1,689 1,58 0,220 1,80 0,544 0,700 0,558 1,758 0,908 0,886 1,656 1,243 0,889 0,763 1,104 1,388 0,83 1,803 1,815 1,57 0,814 1,669 0,552 1,175 0,731 0,907 0,450 0,731 0,263 1,83 1,95 0,46 0,655 0,311 1,330 0,785 1,136 1,965 1,263 0,145 1,753 1,868 0,365 1,655 1,49 0,386 1,2 0,764 0,41 1,357 1,668 0,132 1,88 1,604 0,744 0,193 0,827 1,685 0,830 1,919 0,853 0,793 1,511 1,858 0,170 1,208 1,330 0,624 0,744 0,379 1,163 0,40 0,135 1,620 1,328 1,960 0,669 1,588 1,893 1,572 1,0 0,516 0,894 0,312 1,710 1,562 0,12 1,358 1,872 0,763 0,463 1,192 1,509 1,573 1,9 1,104 0,166 0,442 0,352 1,933 0,358 0,932 0,280 1,446 1,705 1,430 1,399 1,72 0,539 1,131 1,131 1,131 1,568 0,567 1,443 1,402 1,969 1,87 0,990 0,832 1,869 1,642 1,279 1,379 0,723 0,165 0,771 1,586 0,422 0,103 1,858 1,131 1,261 0,897 1,953 0,140 0,141 1,286 0,28 1,265 0,480 0,686 1,103 1,151 1,720 1,304 0,682 1,887 1,975 0,197 0,391 1,919 1,742 1,818 1,411 1,356 0,129 1,584 1,319 0,90 1,741 1,712 0,872 0,430 0,952 0,942 1,798 0,457 0,180 0,783 0,129 0,806 0,7 0,693 0,215 0,598 1,865 1,421 0,452 1,369 0,78 1,504 1,76 1,757 0,515 0,157 1,765 0,990 1,586 1,954 1,370 1,323 0,375 1,928 1,808 1,860 1,134 0,490 0,699 0,500 1,79 1,320 1,18 0,488 1,574 1,703 1,162 1,629 1,539 1,489 0,40 1,378 0,424 1,916 0,574 0,621 0,392 0,601 0,453 0,906 1,758 1,801 1,877 0,560 1,691 0,279 1,362 0,116 1,365 0,350 0,763 1,656 1,203 1,755 0,51 1,937 1,438 1,17 0,526 1,885 0,461 1,758 1,434 0,288 1,142 1,218 0,742 0,437 0,70 1,488 0,522 1,406 0,297 1,30 1,931 1,498 1,282 0,20 1,85 1,888 1,495 0,286 1,891 0,835 0,471 0,86 0,100 0,715 0,427 0,596 1,427 0,907 0,268 1,694 0,589 1,714 1,258 1,974 0,485 1,27 0,557 1,739 1,152 1,891 0,486 1,107 0,785 1,325 0,593 0,20 1,620 0,858 1,236 0,215 1,345 1,950 0,592 0,722 0,943 0,469 1,875 1,259 0,465 0,419 1,291 1,231 0,831 1,728 0,642 0,524 0,228 1,307 1,233 0,345 0,217 1,473 1,171 0,951 1,371 1,887 1,608 0,976 0,790 1,707 1,791 1,388 0,922 0,897 0,61 0,390 1,586 1,7 1,8 0,224 0,499 1,183 0,508 0,130 0,299 0,415 1,890 1,240 1,284 0,858 0,720 0,568 1,268 0,650 0,949 1,908 0,820 0,872 1,122 0,867 0,944 0,660 0,919 0,943 0,60 0,477 1,754 0,954 0,998 0,998 1,563 1,208 1,937 0,710 0,926 1,546 0,307 0,633 1,548 0,151 1,409 1,756 1,180 1,940 1,557 0,724 1,800 1,882 0,226 0,967 0,195 0,894 0,169 1,225 1,125 1,529 1,212 0,23 1,760 1,256 0,416 0,532 0,681 0,221 1,414 1,904 0,250 0,652 0,661 1,887 1,258 1,529 0,986 0,484 1,220 1,417 0,577 1,401 0,301 0,875 0,627 0,389 1,673 1,733 0,414 1,817 1,909 0,130 1,194 0,439 1,389 1,861 1,672 1,776 1,287 0,604 1,902 1,775 0,719 0,998 0,207 1,368 1,664 0,960 0,75 1,476 1,428 0,446 0,989 1,900 0,673 1,107 1,377 1,527 1,450 0,204 1,749 0,562 1,784 1,207 0,90 0,568 1,454 1,280 1,755 1,368 1,342 1,535 1,26 1,293 1,205 0,204 0,137 1,336 0,880 0,857 0,744 0,568 0,177 1,646 0,872 0,2 1,121 0,408 1,937 1,707 0,895 0,662 1,614 0,69 1,143 1,425 1,887 0,459 0,696 1,731 1,812 0,545 1,538 1,597 0,68 1,179 0,607 1,830 0,593 0,885 1,660 0,755 1,856 0,182 1,940 1,639 1,498 0,586 1,650 1,153 0,734 1,464 0,424 0,642 1,274 0,323 1,417 0,501 0,69 1,735 0,452 1,462 1,689 1,771 1,284 1,235 1,843 0,502 0,893 0,154 1,456 1,693 0,469 1,53 1,911 1,98 0,111 1,329 0,461 1,727 0,415 0,454 0,558 0,71 1,610 0,742 1,942 0,787 0,49 0,242 1,286 1,63 1,134 1,53 0,740 1,997 0,35 0,527 1,65 1,837 1,643 0,293 0,233 1,851 0,51 0,585 0,87 0,935 0,840 0,384 0,196 1,345 0,529 0,952 0,243 0,556 1,941 1,532 1,717 0,957 1,188 1,487 0,201 0,773 0,299 0,869 0,207 1,511 1,914 1,638 0,784 1,312 0,173 1,826 0,478 1,433 0,981 0,511 0,167 1,178 0,217 1,301 1,363 0,422 1,585 1,762 1,935 0,456 0,956 0,999 0,821 1,160 1,835 1,188 0,781 1,742 1,532 0,573 1,421 0,602 0,923 1,415 1,802 1,476 1,59 0,604 1,512 0,443 0,855 0,732 0,860 0,790 1,486 1,766 1,654 0,38 1,704 0,640 0,357 1,947 0,883 1,249 1,369 0,125 0,98 1,923 0,975 1,125 1,408 1,877 0,30 1,68 1,664 1,339 0,147 0,862 1,814 0,123 1,285 0,774 0,49 0,177 1,830 0,512 1,795 0,854 1,605 1,206 0,410 1,596 0,312 1,250 1,576 0,795 0,733 1,984 1,848 0,809 0,328 1,245 1,378 1,121 0,598 0,528 1,980 0,984 0,636 0,208 0,398 0,600 0,147 1,643 0,326 0,687 1,56 0,417 0,119 1,598 0,576 1,663 0,45 1,870 0,843 0,93 0,513 1,949 0,132 1,349 0,508 1,841 1,967 1,299 0,148 0,218 0,49 1,662 1,253 1,810 1,561 1,704 0,562 1,76 0,615 0,502 0,327 0,989 0,963 1,962 0,101 1,479 0,251 1,899 1,359 0,897 0,164 0,54 0,286 1,212 1,279 0,393 0,688 0,704 1,281 0,77 1,142 0,21 1,632 0,513 1,679 0,977 0,367 0,977 1,832 0,268 1,336 0,889 0,314 0,141 1,792 1,757 0,550 1,411 1,373 0,357 0,416 0,690 1,237 0,957 0,367 0,611 0,787 1,265 0,548 0,611 0,563 1,403 0,37 0,683 0,397 0,41 0,765 1,346 1,25 1,21 1,328 0,70 1,901 0,6 0,79 0,663 1,957 0,682 0,489 1,657 0,46 0,949 0,916 1,894 1,383 1,525 1,292 1,314 1,68 1,941 1,663 1,289 1,781 0,114 0,272 0,576 0,784 1,654 1,879 1,585 0,959 1,315 0,625 1,885 0,940 0,519 1,8 1,741 1,19 1,318 0,111 1,728 0,650 0,652 1,753 1,324 1,216 0,506 1,990 0,870 1,947 0,368 1,267 0,962 1,424 0,805 0,896 0,550 1,349 1,761 0,585 0,819 1,163 0,24 0,459 1,562 0,680 0,368 0,483 1,736 0,532 1,403 1,312 0,345 1,810 1,843 1,64 1,810 1,878 0,297 1,816 0,334 0,411 1,879 0,295 0,82 0,505 0,189 1,408 0,314 0,136 0,880 1,51 0,991 1,524 1,509 0,361 1,438 1,397 0,586 1,537 0,239 0,675 1,745 0,88 1,797 0,317 1,737 0,278 0,475 0,625 1,590 1,758 1,591 0,325 0,836 1,48 1,623 1,440 0,744 0,276 0,894 0,214 0,907 1,410 1,237 0,252 1,490 1,885 0,713 1,75 1,722 0,595 0,495 0,810 0,976 0,262 0,302 1,284 1,44 1,11 0,240 0,355 1,378 1,934 1,771 1,808 0,519 0,943 0,520 0,563 1,954 1,468 0,395 0,400 1,89 1,104 1,124 1,844 1,888 0,613 0,507 1,560 1,148 1,878 1,144 0,490 1,330 1,719 0,932 0,673 0,196 0,622 0,508 0,348 0,580 0,685 0,620 0,640 1,961 0,859 1,912 0,852 0,519 1,215 0,131 0,862 0,879 0,162 0,441 0,107 0,968 1,567 1,831 0,858 1,487 1,35 1,883 1,419 1,993 1,462 0,895 1,882 0,480 0,348 1,315 0,54 1,587 1,571 1,191 0,486 1,439 1,806 0,298 0,847 0,102 1,209 1,808 1,830 1,339 0,576 0,649 1,680 0,638 0,741 1,68 0,492 1,581 0,687 0,931 1,27 0,966 0,80 1,489 0,562 0,201 1,821 1,633 1,474 1,292 1,365 0,690 1,205 0,619 1,201 0,137 0,853 1,159 1,318 1,875 1,497 1,527 0,277 1,694 0,152 1,18 0,742 0,881 1,59 0,1 1,101 1,500 0,662 1,609 0,114 0,730 1,283 1,95 1,171 1,872 0,788 1,666 0,642 1,930 0,180 1,890 1,763 0,352 0,542 0,716 1,80 0,805 1,401 0,980 1,898 1,111 0,612 1,521 0,406 0,666 0,664 1,378 0,559 1,391 1,277 0,853 0,880 1,997 0,21 1,508 1,612 1,16 1,947 0,106 1,498 0,346 1,368 1,924 1,627 0,927 1,846 0,145 1,30 0,522 1,196 1,442 1,702 1,177 1,868 1,200 1,631 0,644 0,672 0,373 0,648 0,21 1,120 1,216 1,950 1,338 1,955 0,968 1,315 0,291 0,644 0,783 1,776 1,329 0,435 0,505 1,534 1,873 1,448 0,106 1,662 0,203 1,410 1,84 1,435 0,283 1,128 0,664 1,395 0,174 1,533 0,560 1,23 0,4 1,220 0,859 0,897 1,120 1,10 0,954 0,55 0,996 1,701 1,735 1,253 1,795 0,381 1,476 0,687 1,796 0,426 1,922 1,627 0,334 0,730 0,62 0,272 0,27 0,943 1,545 0,437 0,653 1,298 1,879 1,844 1,634 1,240 1,95 1,53 1,897 0,798 1,884 0,940 1,415 1,356 0,700 0,659 1,997 1,287 0,995 1,919 1,104 0,356 1,768 0,214 0,63 1,837 1,330 1,663 1,782 1,490 0,747 0,658 0,589 0,303 0,209 1,215 1,490 0,878 1,399 0,694 0,407 1,96 1,869 0,653 1,966 0,411 1,312 0,666 0,154 1,165 0,478 1,220 1,705 1,290 0,770 1,618 0,634 0,688 1,313 1,122 1,105 1,251 0,123 1,514 0,864 1,861 0,97 1,230 0,860 1,173 0,740 1,222 1,203 1,11 0,153 1,549 0,804 0,952 1,571 0,468 1,793 0,229 0,265 1,170 0,885 0,927 1,810 1,473 0,968 0,196 1,290 0,541 0,329 1,570 0,857 1,286 1,666 1,323 1,546 0,477 1,341 0,948 0,359 0,641 0,261 0,262 0,329 1,823 0,813 1,451 0,220 1,930 0,928 1,103 1,568 1,913 0,6 1,856 1,158 1,358 1,508 0,378 1,983 1,504 1,634 0,611 0,30 0,870 0,555 0,305 0,569 0,789 0,230 1,664 1,920 0,447 0,915 0,714 1,654 0,693 0,637 1,854 1,748 0,488 0,559 1,855 1,32 1,296 1,404 0,201 1,534 0,606 1,745 0,243 1,61 0,333 1,212 0,279 0,545 0,271 0,557 1,419 1,686 1,141 0,381 0,564 1,175 0,416 0,867 0,59 1,138 0,678 1,661 1,400 0,740 0,286 0,473 0,842 0,870 0,960 1,19 1,326 1,752 1,997 0,643 0,14 1,849 1,29 1,322 0,670 0,437 1,956 0,143 0,804 1,124 1,792 0,604 1,99 1,505 0,778 0,651 0,849 1,3 1,370 0,684 0,614 1,848 1,454 1,913 1,62 1,456 0,897 1,812 1,819 1,639 0,479 1,65 1,852 1,722 0,777 0,981 1,998 0,987 1,646 1,246 0,385 1,611 1,767 1,212 0,956 1,802 0,486 1,6 1,136 1,953 0,698 0,995 1,217 1,163 1,762 0,430 1,896 1,658 1,880 1,39 0,685 0,134 0,922 1,696 1,843 0,865 0,314 1,781 1,501 0,18 1,249 1,337 1,81 0,703 1,618 0,42 1,223 1,65 1,146 1,364 0,664 0,420 0,55 1,790 1,581 0,491 1,186 1,706 1,387 1,596 1,346 1,271 1,505 1,766 0,480 1,243 0,700 1,898 1,430 0,319 0,198 1,101 1,776 1,320 0,195 0,525 0,78 1,423 1,807 0,957 0,197 1,405 0,669 1,846 0,895 1,168 0,140 1,330 1,71 0,173 1,815 1,358 1,198 1,813 0,689 1,675 1,903 0,946 1,110 0,502 0,768 1,883 0,505 0,47 0,332 1,228 1,660 0,598 1,412 1,993 1,731 1,62 1,205 0,148 0,436 0,452 1,372 1,803 0,484 1,146 1,580 1,154 1,857 1,927 0,869 1,599 0,830 0,845 1,799 1,122 0,142 1,234 1,213 1,937 0,201 0,896 0,102 0,890 1,263 1,545 0,690 0,69 1,461 0,460 0,462 1,802 1,102 0,408 1,489 0,104 1,105 0,146 1,394 0,492 1,931 0,502 1,175 0,748 1,736 0,522 0,370 0,683 1,399 0,817 0,102 0,416 0,647 1,851 0,331 1,520 1,340 1,623 1,696 0,691 0,979 0,848 0,348 1,394 1,631 0,365 1,912 0,656 0,463 1,861 1,681 0,973 1,42 0,334 1,704 1,265 0,712 1,560 1,194 1,362 0,213 0,703 0,846 0,160 1,750 1,140 1,727 0,292 1,419 0,151 1,377 1,412 0,505 1,486 1,289 0,80 1,340 1,249 0,455 1,980 1,5 0,400 1,343 0,777 0,163 1,406 1,233 0,397 1,74 0,568 1,915 1,258 0,155 1,747 1,181 0,332 1,781 0,683 0,619 1,277 0,3 0,2 1,386 0,522 1,403 0,159 1,913 1,908 0,772 0,531 0,378 1,415 0,32 0,925 1,556 1,641 1,142 0,370 0,940 1,915 1,744 1,913 0,49 0,158 0,490 0,898 0,832 0,208 1,459 0,861 0,471 0,679 1,460 1,606 0,319 1,196 1,597 1,263 1,407 0,424 1,196 0,684 0,36 1,680 0,579 0,534 0,506 1,447 0,866 0,479 1,911 1,575 0,135 0,988 1,341 1,321 0,765 1,933 1,229 1,383 0,216 0,809 0,979 0,537 0,183 0,467 0,501 0,117 0,149 1,61 1,419 0,882 0,15 0,692 0,277 1,737 1,799 1,973 1,664 0,906 1,7 0,994 0,67 0,829 0,680 0,673 1,513 0,28 0,805 1,811 1,256 0,958 1,239 1,932 1,771 1,733 1,307 0,46 1,970 0,798 1,743 0,599 1,318 1,292 0,771 0,856 0,73 0,792 1,873 0,644 0,851 0,932 0,287 1,48 0,464 0,148 1,457 0,235 1,605 0,71 0,453 1,596 0,412 0,542 0,865 1,728 0,751 1,436 1,868 0,851 0,453 1,46 1,732 0,84 0,478 1,385 0,315 1,581 0,796 0,982 0,22 0,531 1,55 0,908 1,544 0,174 0,61 0,968 0,531 1,801 0,291 1,404 1,105 0,81 0,116 1,782 1,181 0,329 1,337 1,318 1,858 0,684 0,623 1,785 0,199 1,67 1,668 0,896 1,747 1,528 1,922 0,962 0,994 0,69 0,241 0,30 1,619 1,937 1,59 1,396 0,531 0,902 0,789 0,938 1,444 1,718 1,480 0,187 1,403 0,478 1,997 0,704 0,106 0,858 1,439 0,422 1,81 0,128 1,189 1,646 0,382 1,21 1,281 1,490 1,72 0,945 0,317 0,331 0,56 1,4 0,235 1,301 1,162 1,925 1,128 0,881 0,478 1,679 1,60 1,292 0,44 1,287 1,402 1,260 1,48 0,883 0,944 0,574 0,794 0,614 1,993 0,418 1,661 0,432 0,619 0,230 0,454 1,838 0,779 0,701 0,949 1,24 1,722 0,571 0,578 0,369 1,657 1,512 1,238 0,640 1,169 0,463 0,988 0,774 1,64 0,748 1,357 0,833 1,211 0,114 0,920 0,50 0,554 1,941 0,714 1,876 1,958 0,528 0,266 1,949 1,924 0,562 1,950 1,768 1,573 0,586 0,77 0,668 1,275 0,637 0,283 0,384 0,675 1,541 1,413 1,169 1,227 0,376 1,902 1,976 1,96 1,294 0,468 1,204 1,324 0,816 1,147 0,956 0,932 1,17 0,688 1,4 1,580 0,924 1,284 1,466 1,251 0,287 1,798 1,650 1,378 1,460 1,863 1,727 0,427 1,6 0,393 1,680 1,856 1,926 0,6 1,265 1,957 0,434 1,775 1,549 0,721 1,767 1,879 0,651 0,37 1,532 1,488 0,546 0,234 1,770 1,46 1,963 0,266 0,55 1,624 0,291 1,263 0,567 0,962 1,488 1,747 0,439 1,450 0,28 0,745 0,424 1,291 0,321 0,928 0,80 1,896 1,456 1,465 1,52 1,598 0,238 0,198 0,71 1,279 0,91 1,939 1,552 0,45 0,190 1,414 1,326 1,85 0,278 0,30 0,915 0,46 1,795 1,583 1,158 1,91 0,217 1,939 0,25 1,158 0,488 1,789 0,732 1,230 0,721 1,309 0,41 1,460 1,525 1,706 1,161 0,482 1,453 1,899 1,724 1,226 0,154 0,935 1,812 0,33 0,288 1,586 0,259 0,442 1,637 0,470 0,474 0,365 0,300 0,799 0,2 0,292 0,14 0,481 0,301 0,748 1,183 1,853 0,215 1,855 1,326 1,691 1,844 1,774 0,856 1,219 0,593 0,930 0,405 1,671 0,496 0,819 0,751 0,607 0,628 0,856 0,886 0,973 0,165 1,222 0,144 0,921 0,253 1,157 0,113 1,470 1,50 1,770 1,931 1,125 0,742 1,773 0,785 1,768 1,452 1,710 1,654 0,104 0,481 1,747 1,453 0,392 0,249 1,73 1,59 1,370 0,363 0,774 0,716 0,542 0,237 1,84 1,636 1,693 1,883 1,675 0,190 0,774 0,32 1,580 1,141 0,790 1,812 1,419 0,162 1,305 0,48 1,61 1,954 1,829 0,606 0,718 0,489 1,293 0,246 0,205 1,213 1,47 0,212 1,723 0,161 0,725 0,56 1,778 1,33 0,920 0,512 1,630 1,728 1,649 0,362 1,983 1,44 0,851 1,628 1,991 0,67 1,583 0,932 0,674 0,81 1,645 1,403 1,566 1,508 0,797 0,216 0,468 0,508 0,628 0,476 0,726 0,612 1,585 1,907 1,219 0,500 1,458 0,552 1,81 0,712 1,311 0,491 0,534 0,715 0,118 0,124 0,281 1,997 1,321 0,437 0,20 1,270 1,354 1,763 0,321 0,504 1,419 0,246 0,938 0,822 1,421 1,466 0,255 1,47 1,728 0,479 1,327 0,936 1,605 0,124 1,59 1,178 0,822 0,75 1,874 1,503 0,546 0,886 0,503 0,372 1,535 1,721 1,821 0,340 0,744 0,532 1,895 1,107 0,599 0,300 0,69 0,702 0,834 0,218 0,584 0,781 1,581 1,741 0,966 1,299 0,865 0,86 1,620 0,842 1,683 0,946 1,906 0,29 1,34 1,153 1,690 0,920 0,375 0,925 1,780 0,164 1,249 1,468 1,389 0,21 1,549 1,104 0,153 0,256 0,313 0,69 1,866 1,851 1,29 1,206 0,798 0,359 1,854 0,45 0,276 0,406 0,302 0,344 0,439 1,88 0,42 0,890 0,416 0,796 0,651 1,822 1,370 1,153 1,274 0,273 0,165 0,781 0,537 0,862 1,161 1,851 1,198 1,595 0,775 0,808 0,502 1,775 0,912 1,42 1,978 1,624 1,866 0,944 0,511 0,897 0,136 0,835 1,785 1,709 0,555 0,768 0,523 0,117 1,308 1,472 1,226 1,887 0,177 0,993 0,181 1,439 0,304 0,810 0,43 1,858 0,94 1,481 1,411 1,160 0,321 1,350 1,402 1,762 0,772 0,6 0,177 0,824 1,333 0,388 0,596 0,208 1,386 1,172 0,179 0,724 0,317 1,763 1,981 1,187 1,220 1,95 0,613 0,714 1,816 0,716 1,809 1,35 1,497 1,490 0,226 1,574 0,820 0,573 1,795 0,956 0,979 1,466 0,68 0,864 1,710 1,263 0,959 1,535 1,866 1,854 1,554 0,196 1,366 1,793 0,653 1,862 1,489 1,896 1,365 1,22 0,621 0,584 0,962 0,101 1,611 0,770 1,563 0,410 0,281 0,201 0,68 0,574 1,401 1,644 1,701 1,908 1,993 0,319 0,281 0,40 1,537 0,925 1,9 1,533 0,726 0,306 1,294 0,27 0,79 1,18 0,325 1,412 0,870 1,182 0,177 0,850 0,282 0,818 0,817 0,500 0,556 0,288 1,422 1,560 0,811 1,185 1,31 0,725 0,608 1,544 1,385 0,820 1,940 1,389 1,635 1,663 1,79 1,446 0,245 0,697 1,2 1,964 0,237 0,143 1,304 1,449 0,180 1,351 1,825 1,672 0,221 1,534 0,242 0,217 0,149 1,837 0,272 0,373 1,195 1,241 1,177 1,838 1,744 1,883 0,55 1,888 1,147 0,511 0,441 0,740 1,515 0,129 1,386 1,761 1,395 0,349 0,879 1,837 1,671 1,494 1,464 1,53 0,150 1,478 1,344 0,307 0,147 0,884 1,779 0,814 0,957 0,263 0,603 1,41 1,434 0,88 0,307 0,57 0,357 0,194 0,682 0,393 1,428 1,425 1,273 0,319 1,80 1,443 0,764 1,472 1,213 0,175 0,557 0,592 1,107 0,240 1,988 1,222 0,496 0,990 0,343 0,761 1,555 1,626 0,432 1,842 1,984 0,127 1,873 0,57 1,5 1,217 1,61 1,851 0,377 0,218 1,764 1,664 0,836 1,575 0,867 1,443 1,287 0,25 0,321 0,929 1,407 0,109 1,15 0,861 0,13 1,257 1,80 1,151 0,881 1,114 1,238 0,145 0,982 0,642 1,36 0,144 0,631 1,834 1,733 0,276 1,963 1,505 0,386 1,160 0,153 0,951 1,801 0,170 1,948 0,125 0,161 0,463 1,211 1,51 0,816 0,846 0,953 1,775 1,544 0,946 0,944 1,647 0,949 0,637 0,858 1,153 1,933 1,364 1,500 0,828 1,893 1,981 1,216 0,105 0,757 1,115 1,855 1,781 1,246 1,717 0,573 0,99 0,870 0,100 0,953 0,606 1,362 1,393 0,377 1,444 1,943 1,356 0,174 0,423 0,181 1,18 1,818 1,501 0,909 1,565 0,195 1,553 0,256 0,69 1,492 1,306 1,326 1,361 0,86 1,117 0,512 1,225 1,789 1,3 0,996 1,44 0,973 0,422 1,950 0,229 1,593 1,423 0,235 0,68 0,971 1,260 1,552 1,507 0,640 0,585 0,570 0,221 1,868 0,290 1,110 0,257 0,816 1,787 0,500 1,987 0,656 1,356 1,593 0,552 0,30 0,663 0,872 1,547 0,160 0,181 1,492 1,409 1,980 0,111 1,521 1,743 1,819 0,650 1,257 1,28 1,777 0,591 1,766 0,796 1,722 0,440 1,517 0,877 1,101 1,14 1,99 0,609 0,493 0,221 0,734 0,402 0,970 0,954 0,144 1,353 1,935 1,113 0,532 0,302 1,767 0,406 0,426 0,425 0,299 0,814 1,377 1,106 0,163 1,849 1,549 1,396 1,500 1,711 1,164 0,480 0,277 0,411 0,230 0,886 0,976 1,303 0,982 0,364 0,63 1,67 1,834 1,373 1,670 0,424 1,169 1,89 1,176 1,267 1,671 0,277 1,171 1,317 0,714 1,28 1,919 0,438 1,620 0,90 1,52 0,408 0,520 1,315 0,502 1,134 0,284 0,892 0,206 0,803 0,118 1,637 1,379 0,389 0,950 0,609 1,670 0,930 1,362 1,270 0,842 0,278 1,615 1,929 1,521 0,414 0,147 1,761 0,262 1,222 1,965 0,773 1,961 0,261 1,496 0,127 0,309 1,241 1,194 0,800 1,811 1,346 1,795 1,353 0,900 0,858 1,485 1,886 0,731 1,117 1,630 0,898 0,598 1,563 0,480 0,296 1,200 0,807 1,867 0,705 1,341 0,868 0,377 0,352 1,438 0,625 1,44 0,631 1,21 0,463 1,644 1,276 0,930 1,124 1,731 0,828 0,852 0,68 0,578 0,258 1,642 0,415 0,825 1,589 1,278 0,370 1,422 0,271 1,181 1,128 1,489 1,801 0,265 1,694 1,557 0,926 0,743 1,817 1,308 1,783 0,373 0,395 0,712 0,83 0,389 0,154 0,720 0,192 0,257 0,901 1,683 1,333 0,337 0,101 1,108 1,190 0,502 0,837 0,448 0,895 1,83 0,325 1,381 0,534 1,452 1,89 1,955 1,450 0,748 0,315 0,720 1,592 1,379 0,949 1,228 0,530 1,12 1,108 0,464 0,871 1,265 0,972 0,207 1,510 1,83 0,294 1,6 1,616 0,886 0,183 1,66 0,317 0,258 1,598 0,893 0,483 1,669 1,955 1,248 0,394 0,304 1,36 1,358 0,852 1,460 1,110 0,48 1,227 1,865 1,584 0,112 1,563 1,364 0,855 0,330 1,963 1,352 0,68 0,199 0,651 0,593 1,470 1,464 1,143 1,878 1,379 0,462 0,680 1,322 1,619 1,382 1,690 0,136 1,718 0,825 0,121 0,24 0,904 0,673 0,25 1,377 0,821 0,696 0,864 0,409 1,312 1,825 0,515 0,186 0,500 1,232 1,998 0,156 0,687 0,932 0,987 0,189 1,342 1,959 1,100 1,747 0,272 0,690 0,379 0,574 0,157 0,994 1,777 1,92 0,308 1,648 1,3 1,262 0,492 0,484 1,766 0,587 1,345 1,485 1,120 0,571 1,362 0,94 0,227 1,371 1,668 0,479 1,925 0,58 1,695 0,793 0,169 0,37 0,611 0,117 0,867 1,215 0,356 0,685 1,157 1,365 1,858 1,962 0,276 1,464 1,848 1,134 1,937 1,488 0,456 1,698 1,34 0,832 1,959 1,948 0,571 1,296 1,677 1,538 0,45 0,323 1,174 0,487 1,818 1,243 1,234 1,998 1,169 0,687 0,16 0,222 0,747 0,818 1,98 1,336 1,757 1,684 0,389 1,645 0,466 1,219 1,329 0,486 0,251 0,359 0,755 1,545 0,683 0,657 1,936 0,785 0,224 1,136 0,151 1,50 1,382 1,575 1,273 0,751 1,61 0,924 0,112 1,587 0,301 0,606 1,694 1,21 1,317 0,296 1,981 1,898 1,997 0,385 1,4 0,449 0,721 1,125 0,8 1,390 0,558 1,501 0,324 0,770 1,749 0,628 1,466 0,90 0,385 1,253 0,791 1,478 0,262 0,470 0,742 1,699 1,414 0,427 1,231 1,434 0,653 0,388 0,659 0,641 1,522 0,290 1,397 1,34 1,856 1,510 0,34 1,907 0,214 0,32 0,605 0,41 1,213 0,508 1,55 0,32 0,164 0,522 0,864 1,861 0,260 1,241 0,546 1,773 0,993 0,700 1,880 0,791 1,991 0,694 1,529 1,537 0,625 1,446 1,290 1,223 0,213 0,863 1,96 1,988 1,299 1,880 1,510 0,552 0,255 1,997 0,25 0,287 1,759 0,422 1,477 0,282 1,964 1,352 0,776 0,239 1,755 0,600 1,112 1,364 1,318 1,314 1,259 0,812 0,371 0,961 0,71 0,115 0,365 0,492 1,757 0,665 1,71 1,170 1,586 1,649 0,525 1,684 0,984 1,616 1,305 1,779 1,919 1,790 1,320 0,907 0,853 1,286 0,43 0,868 1,834 1,838 1,890 0,618 0,689 0,525 1,280 0,167 1,454 0,303 1,897 0,691 1,384 0,462 0,78 1,796 0,117 1,896 1,746 1,76 0,785 0,832 0,826 1,336 1,505 0,618 1,325 0,747 1,487 1,864 0,785 0,810 1,552 1,725 1,143 1,919 1,289 0,181 1,897 1,3 0,222 1,982 1,960 0,907 1,492 1,473 1,997 1,587 0,122 1,188 1,676 1,492 1,418 1,793 1,92 0,747 0,436 0,208 0,258 0,975 1,635 1,787 1,271 1,425 0,712 0,165 0,356 1,623 0,835 1,250 1,354 1,94 1,1 0,60 0,525 0,410 1,20 0,96 1,620 0,152 0,979 1,420 1,338 0,766 1,708 0,847 1,830 0,981 0,397 1,30 0,941 0,957 0,10 0,982 1,435 1,804 1,550 1,654 1,402 0,998 0,965 0,145 1,542 0,481 0,63 0,485 1,76 1,802 0,575 1,434 0,762 1,763 0,776 1,435 1,240 0,421 1,396 1,289 1,696 0,777 0,20 1,534 1,399 1,339 1,446 1,516 1,919 1,331 0,536 0,941 1,485 1,48 0,411 1,584 1,49 0,278 0,128 1,779 0,409 1,354 1,643 1,340 1,83 1,963 0,893 0,819 1,685 0,596 1,437 1,726 0,225 0,60 1,579 0,895 1,309 0,213 1,168 0,252 0,724 1,774 0,849 1,229 1,607 1,258 0,236 1,206 0,477 0,313 1,496 1,329 0,787 1,301 0,295 1,993 0,606 1,42 0,282 0,276 0,41 1,693 1,28 0,777 1,852 1,850 1,416 0,870 1,103 0,576 0,1 1,259 0,297 1,979 0,203 1,760 1,587 0,285 1,657 1,577 1,634 0,687 0,87 0,318 0,919 1,108 1,261 1,67 1,276 1,756 1,431 0,909 0,454 0,300 0,228 1,575 1,137 0,384 0,144 0,292 1,21 0,716 0,597 1,49 0,895 0,641 0,568 1,461 0,426 1,874 0,806 1,746 1,300 1,275 0,338 1,47 0,831 1,201 1,569 1,305 1,562 1,551 1,950 0,323 1,280 1,581 1,220 1,783 0,745 0,149 0,587 0,274 1,115 1,464 1,850 1,513 0,970 0,796 0,147 0,771 0,163 0,48 1,676 0,74 0,720 1,151 0,169 0,33 1,275 0,420 1,365 1,75 0,129 0,367 0,138 0,431 1,261 0,294 0,385 1,372 0,177 1,712 1,673 0,633 1,919 1,285 0,544 1,970 0,477 0,171 1,34 0,972 1,763 0,278 0,755 0,100 0,522 1,464 1,120 1,642 1,503 1,948 0,759 1,908 1,570 1,823 0,775 0,725 0,458 0,294 1,390 1,300 1,509 1,769 0,992 0,363 0,274 0,295 1,581 1,492 0,280 0,583 1,700 0,249 0,849 1,865 1,325 0,115 1,979 1,654 0,79 1,997 0,411 0,288 1,459 1,171 0,151 0,952 1,472 1,516 0,921 0,28 0,273 1,267 0,754 0,180 1,939 1,617 1,504 0,77 1,260 1,777 0,870 0,104 0,973 1,130 1,215 1,884 1,235 0,739 1,390 1,559 1,721 0,348 1,348 0,810 1,4 0,427 1,820 1,871 0,816 1,964 0,716 0,284 0,754 0,317 1,584 0,920 1,284 0,729 1,747 1,974 0,559 0,132 1,130 1,315 0,993 0,579 1,829 0,811 0,450 0,492 1,531 0,506 0,988 1,948 1,968 0,109 1,189 0,954 1,463 0,432 1,117 0,730 0,632 0,238 1,747 0,412 1,484 1,214 1,972 0,76 0,983 1,450 1,263 1,129 0,801 0,569 0,68 0,104 1,719 1,222 0,302 1,762 1,710 1,374 0,636 1,615 0,483 0,342 1,483 1,676 0,159 1,312 1,855 1,179 0,544 1,301 1,509 0,332 0,888 1,799 0,334 0,342 1,501 1,393 1,606 0,798 1,77 1,744 1,991 0,703 0,108 1,166 0,811 1,771 0,859 1,125 0,924 0,811 1,426 0,714 1,396 1,406 0,569 1,961 0,831 0,368 0,252 1,937 0,15 0,472 0,427 1,880 0,247 0,951 0,234 1,575 1,979 1,629 1,820 1,682 1,927 1,66 1,183 1,955 0,536 0,524 1,12 0,20 1,78 1,595 1,476 1,718 1,297 1,94 0,439 1,67 1,989 1,437 1,309 1,195 1,939 0,471 1,246 0,293 1,883 1,283 1,935 1,172 1,275 0,189 1,145 1,982 1,693 1,76 1,819 0,817 0,616 1,287 1,665 1,799 0,846 0,637 0,813 0,35 1,492 0,464 1,910 0,242 0,337 1,774 1,514 1,485 1,454 0,158 1,171 1,193 1,169 0,90 0,784 0,171 0,476 0,353 0,212 0,677 0,989 0,805 0,279 1,99 0,39 1,232 1,694 0,994 0,840 1,814 1,329 0,563 1,772 1,126 0,596 0,895 0,116 0,63 0,121 0,171 0,163 0,252 0,713 1,661 0,172 1,671 0,670 0,80 1,362 0,36 0,960 0,239 0,149 0,4 0,915 0,132 0,97 0,794 0,781 1,508 1,23 1,702 1,578 0,281 0,891 0,458 1,819 0,341 0,94 0,722 1,609 1,965 1,874 0,136 0,696 1,151 0,496 0,210 0,824 0,837 1,751 1,268 1,345 1,966 0,134 1,726 0,62 0,918 1,848 1,541 0,350 0,646 1,105 0,94 0,96 0,331 0,302 1,247 1,277 1,40 1,538 0,812 0,764 0,505 0,71 0,581 0,725 0,910 0,289 0,272 0,974 1,111 1,341 0,297 1,210 0,100 0,124 0,301 1,426 1,927 0,340 1,139 0,577 1,672 0,117 1,119 1,250 1,194 0,256 1,412 0,698 0,374 1,145 0,960 1,818 0,34 1,151 1,993 0,735 0,602 1,555 1,520 0,777 0,57 0,214 0,658 0,684 1,432 0,978 0,272 1,368 0,222 0,684 1,768 1,983 1,294 1,879 0,201 1,104 0,871 1,987 1,803 1,125 0,431 0,517 1,308 1,454 1,61 0,476 0,863 1,388 0,404 1,52 1,473 0,251 0,295 0,381 0,772 1,921 0,69 0,306 1,677 0,728 1,702 0,881 1,553 1,129 1,391 0,914 1,274 0,724 0,722 0,950 1,464 0,917 0,630 0,230 1,850 1,669 1,429 0,896 1,449 0,856 0,223 1,785 0,621 1,381 1,134 0,445 0,221 1,782 0,387 1,848 1,306 0,128 1,911 1,393 0,585 0,892 1,908 1,914 1,813 1,927 1,818 1,752 1,367 0,963 1,254 1,191 1,339 1,915 0,915 1,196 1,804 0,499 1,109 0,669 0,591 1,211 1,80 1,973 0,605 0,531 1,950 0,159 0,942 0,574 0,826 0,673 1,759 1,793 1,646 0,624 0,56 0,132 1,639 0,713 0,120 0,93 0,493 1,842 1,322 0,835 1,708 0,469 1,22 0,231 0,996 1,617 0,562 1,996 1,935 1,118 0,971 1,279 0,585 1,149 0,324 0,108 1,792 0,424 0,647 0,191 0,3 1,201 0,370 1,336 0,966 0,100 0,90 1,279 0,836 1,211 0,606 1,954 0,809 0,146 1,144 0,582 0,822 0,331 1,724 0,542 1,254 1,514 0,356 0,182 1,529 0,685 0,440 1,579 1,850 0,0 1,77 1,807 0,621 0,295 1,817 1,138 1,421 0,585 0,454 1,262 0,691 1,212 0,286 0,609 1,37 1,147 0,454 1,899 1,859 0,272 0,699 0,310 0,227 1,103 0,738 1,379 0,420 1,738 0,841 0,442 0,673 0,359 1,651 0,663 1,491 0,248 0,481 1,423 1,144 0,931 0,602 1,765 0,435 1,771 1,786 1,728 1,586 1,856 1,623 0,760 1,864 1,262 1,785 1,949 1,325 1,277 0,27 1,489 0,267 0,213 1,981 1,0 0,5 0,102 1,759 1,240 1,121 0,920 1,882 0,724 0,961 0,753 1,857 1,328 0,99 1,25 0,838 1,768 1,23 0,890 0,165 1,551 0,639 1,890 0,228 0,133 1,446 0,464 0,938 0,371 1,439 0,485 1,888 0,131 1,285 1,96 0,672 1,879 1,653 1,929 1,897 1,779 1,980 1,459 1,661 0,103 1,448 1,67 1,171 1,523 1,986 1,393 0,170 0,943 1,592 0,3 0,593 0,623 1,0 1,233 0,785 1,591 0,50 0,607 0,712 0,524 0,816 0,489 0,498 1,948 0,299 0,779 1,5 1,312 0,579 0,984 0,222 1,987 1,180 0,373 1,908 0,234 1,177 1,28 1,172 1,898 0,472 1,803 0,625 0,160 1,834 1,284 0,249 1,873 0,604 1,594 0,40 0,674 1,598 1,170 1,384 1,500 1,340 1,756 1,584 1,676 1,265 1,120 1,765 1,433 0,926 0,167 0,825 0,238 0,389 0,401 0,655 0,72 0,696 0,987 1,611 0,800 0,160 0,495 1,495 0,851 0,400 1,757 1,371 0,422 1,133 1,812 0,527 0,873 0,170 1,266 0,949 0,504 1,977 0,209 1,757 1,662 0,42 1,474 0,828 1,244 0,759 1,366 0,801 1,603 0,765 0,963 1,282 1,631 0,576 1,487 0,225 1,918 1,729 1,760 0,491 1,33 0,662 1,788 0,526 1,407 1,940 1,588 0,725 0,509 1,107 1,190 1,906 0,736 0,636 0,885 0,298 0,915 0,49 1,299 1,798 1,301 1,343 1,555 0,391 0,502 0,521 0,333 1,200 0,280 0,903 0,490 1,633 0,118 1,936 1,198 0,334 0,914 1,465 1,61 1,81 1,136 0,260 1,5 1,645 0,246 0,376 0,133 0,387 1,542 0,337 1,455 0,563 1,92 0,907 0,215 1,127 0,79 0,212 0,980 1,618 1,756 1,896 1,231 0,787 0,664 1,322 1,652 1,771 0,856 0,960 0,588 0,406 0,699 0,629 0,347 0,445 0,111 0,525 1,356 0,199 0,182 0,840 1,298 1,427 1,391 1,970 1,251 0,740 0,462 0,360 0,261 1,602 1,890 0,241 0,126 1,304 1,685 1,563 1,423 1,184 0,804 0,213 1,491 1,646 0,38 1,284 1,947 1,476 0,340 0,755 0,254 0,633 0,267 0,490 0,852 1,258 0,689 1,892 1,841 0,556 0,572 1,263 0,489 1,588 0,881 0,998 0,285 0,272 0,529 1,206 0,674 1,759 0,953 1,787 0,781 1,851 1,692 0,855 1,353 1,596 1,630 1,37 0,853 1,981 0,276 0,870 1,721 1,707 1,591 0,629 0,886 0,343 1,237 1,537 1,298 0,676 1,919 1,235 1,775 0,299 0,355 1,2 1,658 1,689 1,708 0,739 0,75 0,448 1,947 0,693 0,889 1,260 1,640 1,826 1,801 0,847 1,915 1,819 0,339 0,510 1,672 1,761 1,574 1,107 1,37 1,941 1,77 0,842 0,408 1,821 0,237 0,244 1,859 0,211 0,518 0,139 0,107 1,42 1,14 0,619 0,785 1,387 1,243 0,976 1,845 1,665 0,580 1,213 0,158 0,335 0,324 1,970 1,351 0,171 1,582 1,118 0,857 1,295 0,924 1,311 0,517 0,8 0,493 0,875 0,29 1,118 0,327 1,645 1,473 1,39 1,26 0,16 0,55 0,566 0,610 1,881 0,77 1,143 1,309 0,667 0,757 0,56 0,908 0,383 0,41 0,472 0,472 1,905 1,833 0,585 0,819 0,786 0,504 1,8 1,569 0,518 1,82 1,768 1,879 1,53 1,427 0,445 1,286 1,964 0,812 1,458 1,105 0,4 0,729 0,230 1,810 1,352 0,524 0,283 0,18 0,51 0,431 0,728 0,693 1,284 0,565 1,317 0,885 1,843 1,479 0,180 1,487 0,483 1,11 0,936 0,846 0,905 1,68 0,119 1,560 1,216 0,99 1,653 0,21 0,980 1,261 0,455 0,728 1,213 1,816 0,911 1,373 0,575 1,890 1,167 0,732 0,50 1,129 0,384 0,410 1,586 1,59 0,64 1,881 0,855 1,477 1,880 1,368 0,276 0,120 0,318 1,155 1,691 1,166 1,470 1,176 0,119 0,702 1,488 0,929 0,605 1,94 1,839 1,516 0,807 0,655 0,926 0,89 1,714 1,743 1,803 0,931 0,328 0,632 1,915 0,113 1,827 1,672 0,117 0,160 1,638 0,756 0,306 1,791 1,757 0,526 0,270 0,859 0,941 0,941 0,398 1,808 0,37 1,138 1,205 1,629 1,561 0,755 0,697 0,478 0,466 1,601 0,101 0,333 1,251 0,711 1,58 0,412 1,707 1,540 1,798 0,512 1,544 0,99 1,540 0,396 1,982 0,159 1,287 1,225 0,63 0,778 0,543 1,611 0,822 0,262 0,349 0,3 0,916 0,377 0,83 0,706 0,187 0,628 1,767 1,924 0,491 0,116 1,60 0,468 0,899 1,16 1,293 0,942 1,603 0,227 1,277 1,411 1,670 1,681 1,391 0,225 0,371 1,374 0,573 1,653 1,772 0,852 1,364 0,953 0,574 1,390 0,135 0,398 0,361 0,192 0,253 1,649 1,783 1,694 0,87 0,688 1,956 0,576 1,936 0,312 1,97 0,458 1,568 0,119 0,599 0,604 1,242 1,749 1,707 1,897 1,473 0,828 0,141 0,977 0,145 1,748 1,526 0,44 0,509 1,602 0,767 1,654 1,474 1,74 0,988 1,948 1,613 0,28 1,579 1,474 0,479 0,722 0,199 0,467 0,737 1,40 1,841 0,888 0,992 0,571 1,506 1,2 0,103 0,962 0,976 0,76 0,552 0,953 1,564 1,103 1,973 1,447 1,929 0,51 1,637 1,361 1,449 0,645 0,310 0,385 0,999 1,235 1,857 1,602 1,99 0,608 0,576 0,893 1,239 1,553 0,202 1,109 0,13 1,454 0,524 0,420 1,636 0,324 0,832 1,519 0,336 1,11 1,418 1,676 0,759 1,874 1,576 1,259 1,701 1,922 1,454 0,958 0,129 1,379 1,615 0,916 1,823 1,73 0,403 0,174 0,339 0,84 0,18 1,141 0,539 0,500 1,430 1,40 1,894 1,368 1,644 1,923 1,447 1,422 1,404 1,6 1,610 1,788 0,711 0,850 1,95 1,991 1,946 1,390 0,558 0,789 1,45 1,498 1,791 1,710 1,288 0,176 0,552 0,283 1,432 1,289 1,190 0,165 0,496 0,720 1,516 1,441 1,623 0,984 1,28 0,231 0,258 0,856 0,88 0,330 0,363 1,365 0,472 0,971 0,295 1,131 0,212 1,219 0,972 1,849 0,147 0,732 1,248 1,606 0,647 0,356 1,277 0,92 1,42 1,661 0,745 0,714 0,948 1,303 1,559 0,775 1,147 0,152 0,660 0,526 1,488 0,350 1,339 0,563 1,476 1,764 1,219 1,765 1,869 1,248 0,462 1,379 0,848 0,69 0,850 0,644 0,745 0,748 0,981 1,100 0,871 0,34 1,891 0,962 0,544 0,760 0,290 1,915 0,933 0,837 1,683 0,7 1,930 0,607 0,729 0,266 1,625 1,611 1,440 0,88 1,13 1,959 0,752 0,45 1,975 1,87 0,605 1,461 1,93 1,683 1,793 0,382 1,154 0,452 1,472 0,908 0,518 0,110 0,191 1,712 1,611 1,288 0,495 1,509 1,614 0,548 0,534 1,754 0,575 0,171 1,743 1,802 1,972 0,313 1,896 0,464 0,470 0,950 0,47 0,753 1,493 1,47 0,59 1,118 1,445 1,106 0,109 1,810 1,876 1,930 1,768 0,668 1,392 1,947 0,12 1,803 1,658 0,591 1,319 1,226 0,147 1,547 1,774 1,923 0,790 0,250 0,662 0,300 0,14 1,189 0,89 1,170 0,427 0,311 0,183 0,520 1,559 0,511 0,840 0,59 1,108 1,764 0,317 1,711 1,840 0,4 0,538 1,195 0,341 1,38 1,652 0,405 1,395 0,333 0,119 0,416 1,404 1,15 1,28 1,594 1,958 0,414 0,155 0,281 0,662 1,515 1,972 1,281 1,320 1,937 1,652 1,41 1,82 0,361 0,885 0,584 1,283 1,478 1,365 1,615 1,289 0,879 0,569 0,366 1,148 1,52 0,658 0,56 1,146 1,956 1,87 0,395 0,214 0,613 1,526 0,5 0,592 1,774 0,529 1,812 0,579 1,581 0,602 1,977 0,755 1,372 1,611 1,604 0,474 0,472 1,123 0,699 0,799 1,420 1,310 1,708 0,450 0,99 0,427 1,450 1,740 1,684 0,624 1,444 0,227 0,395 0,887 1,348 1,411 0,406 0,153 0,304 0,816 1,994 1,819 0,379 1,227 1,576 1,585 1,629 1,874 1,848 1,7 0,836 0,512 0,875 0,43 1,159 1,706 0,653 0,641 0,893 0,849 1,703 1,104 1,480 0,211 1,223 1,17 0,233 0,565 0,603 1,868 0,626 1,948 1,188 1,403 1,796 1,82 0,232 1,744 0,675 1,552 1,969 1,304 0,261 0,790 0,820 0,465 1,449 0,765 1,490 0,638 1,468 0,630 0,446 1,690 1,316 0,963 1,413 0,364 1,532 1,611 0,152 0,15 0,168 1,229 1,725 1,864 1,342 1,172 1,442 0,274 0,539 1,598 0,969 0,381 1,309 1,635 0,80 0,470 0,718 0,262 0,575 1,742 1,179 0,474 0,554 0,464 0,414 0,399 1,607 0,902 0,4 0,830 0,906 0,240 0,460 1,802 1,24 1,840 0,645 1,745 1,956 1,749 1,986 0,302 1,545 0,502 0,443 0,258 0,423 0,34 1,677 1,566 0,638 1,424 0,791 1,413 1,446 1,28 1,402 1,200 1,828 1,480 0,797 0,660 1,238 1,776 0,866 1,368 1,348 0,168 1,189 0,459 0,631 0,448 0,329 0,975 0,717 1,690 0,784 1,318 0,706 1,316 1,847 0,532 1,2 1,325 0,870 0,735 1,547 0,832 0,298 1,199 0,285 0,557 1,780 0,104 1,36 0,809 1,934 1,252 1,886 0,671 1,314 1,759 1,772 0,380 1,335 1,868 0,784 0,492 1,761 1,268 0,125 1,382 0,375 0,219 0,392 1,909 1,147 1,885 0,674 0,275 1,852 0,672 1,800 1,112 0,3 1,614 1,968 0,863 1,697 1,189 0,136 0,856 0,308 1,150 1,781 0,47 1,39 0,634 0,387 0,107 0,483 0,250 0,354 1,814 0,159 1,623 1,225 1,826 1,526 0,264 1,731 0,931 0,519 0,479 0,174 0,441 1,98 1,821 1,194 1,99 0,531 1,911 0,279 1,201 1,204 0,714 0,344 1,736 0,92 0,744 1,605 0,7 0,880 0,751 1,916 0,127 1,628 0,161 0,684 0,663 0,173 1,295 1,84 1,762 0,362 1,519 0,631 1,647 1,241 0,380 0,663 1,629 1,998 0,928 0,932 0,782 1,770 1,387 1,211 1,800 0,425 1,501 1,128 0,429 0,698 1,779 1,967 1,624 0,396 0,605 1,7 1,295 1,797 1,506 0,156 0,117 0,449 1,898 0,61 0,40 1,417 1,778 1,288 1,71 0,538 1,837 0,159 0,768 1,648 0,800 0,130 1,644 1,301 0,346 0,677 1,90 1,733 1,756 1,122 1,8 0,851 0,194 0,655 0,243 1,744 0,163 1,497 0,971 0,508 1,991 0,167 0,67 1,970 0,450 1,373 1,850 1,341 0,86 1,443 0,259 0,492 0,859 1,809 1,713 1,944 0,32 0,166 1,672 1,822 0,606 1,527 1,383 0,711 1,244 1,258 1,998 1,763 0,691 0,897 1,11 1,106 1,721 1,909 1,530 0,757 1,515 1,948 0,357 1,437 1,563 0,119 1,373 1,706 0,8 0,879 1,196 0,8 1,140 1,676 0,406 1,528 0,494 0,928 1,34 0,605 0,699 0,630 1,171 0,968 1,542 1,101 0,455 1,812 1,711 0,482 1,161 1,937 0,316 1,609 1,639 0,949 1,257 0,620 0,435 1,308 0,377 0,83 0,64 1,262 1,203 0,317 0,375 0,434 0,144 0,265 1,679 0,44 0,764 0,166 1,871 1,754 1,975 0,175 0,774 1,625 1,910 1,693 1,270 0,742 1,396 0,748 0,381 1,451 0,181 1,720 1,564 1,973 0,580 1,922 1,786 1,886 0,740 1,434 0,499 1,740 0,572 0,554 1,903 1,674 1,57 0,189 0,453 1,733 0,95 0,676 0,475 1,669 1,761 0,166 0,941 1,48 1,251 0,215 1,30 0,148 0,281 0,94 1,21 1,452 1,635 0,883 1,177 0,558 1,905 0,178 0,945 0,866 0,602 1,908 0,636 0,454 1,644 0,83 0,325 0,999 1,105 0,957 1,335 1,475 1,238 1,60 0,4 0,545 0,400 0,478 0,665 1,314 0,722 0,376 1,997 0,578 0,389 1,26 1,330 1,927 1,351 0,177 1,959 0,740 0,950 0,738 0,154 0,879 0,709 0,865 0,284 1,761 0,734 1,385 0,27 1,143 0,445 1,636 1,856 1,825 0,242 1,875 1,689 0,470 0,688 0,292 1,858 0,96 0,560 1,125 1,442 1,390 1,596 0,208 1,368 1,52 0,933 1,493 0,350 1,204 1,481 1,967 0,858 1,546 0,798 1,582 1,990 1,382 1,797 0,58 0,443 1,88 1,57 1,368 0,452 1,269 0,643 0,701 1,758 1,616 0,26 1,591 1,865 0,460 0,413 1,893 0,525 0,615 1,474 0,592 0,64 0,376 0,343 1,856 0,530 1,213 0,629 1,67 0,431 1,296 0,3 0,921 1,725 1,765 1,130 0,901 0,535 0,246 1,88 0,935 1,14 0,567 0,457 1,540 0,789 0,491 1,45 0,824 0,574 0,959 0,917 1,559 1,450 1,249 0,840 1,789 0,64 1,265 0,736 0,908 0,223 1,514 0,550 1,395 1,210 0,416 1,451 0,7 0,296 0,224 0,794 0,690 0,325 1,452 0,878 1,879 0,644 1,693 0,98 0,218 0,916 1,690 1,107 0,258 0,363 0,471 0,787 0,158 1,590 1,933 1,342 1,401 0,789 0,154 1,963 0,430 1,326 1,941 1,136 1,629 1,354 0,413 0,416 1,582 1,993 0,925 0,405 1,700 1,108 1,678 0,733 0,170 0,101 0,5 1,991 0,596 1,626 0,797 0,104 1,949 0,675 0,262 0,102 0,809 0,950 0,794 1,850 0,831 0,956 0,99 1,719 1,553 0,962 0,797 0,611 0,544 1,877 0,157 0,754 0,785 1,673 0,334 1,621 0,12 1,35 1,604 1,118 0,740 0,695 1,73 1,65 1,892 1,899 1,875 0,992 1,76 1,595 0,533 0,55 0,212 1,354 0,574 0,127 1,939 1,966 1,261 1,702 0,859 0,750 1,947 0,601 1,483 0,264 0,275 0,778 0,982 1,200 1,145 0,836 0,654 0,479 1,848 1,289 1,179 1,669 0,268 0,455 1,593 0,429 0,806 0,765 0,57 0,216 1,254 1,409 0,344 0,415 0,959 0,495 1,519 1,157 1,955 0,847 1,502 0,655 1,717 1,286 1,240 1,36 1,516 0,283 1,414 1,972 1,566 0,4 0,87 1,382 1,377 0,379 1,320 0,377 0,565 0,442 1,851 1,829 0,751 1,495 0,434 0,804 0,647 0,534 0,66 1,580 1,745 1,801 0,521 0,488 0,670 1,121 0,509 1,99 0,5 0,684 0,771 0,978 1,825 0,362 0,121 1,853 0,610 0,152 1,788 0,396 0,341 0,23 1,651 0,15 0,98 0,389 1,906 1,288 0,908 0,621 1,395 0,793 0,417 1,21 0,56 0,42 0,540 0,522 0,517 0,588 0,983 1,231 0,777 1,790 0,53 1,974 1,139 1,903 1,249 0,332 0,881 1,94 0,596 1,853 1,799 0,567 1,667 1,268 0,444 0,604 0,852 1,768 0,883 0,515 1,169 1,551 1,601 1,871 1,616 1,783 1,696 1,896 0,688 0,983 0,960 1,783 0,651 1,103 0,195 0,1 1,499 0,149 1,422 0,18 1,211 1,839 0,837 1,548 0,606 0,489 0,743 1,130 0,503 1,367 1,843 0,909 0,637 0,324 0,997 1,845 1,427 1,462 0,62 1,352 1,809 1,279 1,780 0,707 0,240 0,458 0,775 0,722 1,280 0,816 1,778 0,742 0,544 1,20 1,30 0,29 1,951 0,601 0,299 0,856 0,22 1,145 1,263 0,697 0,827 0,972 0,802 1,328 1,45 1,824 0,572 0,518 1,785 1,331 1,412 0,855 0,205 1,897 0,836 0,689 0,951 1,575 0,38 0,892 1,81 0,643 1,554 0,487 1,61 0,509 1,894 1,302 1,624 1,647 1,719 1,63 0,15 1,28 1,646 0,413 1,445 1,733 0,309 1,280 0,789 0,447 0,873 1,670 1,846 1,2 0,587 0,372 0,570 0,243 0,869 0,622 0,41 1,678 0,440 1,839 1,94 1,376 0,431 0,767 1,745 0,88 1,560 0,38 1,716 0,974 1,880 1,137 1,497 0,230 1,101 1,456 1,646 0,453 0,677 1,230 0,784 0,839 1,694 0,67 1,173 1,866 1,498 0,200 0,571 1,397 1,48 0,787 1,102 1,160 1,362 1,687 1,361 1,998 1,215 1,243 1,333 0,3 1,409 1,336 0,739 0,162 0,557 0,609 0,140 1,483 0,575 0,603 1,773 1,837 1,264 1,428 1,663 0,391 0,129 0,550 0,763 1,292 0,780 1,715 0,919 0,533 1,449 1,797 0,490 0,728 0,923 0,562 1,586 1,134 0,284 1,459 0,321 0,383 1,871 0,73 1,636 0,483 0,568 0,260 0,595 0,289 0,131 0,924 0,861 0,324 1,291 0,136 1,402 0,130 0,443 0,560 1,608 0,32 0,530 0,562 0,998 1,469 1,524 0,111 0,221 0,414 0,904 1,777 0,300 1,798 0,284 0,402 1,188 1,43 0,533 0,292 1,298 0,918 0,512 0,399 0,485 1,0 1,319 0,946 1,811 0,489 1,396 1,753 1,192 1,625 0,2 0,661 0,221 1,249 0,312 1,273 0,8 1,611 0,559 1,767 1,743 0,982 0,415 1,890 0,746 0,963 1,744 1,577 1,256 1,122 1,772 0,909 0,801 1,345 1,37 1,21 1,967 0,532 0,80 1,980 0,509 1,425 1,948 0,131 0,846 0,890 0,339 1,714 1,122 1,214 0,777 0,908 0,464 1,903 0,695 0,392 0,937 1,437 1,344 0,843 1,736 1,932 0,2 1,803 1,209 0,178 0,898 0,313 0,518 1,947 0,976 0,4 1,7 1,85 1,698 1,930 0,253 0,852 1,532 1,497 1,392 1,600 1,343 0,901 0,555 1,368 0,258 1,689 0,744 1,159 0,204 1,239 0,164 1,465 1,496 1,262 1,539 1,65 0,444 0,282 1,603 1,12 0,293 1,5 0,560 0,733 0,224 0,675 1,731 0,66 1,745 0,828 0,532 1,762 0,898 0,827 1,421 1,619 1,745 0,982 1,159 1,82 1,600 0,602 0,329 0,650 0,767 0,347 0,7 1,267 0,472 0,122 0,611 0,262 0,780 1,887 0,575 0,752 1,491 0,130 1,510 1,132 0,698 1,943 0,317 0,875 1,135 0,872 0,725 0,336 0,542 0,189 0,873 1,362 1,668 0,399 0,942 0,949 1,685 1,875 0,929 0,601 1,577 0,668 1,269 1,523 0,699 0,981 1,132 0,22 0,976 1,231 0,432 1,115 0,923 1,150 1,143 0,41 1,882 1,461 0,745 0,691 1,129 0,565 0,983 0,716 1,903 1,52 0,337 1,370 1,624 0,964 1,33 1,383 0,195 1,117 1,244 0,811 1,237 1,40 0,667 0,149 1,0 0,819 1,152 1,588 0,812 1,918 1,264 1,120 1,338 1,736 0,827 1,334 0,70 0,91 1,537 1,311 1,777 1,263 1,990 0,117 0,655 1,951 0,119 1,993 1,921 1,136 0,872 1,621 1,129 1,969 1,828 1,929 1,444 1,874 1,521 0,730 1,518 1,486 0,497 1,903 1,501 1,37 0,748 1,652 1,233 0,386 0,13 0,600 0,772 0,566 1,293 0,572 0,552 0,556 0,822 1,370 1,146 0,775 1,292 1,354 0,246 1,989 0,818 0,830 1,768 0,502 1,582 0,819 0,276 1,505 0,817 0,2 0,834 1,99 1,995 0,199 0,199 0,995 0,291 1,463 0,26 0,704 0,532 0,947 1,445 0,17 1,479 1,700 1,481 0,82 0,163 1,593 0,954 0,998 0,121 1,589 1,412 0,572 0,355 1,493 0,674 1,431 0,994 1,921 1,284 0,105 0,540 0,833 1,511 1,887 1,510 0,471 1,495 1,372 0,928 1,676 1,329 1,981 1,760 0,663 1,177 0,6 1,422 0,645 0,390 1,575 1,568 0,792 1,543 0,231 1,812 1,592 1,280 1,38 0,470 1,228 0,279 0,603 1,108 0,418 1,277 0,798 1,286 0,869 1,576 1,857 0,400 0,891 1,204 1,413 1,739 1,947 0,123 0,667 0,831 1,57 1,719 0,115 0,976 1,899 1,288 1,83 1,190 1,77 0,449 1,236 0,447 0,459 1,440 1,619 0,668 0,679 1,790 0,542 1,372 0,655 1,481 1,977 1,657 1,863 1,332 0,732 1,521 0,726 1,32 0,409 0,703 1,421 0,69 1,181 0,348 0,875 1,487 0,374 0,316 0,644 0,8 1,904 1,297 0,322 0,954 0,772 1,127 1,957 0,933 1,792 1,241 1,356 0,747 1,708 0,98 0,897 0,446 0,803 0,681 0,512 0,30 1,157 0,594 1,398 1,768 1,717 0,586 1,704 0,27 1,598 1,499 0,508 1,272 0,670 0,577 1,171 1,701 1,560 1,441 0,734 0,419 0,283 0,987 1,898 1,219 1,778 1,103 1,249 1,301 1,86 0,816 0,783 0,290 0,990 1,547 1,151 1,508 1,90 0,795 1,804 0,489 0,766 1,538 0,725 1,970 0,125 1,189 1,958 1,990 0,500 1,715 0,855 0,987 1,551 0,794 0,583 0,598 0,594 0,109 1,509 0,826 0,696 1,884 0,690 1,993 1,993 1,459 1,401 1,204 1,297 1,64 1,100 1,985 1,340 1,985 0,60 1,251 0,510 0,152 1,579 0,523 1,458 1,840 1,894 1,435 1,736 0,545 0,214 1,660 1,559 0,102 1,462 1,103 1,404 1,719 1,518 1,853 1,638 0,646 1,205 0,561 0,34 1,684 0,929 1,974 0,290 1,381 0,598 0,35 1,615 1,400 1,256 1,469 0,69 1,323 1,133 0,660 1,616 1,904 0,877 0,674 0,810 1,250 0,224 1,538 0,654 1,721 0,470 0,353 0,300 1,558 0,261 1,109 0,128 1,298 1,619 1,114 1,226 0,333 0,750 0,706 0,700 0,526 1,281 1,713 1,359 1,476 1,689 1,906 0,990 1,200 1,281 0,145 1,604 0,788 0,68 1,331 0,15 1,610 0,143 1,744 1,652 1,607 1,953 0,494 0,711 1,905 0,496 1,656 1,942 1,347 1,830 0,873 1,282 1,845 1,390 0,130 1,629 0,395 0,663 0,771 1,34 1,830 0,9 0,944 1,531 1,41 0,76 0,743 1,784 0,655 0,538 0,831 1,905 1,310 0,438 0,723 1,476 0,414 1,724 1,999 1,720 1,950 0,799 1,777 0,47 1,696 1,435 0,874 0,971 1,29 1,692 0,901 0,99 0,849 0,191 1,571 1,131 1,750 0,288 1,815 1,35 0,621 1,135 1,834 0,751 1,161 1,278 0,988 1,840 1,554 0,914 0,815 1,504 0,874 0,369 1,71 1,989 1,725 0,625 1,189 0,728 1,996 0,338 1,829 1,910 1,562 1,383 0,914 0,849 1,108 1,265 0,60 1,508 1,444 0,758 1,228 0,573 0,465 1,917 1,349 1,265 0,575 1,98 1,641 1,780 1,763 1,639 1,565 1,667 0,783 0,576 1,311 1,424 0,464 0,700 0,895 0,977 1,227 0,884 1,589 1,799 1,1 1,56 1,616 1,144 1,77 1,386 1,695 1,13 0,442 1,77 0,292 1,694 0,708 1,569 0,946 1,854 1,188 1,599 1,828 1,529 1,136 0,698 0,719 0,177 0,731 1,540 1,376 1,758 1,131 0,148 1,722 1,687 0,744 1,198 0,687 0,31 0,340 0,544 0,257 0,451 1,530 1,482 1,796 1,481 1,997 0,185 0,988 0,670 1,512 0,825 0,180 1,269 0,296 1,962 0,258 0,519 0,890 1,707 1,602 1,744 1,180 1,763 0,207 0,997 0,192 1,315 1,421 0,683 1,952 1,18 1,688 1,608 0,664 0,428 0,140 0,348 0,403 1,682 1,708 0,652 0,242 0,567 1,520 0,546 0,19 0,43 0,483 0,666 1,834 0,264 0,883 0,724 1,581 0,835 0,581 0,999 0,675 0,472 1,705 1,4 0,654 0,98 0,654 1,232 1,964 1,716 1,776 0,505 0,364 1,176 0,281 0,418 0,87 1,124 0,428 1,945 1,871 1,723 1,521 0,839 1,652 0,707 0,722 1,463 0,835 0,232 1,3 1,626 1,102 0,534 0,841 1,893 1,94 1,903 0,97 0,390 1,317 1,248 0,674 0,601 1,218 1,994 1,416 1,267 1,294 1,572 0,774 1,117 0,105 1,177 0,379 0,263 0,853 1,340 0,288 1,819 0,378 1,55 1,476 0,616 0,55 1,48 0,275 1,835 1,530 0,106 1,276 1,324 0,981 1,658 0,199 1,283 0,723 1,646 1,618 0,703 1,586 0,138 0,476 1,832 0,113 1,343 0,781 1,736 0,664 1,360 1,741 1,891 1,276 1,711 1,713 0,569 0,467 1,50 1,537 0,436 0,42 1,139 0,200 0,922 0,347 1,139 1,336 1,626 0,420 1,530 0,97 1,279 1,407 0,488 0,751 0,968 1,603 1,129 0,789 0,267 1,155 1,43 1,818 0,594 1,80 1,134 1,329 0,81 0,803 1,8 1,112 0,426 0,837 0,456 0,306 1,349 0,49 0,859 1,768 1,417 1,711 1,618 1,13 0,349 1,826 1,530 0,879 0,706 1,179 0,857 0,682 1,160 0,901 0,20 1,626 1,796 1,925 0,892 0,14 1,22 0,669 0,730 0,527 1,725 0,337 0,118 0,897 0,75 1,596 0,84 0,591 1,806 0,381 1,731 0,259 0,622 1,524 1,70 0,965 1,0 1,765 0,625 1,707 1,687 0,249 0,386 0,754 0,678 1,145 1,483 1,958 0,908 0,185 1,99 0,254 0,959 0,908 1,110 1,689 0,228 1,968 0,653 1,486 1,478 0,39 1,886 1,5 0,460 1,989 1,482 0,620 1,317 1,459 0,209 0,311 1,299 1,337 1,952 1,290 1,124 1,411 0,736 0,736 1,96 0,487 0,921 1,321 0,989 1,945 1,671 0,785 1,89 1,964 1,672 0,876 0,457 0,845 1,44 1,327 0,964 1,648 1,144 0,881 0,419 0,221 0,376 0,523 0,192 1,129 1,940 0,551 1,48 0,261 0,52 1,124 0,975 1,523 0,125 1,453 0,993 1,510 0,462 0,737 1,528 0,422 1,645 0,261 1,478 1,273 1,645 1,702 0,767 1,135 1,383 0,752 0,94 0,13 1,379 0,93 0,984 1,941 1,233 1,954 1,347 0,684 0,938 1,143 1,893 1,250 1,471 0,149 1,346 0,96 1,398 0,698 1,341 0,343 1,948 1,499 0,469 0,366 0,775 1,656 1,150 0,680 0,382 0,55 0,518 1,951 1,88 0,896 1,339 1,854 1,284 0,808 1,589 1,408 0,747 0,67 0,17 1,660 1,585 1,380 0,729 1,490 1,507 0,803 0,939 0,995 1,398 1,858 1,13 0,19 1,859 0,673 0,58 0,848 1,844 1,159 0,334 1,250 1,121 1,706 0,199 1,812 1,106 1,211 1,744 0,669 0,171 1,551 0,951 0,164 0,632 1,83 1,939 1,720 1,309 1,241 0,752 1,429 0,677 0,850 0,154 1,135 1,140 0,729 0,106 1,134 0,958 0,504 1,933 0,685 1,757 1,646 1,756 0,804 0,1 1,208 1,304 1,292 0,358 0,3 0,139 0,760 0,408 1,152 1,677 1,459 1,71 1,462 0,442 0,597 0,652 0,819 1,264 0,36 0,365 0,297 0,67 0,694 0,796 0,847 0,152 1,882 0,199 0,851 1,49 0,101 1,804 0,88 0,318 0,683 0,396 0,230 0,327 0,106 0,131 1,299 1,934 0,51 0,728 1,532 0,650 0,789 0,112 0,241 0,134 0,263 0,716 0,932 0,1 1,842 0,327 0,248 0,376 1,27 0,234 1,604 0,277 0,470 1,146 1,595 1,801 1,11 0,501 0,524 0,179 0,192 0,829 1,363 1,598 1,569 0,656 1,292 0,59 0,494 0,191 1,436 1,552 0,688 1,111 0,382 0,217 0,287 1,375 0,533 1,258 0,958 1,915 1,198 0,291 0,475 0,219 0,599 0,189 1,403 1,114 1,633 0,247 0,360 0,601 1,44 1,619 0,865 0,381 1,318 1,663 0,158 1,185 1,119 1,452 1,785 1,113 0,628 1,379 0,147 1,86 1,518 1,95 1,323 0,72 1,752 1,835 1,253 0,660 1,257 0,478 1,403 1,999 1,749 1,681 1,688 1,285 1,391 0,231 0,606 1,914 1,682 0,723 1,794 1,51 0,940 1,924 1,196 1,599 0,525 0,841 0,256 0,777 0,841 1,724 0,949 0,5 1,419 1,959 0,708 0,899 0,183 1,618 1,64 1,626 1,438 0,644 1,364 0,806 0,792 1,401 1,182 1,332 1,377 1,886 1,634 1,418 1,570 1,889 0,91 1,284 1,891 0,190 0,733 1,222 0,731 1,988 1,172 1,975 0,281 1,105 1,8 1,667 1,621 0,590 0,261 0,190 1,738 0,252 1,917 0,524 0,955 0,195 1,311 1,933 1,428 1,963 1,477 0,759 0,95 0,625 1,335 1,183 1,468 1,335 1,740 0,573 1,736 0,345 0,328 0,111 0,555 1,35 0,894 1,691 1,882 1,890 1,227 1,541 1,6 1,203 1,779 0,75 1,384 1,131 0,899 1,72 0,577 1,268 1,670 1,785 0,894 0,928 1,508 0,553 1,358 1,367 0,278 1,70 1,439 0,311 1,10 0,560 1,770 1,414 0,535 0,350 0,1 1,308 1,556 0,360 1,201 1,991 1,579 1,284 1,530 0,260 0,620 1,258 1,511 1,236 1,687 0,149 0,195 0,767 0,570 0,830 1,952 0,248 0,387 1,144 0,810 1,859 1,30 0,662 0,512 0,860 1,463 1,80 1,467 1,528 0,915 0,560 0,257 0,236 0,749 0,371 1,88 1,645 1,301 0,601 0,882 0,466 1,707 1,941 1,908 1,355 1,111 1,758 0,849 1,554 1,77 0,168 1,67 1,509 0,506 0,641 1,418 0,500 0,762 0,133 0,615 1,209 1,571 0,254 0,465 1,161 1,896 1,374 0,639 1,599 1,62 1,870 1,915 1,190 1,393 0,630 0,980 0,260 1,359 1,572 1,320 1,253 0,903 0,115 1,707 0,364 1,801 1,587 0,914 1,938 0,308 1,608 1,437 0,561 1,582 0,519 1,295 0,572 0,969 0,647 0,857 1,15 0,504 0,167 0,282 0,901 0,215 0,361 1,101 0,699 1,414 1,945 0,960 0,195 0,838 1,513 1,277 1,755 0,646 1,745 1,29 1,606 1,356 1,659 1,94 1,854 1,678 0,815 0,447 1,880 1,452 1,824 0,528 0,586 1,957 1,291 1,846 1,170 1,320 1,885 1,489 1,11 1,414 0,610 0,386 1,377 1,80 1,570 0,411 0,702 0,899 0,361 1,56 0,187 1,144 0,518 1,247 0,669 0,201 0,478 1,341 1,568 0,152 1,217 1,789 0,411 0,106 0,975 1,43 0,953 1,235 0,816 0,442 0,794 1,227 0,289 0,731 1,644 1,89 0,691 1,245 0,566 1,898 0,915 1,990 1,346 1,65 0,93 1,201 0,689 0,543 1,674 1,658 1,968 1,658 1,64 1,851 0,910 0,789 1,393 1,363 1,85 1,870 1,182 0,876 0,475 0,104 1,692 1,562 0,188 1,951 1,464 1,824 1,518 1,995 0,331 1,939 1,565 1,821 1,291 1,407 1,579 1,837 0,269 1,928 1,293 0,743 1,771 0,754 0,220 0,687 0,851 0,209 1,980 1,69 0,163 1,18 1,909 0,942 0,104 0,222 0,402 0,232 0,528 1,417 0,353 0,887 0,349 1,809 0,229 0,590 0,636 0,563 0,832 0,357 1,668 0,383 0,79 1,827 0,162 0,993 0,696 0,790 1,637 0,370 1,252 1,936 1,138 1,943 1,941 1,353 0,395 0,612 1,168 0,678 1,778 1,705 1,212 1,859 0,449 1,569 1,861 1,947 0,51 1,828 0,194 1,948 0,753 0,837 0,292 1,503 1,842 0,815 0,910 1,289 0,117 0,914 1,618 1,906 1,846 0,835 1,381 1,387 1,734 1,476 1,480 0,290 1,286 1,572 1,646 1,279 1,710 0,651 0,409 1,5 0,544 1,173 0,744 1,91 1,843 1,428 1,166 0,480 0,615 1,340 0,341 0,917 0,946 1,797 1,530 1,394 1,519 1,323 0,200 1,952 0,16 1,903 1,871 1,888 1,903 0,379 0,913 0,542 1,314 1,855 1,361 0,272 1,918 1,225 1,779 0,689 1,732 1,170 1,558 0,634 0,245 0,637 1,297 1,674 0,537 1,517 1,597 1,410 1,573 1,687 1,507 1,976 1,534 0,853 0,552 0,91 0,207 1,395 0,272 0,441 1,792 1,397 1,592 1,244 1,57 1,295 0,232 1,422 1,512 1,833 1,346 0,633 1,10 0,990 0,283 1,506 1,250 1,685 0,193 0,552 0,801 0,618 1,754 1,579 1,185 1,927 1,65 1,701 0,787 0,966 0,41 1,627 0,996 1,934 1,75 0,580 0,770 1,455 0,469 1,665 1,786 0,296 1,467 1,899 0,367 0,662 1,190 0,728 1,1 0,489 1,637 1,287 0,426 1,654 0,262 1,154 0,409 0,939 1,456 0,367 0,91 1,534 1,69 1,439 1,83 0,23 1,855 0,35 0,107 1,418 0,71 0,502 0,729 1,560 0,270 0,973 0,432 0,405 0,996 1,793 1,230 0,836 1,646 1,362 1,177 1,870 1,408 0,920 1,553 1,431 1,879 1,333 0,43 0,446 0,584 0,166 0,15 1,448 1,792 1,301 1,221 1,837 1,275 0,781 1,408 1,843 0,240 1,945 0,930 0,865 1,845 0,360 0,899 1,106 1,546 0,123 1,803 0,406 0,327 1,543 1,998 1,946 0,242 0,947 0,205 0,79 1,473 1,73 0,319 0,93 1,240 0,376 1,354 1,765 0,338 1,365 1,474 0,931 0,320 1,698 1,990 1,595 1,826 1,110 1,761 1,580 1,6 0,806 1,95 1,293 1,87 0,775 1,480 0,392 0,346 1,271 1,480 1,83 1,993 1,189 0,813 1,686 1,229 1,561 1,557 0,796 1,587 0,843 1,131 1,268 0,684 0,828 1,709 1,528 1,119 0,679 0,235 0,686 1,142 1,2 1,773 1,582 1,378 0,374 1,259 0,851 1,495 1,636 0,558 0,434 0,205 0,379 0,826 1,751 1,274 1,270 0,789 1,498 1,587 0,837 1,329 1,334 0,301 1,899 0,446 0,101 0,200 0,899 1,430 1,752 1,428 1,60 0,745 0,648 1,440 1,448 0,525 1,28 1,785 1,230 1,310 1,773 0,393 1,9 1,996 0,395 1,173 1,538 0,534 0,758 0,702 1,478 0,810 0,799 0,742 1,978 1,404 0,400 1,941 1,978 0,984 1,718 1,184 1,41 1,555 0,528 0,632 0,759 1,176 1,314 1,955 1,186 0,334 0,210 0,750 0,103 1,888 1,66 1,622 1,700 0,358 0,156 0,127 0,999 1,725 1,849 1,478 1,368 1,544 1,957 0,351 1,787 1,765 0,87 1,539 1,141 0,83 1,765 0,148 1,685 0,639 0,649 1,41 0,862 1,987 1,390 1,359 0,951 1,814 1,663 1,738 0,167 0,415 0,519 0,983 0,87 1,99 1,766 1,488 0,931 1,724 0,480 1,551 0,502 0,455 0,984 0,131 0,875 1,970 0,569 0,227 1,520 1,388 1,715 1,9 1,467 0,231 0,563 0,832 0,549 0,332 0,410 1,661 1,606 1,755 1,679 0,981 1,65 1,429 0,75 1,117 1,33 0,854 0,346 0,273 0,647 0,569 1,561 0,280 1,573 0,46 1,272 1,313 0,440 1,713 0,768 0,177 1,30 0,704 0,900 1,143 0,342 1,33 1,215 0,483 0,663 0,846 0,664 0,370 0,154 0,953 0,615 0,472 1,220 0,907 1,4 1,167 0,354 1,185 1,195 0,433 1,260 1,715 0,83 1,896 1,149 0,589 0,440 0,799 1,174 1,446 1,685 1,254 0,94 0,808 0,647 0,288 0,378 1,953 0,858 1,643 0,992 1,481 0,217 0,865 0,953 1,127 0,615 1,939 0,410 1,853 0,80 0,424 0,538 1,977 1,259 1,875 0,64 1,429 0,534 1,96 0,262 0,133 0,711 1,53 0,823 0,621 1,801 1,115 0,72 0,554 0,411 0,488 0,815 1,861 1,188 0,949 1,583 0,972 0,470 1,239 0,44 0,325 0,958 0,718 0,593 0,204 1,527 1,151 1,218 1,343 0,819 0,756 0,990 1,767 0,170 0,691 1,999 1,346 1,468 0,553 0,404 0,486 1,38 0,338 0,364 1,859 0,275 1,334 0,75 1,993 0,720 0,334 0,568 0,312 0,717 0,952 0,897 0,568 1,336 1,581 1,650 0,53 0,600 1,422 0,342 1,562 0,415 0,632 1,585 1,338 0,604 0,981 1,566 0,63 1,607 0,91 1,566 0,538 0,288 0,484 1,227 0,780 0,19 0,440 1,44 0,550 1,480 1,301 0,751 0,369 0,897 1,601 0,34 0,257 1,729 1,188 1,944 0,954 1,509 1,754 0,98 0,711 0,670 0,558 1,885 1,437 1,912 1,175 0,677 0,762 1,493 0,165 0,875 1,357 1,985 1,669 0,707 0,125 1,722 1,436 0,136 1,326 1,984 1,417 1,547 0,212 0,806 0,726 1,720 0,88 0,569 0,493 0,977 1,959 1,565 0,925 1,326 0,881 0,559 1,900 0,416 1,264 1,723 0,575 1,255 0,162 1,494 0,315 0,744 0,801 1,943 0,543 1,503 0,605 1,775 0,123 1,497 0,629 1,509 1,191 0,496 1,372 0,872 0,404 0,300 0,363 0,384 0,602 0,799 1,785 0,293 0,263 1,883 0,917 1,460 1,715 0,924 0,648 1,172 1,116 1,274 1,379 1,66 1,218 0,128 0,607 0,780 1,346 1,598 1,898 0,411 0,976 0,799 1,895 1,424 0,666 1,457 1,307 0,50 1,878 1,994 1,992 0,39 0,286 0,536 1,745 1,785 0,38 1,248 0,273 0,857 0,867 0,846 0,899 1,281 1,462 1,556 0,455 0,173 1,359 1,18 0,790 1,522 1,569 1,504 0,118 0,651 0,158 1,191 0,299 0,281 1,82 0,707 0,311 1,92 1,502 0,186 0,10 1,618 1,899 1,344 0,679 0,638 0,294 0,208 0,987 0,503 1,209 1,879 0,5 0,763 1,152 1,422 1,276 1,433 0,636 0,738 1,909 1,65 1,164 0,978 0,406 0,659 0,952 0,545 1,469 0,202 0,348 1,266 0,946 1,488 0,931 1,315 0,483 1,891 1,474 0,507 0,12 0,600 1,74 1,17 0,963 0,89 0,185 1,249 1,728 0,578 0,766 0,97 1,98 0,575 0,892 0,958 1,648 0,522 0,922 0,380 1,574 0,748 0,715 1,837 0,944 0,462 1,195 0,674 0,374 0,496 0,883 1,961 1,88 1,543 0,378 1,83 0,139 1,948 1,652 0,294 1,940 1,429 1,50 1,931 1,688 0,210 0,111 1,828 0,211 0,141 0,423 1,522 0,463 0,572 1,509 1,765 0,844 0,47 0,266 1,866 1,584 0,232 1,331 0,138 1,646 1,127 1,134 1,364 1,380 1,981 1,401 0,22 1,975 0,410 0,234 1,882 0,525 0,836 1,523 1,736 0,646 0,281 1,980 0,926 1,961 1,2 0,344 1,379 0,442 1,768 0,470 1,101 1,917 1,971 0,816 1,222 0,565 0,94 0,83 0,98 1,581 1,321 1,926 0,472 1,947 0,745 0,96 1,755 0,354 1,948 1,161 0,860 0,367 1,334 1,641 1,19 0,154 0,274 1,508 1,808 1,956 1,609 1,925 0,396 1,956 0,808 0,614 0,927 1,965 0,324 1,928 1,19 0,571 1,44 1,572 1,318 0,765 0,48 0,882 1,643 1,288 0,624 0,142 1,648 1,807 0,762 0,564 1,336 1,869 1,242 0,483 1,107 0,355 1,533 1,393 0,376 1,555 1,536 1,954 0,871 0,230 0,321 0,971 1,386 0,946 1,544 0,863 1,438 0,793 0,827 0,582 1,545 1,596 0,574 1,605 0,340 1,767 0,730 0,17 0,96 0,583 1,28 1,963 1,217 0,803 1,998 1,568 0,411 0,519 0,4 1,217 0,221 0,935 1,837 1,408 1,646 0,998 1,185 1,460 1,628 0,230 1,427 0,108 0,188 0,964 0,413 0,541 1,763 1,228 1,892 1,175 0,83 0,767 0,231 1,929 0,946 0,465 0,715 1,711 0,792 1,827 0,47 1,668 1,266 0,986 0,335 1,705 1,794 1,105 0,73 1,581 1,764 1,929 1,229 0,753 0,321 1,737 1,79 1,722 0,463 0,49 1,577 1,135 1,123 1,824 1,378 1,413 1,727 0,949 0,58 0,71 0,697 0,824 0,33 1,620 1,155 1,758 0,496 1,665 0,239 0,78 1,153 0,855 0,259 0,161 1,520 0,989 1,111 1,795 0,156 1,937 1,213 0,304 1,187 0,947 1,23 1,453 0,8 1,285 1,907 1,820 1,997 0,375 1,3 1,458 0,587 1,116 0,876 1,282 1,399 1,939 1,430 1,345 0,961 0,589 1,963 1,252 0,227 0,488 0,646 0,827 0,803 0,446 1,228 1,194 1,175 1,72 1,194 0,618 1,124 0,922 1,974 1,466 1,45 1,821 0,186 0,461 0,530 1,572 0,665 0,521 0,360 0,63 0,97 0,230 1,436 0,798 1,244 0,27 0,62 0,570 1,349 1,852 0,928 1,935 0,424 1,127 0,36 0,981 1,537 1,424 0,566 1,734 0,162 0,321 1,409 1,783 0,168 0,920 1,30 1,630 1,750 1,194 0,540 0,531 0,489 0,947 0,665 0,906 1,954 1,516 0,651 1,868 0,578 1,608 0,39 0,247 1,588 1,533 1,42 1,280 0,419 1,738 0,339 0,125 0,830 0,603 0,197 1,448 1,907 1,739 1,35 0,587 1,213 1,159 1,69 1,75 1,571 1,297 1,742 1,594 1,648 1,711 1,646 1,820 1,254 0,139 1,477 0,101 0,676 1,905 1,888 0,395 1,32 1,338 0,292 0,904 0,880 1,934 1,961 1,399 0,774 0,378 0,959 1,325 1,537 0,34 1,329 0,138 1,763 1,108 1,658 1,310 0,402 0,0 1,215 0,734 0,790 1,176 1,419 0,331 0,550 0,138 0,118 0,70 0,743 0,617 0,257 0,873 1,345 0,661 1,863 0,555 1,679 0,809 1,796 1,5 0,934 1,520 0,64 1,457 0,242 1,630 0,502 1,416 1,797 1,26 0,279 1,562 1,122 1,834 1,565 1,764 1,556 0,28 0,787 1,554 0,695 1,659 0,946 1,73 1,352 0,881 0,893 0,439 1,141 0,666 0,528 1,814 1,932 1,292 0,982 0,498 1,995 1,588 1,663 0,657 0,568 0,231 1,291 0,763 0,832 0,944 0,81 1,70 0,562 0,692 0,739 0,18 1,928 1,960 1,186 0,675 0,128 1,519 1,604 1,774 0,482 0,606 0,521 0,155 0,498 1,184 1,441 0,526 0,859 1,307 0,901 0,376 1,487 0,783 0,192 1,171 0,522 1,749 0,848 1,179 1,389 1,442 1,878 1,432 1,212 0,816 1,11 0,675 0,398 0,528 0,740 0,936 0,163 1,837 1,361 0,207 1,838 1,742 0,609 1,640 1,969 0,610 0,62 1,700 1,148 1,869 1,412 0,136 0,237 0,668 1,519 0,499 1,736 0,422 1,935 1,661 1,749 0,436 0,519 1,80 1,158 0,216 1,152 0,429 0,911 0,780 0,915 0,509 1,423 1,616 1,205 1,820 1,68 1,886 1,372 1,536 1,847 1,727 0,979 1,617 0,459 1,45 1,463 1,180 0,545 1,740 0,243 1,45 1,989 1,116 0,652 0,964 0,839 0,897 0,953 1,23 0,530 0,485 1,118 1,866 1,177 0,667 1,912 1,570 0,388 1,819 1,111 0,378 1,145 0,979 1,254 0,893 0,426 0,261 0,926 0,620 0,67 1,584 1,465 0,113 0,632 1,980 1,25 0,939 1,646 0,640 0,387 1,848 0,241 0,775 0,919 1,626 0,946 1,297 0,119 0,694 1,499 0,649 0,217 1,69 1,296 0,69 1,439 0,831 0,304 0,5 0,18 1,426 0,395 0,989 1,717 1,719 0,476 0,503 0,605 0,197 1,972 0,373 0,353 1,276 1,994 0,50 0,970 0,130 0,278 0,367 0,201 0,760 1,770 0,840 0,897 0,463 1,158 0,93 1,676 1,466 0,172 1,964 1,10 1,874 0,778 1,422 1,484 1,101 0,199 1,119 1,870 0,630 1,63 0,391 1,169 0,0 0,833 1,622 0,652 1,57 0,120 1,320 0,263 0,899 1,228 1,358 0,208 0,308 0,600 1,51 0,484 0,570 0,13 1,239 0,690 0,579 0,243 0,263 0,860 1,539 1,970 0,139 0,368 0,247 0,476 0,323 1,806 1,259 1,732 1,676 1,76 0,23 1,9 0,737 0,116 0,487 1,122 0,99 0,543 0,159 1,0 0,493 1,468 1,385 1,468 1,355 0,762 0,373 1,274 1,979 1,299 1,18 0,795 0,512 0,836 0,619 0,399 0,495 0,49 0,498 1,866 0,831 1,476 0,902 1,494 1,642 1,140 1,618 1,367 0,528 0,32 0,846 0,22 0,914 0,878 1,817 0,446 0,257 1,48 0,683 1,951 1,577 1,372 1,455 0,797 0,433 0,36 0,380 1,385 0,228 0,488 0,18 0,21 1,126 0,298 0,235 0,728 1,261 0,858 0,967 0,292 1,318 0,60 1,487 0,139 0,597 1,73 0,841 0,92 1,406 1,761 1,79 0,800 0,938 1,16 0,942 0,928 1,383 1,314 1,886 0,649 0,886 1,424 0,707 0,812 0,583 1,526 1,327 0,487 0,199 0,773 0,372 0,332 0,102 1,325 0,839 0,66 0,549 0,747 1,968 0,583 1,19 1,143 0,188 1,24 1,573 0,379 1,128 1,544 1,555 1,571 1,455 0,266 1,679 1,160 1,10 0,673 0,61 0,335 0,239 0,94 1,100 1,540 0,655 0,408 0,199 1,514 0,927 1,931 1,989 0,828 1,910 1,9 0,627 0,289 1,431 0,565 0,40 1,46 0,750 0,86 1,719 1,49 1,85 1,421 0,887 0,187 0,875 0,541 0,329 0,87 0,987 1,553 0,224 1,139 0,726 1,406 0,966 1,52 1,870 1,656 0,987 0,407 0,198 1,418 1,67 1,468 1,998 0,877 0,238 0,508 0,545 1,948 0,309 0,980 1,173 0,751 0,278 1,911 0,325 1,987 1,714 1,608 1,702 0,788 0,570 1,987 1,693 1,967 1,506 1,464 1,695 0,54 1,771 0,126 1,444 0,984 0,196 1,61 1,98 1,218 1,58 1,910 0,288 0,501 1,253 1,321 0,75 1,134 0,288 1,111 1,601 0,459 1,521 0,397 0,218 1,45 0,941 1,339 1,669 0,798 1,363 1,722 0,887 0,279 0,952 0,722 1,317 0,385 1,210 0,892 1,648 0,92 0,923 1,81 1,259 1,316 0,946 0,558 0,509 0,941 0,986 0,422 1,378 0,600 1,621 0,654 0,362 1,739 1,756 1,867 0,762 1,664 1,105 0,124 1,720 1,136 0,321 1,367 1,440 0,249 1,435 1,169 1,265 0,245 1,682 0,142 0,710 0,0 1,31 1,834 0,796 0,644 1,898 0,304 0,586 0,605 0,553 1,129 1,77 1,807 1,58 0,132 0,1 0,398 1,544 0,750 1,950 0,415 1,447 1,69 0,634 0,5 1,869 1,467 1,661 1,71 1,112 1,972 1,635 0,650 1,300 1,991 0,815 0,567 1,926 0,998 0,401 0,755 1,234 1,837 1,93 0,198 1,678 1,247 1,106 0,994 0,879 0,702 0,131 0,894 0,890 1,426 0,189 0,822 0,251 1,293 1,437 0,261 1,852 0,645 0,602 0,461 1,495 0,581 0,18 0,613 0,979 0,628 0,308 0,506 1,270 0,8 0,184 0,317 1,625 0,666 1,378 0,491 0,305 0,430 1,791 1,260 1,919 1,140 1,399 0,957 0,251 0,569 1,188 1,669 0,677 1,628 1,253 1,26 1,323 1,422 1,418 0,972 0,758 1,727 0,102 0,468 1,842 0,674 1,921 0,393 0,5 1,156 0,524 0,835 0,374 1,657 1,808 1,499 0,304 0,171 1,590 0,158 0,113 0,334 0,690 1,513 1,461 0,223 1,754 1,491 0,665 0,884 0,380 0,48 0,312 1,508 1,495 0,908 0,791 1,308 1,449 0,431 0,981 1,2 0,508 1,740 1,331 1,505 0,61 0,794 0,713 1,450 0,855 1,188 0,339 0,270 0,23 1,716 1,840 0,203 0,759 1,979 0,631 0,551 1,961 0,809 1,635 1,1 1,998 0,927 1,884 0,369 1,915 0,967 1,423 0,698 1,700 0,73 0,734 0,74 1,278 0,450 1,393 1,198 1,759 0,313 0,476 1,919 1,745 1,594 0,728 0,418 0,973 0,542 1,634 0,777 0,460 1,934 1,646 0,780 1,246 1,369 0,513 0,831 1,284 1,515 1,311 0,89 0,285 0,352 1,128 1,81 1,184 0,661 1,246 1,631 0,153 1,514 1,252 0,567 1,578 0,277 1,671 1,31 1,65 1,396 0,428 0,604 0,191 1,688 1,892 0,870 0,250 0,734 0,292 1,311 0,517 0,225 1,111 0,469 1,615 0,445 0,698 1,539 1,365 1,532 0,609 1,555 1,963 1,591 1,739 1,380 1,527 0,650 1,286 1,676 0,111 0,242 0,789 0,708 0,456 1,772 0,592 1,495 0,810 1,508 0,128 0,920 0,831 0,118 0,897 0,289 0,202 0,422 1,288 0,133 0,426 1,291 0,172 0,475 1,788 1,344 0,79 0,923 1,170 1,33 0,2 0,833 1,352 1,821 0,745 0,549 0,997 0,236 1,784 1,530 0,565 1,426 0,408 0,956 0,795 0,196 1,369 1,373 1,374 1,68 1,817 1,892 1,160 1,350 1,483 1,293 1,865 0,518 1,588 1,617 0,605 0,552 1,186 0,295 1,321 0,329 0,726 1,654 1,505 1,915 0,853 1,429 0,963 0,31 0,184 0,83 0,99 0,838 1,705 0,860 1,516 0,243 1,844 1,296 1,930 0,971 1,261 0,86 1,244 1,264 0,591 1,308 0,981 0,19 1,877 0,819 0,464 1,217 1,108 1,847 1,939 0,569 0,154 0,745 0,394 1,518 1,799 0,787 1,455 1,645 1,705 0,901 1,236 1,456 0,155 0,294 1,345 0,121 1,671 0,748 0,101 0,715 0,376 1,168 1,479 0,822 0,47 0,388 1,860 0,875 1,915 0,901 0,183 1,183 0,223 0,57 0,883 1,665 1,536 0,498 1,190 0,862 0,629 1,577 1,854 0,194 0,677 0,994 1,672 1,219 1,321 1,21 1,472 0,887 0,563 1,691 1,128 1,623 1,126 1,769 1,391 0,446 1,649 1,232 0,879 1,132 0,83 1,860 0,139 1,471 0,630 1,620 0,902 1,200 1,836 0,651 0,769 1,713 1,879 0,161 0,501 0,704 1,273 0,240 0,710 0,19 0,486 1,94 0,690 0,482 0,847 0,215 0,121 0,819 0,104 1,447 1,848 1,276 1,432 0,21 1,227 0,970 1,442 1,273 0,267 0,28 1,30 1,874 0,799 0,535 0,148 1,201 0,383 1,649 0,340 0,854 0,764 0,877 0,611 1,280 1,27 1,811 0,118 0,411 1,683 1,867 0,574 0,593 1,393 1,820 0,260 1,321 1,386 0,175 0,713 1,997 0,736 1,294 1,550 1,603 1,521 0,28 1,7 1,943 0,486 1,55 1,154 1,734 1,551 0,592 1,546 1,806 1,929 0,686 1,45 1,970 0,739 0,642 0,671 0,240 1,11 1,334 1,696 0,973 1,2 0,12 0,516 1,7 1,104 0,322 0,829 0,260 0,795 1,237 0,914 0,623 1,844 0,919 0,958 0,645 0,692 1,763 1,217 0,422 1,328 0,539 0,250 1,992 1,347 0,744 0,780 1,416 1,814 1,853 1,118 1,791 1,334 1,982 0,194 0,316 1,643 1,463 0,37 1,718 1,164 1,737 0,474 0,654 1,209 0,681 0,585 0,901 1,127 0,815 1,705 0,177 0,513 1,169 0,607 0,350 1,680 0,138 0,562 0,943 0,437 1,363 1,736 1,923 0,616 0,320 1,541 0,313 0,185 0,302 0,550 1,554 1,726 0,508 1,189 0,692 1,190 1,315 1,315 0,405 0,550 0,708 1,814 0,904 1,954 0,105 1,956 1,122 0,858 0,755 0,847 0,730 1,475 0,467 1,474 0,950 1,797 0,296 0,861 0,170 0,377 0,644 1,642 0,627 0,89 0,340 0,977 1,897 1,458 0,575 0,95 1,329 0,50 0,701 0,959 1,719 0,836 1,140 0,614 0,605 1,99 0,628 0,178 1,634 0,135 0,5 0,96 0,604 0,428 1,744 1,866 0,985 1,440 0,536 1,915 0,416 0,393 0,768 1,788 1,886 1,656 1,333 1,305 0,179 0,361 0,983 1,873 0,533 0,495 1,772 0,997 1,29 0,539 0,819 0,714 1,22 0,642 0,807 1,127 1,274 0,111 1,338 1,645 0,178 1,796 1,172 0,214 0,671 1,539 0,585 1,708 0,906 1,251 0,451 0,197 1,539 0,745 0,227 1,619 0,939 0,555 1,180 1,365 1,785 1,353 0,462 1,744 1,843 0,500 0,553 0,580 1,954 0,465 0,101 0,975 0,27 0,692 1,982 1,688 0,27 1,981 1,537 0,692 1,465 0,639 1,276 0,508 0,394 1,588 0,335 0,795 0,584 0,135 1,904 1,525 0,549 1,553 1,21 0,588 1,145 1,194 1,441 0,601 1,582 0,214 1,45 1,820 0,550 0,737 0,62 1,485 0,918 1,588 0,757 1,902 1,576 1,302 0,564 1,348 0,763 0,856 0,441 1,251 1,45 0,429 0,407 0,909 1,520 1,860 1,567 0,948 1,840 1,8 0,503 1,819 0,225 1,47 0,275 0,649 1,758 0,338 0,647 1,612 1,623 0,884 0,706 1,603 1,271 0,5 1,147 0,991 0,447 0,920 0,618 1,853 1,660 1,481 0,322 0,754 0,645 0,295 0,171 1,671 0,655 0,524 0,484 1,814 1,710 0,841 0,822 1,257 1,147 1,332 0,587 0,324 1,737 0,252 0,612 0,579 1,5 0,93 0,59 1,931 1,751 1,378 0,43 1,85 0,279 0,664 0,681 1,293 0,569 1,586 1,980 0,118 0,662 1,427 1,530 0,515 1,343 0,697 0,162 0,185 0,211 1,452 1,767 1,634 0,266 0,444 0,858 1,157 1,16 1,438 0,999 0,227 1,591 0,650 1,602 1,56 0,236 0,73 0,439 0,850 1,734 0,427 0,459 1,828 1,591 0,325 1,535 0,835 0,131 1,453 0,809 1,740 1,24 0,729 1,966 1,293 0,344 1,668 1,890 1,861 1,167 1,402 0,317 1,694 1,46 1,889 0,219 0,133 1,631 0,605 1,988 0,47 0,389 1,254 0,776 1,127 0,874 0,371 0,355 1,871 0,466 0,707 1,880 0,307 1,862 1,387 1,209 1,899 1,400 1,905 0,23 1,448 1,604 0,744 0,945 0,239 1,489 0,843 0,456 1,864 0,9 1,677 0,620 0,57 0,231 1,575 1,471 1,201 0,992 0,275 0,803 1,216 0,826 1,253 1,955 0,435 0,563 0,49 0,663 0,645 1,687 1,727 1,272 1,913 1,609 0,204 1,980 1,688 1,16 0,722 1,691 0,343 0,459 0,573 1,46 1,89 1,977 0,598 0,377 1,168 0,663 1,388 0,107 0,560 0,817 1,706 1,725 1,197 0,377 0,625 1,452 1,350 1,598 1,330 0,387 0,896 0,638 1,325 1,954 0,154 0,19 1,70 1,331 1,182 1,639 0,831 0,151 1,311 0,187 1,925 1,565 0,180 0,860 0,287 1,141 1,761 1,181 0,93 1,304 0,782 0,997 1,922 1,832 1,5 1,238 1,703 0,213 1,779 1,320 0,743 0,206 0,30 0,126 1,887 1,759 0,918 1,539 0,401 1,294 0,657 0,76 0,951 0,935 1,424 0,759 1,783 0,343 1,877 1,300 0,688 0,206 1,145 0,594 0,979 0,896 1,745 0,490 1,390 1,510 1,62 0,963 0,159 0,196 1,712 1,477 1,665 0,44 1,121 0,152 0,544 0,825 0,164 0,338 0,36 0,285 0,581 1,497 0,7 1,784 0,454 0,176 1,598 1,396 0,198 1,619 1,956 1,367 0,91 0,565 1,985 0,552 1,347 1,197 1,330 1,498 0,919 1,493 0,138 0,413 1,812 1,257 0,932 1,165 1,543 1,217 0,518 0,669 1,937 0,647 1,59 0,778 1,915 0,854 1,713 0,332 0,320 1,595 0,590 1,882 0,247 0,173 0,673 1,561 0,21 0,24 1,61 0,183 0,963 0,477 1,719 1,938 0,749 1,511 1,886 1,168 0,344 0,275 1,835 0,146 1,389 0,878 0,647 0,183 0,753 1,924 1,739 0,40 0,730 1,677 1,766 0,862 0,447 1,429 1,167 1,502 0,377 1,904 0,326 1,996 1,617 0,803 0,954 0,301 1,424 0,459 1,267 0,438 0,471 0,254 1,372 1,651 0,610 0,435 0,462 0,190 1,542 1,436 0,695 1,158 1,920 1,411 1,726 1,196 1,76 0,352 0,657 1,752 1,77 1,210 1,531 1,119 0,12 1,802 1,740 1,808 0,586 0,463 1,476 1,582 1,763 0,449 0,320 0,262 0,311 1,338 1,795 1,957 0,543 1,46 0,690 1,899 1,925 1,555 1,269 0,60 1,575 0,310 1,156 0,0 1,371 1,187 1,166 0,462 1,731 1,511 0,961 0,906 1,461 0,774 0,388 1,716 1,127 1,307 0,537 0,478 0,5 1,257 0,106 1,326 1,860 1,731 1,784 1,348 1,422 1,616 1,820 1,510 1,359 1,426 1,510 0,516 1,333 1,299 1,905 1,832 0,651 0,254 1,166 0,64 0,138 0,696 0,918 0,209 1,38 1,574 1,613 1,506 1,240 1,29 1,661 1,949 1,207 0,114 1,621 1,160 1,532 0,986 1,714 1,569 0,134 0,560 0,416 1,152 1,974 0,747 1,523 0,659 1,259 0,614 1,337 0,279 1,427 1,308 1,328 1,685 0,596 0,583 0,157 1,94 1,607 1,548 1,88 1,757 0,453 1,378 0,707 1,249 1,956 1,450 0,941 0,966 0,771 1,436 1,314 0,568 0,306 1,501 0,62 1,212 1,639 0,796 1,87 1,282 0,507 0,237 1,777 1,769 0,58 1,715 1,916 0,104 0,538 1,314 0,328 0,24 0,441 1,570 0,916 1,126 0,499 1,853 0,645 1,572 1,594 1,943 1,654 0,519 0,721 1,837 1,985 1,197 0,624 1,175 0,373 1,1 0,939 0,787 1,267 0,546 0,364 1,929 1,2 1,687 0,664 1,323 0,117 0,702 1,231 1,638 0,701 0,709 1,861 0,112 0,160 0,12 0,268 1,463 0,14 0,37 1,327 0,963 1,270 0,0 1,8 0,939 0,150 1,611 1,484 0,734 0,611 0,809 0,539 0,817 1,304 0,236 0,976 0,860 0,979 0,262 0,52 1,737 0,560 0,265 1,523 1,660 1,567 0,164 1,520 1,612 0,129 0,175 0,808 0,18 1,523 0,82 1,161 0,980 1,443 1,52 1,390 0,242 0,480 0,440 0,981 1,433 0,717 1,992 1,256 1,8 1,639 0,398 1,110 0,7 0,640 1,689 0,945 1,34 0,51 1,499 1,541 0,115 0,711 1,319 0,373 0,686 0,860 1,401 1,917 0,647 0,682 1,263 0,502 1,658 1,18 1,302 0,992 1,26 0,222 0,736 1,684 1,851 1,938 1,63 0,824 0,539 1,75 1,354 1,283 1,384 1,69 0,875 0,261 0,813 0,82 0,348 0,467 0,181 0,492 0,963 0,920 0,698 0,255 1,617 0,337 0,870 1,914 0,862 0,606 0,955 0,202 1,102 0,235 0,885 0,34 0,851 0,438 0,868 0,453 0,74 0,759 0,470 1,763 1,778 0,838 0,104 1,585 0,192 0,241 1,66 1,367 0,804 0,406 0,779 0,80 0,841 1,847 0,23 1,676 0,787 1,161 1,320 1,103 1,783 0,141 0,296 0,273 0,864 0,186 0,489 1,457 0,930 0,974 1,625 0,918 0,166 0,503 1,282 1,578 0,889 1,441 1,203 1,191 1,936 1,230 0,838 1,960 1,176 0,101 0,857 0,76 0,680 0,336 0,770 1,162 1,987 1,786 0,498 1,84 1,208 1,684 1,564 1,620 1,952 1,238 0,878 1,798 1,693 0,4 0,984 0,645 0,299 1,908 0,834 1,5 0,466 1,813 1,429 0,742 0,46 0,191 1,637 0,791 1,398 1,801 1,570 0,998 0,916 0,44 0,104 0,198 0,700 1,980 1,909 0,560 0,346 1,766 1,293 0,695 1,472 0,602 0,349 0,66 0,766 1,296 0,134 1,155 0,63 1,691 0,725 1,350 1,753 0,690 0,552 0,634 1,690 1,196 1,96 0,899 1,856 1,925 0,182 0,431 1,843 0,533 1,478 1,497 0,364 0,505 0,987 1,34 1,863 0,514 1,376 1,976 0,103 0,885 1,39 0,482 0,664 0,708 0,720 1,390 1,497 0,223 0,644 0,349 0,85 1,232 1,664 0,132 1,942 1,0 0,925 1,902 0,324 0,803 0,22 0,162 0,649 1,948 0,192 0,667 1,282 0,114 0,304 1,251 0,883 1,475 1,389 0,537 1,477 1,700 0,36 0,561 0,342 0,781 1,935 0,403 1,88 0,577 0,76 0,819 1,111 1,870 1,752 1,146 1,352 0,682 1,964 1,750 1,27 0,42 1,650 1,772 0,589 0,308 0,826 0,318 1,193 0,172 0,331 1,696 1,33 1,672 0,749 0,647 1,170 1,131 0,288 1,874 0,549 1,484 0,288 1,730 0,674 1,384 1,757 1,245 1,618 1,876 0,103 0,517 0,659 1,864 1,941 1,292 1,289 1,698 0,599 0,319 1,155 0,967 1,66 1,396 1,976 0,87 0,62 1,144 0,719 1,785 1,970 1,82 1,631 0,761 0,855 0,78 0,673 1,843 1,16 1,247 0,178 1,783 1,886 0,72 0,826 1,963 1,162 1,562 1,492 0,923 0,719 0,689 0,496 1,725 1,740 1,789 1,894 1,124 1,527 1,354 0,143 1,962 1,359 0,621 1,989 0,620 1,703 0,293 1,152 0,627 1,115 1,900 0,758 1,134 0,340 1,952 0,743 1,399 1,599 0,895 0,149 1,93 0,225 0,172 1,887 1,225 0,951 0,654 1,82 1,746 0,138 1,654 1,770 0,779 1,449 1,146 1,439 1,781 1,805 1,653 1,13 1,653 0,812 1,482 0,815 0,870 0,552 0,118 1,313 0,738 0,354 0,514 0,5 1,360 0,75 0,425 1,756 0,496 0,478 1,183 0,435 0,151 0,634 1,783 1,259 0,31 1,51 0,905 0,803 0,457 1,369 0,286 0,886 1,308 1,688 1,351 0,548 0,944 1,527 0,388 1,868 1,983 1,399 0,547 1,371 1,500 0,977 1,677 0,788 0,702 1,786 1,250 1,83 1,269 1,14 0,408 1,37 1,907 0,703 0,838 1,233 0,267 1,583 0,488 1,389 1,38 1,378 0,890 1,66 1,612 1,247 0,12 1,874 0,839 0,482 1,546 1,923 0,436 0,611 0,964 1,104 0,314 0,470 0,583 1,998 1,147 0,628 0,203 1,398 1,746 1,241 1,252 1,758 0,448 1,454 1,974 1,996 1,571 1,94 1,638 0,391 0,918 1,746 0,838 1,747 1,234 1,879 0,948 0,399 1,715 0,647 1,94 0,378 0,822 0,860 1,672 1,619 0,334 1,506 1,61 1,931 1,62 1,202 0,734 0,781 0,579 0,471 0,371 0,641 1,420 0,448 1,293 0,887 1,428 0,530 0,685 0,446 1,382 1,912 1,595 1,312 1,547 1,143 1,108 1,928 0,994 1,608 0,654 0,51 0,906 0,48 0,228 0,608 1,801 0,606 1,95 0,865 1,244 1,641 1,780 1,900 0,521 1,892 0,872 1,963 0,31 1,644 0,969 1,343 1,516 0,51 1,387 1,14 1,178 0,880 1,950 1,762 1,897 1,425 0,911 1,966 0,583 1,920 0,391 0,680 1,671 0,824 1,623 0,953 1,180 1,835 0,781 0,200 1,196 1,55 1,629 1,835 0,722 1,967 0,323 0,971 1,548 0,725 1,851 0,536 1,575 0,102 0,220 0,684 0,426 0,994 1,385 1,557 0,466 0,72 1,287 0,0 1,510 1,5 0,999 0,297 1,239 0,459 0,834 0,990 1,39 1,656 0,232 0,844 1,811 0,815 0,701 1,536 1,122 1,635 1,522 0,641 1,839 0,485 1,76 1,879 0,924 1,297 1,138 0,901 0,151 1,360 1,106 0,872 1,956 1,840 0,468 0,928 0,946 0,272 1,957 1,938 0,102 1,491 1,436 1,558 0,354 0,574 0,797 0,849 1,277 0,279 1,345 1,89 0,373 0,8 0,991 0,818 1,346 1,787 0,826 0,925 0,330 1,806 1,139 1,692 0,241 0,208 0,373 1,122 0,919 1,959 1,207 0,664 0,596 0,267 1,17 1,892 0,883 1,268 0,914 1,662 0,384 0,574 0,904 1,755 1,14 0,115 1,684 1,564 0,868 1,899 0,4 0,637 1,833 1,177 0,360 0,161 0,985 1,867 1,832 0,117 1,409 0,237 1,966 0,576 1,874 1,628 0,196 0,321 0,893 0,83 0,638 1,826 0,7 1,489 1,613 1,760 0,566 1,698 0,471 0,270 1,560 0,158 0,416 1,924 0,578 1,501 1,885 1,166 0,204 1,783 1,5 1,473 1,798 0,509 1,338 1,774 1,36 0,984 0,319 0,550 0,312 1,670 0,220 1,104 1,563 0,382 0,79 0,209 1,268 0,923 1,746 1,673 1,752 1,574 1,726 1,419 1,696 0,76 0,655 1,80 0,423 0,762 0,125 0,811 0,888 0,837 1,60 1,143 0,761 0,698 1,390 1,341 1,727 1,686 1,231 1,820 1,27 0,591 1,604 0,478 0,83 0,403 1,299 0,954 0,700 0,647 0,106 0,963 0,205 0,539 0,575 0,33 1,957 0,951 1,459 1,661 0,380 1,831 1,255 0,224 1,966 1,329 0,595 0,189 0,68 1,854 0,110 0,862 1,400 0,91 0,131 0,478 1,87 1,202 0,110 0,911 0,851 0,606 0,201 0,163 1,951 0,736 1,151 0,924 0,998 0,843 1,453 0,602 1,772 0,121 1,763 1,282 0,868 0,860 0,482 0,429 0,61 1,248 0,124 1,909 0,570 0,647 0,178 1,959 1,264 1,52 0,285 0,479 0,5 1,835 1,49 1,642 1,137 0,374 1,587 0,230 0,523 0,32 1,868 1,228 1,501 0,802 1,694 1,180 1,755 1,76 0,793 0,163 0,874 1,524 1,806 0,259 0,408 1,207 1,152 1,178 1,366 0,290 0,174 0,433 0,136 1,493 1,717 0,77 1,481 0,977 1,962 1,316 0,253 0,107 1,669 1,844 1,505 1,768 1,536 0,265 1,667 1,134 0,361 0,214 1,582 1,244 0,653 1,885 1,793 1,893 1,648 1,868 0,526 1,523 0,538 0,425 1,851 0,686 0,324 0,744 1,221 1,462 1,947 0,575 1,666 1,902 1,892 1,593 1,486 1,650 0,414 0,33 1,361 0,354 1,566 0,408 1,241 0,49 0,526 1,563 0,205 1,580 1,748 0,239 0,214 1,621 0,930 0,147 1,883 1,141 1,631 0,760 0,281 1,534 1,155 1,159 0,215 0,253 1,503 1,652 0,316 0,73 1,991 0,556 1,148 1,649 1,554 0,466 0,129 1,423 1,489 0,321 0,102 0,450 0,29 0,381 0,156 1,31 1,934 1,761 1,483 0,742 0,253 0,377 1,301 0,888 0,471 1,570 1,671 1,642 0,377 0,27 0,980 1,859 1,354 0,515 0,468 1,146 0,100 0,20 0,539 1,161 1,67 0,500 0,667 1,921 1,315 1,651 0,66 1,769 0,753 1,900 1,947 0,221 0,17 0,730 0,546 1,767 0,197 1,356 1,548 0,933 1,797 0,910 1,79 1,547 1,161 0,997 1,163 0,907 1,528 0,448 1,450 0,642 0,90 1,8 0,37 0,311 1,609 0,92 1,721 0,62 0,552 0,764 0,6 0,79 1,484 1,310 1,186 1,759 0,744 1,471 1,306 1,882 0,643 1,912 1,35 0,554 1,900 0,115 0,930 0,565 0,462 0,704 0,946 0,541 1,271 1,525 0,364 1,450 1,856 1,51 1,927 1,121 1,853 0,314 1,959 1,582 1,969 1,951 1,712 1,873 1,224 1,438 1,13 0,338 0,74 1,48 0,696 1,563 1,145 1,732 0,913 0,753 1,39 1,355 1,361 1,863 0,818 1,728 1,498 1,225 0,895 0,19 0,262 0,185 1,483 0,676 0,682 1,308 0,931 1,764 1,773 1,746 1,298 1,220 1,304 0,192 0,519 1,246 1,550 1,525 0,874 0,734 0,47 1,151 0,884 0,988 1,553 0,758 1,894 1,155 0,583 1,623 1,705 1,747 1,742 0,317 1,804 0,364 0,464 0,849 1,145 1,287 1,382 1,929 1,417 0,761 1,966 1,760 0,169 0,39 0,311 0,369 0,171 1,54 1,45 0,44 0,388 1,622 0,854 0,551 1,967 1,371 1,489 0,662 1,658 1,402 1,234 0,162 1,653 1,119 0,451 1,497 1,464 0,594 0,84 0,697 1,657 1,579 0,135 0,737 0,486 1,282 0,681 1,354 0,517 1,296 0,961 1,244 0,384 0,169 1,953 0,96 1,279 1,547 1,510 1,18 1,596 1,307 1,442 1,564 0,646 1,706 1,577 0,976 0,514 0,6 0,610 0,908 1,171 1,380 1,796 0,953 1,362 0,670 0,506 1,125 1,779 0,315 1,915 0,3 1,195 0,751 1,447 1,142 0,373 1,890 0,252 1,384 0,162 1,326 1,900 1,175 0,455 1,983 1,119 0,541 1,821 0,381 0,291 1,603 0,511 0,895 1,447 1,933 1,922 0,60 1,495 1,879 0,894 1,466 1,109 0,101 0,916 0,268 1,478 0,854 0,958 0,571 0,256 1,581 1,191 0,781 1,643 0,796 0,333 0,902 1,4 0,980 1,330 1,339 0,77 0,977 0,734 1,930 1,148 1,466 1,359 0,93 0,86 1,827 0,387 1,520 0,13 0,964 0,175 1,524 0,231 0,916 1,56 0,893 1,645 1,828 1,139 0,448 0,5 1,217 1,343 1,842 0,1 1,232 0,337 1,523 1,980 1,768 1,193 0,142 1,443 1,374 1,368 1,49 1,430 1,440 0,252 1,648 0,561 1,322 1,706 1,87 0,830 1,109 1,640 0,970 0,291 1,610 0,430 1,716 1,276 1,165 0,454 0,94 1,485 0,913 0,533 0,82 0,568 0,721 1,485 0,359 1,996 0,342 1,350 0,83 0,430 1,810 0,908 1,365 1,237 1,65 1,134 1,371 0,27 1,332 1,407 1,343 1,522 0,170 0,549 1,183 0,766 0,721 1,835 1,753 1,990 1,148 1,958 1,627 0,108 0,669 0,126 1,659 1,565 1,683 1,520 0,305 1,44 1,773 1,86 1,75 0,784 0,586 0,498 0,489 0,584 0,952 0,131 0,325 0,282 1,544 1,990 1,829 0,916 0,622 0,494 1,599 0,516 1,365 0,826 1,97 0,216 1,627 1,128 0,667 0,403 1,693 0,745 0,370 0,189 0,550 0,520 0,117 0,603 1,665 1,802 0,680 1,159 1,258 1,14 1,397 0,171 0,287 1,450 1,364 1,900 0,199 1,223 1,868 1,355 1,236 0,666 0,747 1,23 0,77 0,889 1,702 1,503 0,939 0,169 1,825 0,306 1,673 0,284 0,578 1,667 1,480 1,262 0,181 1,681 0,451 0,817 0,732 0,474 0,461 0,436 0,915 1,779 0,205 1,933 0,588 1,284 1,230 1,186 0,696 0,407 0,340 1,1 1,502 1,611 1,249 1,655 1,346 0,362 1,126 1,278 1,475 1,209 1,697 0,414 1,277 0,679 0,895 1,976 1,812 0,953 1,459 0,452 1,53 1,37 0,904 1,399 1,563 1,124 1,750 1,719 1,205 1,151 1,224 0,612 0,210 0,211 0,756 0,506 0,77 1,988 0,224 1,688 1,526 1,550 1,437 0,467 1,211 0,537 0,893 1,900 1,285 0,411 1,613 1,445 1,56 0,712 0,132 1,458 1,974 0,885 1,111 0,346 1,939 1,476 1,904 0,426 1,441 1,4 1,359 1,958 1,740 0,210 0,138 1,466 1,503 0,605 1,373 0,236 0,315 0,266 0,678 1,211 1,644 1,876 0,866 1,272 0,808 1,966 0,861 0,923 1,415 0,974 1,734 0,468 0,652 1,863 1,141 1,178 1,445 1,594 0,804 0,953 1,137 1,980 1,867 0,266 0,802 0,361 0,505 0,461 1,962 0,136 1,456 1,753 0,729 1,226 1,876 0,750 1,452 0,197 0,577 0,93 1,963 0,27 0,41 0,743 0,143 1,951 1,393 0,375 0,772 1,55 1,108 1,119 0,7 1,988 1,438 0,673 0,369 1,923 0,262 1,603 0,229 0,64 0,312 1,233 1,744 1,240 1,362 0,626 1,959 0,373 1,634 0,377 0,366 0,209 0,82 1,825 0,598 0,854 1,989 0,427 0,604 1,904 1,866 1,555 0,672 0,990 1,473 0,629 0,445 0,386 0,907 0,493 1,80 1,858 1,31 0,26 1,577 1,477 0,183 0,508 1,960 0,294 0,116 1,233 1,206 0,451 1,527 0,326 1,729 0,966 0,931 1,686 1,772 0,39 0,284 1,200 1,879 0,984 1,455 1,837 1,911 0,373 0,100 0,746 0,778 1,144 1,191 0,64 0,515 1,412 1,694 1,739 0,492 1,780 0,602 1,239 1,974 1,947 0,143 1,517 0,338 0,895 0,494 1,189 0,313 0,306 1,800 0,873 0,35 1,237 0,107 1,552 1,984 0,839 1,909 1,328 1,646 1,602 1,787 1,176 1,544 0,288 1,64 1,972 0,112 0,869 1,96 1,170 1,179 0,536 0,883 1,669 0,927 1,805 0,337 0,150 0,866 0,304 1,626 1,762 1,739 1,256 0,487 1,406 1,119 0,383 0,530 0,849 1,340 0,818 1,89 1,184 1,973 1,479 0,134 0,24 0,149 1,328 1,407 1,229 1,381 1,565 0,159 1,426 1,895 0,420 1,682 1,218 0,591 0,48 1,472 1,615 1,643 1,140 0,428 1,943 0,738 1,542 0,439 0,586 1,710 0,14 1,507 1,208 1,642 0,196 0,400 1,930 1,257 1,728 0,436 0,19 1,562 0,720 1,403 1,86 1,71 1,826 1,167 1,288 1,118 1,202 1,85 0,517 1,222 1,522 0,598 1,947 0,811 1,968 1,668 1,84 1,19 1,273 0,198 1,474 1,148 0,793 0,601 1,289 1,7 0,226 1,898 1,97 1,715 1,315 0,543 0,125 0,162 1,15 1,985 0,936 0,863 1,870 1,115 0,238 0,462 0,806 1,354 0,144 1,841 0,335 1,612 1,170 1,279 0,981 1,834 0,725 0,981 0,215 0,191 1,137 1,401 1,98 1,690 1,810 0,424 0,855 0,921 1,82 1,198 0,297 0,100 1,97 0,788 1,221 1,184 1,245 1,958 1,2 0,961 1,425 0,349 1,111 1,871 0,562 1,655 1,150 1,12 1,117 0,721 0,64 0,73 1,926 0,962 0,202 1,180 1,471 0,82 1,710 1,485 0,941 1,35 1,523 1,497 1,515 1,891 0,897 1,265 0,333 1,132 0,852 0,368 1,493 0,856 1,982 1,12 1,183 0,240 1,72 0,675 0,453 0,953 1,354 0,855 1,391 1,657 1,57 0,720 0,671 0,987 1,605 0,575 1,446 1,869 0,278 1,656 1,84 1,773 0,425 1,593 1,61 0,845 1,463 0,393 1,70 1,771 0,787 1,781 1,576 1,320 1,266 0,119 0,405 1,809 1,984 0,322 1,143 0,592 0,404 0,238 1,126 1,448 1,951 0,477 0,548 1,821 0,475 0,783 1,278 0,818 1,310 0,639 1,418 1,296 0,81 0,475 0,120 0,751 0,567 0,661 1,110 0,362 1,447 0,567 1,106 0,12 0,959 0,782 0,677 0,223 1,62 0,937 1,417 0,778 1,196 0,869 0,805 1,622 0,736 1,5 1,83 0,454 1,495 0,260 1,195 0,999 1,770 1,18 1,299 1,299 0,573 0,472 0,270 0,574 1,868 1,12 0,214 0,616 1,713 0,467 1,744 0,337 0,793 0,385 1,795 1,814 0,25 0,171 1,886 1,692 1,279 1,384 1,641 0,249 1,742 1,940 0,132 0,843 1,324 0,466 1,384 1,643 1,204 0,824 0,44 0,195 1,901 0,294 1,618 1,20 0,934 0,581 0,27 1,154 0,449 0,855 0,400 1,196 1,790 1,129 1,130 1,328 1,117 1,131 0,556 0,451 0,132 1,891 0,136 1,751 0,412 1,342 0,185 1,879 0,875 1,60 0,541 0,390 0,974 1,264 0,7 1,847 0,56 0,508 1,4 0,492 0,842 1,139 1,746 0,793 0,201 1,239 1,179 0,188 0,46 0,222 0,311 0,680 0,523 1,616 1,637 0,165 0,203 0,569 0,426 1,516 1,721 1,96 0,541 1,76 1,702 1,577 0,319 1,654 1,929 1,434 1,877 0,87 1,3 1,774 0,996 1,813 1,374 1,975 1,922 0,785 0,823 0,856 1,423 0,111 1,190 0,627 0,762 1,33 1,620 1,872 1,51 1,744 1,688 0,417 1,315 1,307 0,4 1,401 1,216 0,161 0,649 1,657 1,628 0,987 1,881 1,449 0,666 1,641 0,777 1,411 0,262 0,814 0,90 0,496 1,560 1,145 0,462 1,194 1,822 0,430 0,658 0,595 1,742 0,222 0,385 1,76 0,97 1,136 0,806 1,632 1,186 1,455 0,183 0,288 0,116 1,210 1,956 0,804 1,789 1,904 0,903 1,407 1,541 0,878 0,598 0,578 0,934 1,287 1,877 1,720 1,396 1,756 1,512 1,206 1,595 1,980 1,803 1,39 1,940 1,881 1,687 1,198 0,549 0,777 0,132 1,847 1,604 0,753 1,969 1,625 1,498 0,633 0,261 0,828 1,212 1,519 1,346 0,851 1,131 0,641 0,707 1,335 0,854 0,906 0,59 0,92 1,376 0,38 1,103 1,59 0,271 1,832 1,648 1,285 1,270 0,996 0,326 1,447 1,476 1,638 1,583 1,499 1,123 1,271 0,590 1,564 0,295 1,114 0,749 1,775 0,770 0,632 0,168 1,202 1,975 0,410 0,595 0,440 0,35 1,805 0,624 1,596 0,232 1,32 1,107 1,912 1,833 0,993 0,620 0,794 1,822 0,166 1,499 1,30 1,801 1,828 0,272 0,834 1,31 1,217 1,823 0,968 0,425 1,640 0,924 0,490 0,40 1,639 0,107 1,4 0,451 0,837 0,698 0,507 0,928 1,539 0,783 0,782 1,873 0,256 0,358 1,676 1,300 1,14 1,39 0,765 1,339 0,261 1,933 0,443 0,7 0,208 0,694 0,717 0,810 0,327 1,396 1,607 1,446 0,279 0,267 0,380 0,129 1,385 1,587 1,617 1,332 0,362 0,873 0,359 0,675 0,714 0,289 0,427 1,591 1,77 0,599 1,929 1,258 0,856 0,909 0,779 0,357 1,963 0,786 0,566 1,821 0,729 1,143 1,195 1,966 0,445 1,830 0,952 1,167 1,389 0,737 0,115 1,66 1,664 1,449 1,23 1,890 0,116 0,377 0,52 1,10 1,410 0,481 1,450 1,823 1,242 0,880 1,687 1,818 1,358 1,389 0,60 0,661 1,264 1,54 1,812 1,583 1,658 1,754 1,538 0,676 1,862 0,926 1,310 0,678 0,473 0,102 1,212 1,455 0,516 1,470 1,427 0,686 1,190 1,126 1,910 1,345 0,114 0,472 0,758 1,676 1,735 0,991 1,947 1,112 0,273 0,733 1,194 0,804 1,876 1,832 1,667 0,826 1,13 1,580 0,131 0,489 1,333 1,489 1,438 1,184 0,262 1,844 0,195 1,166 0,646 1,505 0,823 1,830 0,670 0,461 1,382 0,981 0,816 1,367 0,483 1,445 1,9 0,70 0,367 1,995 1,127 0,653 0,477 1,426 0,63 0,943 0,361 1,528 0,549 1,4 1,799 0,855 1,131 0,234 1,414 1,599 0,583 1,63 0,796 1,618 0,300 0,738 1,470 1,207 0,276 1,632 1,69 0,108 1,760 1,723 0,379 0,247 0,997 0,869 0,335 0,915 0,651 0,77 0,338 1,242 1,374 0,783 0,526 0,77 0,940 0,296 1,596 1,910 1,418 0,377 1,850 1,992 0,694 0,788 0,883 0,367 1,650 1,858 0,553 1,899 1,278 0,365 0,133 0,367 0,463 1,309 0,310 0,75 0,505 1,748 0,142 0,166 0,57 0,179 0,483 0,574 0,415 1,243 1,94 1,923 0,466 0,575 1,62 1,401 1,354 0,781 1,656 0,94 0,545 0,799 1,386 0,891 1,645 1,381 0,578 1,426 1,143 1,316 0,77 1,793 0,250 0,821 0,70 1,262 1,178 0,697 0,129 1,474 1,742 1,416 1,371 0,631 1,367 0,229 1,650 1,299 0,328 1,694 1,854 1,557 0,176 0,267 0,512 0,408 1,376 0,976 1,962 1,957 1,350 1,851 1,666 1,653 0,582 0,925 1,62 1,63 1,782 1,242 0,405 0,63 1,405 0,368 1,249 1,58 1,137 0,396 0,381 0,630 1,970 0,884 0,133 0,588 1,633 1,389 1,851 1,650 0,968 1,98 1,988 1,326 1,339 1,412 1,514 0,307 0,94 1,696 0,461 1,290 0,998 1,240 0,312 0,818 1,344 1,708 0,406 0,319 1,221 0,84 0,135 0,987 0,472 0,371 1,768 0,951 1,915 1,777 1,560 0,167 1,944 1,368 0,292 0,128 0,450 1,955 1,616 1,896 1,287 0,683 1,973 0,443 1,579 1,295 1,610 1,429 0,214 1,73 1,442 1,742 0,423 0,558 1,210 0,701 1,750 0,757 0,431 1,52 1,312 1,427 1,548 0,121 0,16 1,670 0,77 0,74 0,115 1,533 1,587 0,682 1,565 1,180 1,965 1,454 1,281 0,871 0,701 0,394 1,416 1,667 0,219 1,981 0,580 0,867 0,482 0,704 1,997 0,292 1,869 1,974 1,708 0,449 0,32 1,893 0,672 1,480 1,28 0,529 1,137 0,135 1,109 0,359 0,241 1,711 1,295 1,809 1,720 1,96 1,451 1,93 0,384 0,693 1,969 1,223 1,787 0,901 0,642 1,846 0,434 1,593 1,550 0,931 1,982 0,324 0,788 0,164 1,72 0,156 1,363 1,656 0,2 1,727 0,383 0,991 0,523 1,787 0,112 0,732 1,241 1,108 0,435 1,261 0,984 0,636 1,238 1,382 0,899 0,540 1,322 0,950 0,403 1,208 0,257 0,397 1,890 0,757 1,456 0,284 1,14 1,13 1,56 0,43 1,646 0,464 1,190 1,776 0,511 1,700 1,446 1,906 0,32 0,833 0,608 1,229 1,147 0,91 1,393 1,763 0,603 0,896 1,935 0,209 0,141 0,903 0,384 0,307 1,712 1,649 1,370 1,809 0,933 0,962 1,222 1,688 1,819 1,275 0,922 1,175 1,791 0,851 0,178 1,910 0,953 1,60 1,953 0,163 1,860 1,254 1,65 1,322 0,816 0,547 1,638 0,385 0,287 1,892 0,362 0,107 1,276 1,890 0,962 0,476 1,258 1,634 1,245 0,338 1,989 0,17 0,383 0,951 0,140 1,681 0,172 0,59 1,289 1,418 0,710 0,551 0,315 1,825 0,322 0,307 1,716 1,887 0,574 0,868 0,494 0,318 1,960 1,138 1,269 1,927 1,51 1,747 0,962 1,472 1,310 0,638 1,24 0,908 0,12 0,261 1,132 1,960 0,211 0,850 1,693 1,261 0,869 1,857 0,963 0,389 1,390 1,822 1,810 0,161 1,886 0,585 1,302 1,774 1,167 1,374 0,722 0,330 0,324 1,158 0,574 0,777 1,528 0,806 1,878 1,587 1,837 1,36 0,301 1,801 1,480 1,211 1,606 1,528 1,595 1,758 1,480 1,154 1,611 0,73 0,71 1,183 1,785 1,447 1,703 1,529 0,802 0,29 1,484 0,386 1,129 0,121 1,831 1,680 0,901 0,558 0,111 0,723 0,94 0,101 1,227 0,663 0,634 0,318 1,115 1,779 1,508 0,600 0,798 0,724 1,380 1,594 0,974 1,465 0,602 1,946 0,853 1,76 1,508 0,369 1,223 0,170 1,17 0,932 0,901 0,461 0,919 0,702 0,279 1,891 0,704 0,723 0,272 0,952 1,900 0,435 0,60 1,235 0,817 0,953 1,683 0,639 0,803 0,978 0,454 0,890 1,84 0,837 1,530 0,396 1,39 1,211 1,485 0,766 1,616 0,6 0,1 0,544 0,768 1,855 1,333 1,777 1,189 1,158 1,609 1,578 0,367 1,639 0,769 0,6 1,784 0,256 1,645 1,240 0,551 0,723 0,839 0,150 0,450 0,18 1,922 0,939 1,741 0,467 1,480 1,947 0,907 0,719 0,511 0,381 1,919 0,944 0,131 0,756 1,233 0,166 0,693 1,624 0,547 1,60 1,866 0,826 1,762 0,684 0,773 1,331 0,151 1,232 1,520 0,599 1,349 0,865 1,879 1,794 0,363 0,268 0,10 0,397 0,659 1,935 1,192 0,963 1,713 1,455 1,118 1,266 0,652 0,340 0,897 1,976 0,597 0,574 1,107 1,32 1,479 0,181 0,341 0,442 0,451 0,413 1,286 1,874 1,366 0,911 1,859 1,0 1,994 1,63 0,146 1,286 1,401 1,858 0,813 1,311 1,800 1,301 0,702 0,95 1,35 0,500 0,596 1,476 0,371 0,574 0,441 0,273 0,739 1,10 0,976 0,74 1,111 0,121 1,44 1,399 1,459 0,320 0,476 0,364 0,908 1,323 0,852 0,965 1,196 1,58 0,400 0,161 1,829 1,399 1,884 0,826 1,365 0,220 1,154 1,324 0,628 0,332 0,831 1,543 1,170 1,227 0,339 1,346 0,839 1,430 1,982 1,339 1,727 1,858 0,91 0,102 1,463 1,167 1,981 1,54 1,722 0,585 0,426 1,316 0,111 1,153 0,932 0,785 0,209 1,676 0,529 0,463 1,679 0,143 0,703 1,7 1,442 1,759 0,147 0,484 0,862 0,759 0,705 0,880 0,815 1,200 1,227 0,468 0,789 1,674 1,187 0,926 0,407 1,659 0,485 0,17 1,392 0,131 0,110 1,659 0,411 1,595 0,693 0,391 1,772 0,54 1,423 0,27 0,798 0,140 0,649 0,849 0,254 1,537 0,88 0,712 1,322 1,985 1,367 0,92 0,69 0,60 1,352 0,599 1,866 0,381 1,73 0,540 1,648 1,238 1,128 1,859 1,340 1,859 1,290 0,201 0,857 0,737 0,520 1,681 1,563 0,484 0,980 1,467 1,902 1,107 1,563 0,64 1,324 1,147 1,17 0,317 1,53 0,896 1,204 1,522 1,303 1,168 0,256 1,382 1,830 0,425 1,194 1,191 0,749 0,402 0,946 1,68 0,674 0,615 1,844 0,894 1,825 0,306 0,358 1,580 0,715 0,111 1,671 1,676 0,108 1,244 0,137 1,13 0,826 1,708 1,398 0,58 0,894 0,407 1,559 1,789 1,363 0,842 0,892 0,966 0,605 0,824 0,534 0,243 0,743 0,342 1,828 0,957 0,490 1,764 0,80 1,72 0,635 1,129 1,55 0,172 1,915 0,726 0,439 0,212 1,924 1,237 1,559 0,419 1,449 1,661 1,134 0,75 0,987 0,694 1,457 0,504 1,49 0,10 1,207 1,305 0,385 1,729 0,690 0,135 0,616 1,216 0,938 0,766 0,226 1,954 1,153 1,773 1,761 0,106 0,82 1,354 1,964 0,897 0,358 0,421 1,224 1,975 0,941 0,659 0,763 1,228 0,706 0,430 0,760 0,110 1,11 1,985 0,660 0,734 1,20 1,652 1,822 1,632 0,253 1,462 1,3 0,463 1,867 0,150 0,10 1,33 1,724 0,567 1,28 1,731 0,258 1,48 1,98 0,661 0,830 1,712 0,739 0,99 1,520 1,497 1,544 1,875 0,617 0,76 1,385 0,184 1,809 0,743 1,104 0,236 1,913 1,650 0,645 0,874 1,95 0,864 1,422 1,428 0,631 1,813 1,823 1,449 1,892 0,689 0,114 1,606 1,438 1,170 1,891 1,882 0,178 0,431 1,853 0,129 0,780 1,253 1,322 1,727 0,489 1,420 0,89 0,962 1,957 0,636 0,421 0,702 1,727 1,455 1,404 1,213 0,923 1,684 0,813 0,371 1,53 1,924 1,566 0,721 1,668 1,254 1,909 0,483 0,20 0,102 1,752 0,820 0,876 1,937 0,687 1,229 1,122 1,551 1,83 1,816 1,33 0,39 1,864 0,747 1,333 0,932 1,46 1,49 1,641 1,768 1,720 1,284 1,948 1,339 0,242 1,797 0,704 1,912 1,287 1,751 1,882 1,457 1,203 1,528 0,844 1,297 0,598 1,930 1,66 0,124 0,859 0,265 0,882 0,404 0,90 1,698 0,772 0,810 0,117 1,217 0,74 1,737 1,707 1,842 0,656 0,729 0,848 1,818 0,408 1,813 0,172 0,633 1,612 1,817 0,410 1,989 0,631 1,733 0,430 1,486 0,702 1,900 0,718 0,310 0,395 0,572 0,797 0,734 1,253 1,255 1,146 0,521 1,262 1,75 0,402 0,114 1,758 1,869 1,438 0,361 0,531 0,565 0,585 0,679 0,421 0,664 0,313 0,60 0,213 1,272 0,260 1,536 1,928 1,410 1,633 0,685 1,437 1,373 0,903 0,336 1,364 1,778 1,657 1,280 1,54 1,70 0,623 1,470 1,985 1,851 1,678 0,538 0,885 0,1 0,382 0,283 1,388 1,199 0,941 1,995 1,413 0,736 0,31 1,198 0,142 1,406 1,532 0,554 1,836 1,846 1,981 1,130 1,899 0,2 1,491 0,455 0,611 1,232 1,92 1,234 0,712 0,878 1,913 0,323 0,523 1,986 1,684 0,199 0,489 1,414 0,521 0,945 0,333 1,771 0,423 0,956 0,667 0,256 1,661 0,623 1,727 1,486 0,313 0,302 0,106 1,362 0,968 0,353 0,21 0,491 0,589 1,108 1,607 0,599 0,548 1,228 0,105 0,660 1,304 1,947 0,418 1,156 1,617 1,689 1,12 0,675 1,841 0,322 0,86 0,150 0,730 0,194 0,783 0,520 1,436 1,436 0,108 0,420 1,449 0,539 1,520 0,415 0,992 1,577 0,839 0,189 0,711 1,368 1,484 0,734 1,173 0,590 0,886 0,815 0,683 1,992 0,717 1,867 0,897 0,849 1,960 0,204 0,872 1,103 1,436 0,524 0,43 0,674 1,870 0,294 0,302 1,50 1,345 0,244 1,34 0,24 0,105 1,978 1,481 0,411 1,518 1,245 1,638 0,706 0,191 1,903 0,310 0,965 1,760 0,159 0,41 1,181 0,988 0,202 0,503 1,736 1,70 0,159 1,28 1,73 0,736 1,301 0,915 1,396 1,7 1,66 0,988 0,576 1,979 0,603 0,525 0,133 1,750 0,701 0,630 0,138 1,232 0,318 1,839 1,728 1,239 0,414 1,75 0,515 0,291 0,168 1,397 0,89 1,726 0,673 0,543 0,97 1,913 0,154 1,567 1,617 1,604 0,458 1,792 1,319 0,900 1,502 0,514 0,11 1,348 0,957 1,33 1,185 1,569 1,605 1,71 1,783 1,909 1,990 0,333 1,671 0,213 1,998 0,68 0,562 0,820 0,36 0,205 1,377 1,935 0,624 0,819 1,645 1,44 0,443 0,638 0,427 1,177 1,322 0,433 0,424 1,702 1,462 0,80 1,80 0,786 1,323 0,200 1,810 1,125 1,311 0,840 0,105 0,228 1,561 0,735 1,210 0,363 0,978 0,160 1,487 0,613 1,193 0,846 1,60 0,519 0,413 1,516 1,164 0,177 1,494 1,233 1,162 1,165 0,943 0,88 0,753 0,815 1,894 0,847 1,125 1,660 0,566 0,450 1,597 1,48 1,629 1,885 0,867 0,620 1,118 0,107 0,196 1,484 0,444 0,280 0,876 0,189 1,512 1,767 1,705 0,781 1,661 0,282 1,102 0,202 1,878 1,621 1,334 0,652 0,193 0,288 1,716 1,790 1,262 1,43 0,45 1,935 1,153 0,697 1,51 0,103 1,608 0,428 1,99 0,686 1,446 1,496 0,101 0,296 1,21 0,353 1,492 1,760 0,471 0,874 1,212 0,868 1,966 1,55 0,17 1,865 0,167 0,706 1,487 1,16 0,547 1,266 1,265 1,435 0,314 1,455 1,432 0,583 0,980 1,935 0,985 1,722 1,12 1,70 1,3 1,368 1,112 1,839 0,960 1,859 0,803 1,219 0,626 0,959 0,365 0,114 0,107 1,426 1,759 0,855 1,538 1,313 1,67 0,381 0,784 1,610 1,779 1,344 0,437 1,636 1,537 0,456 0,68 1,28 1,810 1,323 1,107 0,867 0,660 0,815 1,227 0,849 0,171 1,705 0,496 1,81 1,371 1,318 1,426 0,509 1,784 1,841 1,287 0,882 1,206 1,629 0,226 0,889 0,143 0,608 0,483 1,750 1,729 0,514 0,439 1,78 1,293 0,869 1,463 0,652 0,919 0,764 1,10 0,425 0,767 0,547 1,808 0,932 1,60 1,779 1,975 0,484 0,102 1,148 0,960 0,405 1,492 0,761 1,471 0,751 1,994 1,858 1,703 0,245 0,240 1,175 0,18 0,973 0,709 0,455 1,898 0,16 0,418 0,332 1,887 0,334 0,509 0,361 0,178 0,790 1,442 0,322 1,822 1,165 1,932 1,403 0,161 0,895 0,324 0,852 1,541 1,595 0,698 0,505 1,87 1,576 0,939 1,196 1,581 1,121 0,435 0,998 0,994 0,867 1,752 0,731 0,488 0,927 0,960 1,761 1,979 0,188 0,976 0,717 0,492 1,167 0,554 1,471 1,998 1,389 1,56 1,4 0,717 1,543 1,42 0,427 0,878 1,562 1,253 0,93 1,981 0,579 0,383 1,821 1,298 1,140 0,870 0,262 1,388 1,281 0,487 1,945 0,876 1,499 1,705 1,469 0,392 1,822 1,470 1,886 0,432 0,924 0,862 1,568 1,808 1,505 0,582 0,657 1,899 1,959 0,791 0,331 0,368 1,593 0,15 1,887 0,363 1,183 0,189 1,812 1,639 0,451 1,232 1,518 1,981 0,467 0,232 0,899 0,771 0,285 0,7 0,882 1,821 1,834 0,372 1,788 0,170 0,699 0,441 1,577 0,482 1,422 0,545 1,32 0,627 1,153 0,108 1,383 1,725 1,885 1,685 1,358 0,919 0,298 0,884 0,489 1,215 0,137 1,63 1,75 1,565 0,445 0,460 1,866 0,239 1,220 0,227 1,967 0,133 1,728 0,700 0,44 0,809 1,953 1,607 1,631 0,276 0,107 1,80 0,201 1,731 1,410 1,395 0,630 1,528 0,933 0,102 1,883 0,854 1,913 1,498 0,119 1,707 1,120 0,352 1,506 1,14 1,545 1,451 1,101 0,880 0,545 1,404 0,845 0,995 0,387 1,561 0,432 1,358 1,614 0,979 0,947 0,842 1,820 0,672 0,720 1,168 1,154 1,440 0,950 0,786 1,299 1,653 1,106 1,992 0,909 1,897 1,245 1,485 1,575 0,715 1,379 1,237 0,356 1,463 1,490 1,426 1,775 0,633 1,950 1,983 1,199 1,475 1,709 0,767 1,886 0,452 1,826 0,102 0,751 1,436 1,124 0,37 0,98 1,735 0,62 0,495 1,757 0,522 0,159 1,302 1,569 1,154 0,619 0,882 0,740 1,893 0,359 0,451 1,719 1,547 1,234 1,568 0,454 0,922 1,812 0,697 0,410 1,636 1,599 1,458 0,995 1,956 0,762 1,297 0,463 0,255 1,84 0,960 1,282 1,619 1,176 0,260 0,710 1,855 0,186 1,389 0,765 0,799 0,876 0,902 0,785 0,210 1,3 1,956 0,32 0,142 1,899 0,307 0,866 0,38 1,294 1,979 0,177 1,169 1,390 1,512 0,266 1,444 1,436 1,830 0,707 1,91 0,500 0,214 1,416 0,830 1,142 1,623 1,768 1,28 0,394 0,740 0,435 0,611 0,461 1,791 1,18 1,83 0,51 0,256 1,152 1,14 0,447 0,370 0,403 1,754 0,292 1,504 0,317 1,992 0,277 1,209 0,875 1,400 0,727 1,279 0,582 0,881 1,608 1,876 0,353 0,407 1,421 1,32 0,639 1,972 0,834 0,654 1,970 0,348 0,230 0,449 0,513 1,152 0,764 0,387 0,220 1,891 0,656 0,436 0,836 0,831 0,444 1,174 1,899 0,929 0,191 0,728 0,330 1,295 1,478 0,514 0,45 0,493 0,794 0,609 1,457 1,697 1,592 0,642 0,798 0,505 0,767 1,975 0,780 1,567 1,789 1,170 0,864 1,758 0,605 1,205 0,413 0,916 1,589 1,977 0,433 0,345 0,191 0,532 1,242 1,536 1,523 1,170 1,341 1,973 1,184 0,130 1,850 1,841 1,137 0,62 1,961 1,179 1,616 0,706 1,518 0,562 1,655 0,800 0,597 0,869 1,864 1,482 0,915 1,27 0,235 0,94 1,378 1,295 0,274 0,621 1,491 1,695 1,47 0,715 0,160 1,942 0,585 0,356 0,710 1,913 1,712 1,435 0,172 1,770 0,8 1,756 0,459 0,550 1,154 1,231 1,223 1,43 1,667 0,73 1,369 0,360 0,305 0,520 1,448 0,652 0,58 1,265 1,961 1,94 0,462 0,522 0,269 0,778 1,841 1,98 0,833 0,233 1,920 1,122 0,646 0,679 0,579 0,852 1,235 1,584 0,699 0,829 0,749 1,42 1,970 1,707 0,875 0,648 0,11 1,603 1,589 1,990 1,168 1,187 0,145 1,364 0,257 1,928 0,927 1,677 0,216 0,359 0,3 0,126 1,597 1,913 1,422 1,154 0,770 0,633 0,118 0,173 1,405 0,617 0,330 1,604 1,645 1,534 0,723 0,911 0,494 0,630 0,691 1,826 0,481 0,277 0,387 1,79 0,788 1,650 0,129 0,74 0,873 0,182 0,554 0,3 0,300 0,391 1,25 0,423 0,819 1,711 0,911 1,343 1,234 1,283 0,837 1,838 0,116 0,953 1,999 1,201 0,214 1,206 1,196 1,461 0,935 1,802 1,712 1,938 0,34 0,791 0,486 1,58 0,771 1,296 0,363 1,855 0,306 0,706 0,72 0,49 0,884 1,936 1,113 0,364 0,343 0,50 1,281 1,589 1,273 1,157 1,945 0,725 1,227 0,565 0,53 1,273 1,484 1,779 1,7 1,482 1,453 0,456 0,283 1,776 0,213 0,352 1,919 0,988 1,623 1,12 1,490 0,888 1,769 0,804 1,581 0,615 1,712 1,357 0,329 0,476 1,950 1,944 0,917 1,217 0,702 0,111 1,381 0,888 1,903 1,468 0,251 1,544 0,910 1,781 1,315 1,36 0,226 1,270 0,368 1,378 1,909 0,608 0,622 1,130 0,881 1,984 0,432 1,73 1,997 1,743 0,646 0,306 1,963 0,658 1,439 0,661 1,653 0,588 0,612 1,900 1,836 0,361 1,286 1,404 1,890 1,299 0,611 1,593 1,408 1,55 1,288 1,763 1,254 0,76 0,567 0,832 1,948 1,111 1,952 1,327 1,400 1,147 1,172 0,37 1,520 0,993 0,328 0,981 0,825 0,724 1,717 0,987 1,682 1,501 0,711 0,884 1,840 0,649 1,644 1,804 1,746 0,569 1,799 0,871 0,297 1,99 0,805 0,532 1,240 0,404 1,90 0,519 1,856 0,772 1,865 1,622 0,628 1,277 0,715 0,673 1,763 1,482 0,326 0,708 0,698 1,352 1,403 1,21 0,728 1,822 0,443 1,436 1,381 0,409 1,703 0,182 1,929 1,121 1,40 1,170 0,511 0,319 0,834 0,562 0,888 1,774 1,525 0,997 0,347 0,760 1,51 0,921 1,435 0,963 1,176 0,273 1,410 0,984 0,654 1,779 1,345 0,714 1,919 1,311 1,658 1,140 1,324 0,240 1,524 1,563 0,492 1,300 1,657 1,940 0,921 0,464 0,799 1,313 1,134 0,314 1,338 0,392 1,726 1,590 0,51 0,821 1,28 1,96 1,363 0,83 0,716 0,221 0,74 0,964 1,19 0,225 1,61 1,635 1,178 0,460 1,854 0,883 0,402 1,775 0,334 1,526 0,622 0,827 1,841 1,643 1,241 0,995 1,596 0,981 1,849 0,251 0,657 1,591 0,732 0,257 1,201 0,317 1,693 1,37 0,118 0,578 1,695 1,42 1,151 1,193 0,852 0,789 0,764 0,106 1,114 0,775 1,792 1,937 1,433 0,75 0,923 0,92 0,447 1,6 1,830 0,287 1,221 0,498 0,750 1,404 1,616 0,882 1,425 0,419 0,495 0,120 1,521 0,263 1,867 0,350 0,330 1,109 1,444 0,304 1,956 1,986 1,737 1,958 0,698 1,212 1,598 0,365 0,764 1,341 0,875 0,341 0,326 1,271 1,295 0,99 0,748 0,42 1,305 0,254 1,24 1,898 0,434 1,947 1,134 0,701 1,556 1,452 0,748 0,952 1,426 0,373 0,538 0,902 0,404 0,72 0,586 1,917 0,597 0,619 1,485 0,834 1,847 0,714 1,369 0,375 1,991 0,346 0,486 1,13 0,294 1,31 0,380 1,199 1,834 0,745 1,889 0,67 1,768 1,103 0,557 0,456 1,741 0,774 1,203 0,682 0,873 0,452 1,802 0,107 1,479 1,734 0,853 1,501 0,584 1,587 0,787 0,804 0,41 0,645 0,192 1,340 1,35 0,0 0,601 0,603 1,704 1,166 0,750 0,636 1,104 1,254 0,314 1,779 1,906 0,861 0,150 1,86 1,86 1,509 0,432 1,969 1,451 0,354 1,612 1,25 0,629 0,306 0,555 1,489 1,232 1,106 1,974 0,850 1,991 0,849 1,710 1,288 1,160 1,280 1,339 1,37 0,668 1,210 1,574 0,860 0,148 1,347 1,488 0,480 1,366 1,121 1,915 1,442 1,270 0,595 0,324 0,710 0,327 0,676 1,217 0,751 0,454 0,875 0,931 1,298 1,792 1,700 1,950 1,402 1,583 0,421 1,345 0,346 0,596 1,512 0,600 1,125 1,533 1,916 0,374 0,851 1,409 1,675 0,229 1,947 1,437 0,113 0,737 0,777 0,692 1,681 1,115 0,484 1,535 1,228 1,945 1,519 1,130 0,22 1,700 1,206 0,178 1,2 0,132 1,336 1,588 1,202 0,915 0,714 1,468 1,22 1,155 1,994 1,956 1,343 0,105 1,65 1,173 0,361 0,209 1,577 1,123 1,519 0,458 1,719 0,203 1,924 0,154 1,570 1,221 0,238 1,845 0,540 0,709 0,545 1,168 0,31 0,369 0,696 1,208 0,102 1,431 1,270 1,424 0,142 1,914 0,277 0,389 0,105 0,485 0,67 1,524 1,100 0,550 1,171 0,126 1,102 1,118 1,728 0,526 1,656 0,376 0,175 0,835 1,103 1,581 0,923 1,217 1,356 1,255 0,385 1,264 1,408 1,22 0,177 1,917 0,171 0,607 0,813 0,261 1,234 0,601 1,919 0,92 1,653 1,655 1,841 0,602 1,407 0,148 1,411 1,319 1,4 1,805 0,531 0,432 0,929 1,594 1,834 0,963 1,444 1,730 1,868 0,356 0,394 0,159 0,690 1,555 0,670 0,901 1,73 0,927 0,842 1,24 1,719 0,757 0,288 1,543 0,442 0,505 1,805 0,960 0,387 1,741 1,732 1,955 0,917 0,989 1,470 1,967 0,699 1,623 1,919 0,325 1,17 1,737 1,725 1,252 1,422 0,532 0,812 0,818 0,525 0,718 1,367 1,18 0,373 0,372 0,450 0,360 0,291 0,786 1,471 0,196 1,231 1,383 0,685 0,498 0,333 0,252 1,796 1,295 1,116 0,591 1,968 1,965 0,102 0,607 1,939 1,340 0,594 0,960 0,310 0,850 1,786 0,538 0,320 0,24 1,302 1,330 0,427 1,523 1,598 0,630 1,455 1,134 0,403 0,177 0,813 1,718 0,144 1,452 1,658 1,222 0,634 1,196 1,750 1,220 0,110 1,597 0,328 1,816 1,148 1,749 1,235 0,168 0,215 1,729 1,809 1,413 0,716 0,337 1,519 0,513 1,498 1,624 1,352 1,612 0,580 0,877 0,610 0,475 0,992 1,489 1,940 1,903 0,91 0,127 0,206 1,238 1,639 0,20 0,83 1,87 1,210 1,799 1,386 0,81 0,107 1,823 0,181 1,877 1,37 0,421 0,679 1,967 1,586 1,61 1,120 0,803 1,302 1,36 1,972 1,278 0,145 1,189 0,864 1,424 0,591 0,380 0,58 1,409 1,708 1,352 1,43 0,258 0,267 1,561 1,786 0,506 1,757 1,784 0,37 0,372 1,584 0,623 1,786 0,921 1,77 1,880 0,778 0,961 1,479 0,125 1,236 1,520 0,558 1,897 1,764 1,644 1,353 0,260 0,162 0,528 0,354 0,103 1,701 1,800 0,77 0,722 0,568 1,140 0,513 1,650 1,783 0,85 0,994 0,997 1,425 0,617 0,836 0,569 1,363 1,44 0,63 0,365 0,87 0,656 0,235 0,728 0,13 1,72 0,380 1,539 1,160 1,511 0,817 1,621 0,722 0,5 1,463 1,350 0,77 0,829 1,562 0,43 1,70 1,652 0,376 1,622 0,384 0,742 0,947 1,333 0,549 1,760 1,758 1,204 0,571 0,828 1,535 0,581 1,890 0,973 1,750 0,890 1,637 1,756 1,86 0,45 0,422 0,123 1,710 1,575 0,477 0,742 0,752 0,872 1,737 1,926 0,648 0,427 0,961 0,127 1,729 1,975 0,486 1,23 1,728 1,312 1,418 0,617 1,875 0,243 1,46 1,880 0,541 0,626 0,765 1,768 1,641 1,171 1,594 0,839 0,619 0,495 0,981 0,162 1,452 0,993 1,279 0,805 1,641 0,7 0,722 1,864 0,908 0,558 1,116 0,94 1,197 0,870 1,545 1,592 0,973 0,497 0,818 0,257 0,725 0,739 1,646 1,972 0,394 0,916 0,829 0,356 0,83 1,396 0,27 1,732 0,912 0,128 0,525 0,936 1,222 0,349 1,209 0,274 1,41 0,478 1,19 1,747 0,444 1,605 1,291 1,93 1,631 0,867 0,754 0,909 1,907 1,667 1,579 0,792 0,662 0,113 0,892 1,528 1,747 1,430 1,89 0,190 0,345 0,331 1,841 1,390 0,909 0,482 1,888 1,908 0,419 1,881 0,669 0,797 0,98 1,203 0,106 1,710 1,940 1,888 1,472 1,123 1,195 1,986 0,447 0,156 1,330 0,789 1,281 1,451 0,302 1,663 1,303 0,868 1,973 0,408 0,878 1,263 1,213 0,349 0,729 1,898 1,81 1,467 0,269 0,244 1,478 0,337 0,548 1,250 0,806 1,942 1,346 0,744 0,687 1,861 0,221 0,852 0,964 1,242 0,789 0,184 0,145 0,277 1,134 1,495 0,97 1,22 1,808 1,866 0,212 1,959 1,722 0,975 1,379 0,767 0,147 0,691 0,254 1,100 0,146 0,449 1,306 0,462 1,438 1,339 0,445 0,436 1,336 1,151 1,346 1,144 0,65 1,878 0,240 0,825 1,773 1,796 0,24 0,716 0,834 0,984 1,744 0,251 1,403 0,101 1,933 0,437 0,850 1,863 1,483 1,325 0,706 1,624 0,477 0,742 0,36 0,709 0,628 1,758 0,922 0,279 1,620 0,750 0,540 1,669 0,517 0,11 1,379 0,837 1,525 0,460 0,461 1,871 0,624 0,276 0,955 1,191 0,645 0,159 0,101 1,246 1,536 1,221 1,770 0,736 1,87 1,335 0,804 1,456 0,677 0,986 1,535 0,991 0,935 1,90 0,146 0,323 0,874 0,766 1,368 1,664 0,483 1,566 0,666 1,225 0,694 0,35 1,345 0,946 1,856 0,63 0,270 1,144 0,899 0,729 1,298 0,508 0,865 0,839 1,942 1,621 1,96 0,530 0,971 0,345 0,470 0,996 1,730 0,936 1,234 1,249 0,970 1,427 0,60 1,933 0,786 0,690 0,269 1,748 1,102 0,240 1,583 0,75 1,522 0,481 1,900 0,235 0,636 1,690 1,628 0,300 0,93 1,81 0,148 0,949 1,388 0,975 1,46 1,965 0,647 0,753 0,439 0,564 0,540 0,998 1,379 1,331 0,340 0,48 0,306 1,804 1,42 1,749 0,569 1,533 1,144 1,443 0,561 0,552 1,671 0,81 1,286 0,250 0,162 0,580 1,138 0,494 0,5 0,442 0,498 1,6 1,416 1,455 1,824 1,417 0,432 0,559 1,528 0,587 0,153 1,747 1,977 0,150 1,35 0,414 1,120 1,319 0,307 1,921 1,681 0,352 1,62 0,416 1,240 0,374 0,405 1,493 0,519 0,977 0,649 1,890 0,376 0,445 1,404 0,615 1,672 1,961 1,577 1,571 1,101 1,797 0,986 0,286 0,974 1,432 0,226 1,755 1,118 1,259 1,12 1,657 1,948 0,67 0,186 1,11 1,107 0,12 0,500 0,709 0,485 0,235 1,822 0,843 0,982 1,811 0,651 0,872 1,662 0,89 0,661 1,523 0,35 1,145 0,120 0,874 1,944 0,876 1,878 1,828 0,906 0,583 1,214 0,491 0,566 1,91 1,975 0,496 1,472 1,551 1,442 0,42 0,539 0,466 0,486 0,22 1,781 1,156 1,763 0,500 0,442 0,48 0,191 1,320 0,991 0,418 1,849 1,202 0,486 0,294 1,139 1,126 0,879 0,185 0,849 0,111 1,543 0,808 1,536 1,6 0,273 0,11 0,161 1,604 1,56 1,645 1,158 1,719 1,730 1,355 1,787 0,25 1,650 0,237 1,556 1,246 0,616 0,478 0,557 0,615 1,877 0,401 0,169 1,397 1,640 0,737 0,893 1,710 0,157 0,319 1,287 0,174 0,168 0,574 0,896 0,469 0,771 1,476 0,91 0,833 1,720 0,946 1,172 0,945 0,604 1,374 0,498 1,429 1,604 1,434 1,99 0,236 0,673 0,379 0,842 0,152 0,136 0,153 0,912 1,149 1,967 1,797 1,798 1,865 1,592 0,179 1,349 1,782 1,333 0,421 1,446 1,431 0,92 1,924 1,249 1,832 1,390 0,137 0,998 0,332 0,702 0,116 0,615 0,884 0,872 0,281 1,65 1,228 1,147 0,963 0,337 0,393 0,748 0,349 1,504 0,223 0,817 1,5 0,568 1,70 1,834 1,460 1,218 1,8 1,807 1,474 0,540 1,865 1,314 0,29 1,227 1,995 1,248 0,336 1,195 0,311 1,365 1,912 1,492 1,190 1,274 1,536 1,473 1,247 0,684 0,84 1,187 0,485 0,508 0,459 0,234 0,514 1,581 1,204 1,534 0,471 0,693 0,390 0,626 0,292 1,148 0,662 1,740 0,78 1,417 0,262 1,823 0,547 1,221 0,300 1,619 0,338 0,382 1,909 0,258 0,177 1,363 0,467 1,412 1,963 1,846 1,863 1,303 0,155 0,207 0,449 0,169 0,974 1,689 0,500 1,827 1,249 1,610 0,518 1,675 1,70 1,627 0,746 1,234 1,478 1,819 0,597 0,607 1,509 0,107 0,682 1,316 1,373 1,95 0,87 0,103 0,461 0,311 1,124 0,488 1,51 0,412 1,347 1,867 0,647 1,723 1,572 1,242 0,191 0,654 0,879 0,416 0,832 0,783 1,656 0,502 0,568 0,484 0,489 1,755 0,337 1,849 1,378 0,168 1,361 1,537 1,278 1,606 0,628 0,614 1,323 1,53 1,379 1,641 0,760 1,758 0,222 1,965 0,285 1,150 1,765 0,14 0,429 1,676 1,565 0,245 0,495 1,187 1,345 0,380 0,886 1,28 1,979 0,987 1,481 0,543 0,160 1,249 0,961 1,53 0,527 0,854 0,808 0,355 0,972 1,713 1,137 1,96 1,325 0,740 0,27 1,562 0,885 0,666 1,47 1,586 0,341 0,285 0,5 1,672 0,392 0,459 0,961 1,816 0,972 0,499 0,952 1,139 1,781 0,12 0,802 0,969 1,18 1,337 0,948 0,870 1,751 1,60 0,227 0,39 0,482 0,751 0,795 1,305 0,487 0,305 0,253 0,251 1,235 0,688 1,396 1,82 0,699 1,256 1,599 1,498 0,987 0,524 1,720 0,315 1,941 0,322 0,444 0,539 1,42 0,241 0,407 1,960 1,160 1,26 0,549 0,748 1,11 1,353 0,80 0,42 0,0 1,633 0,744 1,17 0,707 1,526 1,65 1,131 0,288 0,397 0,147 1,0 0,916 1,350 1,749 0,951 0,71 0,549 1,404 0,764 1,960 1,181 0,332 0,986 1,179 1,340 1,70 1,67 1,829 0,886 1,988 1,957 0,779 0,57 1,344 1,639 0,541 0,975 1,501 0,605 0,211 0,594 0,982 0,728 0,238 1,87 0,285 1,427 1,847 0,409 1,260 0,410 1,706 1,649 1,27 1,800 0,322 0,974 1,432 0,930 0,828 0,408 1,768 1,101 0,448 0,606 1,158 0,858 0,32 0,570 0,46 0,465 1,615 1,385 0,814 1,101 0,87 1,184 0,418 0,124 0,314 1,101 0,84 1,200 0,2 1,832 0,26 1,466 0,272 0,315 1,994 1,733 1,786 0,669 0,654 0,599 1,602 0,455 0,276 0,173 0,429 1,6 0,709 0,957 0,147 1,694 1,631 0,114 1,469 1,300 0,587 1,359 1,641 1,949 0,537 0,525 1,446 0,182 0,784 1,403 1,137 0,433 0,386 1,390 1,202 0,412 1,416 0,902 1,691 0,45 0,142 1,128 1,630 0,425 1,477 1,664 0,271 1,875 1,798 1,23 0,137 1,517 0,720 1,865 0,95 0,638 0,982 0,849 0,722 1,875 1,190 1,581 1,883 0,975 0,295 1,633 0,465 0,904 1,487 0,737 1,170 0,830 1,81 1,227 1,792 0,192 1,778 0,247 0,680 1,816 1,272 0,475 0,313 0,78 0,984 0,905 0,397 1,489 1,272 0,443 1,780 1,181 1,204 0,192 1,371 0,456 1,9 1,477 1,422 1,180 0,975 0,32 0,985 1,866 1,742 0,15 1,316 0,324 0,470 1,769 0,468 0,605 1,638 0,254 0,365 1,570 1,467 0,878 0,3 0,974 1,830 0,551 0,415 0,321 1,792 1,148 0,240 0,609 1,340 0,964 0,143 0,632 1,528 1,919 1,128 0,305 1,202 0,16 0,441 1,416 1,530 1,122 0,309 1,349 1,459 1,782 1,972 0,787 0,236 0,460 0,252 1,95 1,689 0,170 1,19 1,526 0,264 1,396 0,559 1,814 1,772 0,807 0,789 0,331 0,516 1,219 0,557 0,785 1,171 0,707 1,911 0,928 0,613 0,479 0,504 1,535 0,101 1,204 1,905 0,243 0,634 0,150 0,601 1,928 1,421 0,441 1,944 0,450 0,743 1,678 0,38 1,154 0,945 0,920 0,574 0,879 0,983 0,947 0,909 1,299 1,5 1,569 1,753 1,358 0,455 1,284 0,314 1,123 1,35 0,26 1,983 1,540 0,85 0,731 1,921 0,980 1,744 1,958 0,453 0,266 0,721 1,808 0,111 0,51 0,797 1,353 1,466 0,103 0,572 1,940 0,871 0,247 0,239 0,552 0,689 1,191 0,858 0,305 1,901 1,481 1,79 0,502 0,28 0,266 0,319 1,376 0,179 1,233 1,630 0,244 0,598 1,142 0,842 1,129 1,681 1,418 0,748 0,951 1,28 0,207 1,129 1,977 0,910 0,564 1,576 1,825 0,861 0,93 0,762 1,350 0,660 1,405 1,8 1,682 0,639 0,162 1,974 1,110 1,645 1,378 1,939 0,45 0,345 1,620 1,335 1,257 0,562 0,996 1,63 0,165 0,957 1,572 0,204 0,501 0,811 0,941 0,804 0,870 0,71 0,486 1,986 1,78 0,34 0,779 1,883 0,867 0,54 0,414 1,487 1,404 1,442 0,247 0,391 1,762 1,954 0,297 0,954 0,955 0,926 1,438 0,269 1,774 1,892 1,609 1,641 1,882 1,764 0,974 0,283 1,410 1,944 1,737 1,139 0,73 1,389 0,884 1,835 1,417 0,804 1,562 1,683 0,641 1,127 0,43 0,692 0,893 1,449 0,419 0,8 0,723 0,807 1,101 0,827 0,634 0,931 1,922 0,389 0,848 1,445 1,415 1,162 0,399 1,255 0,284 0,266 1,936 0,507 1,123 0,576 1,48 0,858 0,171 1,513 0,674 0,838 1,104 1,814 0,43 0,689 0,918 0,845 0,492 1,796 1,635 1,865 0,268 0,765 1,242 0,169 1,573 0,658 0,775 1,707 1,119 1,515 1,598 1,100 0,566 0,680 0,901 1,237 1,499 1,578 0,172 1,560 1,844 0,805 1,60 0,480 1,45 1,990 1,934 0,359 1,816 1,366 0,635 1,128 1,454 1,812 0,750 0,30 1,931 0,410 1,510 0,752 0,161 0,943 0,939 1,254 1,211 0,108 1,0 0,362 0,504 1,800 0,287 1,631 0,610 1,366 0,853 0,532 1,868 1,487 1,761 1,593 0,138 1,749 1,905 1,537 0,830 1,507 0,299 1,576 1,787 0,816 1,93 0,778 0,695 1,722 0,551 1,649 1,207 0,124 0,603 0,750 0,674 0,17 0,948 0,290 1,275 0,587 1,930 1,393 0,157 0,664 1,230 0,414 1,364 1,78 1,689 0,152 0,127 1,392 0,332 0,504 0,607 0,931 1,279 0,829 0,731 0,626 1,237 0,141 0,503 0,607 1,692 1,218 1,186 0,674 0,938 1,856 1,732 0,734 0,619 0,81 1,253 0,77 1,611 1,182 1,199 1,378 0,110 0,745 1,250 0,495 1,845 0,217 1,166 0,75 0,260 0,499 1,640 0,750 1,173 1,946 1,645 0,25 0,159 1,668 0,550 0,918 1,140 1,270 0,938 0,973 1,931 0,699 0,491 1,907 1,259 1,383 0,359 1,13 1,168 1,662 0,924 0,906 0,332 0,764 1,996 1,456 1,551 1,999 1,341 1,457 0,692 1,217 0,7 0,562 1,198 0,385 1,756 1,435 0,589 1,936 0,349 0,744 1,643 0,386 0,143 0,510 1,68 0,344 1,852 0,662 0,429 1,115 0,448 1,512 1,360 0,281 1,886 1,332 1,355 1,729 1,692 1,278 0,928 0,586 1,195 0,902 1,635 1,622 1,50 0,53 1,419 0,614 1,452 1,102 0,181 1,70 1,252 0,526 0,338 1,237 1,555 1,855 0,659 1,664 1,388 1,261 1,668 1,93 0,949 0,458 0,827 0,475 1,234 0,711 1,997 1,350 0,809 1,324 0,647 1,762 1,884 0,175 0,419 1,121 0,91 0,579 1,643 1,185 1,803 1,788 1,501 0,358 0,298 0,541 1,844 0,876 0,622 1,671 1,696 0,337 1,124 0,53 1,933 1,8 0,850 0,138 1,303 1,458 0,931 1,564 0,907 1,500 1,271 0,45 1,721 1,902 0,585 0,490 1,800 0,923 0,487 1,929 0,617 0,381 1,217 1,638 0,822 1,718 0,809 1,840 0,152 0,331 0,47 1,829 0,202 0,150 1,980 1,647 0,653 0,525 0,144 0,694 0,457 0,723 1,284 1,82 1,785 1,44 1,391 0,487 1,224 0,449 1,139 1,399 0,617 1,95 1,932 1,193 0,598 1,630 1,140 1,113 1,667 1,197 1,670 0,501 1,272 0,589 1,918 0,175 1,986 0,832 1,634 0,737 0,848 0,390 0,51 0,443 1,920 0,552 1,446 0,944 1,992 1,123 1,875 1,283 1,364 0,109 1,245 1,792 1,701 0,232 1,987 1,154 1,519 0,546 1,928 0,1 0,348 1,65 0,275 1,386 1,376 1,680 0,235 0,654 0,386 1,958 1,650 1,528 0,574 1,702 0,724 1,907 0,851 1,693 0,453 0,98 1,461 0,269 1,818 0,551 0,32 0,770 1,533 0,760 0,18 0,142 1,913 0,649 1,675 1,52 1,179 0,494 1,167 0,161 0,321 1,694 0,269 0,404 0,789 1,744 1,321 0,364 0,617 1,60 1,51 0,708 0,416 0,306 1,691 1,335 0,385 0,42 1,155 1,758 0,746 0,291 1,822 0,620 1,757 1,603 1,929 1,357 1,254 0,270 1,58 1,922 0,751 1,864 0,963 1,176 0,84 0,899 0,532 0,459 1,374 0,272 1,824 1,592 0,341 0,194 1,367 1,543 0,180 0,80 0,614 0,670 1,443 1,242 0,261 0,434 1,294 0,660 0,490 0,23 1,131 1,194 0,418 0,848 0,286 1,649 1,566 0,244 1,587 0,399 1,54 0,808 0,444 1,204 1,938 1,561 0,589 0,163 0,326 0,823 1,834 0,787 0,23 1,795 1,973 1,199 0,415 1,906 1,651 0,586 1,32 1,436 1,645 1,3 1,216 0,951 0,173 1,695 1,570 0,758 1,391 0,876 1,111 1,669 0,719 0,881 0,561 0,653 1,832 1,574 0,349 1,759 0,192 1,142 0,279 1,73 1,814 1,606 0,188 1,923 0,700 1,635 0,294 1,367 1,957 1,676 0,24 1,514 1,647 0,604 0,637 1,203 0,596 0,717 0,619 1,281 0,861 0,881 1,526 1,353 0,922 1,284 0,759 1,972 1,952 0,586 0,449 0,395 0,560 0,838 1,867 1,591 1,378 1,478 0,511 0,352 1,594 1,278 1,298 1,756 1,236 0,923 1,768 1,754 1,84 0,230 1,450 1,511 1,913 0,550 0,448 0,290 1,100 0,391 0,698 1,163 1,17 0,100 0,395 0,795 1,641 1,411 0,523 0,230 0,133 1,562 1,321 0,313 0,157 0,260 0,299 0,800 0,831 0,706 0,336 1,751 0,175 1,478 1,849 0,813 1,458 0,506 0,356 1,640 0,831 0,0 1,38 1,711 0,324 1,535 0,57 1,115 0,543 1,560 0,350 1,281 0,578 0,401 0,400 1,234 0,855 0,401 1,914 0,156 1,192 1,340 1,21 0,677 1,448 1,533 0,204 1,405 0,761 1,666 1,45 1,329 0,656 0,587 1,819 1,107 1,171 1,240 0,846 0,652 0,508 0,14 0,119 0,383 0,856 0,392 1,282 1,257 1,441 1,852 0,192 1,957 0,635 0,728 0,333 0,382 0,780 0,225 0,420 0,807 0,620 1,477 1,852 0,223 0,20 0,168 0,480 0,846 0,864 0,496 1,590 1,50 0,732 0,216 1,20 0,513 0,503 1,65 1,838 0,976 1,903 1,672 1,497 1,846 0,995 1,499 0,784 1,551 0,734 0,271 0,289 0,166 1,509 1,122 0,37 0,542 0,618 1,179 0,78 1,450 1,602 1,250 0,236 0,460 0,622 1,694 0,363 1,761 1,606 0,486 0,268 1,909 0,465 1,375 1,483 0,380 0,673 0,206 1,604 1,0 1,376 0,592 0,38 1,756 0,955 0,894 0,90 1,236 1,775 0,774 0,34 0,337 0,25 1,409 0,504 0,118 0,53 0,79 1,275 1,623 0,228 1,446 0,323 1,730 1,232 0,631 0,384 1,765 0,687 0,321 0,257 1,176 0,991 0,616 0,57 1,896 1,449 1,673 1,539 0,840 0,680 1,835 0,73 1,674 0,9 1,48 1,254 0,377 1,500 1,74 1,966 1,476 0,747 1,98 1,577 0,389 0,565 1,765 1,984 0,166 0,270 1,631 1,216 1,339 0,316 0,322 1,271 1,461 1,511 1,632 0,128 0,253 0,852 1,687 1,764 0,673 1,386 1,784 1,121 0,919 1,618 1,82 1,104 1,219 1,789 0,878 0,736 1,136 1,989 0,258 1,724 1,64 0,676 0,754 1,911 1,238 0,708 1,177 0,79 0,563 1,683 1,366 1,965 0,8 1,649 1,157 0,904 1,138 0,790 0,546 0,289 1,613 1,79 0,445 1,364 1,426 1,965 0,481 1,565 1,842 0,933 1,643 0,88 0,589 1,76 1,490 1,670 0,400 1,805 0,25 0,620 1,637 0,581 0,566 0,409 1,993 0,946 0,298 1,616 1,895 0,209 1,234 0,793 1,25 0,283 0,414 1,284 0,523 1,72 0,473 1,146 0,912 0,299 0,285 1,228 1,888 0,377 1,777 0,685 1,865 0,549 0,358 0,902 0,726 1,837 1,595 0,906 0,286 0,755 0,15 1,305 0,353 1,88 0,40 1,744 0,581 1,779 1,342 1,407 0,4 1,808 0,615 1,303 1,866 1,504 1,461 0,925 1,636 0,396 1,742 0,545 1,606 1,618 1,323 1,416 0,860 0,892 0,655 1,240 1,363 0,659 0,809 1,778 0,994 1,158 0,463 1,127 1,403 1,491 0,805 0,559 1,346 0,8 1,987 0,263 1,287 0,533 0,112 1,575 0,804 0,23 0,476 1,67 1,300 0,512 0,153 0,523 0,42 0,195 0,485 0,452 0,213 0,240 1,480 1,510 1,941 0,744 0,24 1,672 0,858 1,325 0,959 0,922 0,598 1,815 1,419 1,814 1,72 1,826 1,134 0,452 0,580 1,531 0,104 0,263 0,650 0,185 0,822 1,620 1,737 0,925 1,983 1,430 0,513 0,128 1,308 0,435 1,837 1,392 1,14 1,703 1,899 1,5 0,303 0,36 1,169 1,230 1,181 0,835 0,835 1,251 1,757 0,189 0,83 1,210 1,748 1,61 0,226 0,120 1,508 0,841 1,220 1,423 1,833 0,649 0,683 1,671 0,452 1,717 0,231 0,779 0,318 0,425 1,333 1,486 0,38 1,539 1,768 1,514 0,496 0,55 1,648 1,814 0,400 1,809 0,154 0,808 1,690 0,642 1,323 1,643 1,738 0,961 0,115 1,750 1,347 0,157 1,206 1,23 1,758 0,65 0,784 1,106 1,41 1,159 1,834 1,997 0,733 1,291 0,935 1,211 0,388 0,50 1,966 0,696 1,690 0,1 1,213 0,198 1,990 0,472 1,72 1,984 0,903 0,585 1,468 0,118 0,913 0,838 0,687 1,668 1,697 1,24 1,678 0,564 0,836 0,817 1,455 0,376 1,181 0,524 1,350 1,124 0,877 0,173 1,81 1,924 0,20 0,655 1,45 1,130 0,18 0,361 0,480 0,883 0,878 1,75 1,128 0,390 0,577 0,667 0,851 1,233 0,419 1,147 1,308 1,91 1,190 0,592 1,141 0,518 1,41 1,943 0,366 1,321 0,227 0,855 0,174 0,491 0,570 1,743 1,949 1,871 1,91 1,865 1,180 0,683 1,967 1,556 1,750 1,961 1,453 0,3 0,341 0,497 0,151 0,40 0,55 1,393 1,151 1,63 0,63 0,339 1,222 1,269 1,745 1,256 1,466 0,861 0,558 0,670 1,943 1,552 1,406 1,892 1,701 0,680 0,654 0,364 0,131 1,627 0,995 1,310 1,427 0,266 0,344 0,716 1,713 1,176 0,245 0,194 1,389 0,537 1,351 1,188 1,108 0,675 1,785 0,149 0,559 1,835 0,103 0,311 0,544 0,77 0,754 1,304 1,546 1,645 0,335 0,595 1,364 0,469 0,70 0,225 0,166 1,841 1,554 1,345 1,424 0,460 1,204 1,539 0,630 0,803 1,741 1,695 0,96 0,108 0,785 0,337 0,562 0,857 1,338 1,600 0,857 1,237 1,329 1,9 1,30 0,662 0,627 0,801 0,609 1,245 0,734 0,94 0,783 1,344 0,662 0,601 1,514 0,507 0,413 0,752 0,839 0,933 0,749 1,996 0,878 1,8 1,606 1,185 0,419 1,767 0,53 1,135 0,379 0,128 1,549 0,638 1,161 1,32 0,854 0,127 0,92 1,623 0,638 1,578 1,880 0,336 1,786 0,866 0,623 1,36 0,259 1,216 1,467 0,931 1,831 0,14 0,779 0,550 0,874 0,494 0,947 0,854 0,410 0,670 1,219 0,892 1,44 1,246 1,401 0,393 0,498 1,851 1,725 0,790 1,544 0,308 0,725 0,330 0,428 0,100 1,777 1,124 1,810 0,902 1,695 1,795 1,792 0,968 1,634 0,309 1,49 0,708 0,278 1,978 1,58 1,384 0,575 1,294 0,544 1,370 1,254 1,559 1,109 0,290 0,418 0,452 0,658 0,62 0,85 1,466 0,989 1,5 0,120 1,842 1,740 1,944 0,286 0,66 1,611 0,865 0,534 0,81 1,438 0,412 1,278 0,438 0,989 0,906 1,404 1,648 0,784 0,488 0,485 1,846 0,241 0,391 1,758 0,338 1,314 0,45 0,941 1,676 0,661 1,252 0,665 1,164 1,63 1,815 1,64 0,274 0,492 0,326 1,778 1,858 1,136 1,978 0,977 0,100 0,538 1,993 1,228 0,985 0,854 1,34 1,982 1,786 1,40 0,1 0,807 0,227 1,690 1,839 0,201 1,176 0,129 1,217 1,879 0,441 1,384 1,69 1,183 0,345 1,332 0,267 1,113 1,442 1,858 1,691 1,258 1,820 1,997 0,27 0,220 1,458 1,895 1,11 1,623 0,603 1,503 0,202 1,387 1,657 1,333 1,599 0,955 0,883 0,13 1,726 0,533 1,507 1,33 0,765 0,897 1,690 0,783 0,603 1,946 1,288 0,112 0,250 1,726 1,745 0,358 0,580 0,526 1,336 0,750 0,286 0,884 1,145 0,62 0,72 1,417 0,236 1,423 0,615 0,900 1,108 1,802 0,79 0,358 1,223 0,175 1,721 1,442 0,386 0,578 1,82 1,33 0,506 0,202 1,865 1,753 1,714 1,140 0,183 0,755 0,588 0,420 1,834 1,271 1,515 0,118 1,850 1,113 1,953 0,867 1,299 0,678 1,167 0,634 1,701 1,724 0,145 1,549 1,495 1,883 1,65 1,79 0,66 0,942 0,321 1,537 0,99 0,731 0,947 1,698 0,796 1,686 0,673 0,545 0,744 0,477 1,827 0,277 1,113 1,160 0,139 1,313 1,95 0,802 0,123 1,338 0,969 0,628 0,423 0,341 0,964 0,873 1,912 1,38 1,213 0,78 0,102 0,75 0,239 1,878 1,595 1,536 0,17 1,551 0,635 0,21 1,572 1,437 1,808 1,498 1,154 1,82 0,821 0,67 0,246 0,681 1,50 1,81 0,631 1,616 0,753 0,74 1,62 1,77 0,675 1,773 0,144 0,267 1,516 1,209 1,59 0,362 0,233 1,517 1,986 1,839 1,174 0,508 1,190 0,424 1,56 1,367 1,611 0,893 1,157 1,696 1,819 1,146 0,622 0,291 0,398 0,265 0,7 1,881 0,424 0,359 0,228 1,680 1,105 1,566 0,45 0,846 0,681 1,652 1,634 1,378 1,178 0,458 1,163 0,748 1,815 0,564 0,102 1,227 0,257 1,485 0,163 0,346 1,14 0,897 0,741 0,165 0,522 1,896 0,337 1,703 1,105 1,90 1,842 1,596 0,608 1,76 0,386 1,65 1,419 1,249 0,234 1,864 0,605 1,142 1,109 0,467 1,557 1,143 0,775 1,731 1,627 0,137 1,166 1,369 1,267 0,748 1,143 0,959 0,687 1,954 0,581 1,368 1,922 1,21 0,885 1,813 0,678 0,6 1,569 0,801 0,451 0,304 1,844 1,831 1,128 1,142 1,287 1,813 1,563 0,813 1,648 0,710 1,439 1,233 1,948 1,739 1,630 1,561 0,516 0,42 1,39 0,808 0,618 1,939 1,806 1,372 1,966 0,791 0,659 0,838 0,996 1,583 1,839 0,96 0,806 1,868 0,991 1,270 1,179 0,618 0,560 1,240 1,108 0,499 1,746 1,982 1,500 1,902 1,474 0,206 0,144 0,345 0,511 1,991 1,288 0,881 0,635 0,125 1,438 1,837 1,796 1,160 1,274 0,351 1,849 1,625 0,919 1,300 0,427 1,626 1,592 0,537 0,588 1,742 0,636 1,983 0,98 1,545 1,384 0,440 1,460 1,964 0,389 1,150 0,581 1,197 0,133 0,81 0,45 0,158 0,349 1,677 0,461 1,974 1,152 1,650 0,426 1,373 0,707 0,409 0,863 0,794 1,738 0,410 1,727 0,699 0,175 1,113 1,573 0,287 0,327 0,818 0,908 0,995 0,913 1,591 1,845 0,392 0,445 0,745 1,815 1,894 0,58 0,697 1,256 1,948 0,946 0,561 1,906 0,606 1,302 1,270 0,722 0,269 1,412 1,709 0,682 1,846 0,912 1,533 0,612 0,365 1,831 0,805 0,225 0,266 1,335 0,278 0,966 1,386 0,824 1,151 0,960 0,682 0,84 0,932 0,637 1,71 1,192 0,571 1,753 0,422 1,297 1,435 1,941 0,835 1,105 1,848 0,136 1,480 0,762 1,440 0,169 0,500 0,589 0,378 0,188 1,475 1,771 0,500 0,271 1,720 0,841 1,997 0,435 0,696 0,870 1,400 0,652 0,763 0,472 1,836 0,843 0,537 1,462 1,468 0,427 0,654 1,185 0,664 0,242 0,424 0,683 0,85 1,929 1,149 0,275 1,41 0,962 1,517 0,524 1,418 0,914 1,461 0,999 0,309 0,69 1,551 1,239 1,538 1,872 0,213 0,738 0,863 0,570 1,989 0,951 0,239 1,235 1,602 0,119 1,136 0,686 1,356 1,158 0,1 1,833 1,467 1,419 0,398 0,586 0,339 1,155 0,196 0,159 1,500 0,317 0,769 1,245 1,563 1,421 0,728 0,437 1,151 0,716 0,771 1,20 0,846 1,543 0,117 1,587 1,914 0,110 0,38 0,573 0,542 1,171 1,796 1,690 0,634 1,659 0,435 0,229 0,389 0,777 1,712 1,437 0,559 1,878 0,10 0,2 1,797 0,24 0,614 1,188 0,244 0,370 1,803 1,210 0,376 0,557 0,683 1,377 0,712 0,575 0,438 1,969 1,609 1,212 1,91 1,841 1,960 1,703 0,428 1,806 0,966 1,637 1,192 1,313 1,17 1,242 0,850 0,219 1,945 0,628 0,211 1,16 0,411 1,741 1,521 1,376 0,155 0,65 1,839 1,234 0,14 0,192 1,513 1,61 1,488 1,618 0,954 0,259 1,960 0,61 1,613 0,382 1,885 0,809 0,35 1,821 0,108 0,746 0,784 1,192 1,363 1,894 1,291 1,422 1,571 1,105 0,444 1,783 1,185 1,427 0,504 0,297 0,882 1,758 0,242 1,398 1,996 1,258 0,486 1,213 1,706 0,675 0,867 1,510 1,732 1,547 0,498 1,859 1,559 1,622 0,253 1,23 1,75 1,103 0,889 0,122 1,482 1,430 1,427 0,527 1,135 1,911 0,653 0,336 0,858 1,823 1,129 1,10 0,106 1,143 0,166 1,511 0,990 0,752 0,672 0,728 1,195 1,797 0,927 1,198 1,97 0,218 1,969 0,694 1,272 1,597 0,76 1,129 0,107 1,139 1,982 0,377 1,283 0,662 1,686 1,640 1,535 0,195 0,748 0,662 1,677 0,440 0,558 1,477 0,852 1,453 0,228 0,798 0,316 1,179 1,330 1,868 0,803 0,876 1,554 1,338 0,810 0,305 1,196 0,991 0,127 1,316 1,666 0,293 0,114 0,174 0,989 0,768 1,114 1,514 1,769 1,439 1,89 0,358 1,421 0,343 1,760 0,19 0,799 0,251 0,414 1,512 1,367 1,768 0,231 1,44 1,669 1,569 1,404 1,780 0,986 1,851 1,277 0,287 1,105 0,745 1,95 1,973 1,244 0,518 0,867 1,483 0,729 1,400 1,346 0,43 1,437 0,348 1,29 0,913 1,804 1,167 0,223 1,298 0,608 0,151 1,660 0,576 1,236 1,264 1,964 1,320 1,256 0,829 0,306 0,212 1,905 1,642 0,658 1,481 0,813 1,584 1,777 0,481 0,92 0,819 0,582 0,62 1,38 0,137 1,340 0,467 1,401 1,712 0,959 0,188 0,295 1,658 0,248 1,804 1,539 1,306 0,901 1,989 1,800 1,793 1,29 1,661 0,513 0,895 0,615 1,471 1,535 0,373 0,879 0,316 1,34 1,743 0,447 1,555 1,168 1,143 0,842 1,539 1,579 1,682 0,93 0,362 1,178 1,444 1,964 0,13 1,506 1,692 0,377 0,163 1,714 0,611 0,766 0,554 1,246 0,747 0,84 0,359 1,662 1,263 1,154 0,499 0,762 1,501 0,880 0,403 1,812 0,987 1,11 1,133 0,665 0,246 0,886 0,988 1,673 0,421 1,310 1,767 1,609 0,393 0,495 0,248 0,519 0,546 0,851 0,323 0,561 1,190 1,892 0,945 1,523 1,869 1,164 1,452 1,124 0,325 0,143 1,618 1,38 1,770 0,515 1,698 0,434 1,228 1,691 1,969 0,971 1,321 0,795 0,118 0,238 1,4 1,792 1,65 0,854 1,531 0,574 0,661 0,196 1,153 1,517 0,320 1,62 1,981 0,41 1,729 0,321 1,474 0,870 1,974 0,743 1,52 0,544 1,88 0,876 1,828 1,125 0,311 0,203 1,743 1,755 1,920 0,538 0,747 1,131 1,198 1,122 1,149 1,694 1,580 1,226 0,671 0,359 0,516 0,581 1,572 0,76 1,178 0,307 1,237 1,361 0,270 0,440 1,646 0,219 1,886 1,491 1,789 1,245 0,629 0,701 0,110 1,429 1,401 0,784 0,300 1,56 0,792 1,488 0,411 1,162 1,427 1,11 0,452 0,942 0,125 1,339 0,858 1,227 0,235 0,826 1,417 1,645 1,831 0,7 0,62 0,791 0,759 1,949 1,370 0,152 1,458 1,952 1,112 0,759 0,953 1,288 1,437 1,602 0,605 0,685 0,477 1,67 1,824 1,153 0,48 0,124 0,461 0,769 1,671 1,453 0,700 0,639 0,479 0,766 0,214 0,130 1,330 0,70 1,919 1,918 0,162 0,674 0,630 1,716 1,368 1,222 0,855 0,541 0,517 1,500 0,112 1,711 0,329 1,273 0,986 1,45 0,53 1,793 0,599 1,851 1,666 0,529 0,984 0,940 1,835 0,501 0,377 0,913 0,454 1,988 0,882 0,320 0,469 1,493 0,300 1,38 0,81 0,921 0,41 1,510 0,354 1,61 1,189 0,426 1,748 1,726 0,46 0,499 1,88 0,521 0,943 1,293 1,433 0,610 1,615 1,133 1,689 1,806 0,785 0,621 0,20 1,187 0,371 0,834 0,781 1,922 0,944 1,164 1,240 1,42 1,754 1,296 0,991 1,605 1,511 1,86 0,406 0,302 1,56 1,72 0,294 1,199 0,322 0,282 1,156 0,897 1,958 1,44 0,671 1,945 1,326 1,852 0,929 1,282 0,305 1,80 1,679 1,799 1,827 0,377 0,127 1,499 1,203 0,61 1,784 1,489 0,934 0,917 0,598 0,774 1,173 1,355 0,836 1,847 0,236 1,44 1,894 1,940 0,310 1,500 0,453 1,254 0,2 0,599 0,242 1,163 0,95 0,366 1,980 0,293 0,919 1,127 1,419 1,707 1,512 0,492 1,509 1,283 0,811 1,489 1,524 1,722 1,701 0,153 1,42 1,667 0,796 0,893 0,722 0,121 0,975 0,390 0,702 1,397 1,613 0,993 0,55 0,49 0,443 1,158 0,653 0,952 1,623 0,310 0,135 1,131 1,170 1,862 0,757 0,293 1,29 1,832 1,544 1,881 0,88 1,627 0,536 0,290 1,404 0,772 0,618 0,347 0,447 1,294 0,65 0,868 1,35 0,378 0,737 0,236 1,813 1,140 1,345 1,113 0,388 1,906 0,701 1,284 0,988 0,886 1,660 0,448 1,319 0,22 0,533 1,10 1,331 0,206 1,315 1,381 1,578 0,3 0,562 1,901 0,311 0,956 0,557 0,477 1,623 1,653 0,865 0,936 1,991 1,73 0,708 1,506 0,124 1,937 1,837 1,891 1,155 0,990 0,44 1,103 0,829 0,23 1,940 0,307 1,554 1,355 0,276 0,879 1,620 1,835 1,801 0,323 1,319 1,827 1,111 0,471 0,846 1,473 0,269 1,863 0,766 0,269 1,438 1,304 1,83 0,792 1,146 1,478 1,532 0,239 0,111 0,950 0,914 1,374 0,468 0,131 0,403 1,526 1,556 0,938 0,872 0,600 1,577 0,586 0,941 1,847 1,62 0,82 0,387 1,641 1,110 1,459 1,739 1,708 0,853 1,576 1,972 0,858 1,100 0,302 0,83 1,798 0,835 1,630 1,589 0,768 1,835 0,453 0,265 0,425 1,439 0,635 1,775 0,498 0,46 0,980 0,440 1,54 0,759 0,152 1,478 1,475 0,181 0,62 1,113 0,479 0,275 0,537 1,372 1,477 1,939 1,979 0,954 1,929 0,549 0,857 0,136 1,114 0,566 1,94 1,807 1,759 1,879 0,496 1,468 1,302 0,841 0,869 0,652 1,963 1,4 1,639 1,370 1,970 1,955 0,182 0,651 1,652 0,494 1,208 1,556 0,70 1,946 1,474 1,999 1,380 1,55 0,63 1,587 1,670 1,656 0,646 0,897 1,915 0,566 1,235 0,858 1,645 0,799 1,752 1,716 1,358 1,222 0,42 1,139 1,530 1,182 0,629 0,331 0,158 0,267 0,489 0,433 1,225 1,468 0,728 1,39 0,640 1,659 0,31 1,657 1,178 1,697 0,710 1,501 1,995 1,169 1,447 0,490 1,11 1,835 1,373 1,612 0,211 0,33 1,426 1,72 1,564 1,260 0,935 0,913 0,663 1,826 0,961 0,56 1,315 1,517 1,222 0,980 0,680 0,568 0,819 1,739 0,467 0,158 0,869 0,48 1,822 1,906 0,249 1,748 1,499 1,478 0,128 1,85 0,212 0,421 0,214 0,422 1,153 1,932 1,474 1,608 0,417 0,899 1,229 0,893 0,600 0,799 1,284 0,634 0,635 0,249 1,604 0,585 0,637 1,619 1,120 0,766 1,588 0,408 0,646 1,743 1,787 0,332 1,835 1,174 0,660 0,320 0,970 0,440 1,870 0,54 0,779 0,880 0,932 1,311 0,464 1,84 0,105 1,647 0,484 1,937 1,668 1,277 1,18 1,275 1,93 0,95 0,99 1,363 0,468 1,322 1,696 1,491 1,700 1,723 0,997 1,306 1,650 1,424 1,569 1,88 1,914 0,106 0,466 0,395 1,680 1,817 1,504 0,810 0,241 1,754 1,678 1,974 1,564 0,900 0,60 0,591 0,913 0,184 0,348 0,358 1,230 1,324 1,198 0,891 0,712 1,431 0,890 1,395 0,635 0,312 1,685 1,398 0,667 0,200 0,705 0,734 0,667 0,693 0,869 0,213 0,521 0,931 1,231 1,422 1,304 1,928 1,903 1,396 1,239 0,945 0,123 0,92 0,545 0,910 1,488 0,625 0,733 1,921 0,16 1,672 0,938 0,109 1,253 0,829 0,259 0,453 0,712 1,483 1,621 0,300 1,817 0,915 1,219 1,12 0,149 1,374 0,889 1,445 1,699 0,133 0,799 0,340 0,952 0,520 0,646 0,713 0,236 0,885 1,735 0,393 1,739 1,858 1,455 0,57 0,21 0,588 0,440 1,160 1,747 0,57 0,47 1,652 1,151 1,624 0,853 1,317 1,163 0,940 0,776 1,240 0,401 0,510 0,301 1,185 0,340 0,826 0,646 1,928 1,830 0,835 1,684 1,75 0,755 0,393 0,468 0,924 0,12 1,105 1,206 0,413 0,460 0,297 0,490 1,82 1,468 0,616 1,986 0,337 0,566 1,926 0,837 0,755 0,879 0,977 0,246 0,85 0,736 1,456 1,36 1,147 1,869 1,733 0,173 1,918 0,956 1,549 1,228 0,919 0,809 1,605 0,256 0,96 1,554 1,559 1,265 0,63 0,674 0,623 1,835 0,287 0,204 0,483 1,667 1,722 1,360 0,810 1,149 1,822 1,315 1,0 0,568 1,789 1,796 0,288 1,215 1,102 0,906 1,503 1,543 1,855 1,209 1,189 1,310 0,42 0,419 1,71 1,559 0,197 0,987 0,160 1,624 1,288 1,650 1,700 0,274 0,166 0,585 1,228 0,278 1,787 0,983 1,63 0,492 1,742 1,4 1,47 0,422 0,466 0,29 1,616 0,505 1,301 1,757 0,204 1,896 0,354 0,671 0,709 1,48 0,905 1,588 0,657 1,403 1,869 0,807 1,210 1,886 0,526 1,106 0,363 0,666 1,39 1,420 0,518 1,383 0,118 1,765 1,18 1,462 0,294 1,101 0,806 1,303 1,321 0,318 0,460 0,638 1,22 0,848 0,223 0,578 0,872 1,14 0,984 1,560 0,315 0,435 0,826 1,286 1,405 0,799 0,43 1,941 0,830 1,286 1,103 0,210 0,331 1,499 1,73 1,426 1,554 0,270 1,627 0,48 0,775 1,57 1,904 1,471 0,394 1,946 1,839 1,817 1,808 0,192 1,631 0,286 1,13 0,417 1,275 1,238 0,921 1,807 1,426 0,926 0,628 0,37 0,50 0,471 1,197 0,707 0,502 1,540 0,139 1,206 0,554 1,1 1,668 0,234 1,422 0,64 0,112 1,98 1,980 0,138 0,74 1,802 0,444 0,171 1,723 0,553 1,362 1,764 1,734 1,538 1,314 1,145 0,590 0,107 1,645 0,534 1,623 1,477 0,150 0,847 0,766 1,555 1,157 0,517 0,717 1,567 0,341 0,385 1,400 1,606 0,535 0,23 1,792 1,788 0,564 0,966 1,234 0,368 1,428 1,319 1,923 0,175 1,245 1,408 1,881 1,524 0,796 1,390 1,180 1,306 1,950 0,861 0,656 1,911 1,581 1,188 0,362 0,86 0,533 1,526 1,359 0,198 0,669 1,6 1,74 0,812 1,564 0,210 0,577 1,455 1,791 0,406 0,914 0,159 0,620 1,868 0,676 1,701 0,234 0,761 1,822 1,502 1,199 0,757 1,780 0,41 0,734 1,902 1,965 1,514 0,227 0,74 1,660 0,873 0,794 1,772 0,481 1,908 0,250 1,860 0,46 0,775 1,963 1,387 1,84 0,802 1,611 1,276 1,150 1,251 0,631 0,443 1,315 1,375 0,858 0,646 0,270 1,577 1,663 1,200 0,293 0,156 1,7 0,951 0,193 1,535 1,621 1,977 1,930 1,819 1,401 0,413 0,59 0,150 1,356 1,724 0,608 0,191 1,366 0,327 0,523 1,424 0,830 1,613 0,123 0,779 1,485 0,646 1,129 0,768 0,182 1,314 1,54 1,395 1,246 0,805 0,475 0,934 0,690 1,291 1,541 1,333 0,374 1,346 0,745 0,105 1,559 1,423 0,709 0,638 0,185 0,721 1,154 0,591 1,715 1,756 0,418 0,606 0,962 1,716 0,36 1,906 1,337 0,190 0,618 1,937 1,502 0,117 1,979 1,604 0,186 1,475 1,588 0,278 1,699 1,486 0,421 0,531 0,878 0,373 1,902 1,238 1,557 0,794 0,647 1,254 0,444 0,744 1,296 1,16 1,42 1,696 1,361 1,248 1,114 1,695 1,511 1,484 1,905 0,386 0,403 0,470 1,935 1,260 0,920 0,380 0,825 0,178 0,137 0,221 0,262 1,521 1,213 1,725 0,365 0,245 0,799 1,376 0,673 0,536 0,313 1,280 1,543 1,727 0,652 1,108 0,375 0,633 0,77 0,650 0,264 0,509 1,85 1,994 0,525 0,388 0,149 1,392 1,769 1,698 0,548 1,605 1,140 0,609 0,18 1,34 1,147 0,569 0,704 1,91 0,369 1,113 0,674 0,889 0,418 0,384 0,714 0,842 1,674 1,373 0,813 1,228 0,51 1,444 1,539 0,98 0,106 1,502 1,188 0,505 1,691 0,556 1,523 0,77 1,825 1,546 1,861 1,900 0,58 1,958 0,643 0,923 0,196 1,948 0,932 1,806 0,936 1,968 0,645 1,174 0,498 1,563 0,103 0,352 1,516 0,46 0,43 1,972 0,297 0,390 1,943 0,267 1,637 1,791 0,20 0,749 1,47 1,198 0,281 1,767 0,772 1,27 1,582 0,214 1,33 1,942 0,448 0,565 0,308 0,630 0,639 1,296 0,812 1,349 0,821 0,976 1,439 0,585 1,671 1,477 1,95 1,12 1,983 1,302 0,503 1,897 0,338 0,348 1,671 0,650 0,543 0,981 0,702 1,845 0,967 1,88 0,837 1,466 0,355 0,355 1,269 1,272 0,348 0,306 0,29 0,882 0,858 0,701 0,212 1,891 1,360 1,430 0,304 0,914 1,758 0,594 1,18 1,411 1,175 0,136 1,912 0,29 1,265 0,507 0,308 1,748 1,753 1,264 0,141 0,999 0,61 0,232 1,17 1,123 0,310 0,49 1,1 1,267 0,57 1,98 0,12 0,56 0,814 1,359 0,169 1,94 1,275 0,59 0,323 1,380 0,174 1,834 0,58 1,583 0,292 1,800 1,349 0,765 0,877 1,697 0,136 1,544 1,849 0,447 0,57 1,126 0,231 0,195 0,295 0,108 0,87 1,623 1,486 1,329 1,426 0,820 1,638 0,660 1,603 1,470 1,423 0,894 0,453 1,86 0,921 1,833 0,149 0,735 0,740 1,723 1,318 1,795 1,191 0,67 1,57 1,363 0,178 1,277 0,194 1,138 0,147 1,780 1,224 1,372 1,29 0,237 0,35 1,159 0,374 0,983 0,930 0,512 1,483 1,851 1,165 0,110 0,467 0,413 1,993 1,56 1,15 1,768 0,386 0,641 1,829 0,760 0,989 0,844 1,222 0,695 1,630 1,854 1,64 0,929 1,131 0,395 0,101 1,196 1,776 0,418 0,8 0,95 0,10 0,994 0,628 0,853 0,591 1,617 1,168 0,776 1,737 0,502 0,67 0,170 0,490 0,390 0,902 0,153 1,788 0,548 0,662 1,564 0,473 0,806 1,849 1,358 0,242 1,999 1,877 0,594 0,215 1,183 1,301 1,721 0,297 1,616 1,23 1,991 1,823 0,395 1,97 0,670 0,507 1,310 1,590 0,878 1,624 1,149 0,340 0,840 1,293 0,279 1,298 0,501 1,918 0,926 1,721 0,259 1,169 0,108 0,527 1,632 0,80 0,136 0,806 1,805 1,709 0,298 1,530 1,604 0,136 1,825 0,131 0,350 1,323 0,793 1,601 1,141 1,46 0,334 1,512 0,475 0,559 0,588 0,652 1,92 1,301 1,149 0,187 1,650 0,867 0,76 0,933 0,282 0,320 0,125 0,176 1,308 0,219 0,873 1,164 1,531 1,492 1,889 0,196 0,115 1,725 1,53 0,751 0,814 1,469 0,187 0,391 0,803 1,79 1,361 1,858 0,857 1,411 1,408 0,942 1,690 1,98 1,794 1,470 1,75 1,648 1,567 1,868 1,81 1,116 0,100 0,141 1,444 0,991 0,989 1,217 0,794 1,3 0,488 1,547 1,190 1,490 0,6 1,402 1,875 0,946 0,380 0,971 1,799 0,129 1,754 1,548 1,147 0,660 1,264 1,387 0,377 1,925 1,865 1,500 0,611 1,590 0,428 0,877 0,789 0,529 0,855 1,508 0,692 1,757 1,590 1,108 1,847 1,62 0,969 0,576 1,610 0,457 0,479 0,49 0,474 0,296 0,602 0,844 1,856 0,749 1,9 1,116 1,407 1,87 0,234 1,102 0,836 1,842 1,873 0,306 1,299 0,367 1,82 0,25 1,960 0,612 1,828 0,440 0,109 0,361 0,448 1,315 0,53 1,365 0,270 0,923 0,934 0,255 1,18 1,853 1,925 1,71 1,872 0,421 0,751 1,658 0,446 0,775 0,918 1,961 1,965 1,598 0,210 1,67 0,750 1,959 0,356 1,540 0,907 0,925 0,841 1,657 0,271 1,474 0,977 1,322 1,473 1,895 1,410 1,410 1,645 0,372 1,655 1,399 1,127 1,840 1,425 1,449 0,278 1,378 0,119 0,360 0,604 0,440 1,78 0,668 1,972 0,697 1,714 1,817 1,152 0,790 0,868 1,419 1,789 1,856 1,479 1,50 1,630 0,941 1,539 0,807 1,235 0,703 1,164 1,385 0,549 1,979 0,841 1,803 0,78 1,559 1,495 0,870 1,154 0,792 1,480 0,382 1,881 0,206 1,742 0,11 0,511 0,684 0,774 1,897 0,976 0,43 1,715 0,806 1,447 1,441 0,871 0,313 0,151 1,52 0,788 1,375 1,954 1,85 0,169 1,404 0,184 1,938 1,671 0,808 0,189 1,426 0,637 0,581 1,413 0,731 0,285 1,460 0,702 1,92 0,278 1,981 1,557 0,380 1,358 1,552 1,504 0,754 0,295 1,45 1,935 1,149 0,750 1,255 1,410 1,301 0,503 1,837 1,297 0,895 1,181 0,156 0,281 0,338 1,732 1,451 1,188 1,384 1,648 1,88 0,613 0,817 0,595 0,540 1,433 0,534 0,419 1,723 0,588 1,935 1,890 0,579 1,863 0,891 1,681 1,903 1,421 0,49 0,783 0,524 1,453 1,885 1,599 0,663 0,594 0,750 1,181 0,923 0,179 0,689 1,291 1,58 0,588 0,165 1,372 1,92 1,825 0,514 1,65 0,688 0,502 0,479 1,614 0,393 0,513 0,654 0,640 0,712 0,248 1,206 1,646 0,846 0,713 0,684 1,876 1,482 0,48 1,554 0,234 0,672 0,810 1,963 0,54 0,598 1,474 1,439 0,712 0,206 1,19 1,641 1,807 0,791 0,394 0,966 0,262 1,342 1,883 0,927 1,225 0,393 1,537 0,785 0,839 0,507 1,97 1,22 0,817 0,92 0,317 0,864 0,81 0,509 1,452 1,762 0,398 1,765 1,253 1,532 0,564 1,794 1,889 0,105 0,526 1,678 1,463 0,254 0,547 1,741 1,820 1,272 1,678 1,960 1,762 1,119 0,136 0,540 1,161 1,908 0,766 0,15 1,356 0,215 0,208 0,671 0,819 1,431 1,855 0,75 1,290 0,921 0,882 0,908 0,653 0,240 1,60 0,545 1,901 0,848 0,876 0,772 0,45 1,334 0,331 0,928 0,853 1,24 1,300 0,826 1,182 1,628 0,154 1,35 0,584 0,760 0,352 0,41 0,843 0,957 0,318 1,574 1,630 1,550 1,254 1,429 0,588 1,550 0,233 0,6 1,274 0,950 0,940 1,619 0,918 1,785 0,104 1,129 0,403 1,163 1,279 1,376 0,187 0,842 0,691 1,752 1,285 1,379 0,23 1,915 0,455 1,204 1,516 0,411 0,199 0,693 1,716 1,514 1,164 1,383 0,350 1,760 0,342 1,76 0,194 1,267 0,16 0,103 0,297 0,699 1,595 1,382 0,960 1,620 1,602 1,748 0,266 0,938 1,972 0,97 0,340 1,315 1,497 1,73 1,501 0,89 1,152 0,18 1,70 1,420 1,94 0,746 0,926 0,188 0,911 1,12 0,516 0,342 1,729 0,696 1,788 1,214 1,476 1,358 0,799 0,108 0,757 1,21 0,480 1,571 1,714 0,927 1,41 1,742 1,856 0,216 0,812 0,520 1,634 0,32 1,445 1,191 0,650 0,491 1,395 0,581 1,837 1,431 1,331 0,418 0,474 0,102 1,4 1,36 1,417 1,524 1,984 1,866 0,711 0,558 0,104 1,965 1,757 1,165 0,331 1,65 1,924 0,517 1,857 0,426 0,739 1,146 0,895 0,796 1,605 0,56 0,381 1,374 1,58 1,994 0,227 0,128 0,889 0,694 0,400 1,27 0,220 1,768 1,18 1,347 1,751 1,254 1,640 0,148 1,385 1,957 1,3 0,811 1,67 1,517 0,637 1,431 1,612 0,179 1,777 1,507 0,353 1,637 1,906 0,969 1,94 0,525 0,410 1,634 1,121 0,922 0,773 0,204 0,92 1,342 1,621 0,527 1,31 0,777 0,642 1,19 0,964 1,859 1,547 0,341 0,118 1,711 1,963 1,84 0,405 0,234 1,685 1,346 1,721 0,364 0,783 0,301 1,401 0,837 1,14 1,801 1,609 0,614 1,556 1,638 1,897 1,618 1,141 0,896 0,777 0,88 0,705 1,2 1,545 0,628 0,498 0,36 1,617 0,53 1,59 1,133 0,106 0,188 1,705 0,120 0,949 1,494 1,135 0,251 0,896 1,679 0,35 0,476 1,294 0,548 1,138 1,737 0,194 0,577 1,122 1,263 1,588 1,782 0,105 0,467 1,531 1,876 1,287 1,750 0,748 1,851 1,863 0,378 1,399 0,562 1,382 1,64 1,624 0,796 0,939 1,849 1,258 1,366 1,769 1,687 1,833 1,842 0,69 1,993 0,605 0,419 1,806 1,96 1,612 0,898 1,41 1,338 0,892 0,912 0,860 1,808 1,216 1,300 1,589 1,85 0,450 0,737 1,10 1,237 1,122 1,728 0,627 0,826 1,870 1,670 0,906 1,442 0,379 0,504 0,178 1,716 1,725 1,156 0,418 1,713 1,352 0,814 1,927 0,136 0,886 1,236 1,593 1,128 0,231 0,105 0,478 1,285 1,630 1,452 0,325 1,19 1,475 0,593 1,704 1,516 1,329 1,499 0,505 0,31 0,819 1,873 1,579 0,299 1,730 0,390 1,937 0,399 0,124 0,845 0,569 0,431 0,634 0,574 0,811 1,604 1,134 1,430 1,833 1,579 0,498 0,629 1,654 0,274 1,731 0,443 0,227 1,897 1,290 0,528 1,119 0,996 0,346 1,418 0,939 0,898 1,486 0,261 1,703 0,542 1,655 1,726 1,862 1,433 1,232 1,86 1,294 1,505 0,756 1,778 0,518 1,576 1,999 0,403 1,868 1,796 0,902 1,972 1,75 0,176 1,283 1,571 1,750 1,267 1,793 0,233 1,482 1,195 0,577 1,693 1,672 0,127 1,77 0,844 0,576 0,74 0,437 0,540 0,45 0,457 1,221 1,712 0,980 1,545 1,922 0,548 0,206 0,825 1,523 1,833 0,821 1,354 0,313 0,82 0,307 1,943 1,677 0,426 1,787 0,823 0,957 0,341 0,427 0,510 1,440 1,801 1,783 1,40 0,273 0,280 1,543 1,513 1,548 1,871 0,181 1,400 0,274 0,292 1,468 1,646 0,899 1,374 0,63 1,841 1,114 1,885 1,701 0,867 0,312 0,541 0,55 0,593 1,407 1,653 0,510 0,321 0,495 0,433 0,61 0,255 0,155 0,673 0,896 1,574 1,820 0,667 1,77 1,17 0,840 1,127 1,450 0,855 0,147 0,753 1,75 0,960 1,377 0,981 0,943 0,605 0,600 1,107 0,651 1,536 1,340 0,299 1,716 1,753 1,800 1,973 1,685 1,714 1,814 1,520 1,197 1,743 1,333 0,842 1,704 0,46 1,56 1,636 0,519 0,883 1,419 0,141 0,206 1,633 0,738 1,863 1,169 0,386 0,897 1,721 1,557 0,246 1,297 0,446 0,856 1,22 0,801 1,929 0,866 1,649 1,40 0,758 0,118 0,603 1,454 1,917 0,104 1,485 1,904 0,384 0,580 1,884 0,502 0,48 1,937 0,929 1,131 0,840 1,974 1,116 1,855 0,364 1,173 0,634 0,422 0,917 0,644 0,654 0,346 0,980 0,163 1,189 0,225 1,724 1,189 1,669 1,788 1,347 1,695 0,273 0,107 0,301 1,871 1,33 0,789 0,154 0,864 1,235 0,777 1,3 1,750 0,38 1,21 0,775 1,19 0,981 0,81 1,548 1,637 0,129 0,133 1,369 1,606 1,855 0,206 0,746 1,448 0,714 1,47 1,116 0,613 0,204 0,392 0,741 1,696 0,498 1,545 0,906 0,751 0,956 1,271 0,612 1,789 1,784 0,157 1,147 0,196 0,276 0,23 1,200 1,156 0,581 0,57 1,551 0,826 0,385 1,793 1,487 0,166 1,619 0,347 1,944 1,99 0,446 1,97 0,581 1,155 0,471 1,848 0,586 1,952 0,833 1,9 1,367 1,342 0,808 1,868 0,934 0,594 1,823 1,106 1,216 1,724 1,895 1,132 0,627 1,733 1,17 1,487 0,1 1,649 0,841 0,591 1,988 1,297 0,489 0,358 1,710 1,889 1,71 1,814 1,453 1,366 1,19 0,828 1,460 0,480 0,357 0,683 1,927 1,489 0,391 1,319 0,530 0,230 0,817 0,47 1,58 0,484 0,998 0,433 0,831 0,646 1,293 1,464 1,788 1,529 0,825 1,725 0,487 0,423 0,805 1,20 1,225 1,521 1,311 1,35 1,499 0,16 0,563 1,194 0,349 1,694 1,979 0,61 1,436 1,583 0,148 0,638 0,957 1,222 0,694 1,260 0,272 0,675 1,100 1,334 0,978 0,575 0,151 1,481 0,700 0,613 1,704 0,136 1,237 0,869 0,609 1,52 0,400 0,991 1,270 1,326 0,905 1,253 1,274 1,262 0,683 1,366 0,163 0,628 1,351 0,860 1,383 0,881 0,285 1,795 0,400 0,777 0,127 0,653 0,649 1,727 0,468 1,168 0,132 1,733 1,806 0,158 1,731 0,53 1,709 1,733 1,469 0,266 1,149 0,889 1,364 0,11 1,530 0,786 1,464 1,282 0,627 1,179 0,681 0,193 0,896 0,217 1,540 1,265 0,450 0,612 1,110 1,414 1,744 1,734 1,274 1,783 1,773 1,463 1,415 0,164 0,928 1,319 1,516 1,881 1,816 1,632 1,221 0,625 1,924 1,221 1,722 1,837 0,118 1,793 0,340 1,261 0,139 0,965 0,888 1,326 1,563 1,0 1,985 1,390 1,623 0,243 0,831 1,791 0,651 0,197 1,449 1,728 1,703 0,248 1,442 1,725 1,672 1,941 0,391 0,77 1,662 1,359 0,79 0,259 0,671 1,583 0,190 1,13 1,865 0,871 0,502 1,640 1,904 0,501 1,480 1,523 1,685 0,580 1,853 0,985 1,630 0,497 1,864 1,841 1,363 1,641 1,655 1,142 0,720 0,381 1,652 0,874 1,463 1,276 0,336 0,845 1,33 1,311 1,148 0,122 1,69 0,710 0,299 1,589 1,847 1,63 1,681 1,594 0,156 1,840 0,718 0,287 1,119 0,840 1,236 0,575 1,710 0,302 1,542 0,467 0,746 1,840 1,687 1,619 0,863 1,792 0,9 0,983 1,165 0,582 1,576 0,751 0,210 1,750 1,988 1,561 1,207 0,74 1,497 0,30 0,747 1,826 1,114 1,392 0,405 1,799 0,839 0,901 1,943 1,465 0,680 0,19 1,175 0,362 1,808 1,860 1,687 0,855 1,853 1,935 0,773 0,349 1,446 0,59 0,265 0,808 1,733 0,979 0,502 1,271 1,958 1,497 0,51 0,848 0,438 1,407 1,70 0,422 0,620 0,826 1,149 1,244 0,389 1,666 1,616 1,771 0,20 1,519 0,649 1,971 0,639 1,144 0,69 0,867 0,780 0,912 0,802 0,517 1,893 0,51 0,514 1,153 1,905 0,997 1,962 1,885 1,422 0,183 1,795 0,185 0,127 1,464 1,550 1,883 0,481 0,730 1,778 0,500 1,237 0,0 1,305 0,558 0,382 1,830 1,760 0,406 1,854 0,716 1,404 0,304 1,279 0,914 0,969 0,235 1,351 0,82 0,179 0,10 0,318 0,392 0,353 0,97 0,841 0,685 0,991 0,208 1,436 1,446 0,7 1,516 0,674 1,460 0,0 1,227 0,742 0,382 1,102 1,425 1,192 0,500 1,78 0,615 0,631 1,783 0,613 1,404 1,428 0,904 0,409 1,755 0,311 1,153 1,991 1,213 0,550 1,965 1,296 1,429 1,45 0,899 0,127 0,49 1,495 0,406 1,898 0,752 0,174 1,403 1,85 1,580 0,864 0,285 1,927 0,166 0,139 1,584 1,837 0,90 0,140 0,720 1,163 1,816 0,689 1,565 1,485 0,620 1,818 0,888 0,342 1,898 0,412 0,613 0,584 0,270 1,80 0,963 1,461 0,380 1,78 0,693 1,805 0,313 1,342 0,37 1,698 1,578 0,77 0,267 1,39 1,803 0,389 0,389 0,221 0,209 1,495 1,642 0,24 1,636 0,456 1,44 1,595 1,416 0,756 0,23 0,666 0,471 0,148 0,145 1,748 1,352 1,25 0,65 0,428 1,691 1,893 1,426 0,639 0,166 1,762 1,316 0,697 0,305 0,613 0,812 0,805 0,732 1,609 0,477 0,185 1,352 1,402 1,464 1,673 0,408 1,214 1,646 0,733 0,279 0,227 1,471 1,839 0,865 0,739 0,369 1,760 1,812 0,74 0,627 0,518 1,311 0,851 0,515 1,990 1,758 0,165 1,798 1,643 1,230 1,826 0,817 1,714 1,612 1,262 1,456 1,221 0,334 1,451 1,373 0,988 0,613 1,627 0,514 0,942 0,524 1,114 1,743 0,606 1,919 0,9 1,744 1,638 1,348 0,483 1,263 1,597 0,48 1,921 1,9 0,327 0,4 1,43 1,645 0,690 0,860 0,406 1,434 1,909 1,326 1,246 1,797 0,783 0,841 1,9 0,546 0,151 1,420 1,689 1,224 1,487 1,385 1,494 1,288 1,482 1,942 0,493 1,252 0,16 1,342 1,405 1,282 1,883 0,816 1,557 1,693 0,82 1,519 0,23 0,398 0,455 1,758 1,534 1,207 1,587 0,277 0,645 0,349 0,705 0,844 1,795 1,406 1,747 1,518 0,346 0,614 0,416 0,582 1,842 0,404 0,683 0,366 1,460 0,108 1,649 1,227 1,611 1,638 0,541 1,367 0,722 1,174 1,576 1,482 0,202 1,429 0,99 1,232 0,639 0,260 1,747 0,91 1,767 1,11 0,36 0,707 1,843 0,424 1,54 0,502 1,84 1,548 1,589 0,533 1,970 1,197 0,962 0,144 0,909 0,604 1,401 0,528 0,119 1,145 0,147 0,898 1,538 1,375 0,897 1,569 0,467 0,814 1,682 0,682 1,50 1,503 0,422 1,164 0,189 1,715 1,658 1,429 0,553 1,952 1,65 1,664 0,708 0,51 0,144 1,559 0,325 1,698 0,117 1,375 1,712 1,206 0,706 0,486 1,510 1,715 1,873 0,277 0,857 0,30 1,860 1,179 0,774 1,220 1,543 1,722 1,553 1,868 0,883 0,396 0,609 0,677 1,77 1,185 0,380 1,703 1,372 0,229 0,826 1,275 0,554 1,965 0,688 0,477 0,498 0,583 0,255 0,802 0,55 0,807 1,60 0,38 1,58 0,6 1,663 0,786 1,180 0,601 1,99 1,404 1,718 1,184 1,263 1,726 0,480 1,679 1,831 0,545 0,530 1,216 0,618 1,836 1,114 0,443 1,248 0,212 0,101 0,803 0,29 0,220 1,791 0,709 0,492 1,714 1,991 0,267 1,710 1,183 1,652 0,537 1,129 0,889 0,411 1,684 1,636 0,847 1,942 0,440 1,278 0,484 0,519 0,789 0,972 0,203 0,827 0,914 0,719 1,131 1,191 1,430 1,506 0,184 1,681 0,547 0,249 1,316 0,319 0,36 1,752 1,679 1,43 1,727 0,33 0,996 1,37 1,449 0,965 0,491 1,933 0,368 0,523 1,684 0,133 0,917 1,734 0,990 1,313 0,504 1,814 1,294 1,90 0,748 1,678 0,24 1,202 0,101 1,365 0,89 0,147 0,597 1,816 0,996 0,888 0,352 0,544 1,636 1,667 1,425 0,595 0,455 0,548 0,245 0,536 1,449 0,683 1,80 1,504 1,959 0,671 1,527 0,656 1,370 1,92 0,233 1,814 0,751 0,968 1,921 1,394 1,894 0,282 0,577 0,783 1,596 0,302 1,543 1,606 1,27 1,827 1,781 1,50 0,576 1,957 1,30 1,647 1,521 1,860 0,616 1,414 1,780 1,726 1,479 1,932 1,569 0,707 1,209 0,109 0,756 1,81 0,822 0,617 1,927 0,64 0,364 1,22 1,270 1,770 1,947 1,204 0,766 1,129 1,433 1,508 0,343 1,453 1,34 0,700 0,780 1,652 0,74 0,715 0,235 0,823 1,304 1,181 1,635 0,869 1,500 0,572 0,536 1,880 0,696 0,191 1,528 1,800 0,286 1,518 0,316 1,922 0,144 1,19 1,898 0,329 1,859 0,872 1,360 0,252 0,212 0,76 0,63 1,397 1,626 1,997 0,842 0,507 0,462 1,437 0,387 1,629 0,223 1,991 0,774 1,326 0,280 1,166 1,971 1,998 1,206 0,94 0,468 1,790 1,193 1,479 0,416 1,502 1,150 0,406 0,170 1,732 0,154 0,589 0,653 1,187 0,848 1,178 1,226 0,998 1,315 1,15 0,277 0,314 1,719 0,424 0,809 1,759 0,295 1,665 1,222 0,207 0,514 0,634 1,808 0,247 0,197 1,12 1,489 1,746 1,842 0,806 1,864 1,939 0,908 0,876 0,42 0,317 0,728 0,708 1,305 0,271 0,413 1,586 1,192 0,488 1,309 0,814 0,191 0,455 1,450 1,74 0,11 1,318 1,294 0,959 0,913 1,506 0,367 0,891 1,445 1,498 0,579 1,207 0,957 1,179 1,674 0,182 0,156 0,145 0,549 1,606 0,315 1,331 0,728 1,716 0,521 0,551 0,865 0,242 1,385 0,964 0,412 0,801 1,673 1,50 0,113 0,872 1,642 1,235 0,255 0,577 0,338 0,713 0,426 0,271 1,525 0,774 0,156 0,732 0,171 0,31 0,482 0,83 0,987 0,570 0,21 1,50 0,854 0,611 0,329 1,7 1,357 1,389 0,199 0,256 1,121 0,498 1,516 0,855 1,963 1,564 0,977 0,928 1,553 1,738 1,846 0,588 0,288 0,484 1,225 0,504 1,164 1,118 0,257 1,151 1,457 0,847 1,134 0,104 1,550 1,114 1,951 1,523 1,688 1,933 0,457 1,801 0,158 1,984 1,987 0,260 1,874 1,904 0,75 1,275 1,629 0,27 1,64 0,751 1,906 0,97 1,589 0,847 0,803 0,242 0,434 1,472 0,282 1,659 1,176 0,66 1,240 0,435 0,341 0,409 0,831 0,981 0,734 1,3 0,30 0,812 0,473 1,284 0,81 1,559 1,106 0,337 0,977 1,717 0,707 0,478 0,961 0,266 1,638 1,560 1,899 1,280 0,978 0,872 1,317 0,945 0,495 1,207 1,246 1,914 1,445 0,613 0,568 1,897 0,18 0,483 0,660 0,989 0,62 0,530 0,7 1,933 1,676 1,607 0,811 1,990 0,148 1,340 0,318 1,811 1,524 1,180 0,852 1,900 0,669 1,743 0,746 0,771 0,70 1,583 1,607 1,640 0,102 1,840 1,531 0,645 1,325 1,523 1,266 0,82 1,915 0,657 1,792 1,599 0,439 0,337 0,526 1,360 0,755 1,629 0,434 1,499 0,671 1,179 1,783 1,285 0,174 1,209 1,60 1,903 0,958 1,217 1,688 0,241 1,129 1,437 0,568 0,546 1,178 1,538 0,681 1,172 1,854 1,493 0,198 0,504 1,634 1,788 0,144 0,47 0,937 0,440 1,698 0,674 0,795 1,473 1,568 0,252 1,318 0,179 0,85 0,741 0,922 1,424 0,339 0,386 0,832 1,555 1,400 0,267 0,111 1,162 0,536 1,765 0,921 1,775 1,395 0,271 0,655 0,257 1,848 0,341 1,906 1,974 1,800 1,551 0,923 1,518 1,663 1,756 0,462 1,974 0,931 0,578 1,412 0,66 0,739 0,200 0,220 0,214 1,977 0,897 1,475 1,270 0,802 1,694 0,836 0,223 1,7 1,657 0,649 0,596 0,995 1,344 0,99 1,724 1,585 0,375 1,7 0,981 0,16 1,23 1,0 0,664 0,291 1,474 1,920 1,320 1,282 1,755 1,49 1,273 1,924 0,930 0,435 0,659 0,28 1,141 1,617 0,989 0,302 1,199 0,443 1,826 1,682 1,47 0,376 1,838 0,311 0,684 0,182 1,812 1,57 0,430 0,468 0,203 0,747 1,520 1,909 0,277 0,87 1,661 0,693 0,457 0,365 1,547 0,969 1,683 0,294 1,442 1,899 1,537 0,228 1,204 0,457 1,881 1,643 0,382 1,356 0,499 1,503 1,848 1,488 1,872 1,799 0,559 0,109 1,526 1,341 0,496 1,536 1,243 0,762 0,762 1,946 0,493 1,74 1,385 1,84 1,216 1,56 1,548 1,648 0,279 1,561 0,825 0,354 0,703 0,115 1,416 1,801 1,189 0,499 0,900 1,488 1,802 0,44 1,280 0,690 1,16 0,920 0,809 1,316 0,83 0,224 0,845 0,776 0,1 0,297 0,557 0,730 0,104 0,391 1,695 1,486 0,291 0,546 0,900 0,657 0,237 0,313 0,10 0,670 1,670 0,125 0,708 0,964 1,345 1,904 1,421 1,816 1,4 0,289 0,150 0,545 0,547 1,74 1,34 0,97 0,170 1,136 0,252 0,645 0,553 0,56 0,131 0,194 0,384 0,18 1,353 1,727 0,814 0,428 0,385 1,141 1,595 0,781 1,929 0,455 0,632 1,132 0,337 0,580 0,884 1,506 1,903 0,709 1,964 1,97 0,540 0,754 1,609 0,347 1,783 0,333 1,456 0,626 0,279 1,161 0,44 0,735 1,261 0,125 1,542 1,745 0,888 1,418 0,344 1,569 0,97 1,289 0,764 0,604 0,330 0,602 0,457 1,384 1,634 0,436 0,783 0,605 1,993 1,358 0,537 0,599 0,227 0,525 0,180 0,732 1,683 0,816 1,164 1,382 1,550 0,916 1,830 1,787 1,139 0,125 1,552 0,779 0,660 1,317 1,736 1,12 1,392 0,370 1,163 0,364 0,76 1,328 0,349 1,557 0,42 1,659 0,202 1,752 0,241 0,924 0,797 1,129 0,924 1,34 0,786 0,281 0,22 1,941 0,228 0,880 1,726 0,477 1,688 0,215 0,803 1,191 0,966 1,290 0,874 0,908 0,233 1,588 1,637 0,494 1,286 0,675 0,43 0,66 0,829 0,351 0,999 1,606 0,255 1,849 0,318 0,424 0,765 0,833 0,78 1,467 0,587 0,923 1,741 1,519 1,715 1,551 1,426 0,149 0,893 0,822 1,288 0,93 0,503 0,750 1,862 1,175 1,955 1,340 0,675 0,691 1,979 1,364 0,685 1,463 1,112 0,105 0,389 0,38 0,63 0,699 0,90 1,812 1,418 0,833 1,820 0,560 1,842 1,834 0,935 1,222 1,645 1,748 1,827 0,606 0,248 1,285 1,952 0,923 0,730 1,317 1,26 0,511 0,409 1,751 1,819 0,882 0,894 1,678 1,338 0,296 0,10 0,686 1,948 0,768 1,939 1,875 1,759 0,439 1,242 1,741 0,389 0,387 1,394 0,976 0,725 1,595 0,213 1,988 0,676 1,288 1,639 1,733 1,845 1,485 1,850 1,680 1,59 0,778 0,890 1,942 1,361 1,362 1,886 0,330 1,950 0,679 0,169 1,662 1,279 1,774 1,776 0,8 0,709 1,258 1,800 0,605 1,124 0,217 1,111 1,645 1,920 1,931 1,237 1,776 1,499 0,730 1,966 1,746 1,830 1,877 0,990 0,380 0,575 1,496 1,342 0,213 0,524 1,975 1,564 1,605 0,706 1,63 1,656 0,835 1,586 0,277 0,286 0,625 0,893 1,396 1,610 1,702 1,341 0,74 0,860 0,207 0,863 1,820 0,475 1,943 1,183 0,233 0,382 1,139 0,645 1,172 1,815 1,966 0,717 0,607 0,355 1,66 1,554 1,371 1,44 0,515 0,639 0,888 0,577 1,5 0,116 0,954 1,378 0,542 1,91 0,691 1,43 1,865 0,178 1,737 1,319 1,198 1,987 1,171 1,659 0,824 1,113 0,316 0,989 0,719 1,133 1,310 0,223 1,945 0,318 0,624 1,788 0,196 0,516 1,577 1,933 1,960 0,76 1,517 1,533 0,19 1,252 0,73 0,242 0,763 0,479 0,483 0,436 0,359 1,865 0,450 1,699 1,457 1,749 1,115 0,864 1,475 0,854 0,95 0,303 0,661 1,785 1,242 1,850 0,737 1,417 0,199 1,194 1,958 0,883 0,378 0,81 1,190 1,363 0,113 1,62 1,933 0,811 1,163 1,605 0,709 1,748 1,755 0,984 0,73 1,330 0,675 0,189 1,89 0,376 0,105 1,596 1,775 0,525 0,568 1,151 1,49 1,468 0,810 1,45 1,660 0,561 1,403 1,864 1,157 0,575 0,667 1,721 1,459 1,203 1,973 0,207 1,779 1,815 1,960 0,864 1,341 1,117 1,461 1,182 0,730 0,527 1,752 1,419 0,457 0,443 0,986 1,204 1,813 1,291 1,646 1,277 0,74 0,987 1,911 0,368 0,267 1,969 1,800 1,10 0,733 0,668 1,776 0,11 1,479 1,720 1,13 1,358 1,900 0,708 0,390 1,156 1,173 1,795 0,163 0,534 0,339 1,796 1,616 0,217 0,150 0,918 0,573 1,179 0,815 1,463 1,863 0,158 1,885 0,526 1,935 0,668 1,299 0,186 0,708 1,805 0,644 0,404 1,652 1,853 1,379 0,74 1,257 1,291 0,904 0,682 1,786 1,205 1,247 0,473 1,239 0,416 0,8 0,834 0,449 1,107 1,235 1,454 1,986 0,888 1,957 1,45 0,279 1,831 1,999 0,426 0,426 1,820 0,793 1,431 0,577 1,918 1,521 1,840 1,616 1,903 1,226 1,774 1,141 0,400 0,702 0,623 1,796 0,266 1,249 1,739 1,711 0,728 0,16 0,926 1,772 0,947 1,696 1,938 0,616 1,684 1,640 0,753 0,268 0,61 1,956 1,669 0,685 1,142 0,300 0,430 0,480 0,686 0,218 0,15 1,133 0,825 1,562 1,461 1,221 1,843 0,257 1,410 1,326 0,202 1,366 0,620 1,148 0,455 1,679 1,984 1,716 1,47 1,603 1,831 1,405 0,903 1,973 1,753 1,384 1,158 0,823 1,326 0,503 0,636 1,733 1,405 0,331 1,848 1,402 0,817 0,388 1,223 1,232 1,591 0,739 0,690 1,466 0,343 0,204 0,917 1,572 1,987 1,311 0,314 1,172 0,823 0,271 1,592 1,792 0,375 0,47 0,824 1,435 0,499 1,582 0,923 0,260 1,617 0,216 1,496 1,477 0,263 0,639 0,817 0,289 1,71 0,48 0,395 1,376 1,794 0,657 1,793 0,271 0,823 0,287 0,332 1,904 0,795 0,986 0,963 0,7 0,967 1,467 1,408 1,928 1,343 1,806 0,951 1,530 0,798 1,385 0,351 1,960 1,859 1,403 1,160 0,735 0,56 1,443 1,342 1,133 0,717 0,814 0,113 1,536 1,349 0,69 0,196 1,530 0,560 0,491 1,292 0,742 0,828 1,360 1,585 1,406 0,92 0,626 0,43 1,137 1,197 0,275 0,406 0,127 0,538 1,542 0,679 1,510 1,642 1,767 1,152 1,162 0,767 0,446 1,110 0,102 1,72 0,984 0,642 0,574 0,971 0,343 1,128 0,576 0,786 1,392 0,608 1,766 0,874 1,252 0,858 0,766 1,733 0,105 1,606 1,44 0,350 1,501 1,564 0,219 0,58 0,539 1,729 1,998 1,888 1,637 0,633 1,736 0,739 0,252 0,309 1,560 0,48 1,322 0,951 1,501 0,543 0,93 1,837 0,334 0,701 0,323 1,489 1,618 0,335 1,594 0,717 1,124 0,756 1,164 1,368 1,317 0,681 0,385 0,912 1,225 1,182 1,758 0,349 1,607 0,758 1,685 0,798 1,881 0,222 1,438 1,935 1,892 0,91 1,271 0,705 0,389 1,263 1,471 0,614 0,723 1,507 0,324 0,684 0,276 0,64 0,7 0,680 1,793 0,874 0,928 0,516 1,976 1,439 0,110 1,128 0,278 1,585 0,110 0,562 1,45 1,713 1,263 1,277 0,388 0,544 0,528 0,700 0,740 0,235 0,773 1,512 0,370 0,356 1,981 1,584 0,418 0,199 1,457 1,877 1,535 0,774 1,346 0,844 0,439 0,224 1,323 1,735 0,485 1,709 1,870 1,187 1,873 0,411 1,367 0,633 0,433 0,493 1,216 0,165 1,170 0,483 0,346 0,132 1,504 1,649 1,126 1,822 0,703 1,204 1,471 0,847 1,280 1,383 1,592 1,488 1,554 1,798 1,440 1,916 1,468 0,0 1,916 1,188 1,735 0,71 1,58 1,202 0,867 1,517 1,439 0,310 0,532 0,554 1,362 0,178 1,421 0,484 0,549 0,472 0,874 0,915 1,303 0,195 1,417 0,713 0,614 0,398 0,537 0,521 1,366 0,659 1,576 1,127 0,856 0,139 0,162 1,418 1,871 1,952 0,52 1,817 1,486 1,960 1,423 0,493 1,843 0,411 0,163 1,771 0,29 0,416 1,222 0,202 0,270 1,521 0,724 0,652 1,119 0,513 0,477 1,256 0,842 0,960 0,683 1,735 0,850 1,309 1,993 0,199 1,396 0,889 1,659 0,975 0,992 0,30 0,6 0,750 1,538 0,719 1,616 0,218 1,823 1,582 0,690 0,415 1,24 0,815 1,780 1,763 0,24 0,687 0,363 1,480 1,831 0,638 1,236 0,778 1,948 1,579 0,929 0,643 0,561 1,411 1,703 1,566 0,976 0,377 1,659 1,876 1,224 0,805 1,167 1,381 0,502 0,454 1,813 0,372 0,719 1,327 1,643 0,356 1,566 1,424 0,124 0,356 1,197 0,270 0,838 1,274 1,224 0,27 0,372 1,772 0,973 1,287 0,806 0,710 1,661 1,320 1,887 0,301 1,521 1,765 1,228 1,367 1,340 1,488 0,272 1,72 1,496 0,282 1,132 1,281 1,374 1,551 0,957 0,105 1,81 1,286 1,12 1,497 1,881 1,722 1,919 1,805 0,322 0,569 0,2 0,793 0,208 1,127 0,186 0,674 1,197 0,287 0,694 1,895 1,8 1,875 1,880 0,906 0,188 1,439 0,724 1,217 1,343 0,679 0,742 0,299 1,878 1,275 0,224 0,626 0,669 1,58 1,928 1,754 1,944 0,685 1,273 0,393 1,665 0,823 1,883 1,598 0,579 0,271 1,854 1,289 1,569 1,945 0,111 1,252 0,950 0,352 1,795 1,313 0,650 1,713 1,972 0,547 0,421 0,936 1,756 0,592 1,312 0,450 1,627 0,518 1,322 0,356 0,46 0,841 0,750 0,132 1,839 1,118 0,675 1,251 1,592 1,736 0,505 1,506 1,633 0,901 0,808 0,955 0,243 1,934 1,443 0,548 1,680 0,628 1,354 1,594 0,15 0,144 0,257 1,999 0,764 1,19 1,982 0,56 1,257 1,605 0,557 1,279 0,936 0,601 0,842 1,378 1,415 1,894 0,287 1,276 0,489 1,66 0,582 0,881 1,499 0,75 1,566 0,93 1,89 0,599 1,540 1,668 1,974 1,963 1,306 0,758 1,462 1,596 0,185 1,328 0,452 1,275 1,771 0,480 1,270 0,775 0,266 0,417 1,817 0,178 0,736 1,228 0,302 1,512 1,889 0,99 0,539 0,209 0,228 0,823 1,757 0,21 0,946 0,778 0,102 0,319 1,61 0,659 1,268 1,32 0,103 1,575 0,864 0,837 0,351 0,729 1,792 1,196 1,277 1,16 0,704 0,729 1,59 1,781 1,282 1,450 1,291 1,33 1,48 1,267 1,388 1,761 0,137 1,8 1,896 1,686 0,195 0,462 0,428 0,759 0,887 0,193 0,950 1,749 1,990 0,547 0,588 1,885 1,91 0,427 0,770 0,805 1,195 1,200 1,132 1,161 1,851 1,152 1,34 1,459 1,392 0,275 1,886 1,478 0,615 1,447 0,901 1,755 0,110 0,543 0,474 1,807 1,84 1,974 0,233 0,455 1,388 1,176 0,432 1,16 1,956 1,681 0,574 1,186 0,844 0,124 1,669 0,225 1,469 1,577 0,931 1,884 1,667 1,372 1,496 0,875 1,785 0,877 1,501 0,675 1,687 1,988 1,348 0,553 1,741 1,82 1,411 0,208 1,176 1,846 0,471 1,128 1,756 0,697 1,443 1,266 1,748 1,758 1,440 1,338 0,74 1,763 0,717 1,16 0,852 1,121 0,131 0,101 0,365 1,127 1,255 0,58 0,445 1,66 0,120 1,555 0,263 0,683 1,111 1,825 0,420 1,437 1,924 1,759 0,653 0,528 1,468 0,739 1,547 1,462 0,814 1,863 1,187 0,176 1,403 1,833 0,601 0,771 1,539 1,647 1,740 1,709 0,30 0,924 1,819 1,445 1,571 0,106 1,545 1,773 1,595 0,329 1,575 0,135 1,913 0,817 1,213 0,403 0,281 1,435 0,778 0,7 0,901 0,167 1,217 0,361 1,627 1,263 0,756 1,607 0,509 1,299 0,653 1,349 1,965 1,414 1,681 0,581 0,225 1,800 0,94 0,924 1,618 1,843 0,631 1,953 1,287 0,700 1,545 1,982 0,643 0,413 1,15 0,218 0,933 1,260 0,144 0,84 1,715 0,406 1,402 1,742 0,721 0,748 1,282 1,685 0,203 0,295 0,722 0,10 1,762 1,791 1,660 0,859 1,145 0,934 1,113 0,494 1,132 0,567 0,659 0,958 0,254 1,918 0,312 0,21 1,33 1,926 1,207 1,984 0,572 1,613 0,132 1,699 1,920 1,322 0,801 1,491 1,161 0,31 0,444 0,811 1,41 0,250 1,120 0,623 1,891 0,206 1,402 1,842 0,760 1,728 0,204 1,954 0,851 1,72 0,747 1,495 0,241 1,572 0,482 0,302 0,394 0,661 0,687 0,802 1,441 0,703 0,423 0,196 0,461 1,588 1,724 1,560 1,687 0,402 1,928 0,394 0,752 0,437 0,292 0,982 1,298 1,375 0,427 0,16 1,267 1,548 1,323 0,180 1,678 0,13 1,214 1,845 1,497 1,622 0,265 1,561 0,637 1,594 0,615 1,671 1,326 1,421 0,183 1,895 0,23 0,997 0,49 0,975 1,967 0,332 0,689 0,810 0,430 0,904 1,547 1,668 1,463 0,122 0,469 0,470 0,612 1,828 0,658 1,154 0,339 1,887 1,324 1,45 1,18 0,515 0,880 1,539 0,638 0,322 0,719 1,80 1,286 0,928 1,809 1,807 1,169 0,94 0,397 0,837 0,117 0,219 1,596 1,830 0,917 1,380 0,734 1,380 1,808 0,135 1,682 0,375 0,310 1,732 0,355 0,841 1,48 1,186 0,490 1,978 0,5 1,143 0,912 1,610 1,156 0,280 1,206 0,542 0,211 1,530 0,766 0,969 0,777 0,48 0,310 1,428 0,450 1,890 1,494 1,803 0,5 0,472 0,457 0,238 1,444 1,91 1,483 0,617 0,179 1,919 1,392 1,396 0,522 1,668 0,919 1,26 0,88 1,499 1,882 0,63 0,700 0,750 0,882 0,619 0,915 1,589 0,905 0,989 0,604 0,458 1,723 0,283 0,94 1,195 1,46 1,436 1,653 1,340 0,519 1,292 0,162 1,892 1,811 1,62 1,249 1,148 0,14 1,686 1,784 0,336 0,910 1,636 1,838 0,365 0,170 0,775 0,557 0,961 1,243 1,457 1,924 1,78 1,758 0,927 1,938 0,980 1,37 1,652 1,157 0,420 1,155 1,603 1,194 1,604 0,831 1,811 0,862 1,71 0,723 1,132 0,806 1,632 0,902 0,294 0,366 1,303 0,247 0,173 0,887 1,631 0,753 0,518 0,377 0,328 1,74 1,898 1,784 1,182 0,627 1,857 1,366 1,451 0,172 1,555 0,861 1,775 1,635 0,382 0,67 0,977 0,638 0,328 0,688 1,45 0,61 1,597 1,841 0,696 0,495 1,836 0,523 0,915 1,83 0,983 0,957 0,874 0,806 0,397 1,15 1,481 0,426 1,387 0,285 0,190 1,867 1,174 0,439 1,901 0,840 0,75 1,75 0,635 1,569 0,697 1,141 1,614 0,456 1,915 0,950 0,889 1,115 0,113 1,962 1,54 1,157 1,143 0,10 1,262 0,539 0,959 1,805 0,732 1,904 1,470 0,842 1,72 1,460 0,694 0,668 1,201 1,558 0,687 1,259 1,597 0,56 0,210 1,492 1,11 0,621 0,809 1,814 0,429 1,697 0,665 1,22 1,691 0,97 0,938 0,600 1,515 0,154 1,813 0,554 0,972 0,693 0,709 0,135 1,707 0,532 1,159 0,480 0,753 0,368 1,745 1,341 0,576 0,296 1,154 0,12 1,219 1,639 0,920 1,997 0,428 0,633 0,394 1,633 0,497 1,850 0,234 1,513 0,574 0,915 1,697 0,232 0,519 1,743 1,504 0,475 1,46 1,427 0,654 0,49 0,558 0,417 1,343 0,458 0,855 0,810 0,125 1,771 0,801 1,773 1,301 0,468 0,479 0,15 1,733 0,945 1,366 0,254 1,134 1,270 1,718 1,205 0,586 1,918 0,208 0,350 0,964 1,113 1,250 0,955 0,377 1,324 1,976 0,168 1,281 0,827 1,133 0,12 0,310 1,580 1,576 1,119 1,313 0,907 0,20 0,244 1,661 0,405 0,340 1,719 1,640 1,432 0,594 1,762 0,339 1,798 0,554 0,32 0,500 0,622 0,782 0,362 0,59 1,634 1,906 0,58 1,358 0,799 1,56 1,604 0,109 1,358 1,293 1,38 1,70 0,51 0,974 0,682 0,684 1,607 1,416 0,610 1,213 0,822 0,25 0,281 1,59 0,907 1,969 1,298 0,372 0,928 0,220 1,640 0,128 1,603 0,432 0,3 1,344 0,724 0,949 1,432 0,548 0,783 0,657 1,519 1,533 0,365 1,378 1,210 0,144 0,71 1,578 1,914 1,694 0,546 1,6 0,768 0,12 1,974 0,535 0,743 1,1 1,133 1,515 1,792 0,929 0,219 0,325 1,792 0,86 1,81 0,213 1,503 1,969 0,217 0,490 1,107 1,910 0,465 0,890 1,877 0,730 1,300 0,732 0,199 0,929 0,202 1,973 1,338 1,686 1,884 1,288 0,873 0,472 0,488 1,593 0,93 0,166 1,61 0,738 0,939 1,367 1,89 1,531 0,603 1,59 1,136 0,550 0,952 1,411 0,121 0,29 1,316 1,620 0,844 0,961 0,600 1,289 0,110 1,260 1,916 0,241 0,760 1,11 0,384 1,832 1,685 0,344 1,160 1,410 1,878 0,158 1,504 0,798 0,917 1,145 1,157 0,944 1,306 1,266 1,563 1,259 1,361 0,88 0,909 1,904 0,977 1,978 1,428 0,643 1,198 1,458 0,641 1,29 0,199 0,889 1,838 1,690 1,820 0,987 0,670 0,23 1,165 1,527 1,651 0,737 1,722 1,545 1,148 0,139 1,247 0,205 0,759 1,915 1,477 1,808 1,349 1,860 0,77 1,76 0,152 1,148 0,124 1,769 1,461 0,826 0,404 1,984 1,145 0,511 0,137 1,981 1,611 1,799 0,370 1,434 0,570 1,937 0,853 0,321 0,828 1,505 0,919 0,789 1,458 1,114 0,143 1,628 0,602 1,285 0,611 0,790 0,1 1,341 1,262 1,326 1,50 0,696 0,231 0,548 1,660 0,245 1,352 0,228 0,488 0,268 0,292 0,529 0,194 0,522 1,641 1,763 1,461 0,927 1,710 1,583 1,395 0,59 1,755 0,811 1,913 1,534 1,202 1,293 1,604 1,50 1,840 1,903 0,300 1,439 0,214 0,142 0,706 1,928 1,39 0,539 0,399 0,623 1,258 0,81 1,707 0,904 1,997 0,248 1,18 1,722 1,19 0,495 0,880 0,873 1,566 0,769 0,585 0,979 0,311 1,546 0,295 1,454 0,660 0,619 1,525 1,115 0,475 1,195 1,52 1,852 0,306 0,30 0,11 0,830 0,507 1,31 1,12 0,447 0,961 0,218 0,571 0,368 1,968 1,574 1,179 0,853 0,627 0,220 0,972 1,337 1,491 0,730 0,21 1,97 1,272 0,69 1,25 0,531 1,413 0,348 1,366 1,43 0,847 1,747 1,461 0,513 0,400 0,192 0,569 1,760 0,694 0,379 0,779 1,187 1,260 0,591 1,145 0,24 0,305 1,750 0,796 0,334 1,980 1,70 0,54 1,167 1,521 0,925 1,266 0,621 1,127 1,122 0,15 0,251 0,285 1,840 0,480 1,701 0,999 1,551 1,777 1,994 1,72 1,425 0,631 0,615 0,994 1,846 0,977 0,181 1,865 1,512 1,551 1,397 1,297 0,48 0,385 0,225 1,101 1,645 0,19 1,202 1,844 1,642 1,740 0,851 1,803 1,340 1,606 0,113 0,473 1,630 1,670 0,770 1,297 0,146 1,776 1,893 1,469 0,747 1,166 1,19 1,28 0,74 0,280 0,573 1,584 1,753 1,82 1,514 1,704 1,197 1,658 1,399 0,479 1,943 0,164 0,549 0,717 0,152 1,373 1,96 0,192 0,118 1,303 0,826 1,139 1,934 1,979 1,357 0,238 0,646 0,278 1,311 1,955 1,208 1,349 0,577 1,239 1,61 0,279 1,962 1,209 1,798 1,373 0,283 1,241 1,191 0,61 1,112 0,410 1,459 0,544 0,359 0,183 1,207 0,933 1,721 1,592 0,699 0,535 1,325 1,8 0,750 0,472 1,578 0,102 0,7 0,713 0,396 1,20 1,171 0,880 0,30 0,58 0,184 1,914 0,594 0,445 0,40 1,617 0,990 1,988 0,854 1,540 1,71 0,758 1,189 1,221 0,31 1,374 0,429 0,308 0,536 1,555 1,361 1,891 1,215 1,471 1,766 0,782 1,238 0,908 1,812 0,658 0,502 0,656 0,827 0,512 1,227 0,691 0,407 1,230 0,761 0,635 1,771 0,34 1,334 0,426 0,904 1,254 0,118 0,612 0,270 1,165 0,124 0,612 1,23 1,814 0,915 0,114 1,323 0,314 1,109 0,946 1,388 1,63 0,517 1,160 1,451 0,485 0,639 1,68 0,943 0,231 0,795 1,803 1,760 0,983 0,459 0,163 0,884 1,146 0,631 0,554 1,234 0,728 0,242 1,958 0,523 1,401 1,452 1,258 0,548 1,950 0,793 1,69 0,136 0,935 1,948 0,593 0,559 0,830 0,367 0,38 1,340 0,899 1,592 0,292 1,929 0,967 1,889 0,982 1,544 1,196 0,368 1,305 1,167 0,902 0,766 1,414 0,193 1,222 0,911 1,164 1,361 1,43 1,278 0,604 0,604 0,860 1,86 0,699 0,432 0,765 0,328 1,129 0,699 1,394 0,947 1,955 0,814 0,107 0,413 1,88 0,450 0,650 1,390 1,531 0,447 0,85 1,50 1,772 0,22 1,202 0,717 0,178 1,210 0,800 0,92 0,630 1,282 0,112 0,327 0,646 1,372 0,411 0,490 0,383 1,515 1,711 0,133 1,459 1,852 0,1 1,166 0,20 1,231 0,310 0,339 0,165 0,791 0,751 1,78 1,787 1,296 0,530 1,845 0,108 1,18 0,291 0,775 0,505 1,188 0,998 0,60 1,833 1,226 0,838 0,640 0,366 0,975 1,842 0,696 0,170 0,347 1,631 1,707 1,23 1,896 1,686 1,635 0,942 1,886 0,993 1,0 1,999 1,526 1,414 1,434 1,122 1,754 1,186 1,256 1,610 1,380 1,813 1,363 1,230 0,260 0,771 1,814 0,513 0,627 0,238 0,280 0,603 0,546 1,466 1,560 1,58 1,522 0,894 0,468 1,978 1,57 1,733 1,821 0,512 0,638 0,249 1,92 1,815 1,964 1,834 0,972 1,976 0,229 0,305 0,454 0,298 1,923 0,316 1,660 0,703 1,457 1,608 0,108 1,198 1,755 1,616 0,33 0,844 1,25 1,32 1,506 1,134 1,360 1,425 0,599 1,195 0,175 0,343 1,526 0,977 0,388 0,162 1,932 1,229 1,587 0,565 1,210 0,537 0,862 1,86 1,887 1,931 0,481 0,894 1,937 0,944 1,605 1,701 0,379 0,638 0,742 0,192 1,132 1,714 0,658 1,264 1,133 0,747 0,295 0,136 1,747 0,964 1,539 1,518 1,705 0,369 1,881 0,437 1,584 1,810 1,107 1,409 1,324 0,812 1,564 0,442 0,292 1,745 0,280 0,737 1,972 1,886 0,561 0,632 0,983 0,625 0,365 1,61 1,552 1,376 1,976 0,829 1,530 0,978 0,50 0,437 0,352 1,290 1,801 0,764 1,464 1,270 0,991 1,496 1,537 1,441 1,584 0,109 0,646 1,979 1,391 0,74 1,382 1,613 0,109 0,990 1,320 0,768 1,908 1,399 0,743 1,472 1,995 1,564 1,35 0,517 1,600 0,132 1,673 0,493 0,473 0,627 1,136 0,410 0,262 0,58 0,662 1,997 1,62 1,728 1,914 0,81 0,784 1,295 0,16 1,188 1,309 0,770 0,537 0,598 1,190 0,206 0,643 1,7 1,485 1,836 1,237 0,840 0,193 0,541 0,689 0,795 1,471 1,380 1,888 1,181 1,678 1,356 0,984 0,668 0,295 1,94 0,655 1,285 0,941 0,463 0,310 0,494 1,143 0,730 0,584 0,455 0,287 0,519 0,526 0,809 1,258 0,483 1,103 1,174 1,505 0,723 0,33 0,493 1,122 1,267 0,268 0,90 0,899 1,755 0,660 0,647 1,533 1,839 0,792 1,693 0,132 0,502 1,468 0,430 0,502 0,436 1,476 1,86 1,499 1,683 0,928 0,752 1,487 1,20 0,856 1,112 0,327 0,411 1,772 0,512 1,949 0,229 1,82 1,886 1,105 0,928 1,141 1,680 1,175 0,341 1,289 0,852 1,847 0,138 1,788 1,736 0,786 1,706 1,420 1,369 1,860 1,560 0,337 0,906 1,620 0,991 1,842 0,465 1,411 0,266 1,395 1,99 0,330 1,915 1,937 0,74 1,871 0,578 0,848 1,142 1,726 0,134 1,21 0,597 0,243 1,238 0,144 0,224 0,44 1,506 1,859 0,469 1,875 0,420 1,724 0,897 1,493 1,51 0,885 0,722 0,949 1,694 0,936 1,918 1,607 1,691 1,627 0,640 1,4 0,399 0,35 1,861 0,799 1,469 1,501 0,641 1,390 0,994 1,239 0,583 1,270 0,210 0,231 1,826 0,590 0,308 1,233 1,95 1,122 1,570 0,726 0,248 1,158 0,769 1,950 1,699 0,20 1,197 1,0 0,544 1,717 0,860 1,685 0,206 1,413 0,413 1,31 0,621 1,460 0,774 1,506 1,162 0,700 0,711 0,817 1,345 0,118 0,738 0,955 0,687 0,552 0,72 0,552 0,648 0,203 0,852 0,794 0,263 0,150 0,71 1,662 1,791 1,598 1,314 1,544 1,632 1,511 0,644 0,201 0,715 0,805 1,329 1,704 0,116 0,809 1,597 0,180 0,462 0,968 1,814 0,601 0,262 0,62 0,149 1,405 0,784 1,918 0,659 1,968 0,278 1,279 0,649 0,391 0,193 1,31 0,104 0,506 1,902 1,847 1,481 1,526 1,397 0,676 1,626 0,911 0,280 0,504 0,773 1,460 1,707 0,436 1,112 0,536 1,258 1,276 0,308 0,920 0,698 0,620 1,437 1,870 0,784 1,210 0,654 1,491 0,548 0,880 0,115 1,748 0,702 1,987 0,772 0,282 0,660 0,290 0,823 1,698 0,302 0,570 0,955 1,824 1,458 1,542 0,941 0,965 1,812 1,881 0,62 0,613 0,504 1,785 1,72 0,205 0,204 0,47 1,12 1,892 0,891 1,150 0,240 0,328 0,556 1,463 1,621 1,139 1,7 1,567 0,948 0,610 1,967 0,744 0,881 1,977 0,448 1,475 1,343 1,737 0,16 1,396 0,630 0,777 0,579 0,814 1,935 0,234 1,287 0,606 1,81 0,363 0,149 0,118 1,582 0,923 1,608 1,687 1,523 1,945 0,452 0,241 1,974 0,471 0,68 1,457 1,317 1,254 0,163 1,755 1,896 1,816 1,254 1,349 1,471 0,393 0,825 0,573 1,758 1,769 0,398 1,681 1,350 1,994 1,636 1,693 1,178 0,241 0,329 0,797 0,723 0,122 0,299 0,550 0,404 0,743 1,83 0,418 0,681 1,845 1,37 1,630 0,824 0,69 0,78 0,980 1,100 0,875 1,581 0,575 1,10 1,172 0,567 1,745 1,28 1,336 1,748 1,442 0,16 1,255 0,983 1,663 0,523 1,566 1,826 1,523 0,808 1,557 1,129 0,299 0,76 0,57 0,252 0,186 0,973 0,450 1,252 0,927 1,840 0,568 0,428 0,705 1,467 1,929 0,319 0,872 1,793 1,269 0,358 1,519 0,995 0,559 0,942 0,303 0,944 0,600 1,821 0,96 0,835 0,465 0,840 0,268 0,867 0,356 1,795 1,368 0,324 1,455 1,267 0,752 0,619 0,709 0,709 0,812 1,499 0,72 1,72 0,762 1,695 0,672 0,15 0,896 0,57 1,766 0,104 0,638 0,473 0,438 1,923 1,192 0,980 1,750 1,142 1,516 1,974 0,118 1,76 1,578 0,349 0,576 0,411 0,925 1,925 0,215 1,828 1,381 0,952 0,583 0,938 1,452 1,51 1,423 0,782 1,813 0,213 0,35 0,944 1,865 0,225 1,750 1,887 0,188 1,368 1,858 0,919 0,798 1,157 1,429 1,979 1,168 0,340 1,396 1,531 0,290 1,405 0,962 0,191 0,478 1,942 1,982 0,858 1,773 0,124 1,867 1,626 1,811 1,38 0,205 1,925 0,626 0,335 1,248 1,643 0,510 1,66 0,703 0,948 1,713 0,528 1,670 1,492 1,136 1,669 0,635 1,627 0,544 0,52 0,978 1,185 0,841 1,150 1,163 1,137 1,519 0,8 1,268 1,715 1,793 0,261 1,23 0,351 1,715 1,891 0,464 1,294 0,681 1,713 0,839 0,814 0,535 0,173 0,609 0,597 1,541 0,282 0,289 1,864 0,40 1,881 1,744 0,372 1,852 0,392 1,216 1,510 1,897 1,172 0,646 0,226 0,991 1,477 0,524 0,966 1,341 0,731 1,527 0,919 0,408 1,799 0,295 1,439 0,656 1,284 0,485 0,402 1,376 1,652 0,844 0,512 0,190 1,24 0,69 1,579 1,132 1,4 1,493 1,61 0,629 1,876 1,560 0,527 1,793 0,993 0,928 1,139 1,101 0,428 1,743 1,25 1,525 0,65 1,867 1,991 0,586 1,410 0,615 1,931 0,267 1,276 1,929 0,26 1,465 0,396 0,926 1,562 1,922 1,917 1,691 1,835 0,78 0,328 0,231 1,880 1,755 1,653 0,232 1,540 0,241 1,320 1,507 0,284 0,725 0,25 1,996 0,904 1,833 0,224 1,629 0,461 0,619 0,393 0,761 1,4 0,916 1,522 0,771 1,984 0,652 0,855 0,37 0,15 0,757 1,48 1,368 1,310 1,236 1,285 0,182 1,891 0,306 0,467 0,967 1,830 0,792 0,423 0,980 1,937 0,579 1,263 0,60 0,231 0,863 1,436 1,347 1,453 1,706 0,160 0,97 1,812 0,200 1,75 1,783 0,551 0,434 0,104 1,858 0,900 1,305 1,569 0,216 1,138 1,247 0,36 0,922 1,574 1,737 1,191 1,6 0,445 1,692 1,98 0,743 1,908 1,359 1,470 1,717 1,373 1,954 0,253 0,981 0,875 1,270 0,313 1,259 0,174 1,761 0,738 0,246 0,192 0,962 0,863 0,134 0,366 1,722 1,894 0,577 0,854 1,124 0,659 1,11 1,942 0,294 1,296 0,137 1,878 0,72 0,621 1,482 1,438 1,79 0,460 1,904 1,559 1,246 1,8 0,618 1,799 1,844 1,550 0,944 0,755 1,324 1,155 1,194 0,299 1,564 0,309 1,239 0,101 1,829 0,129 0,89 1,544 0,270 0,238 1,256 1,988 1,216 0,988 0,968 1,908 0,713 1,232 1,324 0,76 1,234 1,366 0,813 1,865 0,615 0,174 0,916 1,570 1,611 0,193 1,140 0,369 0,461 0,812 0,161 0,210 1,479 0,546 0,110 1,185 1,48 1,957 0,574 0,455 0,32 1,121 1,573 1,277 1,343 0,881 1,53 1,878 1,894 0,738 1,855 1,86 1,846 1,326 0,851 0,644 0,452 0,16 0,3 1,99 0,1 0,71 0,746 1,277 1,76 0,415 1,926 0,578 0,489 1,391 0,15 0,292 0,454 1,167 1,299 0,440 0,724 0,553 0,24 0,568 1,748 1,326 1,408 1,93 0,406 0,92 0,385 1,8 1,416 0,796 0,637 0,864 0,174 1,912 1,101 0,302 1,845 0,480 1,569 1,950 0,872 1,397 0,458 0,227 0,797 0,461 0,653 0,658 1,727 0,458 1,277 1,912 0,947 1,196 1,858 1,475 0,984 1,427 1,884 1,317 1,260 0,908 0,37 1,29 0,32 0,258 1,695 0,997 1,668 0,287 0,741 1,962 0,624 0,963 0,559 1,716 1,690 1,751 0,540 0,858 0,779 0,826 0,99 0,590 1,550 1,90 0,88 1,547 1,774 0,352 1,875 0,875 0,124 0,631 1,423 1,640 0,828 0,529 0,890 1,325 0,648 0,508 0,420 0,261 1,619 1,174 0,840 0,507 0,516 0,937 1,858 0,602 0,615 1,300 0,730 0,498 0,917 1,342 1,585 0,216 1,540 1,948 0,396 0,392 0,134 0,590 1,173 1,394 1,956 1,886 0,503 0,337 1,840 1,282 1,485 0,810 0,673 1,88 1,802 1,563 1,759 0,380 1,30 0,177 1,334 1,844 0,858 0,280 1,524 1,434 1,862 1,806 1,608 1,164 0,774 0,478 1,991 0,394 1,484 1,65 0,671 0,220 0,297 1,404 0,767 1,96 0,373 0,992 1,35 0,286 0,832 1,882 0,574 0,549 0,549 0,82 1,354 1,987 0,320 1,440 1,657 1,714 1,380 1,873 1,122 1,731 0,413 0,849 1,394 1,935 0,858 0,16 0,751 0,951 1,604 0,159 1,500 0,368 1,537 1,264 1,753 1,601 0,836 0,825 1,696 0,493 0,113 0,773 1,529 1,379 1,844 0,811 0,891 0,593 1,764 0,483 1,796 1,799 1,93 1,260 0,799 0,816 0,180 0,292 0,555 0,29 0,91 0,83 0,591 0,889 0,71 1,86 0,474 1,231 0,454 1,620 1,820 1,374 0,43 0,793 1,804 0,37 0,104 1,7 1,378 0,721 0,655 1,699 0,71 1,465 1,458 1,660 1,823 0,822 0,115 0,952 1,874 0,543 0,731 1,515 0,838 0,244 1,532 0,14 0,781 0,377 1,553 1,693 1,642 0,263 1,973 0,29 1,19 1,764 1,352 0,198 0,44 0,982 1,342 0,835 0,668 1,643 0,657 0,192 1,620 1,858 1,756 0,800 0,637 1,851 1,669 1,701 1,65 0,324 1,673 1,733 0,36 1,101 0,485 0,497 0,79 1,906 1,343 0,636 0,138 1,65 1,518 1,95 0,769 1,851 1,755 0,718 0,157 0,566 1,310 0,753 0,964 0,260 1,178 0,963 1,288 0,388 1,944 1,128 1,48 0,79 1,149 0,844 0,911 0,100 1,692 0,529 1,844 0,529 1,996 1,130 1,786 1,606 1,827 1,285 0,314 0,963 1,67 1,724 0,932 1,764 0,698 0,261 1,147 1,238 1,797 1,470 0,883 1,251 1,725 1,615 0,238 0,441 1,846 0,676 1,342 1,883 0,915 0,722 0,373 1,985 0,733 1,917 1,57 1,350 1,350 0,885 1,682 1,156 1,662 1,1 0,618 1,150 1,534 0,541 0,865 0,56 1,607 0,402 0,923 1,188 0,543 0,584 0,348 1,114 0,973 0,989 0,109 0,972 0,233 0,817 0,489 1,500 1,299 1,721 1,726 0,935 0,282 1,270 1,829 0,974 0,455 1,654 0,413 0,233 0,643 0,682 0,527 1,790 0,846 0,995 1,299 1,108 0,981 1,847 1,722 0,467 0,189 1,955 1,718 0,55 1,83 1,503 0,365 0,581 0,461 1,630 1,985 1,151 0,260 1,591 0,868 0,189 1,822 1,680 0,747 0,250 1,442 1,326 0,672 0,291 0,293 0,717 1,881 1,809 0,554 0,401 1,475 0,675 1,602 1,192 0,937 0,643 1,716 1,117 0,134 1,746 1,909 0,105 0,693 0,739 0,886 1,173 1,419 0,580 0,546 1,405 0,422 0,788 0,56 0,803 0,789 0,577 1,123 0,965 1,259 0,599 0,575 0,37 0,302 1,173 1,50 0,478 0,388 0,161 1,967 0,981 0,626 0,614 0,259 1,790 0,867 1,744 0,378 0,699 0,271 1,723 0,490 0,244 1,242 1,589 1,816 0,412 0,525 1,529 0,575 1,42 0,289 1,661 1,12 1,365 1,400 0,245 0,448 0,371 0,554 0,213 1,246 0,415 1,734 1,486 0,492 0,289 1,664 1,187 0,556 0,432 0,772 0,719 1,242 1,361 0,574 1,488 0,913 0,584 1,849 0,205 1,757 1,764 0,928 0,313 1,602 1,901 1,712 0,359 1,595 0,169 0,304 0,878 0,93 1,871 0,71 0,977 0,290 0,959 1,466 1,562 0,72 1,40 1,41 1,763 0,888 0,782 1,524 1,78 1,382 0,124 0,429 0,841 0,950 1,777 0,499 1,828 1,232 0,475 0,513 1,345 1,451 1,863 0,696 1,772 0,442 0,903 0,627 0,110 0,967 1,487 0,117 0,670 0,520 0,500 1,554 0,693 0,838 0,478 1,217 0,557 0,45 0,306 0,784 1,96 0,78 1,687 1,977 1,458 0,603 0,668 0,202 1,339 1,345 0,427 0,177 1,276 1,400 0,737 1,799 1,227 1,791 1,620 1,621 0,74 0,998 0,876 0,731 1,221 1,604 0,989 0,976 0,334 1,453 0,2 1,871 1,99 1,628 1,141 0,688 0,66 1,382 1,551 1,462 1,590 1,37 0,433 1,162 1,539 1,419 1,659 1,138 1,409 1,299 0,652 0,157 1,856 1,991 0,13 0,732 0,841 0,654 1,247 1,880 0,757 0,176 1,365 1,964 0,657 1,25 0,857 1,281 1,237 1,194 1,248 0,551 0,23 0,375 0,19 0,578 1,48 1,862 1,350 1,543 1,566 0,311 1,24 0,964 1,33 0,319 1,204 0,583 1,159 1,245 1,572 1,112 1,383 1,745 1,591 1,163 1,981 1,992 0,672 0,426 1,632 0,288 1,0 0,736 1,168 1,454 0,851 1,598 1,820 0,463 0,59 1,704 1,848 0,578 0,580 0,59 0,899 1,974 1,911 0,688 1,130 0,462 0,449 1,120 1,388 0,78 0,958 0,543 0,660 1,840 1,683 0,416 1,103 0,751 1,427 0,319 1,773 0,430 1,197 0,473 0,707 0,739 0,710 1,427 1,228 1,970 0,599 1,476 0,953 0,852 0,310 1,104 1,38 0,688 0,290 0,572 0,644 0,881 0,920 0,473 1,973 0,682 1,973 1,539 1,71 1,887 1,557 1,540 1,55 1,412 1,139 1,455 0,343 1,127 0,314 1,295 0,354 0,804 1,731 1,599 1,842 0,201 0,248 1,375 1,610 1,66 0,511 0,573 1,822 0,217 0,224 1,532 1,476 1,899 1,949 0,981 1,389 1,529 0,854 1,899 1,984 1,155 1,183 0,430 1,169 1,368 1,733 1,794 1,981 1,303 0,993 1,732 1,426 0,545 1,280 0,929 1,645 0,760 1,548 0,519 1,127 1,579 0,413 0,737 0,184 0,922 0,176 1,912 1,559 0,168 1,354 0,611 0,817 0,801 1,159 1,170 1,201 1,319 1,448 0,862 0,421 1,899 0,414 0,994 1,774 0,123 1,328 1,307 1,100 0,90 1,512 1,279 0,371 1,99 1,312 1,227 0,30 0,146 1,456 0,170 1,312 0,705 1,70 1,827 1,955 1,983 0,233 1,180 1,426 0,476 1,575 0,306 0,840 0,121 0,812 1,650 1,831 1,245 1,653 1,349 0,126 1,115 1,153 1,834 0,804 0,280 0,176 0,899 1,612 0,290 1,870 1,51 1,281 1,912 0,355 0,173 1,249 0,124 1,203 0,533 0,643 0,310 1,584 0,803 0,728 0,181 0,929 0,403 1,487 1,979 0,463 0,687 1,77 1,260 1,829 1,870 1,374 1,795 0,162 1,877 0,15 0,177 0,898 0,169 1,778 1,272 1,354 1,401 1,814 1,557 1,852 1,435 0,766 0,396 1,124 1,790 0,759 1,472 0,3 0,653 0,640 0,211 1,686 0,967 0,734 0,604 1,97 1,715 0,950 0,390 0,741 1,93 1,536 1,284 1,642 0,88 1,820 0,493 0,741 0,467 1,458 0,173 1,222 1,786 0,754 0,572 0,335 1,826 1,39 0,737 0,124 0,877 1,107 0,49 0,491 1,208 1,162 1,36 0,601 1,211 0,759 1,270 0,836 0,245 0,734 0,146 0,191 0,730 0,713 0,374 1,417 0,521 1,322 1,52 1,194 1,411 0,535 1,54 1,352 1,245 1,426 1,864 0,482 0,371 0,559 1,843 1,181 0,612 0,162 0,32 1,751 0,134 1,774 0,557 0,274 1,231 1,317 1,477 0,995 0,100 0,58 0,38 0,86 1,737 0,219 0,281 0,733 1,285 1,701 0,541 1,952 0,314 0,26 1,985 0,330 0,912 1,619 1,458 1,719 1,667 0,907 0,246 0,238 0,696 0,518 0,229 0,490 0,362 1,973 1,466 1,540 0,247 0,57 0,397 0,503 1,875 0,489 1,90 0,949 0,749 1,142 1,993 0,456 0,785 0,884 1,143 0,924 1,344 0,890 1,173 1,305 0,528 0,644 0,999 0,860 1,53 0,324 0,442 1,964 1,940 1,802 0,374 0,782 1,620 1,386 0,7 0,518 0,82 0,151 0,428 1,805 1,378 1,199 0,91 1,288 1,197 1,32 0,636 1,284 1,738 1,806 0,316 0,171 1,359 1,121 0,815 1,462 1,663 0,164 1,355 1,693 0,785 1,837 0,919 1,580 0,936 0,807 0,865 0,280 0,365 1,240 1,152 0,397 1,710 0,943 0,777 0,594 1,670 1,743 0,866 1,956 1,868 1,198 0,587 1,365 0,819 0,385 1,653 1,255 1,279 1,62 1,292 0,679 0,606 0,752 1,865 1,753 1,322 1,245 1,363 0,428 0,124 1,345 0,156 0,285 1,503 1,783 1,975 0,175 0,236 1,450 1,200 1,360 0,378 1,603 1,831 0,223 0,262 1,736 0,64 1,720 1,819 1,475 0,623 1,298 1,768 0,4 1,718 0,779 1,56 1,401 1,794 0,266 1,15 1,299 0,233 1,155 0,259 0,573 0,334 0,396 1,186 1,626 0,231 1,579 0,417 1,978 0,746 0,722 1,699 1,890 1,363 0,73 0,840 0,974 0,198 1,647 0,653 1,332 1,976 0,432 0,77 0,295 1,269 0,394 0,320 0,725 1,769 1,597 1,276 1,152 0,443 1,38 1,423 1,162 1,641 0,632 1,214 0,436 1,836 0,25 1,27 0,729 0,248 0,753 1,519 1,721 0,816 0,503 0,585 0,777 1,491 1,497 1,756 0,302 0,450 1,683 0,374 1,560 1,907 1,536 1,958 0,188 1,212 0,969 0,105 1,188 1,612 0,753 1,87 0,976 1,940 0,990 1,86 1,544 0,766 0,342 1,778 1,59 0,238 1,80 0,585 1,961 0,590 0,816 0,412 0,321 0,799 0,644 0,281 0,535 0,332 1,144 1,139 1,546 0,75 1,462 1,808 1,448 1,227 0,522 0,939 0,866 1,812 1,598 1,405 0,99 1,436 1,658 0,514 1,7 0,56 0,197 1,293 0,568 0,370 0,583 1,28 1,110 1,925 1,698 0,204 0,507 1,2 0,469 0,712 0,525 1,771 1,943 1,741 0,898 1,136 1,184 0,164 1,831 1,882 1,144 1,785 1,266 1,872 1,656 1,143 0,744 0,278 1,951 1,815 0,829 0,521 1,319 0,799 0,736 0,994 0,716 1,37 1,119 1,907 1,35 1,726 1,118 0,698 1,148 0,271 1,417 0,923 0,214 1,181 0,573 0,640 0,789 1,664 0,153 0,149 0,500 0,140 0,168 0,720 0,627 1,416 0,213 0,473 0,232 0,578 1,511 1,507 1,809 0,160 1,643 0,558 0,983 0,816 0,776 1,253 1,727 0,334 1,58 0,481 0,418 1,557 1,743 1,187 1,74 0,782 1,856 1,25 0,361 1,100 0,272 0,859 0,144 1,709 0,840 0,392 1,292 1,622 0,259 1,549 0,862 0,356 0,406 1,747 1,37 1,883 1,566 1,951 1,775 1,782 1,290 0,522 0,154 0,307 1,441 1,419 0,317 1,916 1,58 0,538 0,411 0,226 1,212 1,2 1,252 1,402 0,154 1,436 1,854 1,447 1,969 0,420 0,852 1,588 1,268 1,976 0,551 0,16 0,962 1,963 0,553 0,296 1,344 1,836 0,567 1,175 0,432 1,198 1,3 0,181 0,445 0,471 0,856 1,432 1,827 1,314 0,791 0,605 0,475 0,997 1,925 0,462 1,526 0,836 0,831 1,658 1,440 1,799 1,908 0,29 0,908 1,170 0,648 0,527 0,942 0,989 1,457 1,8 0,62 1,111 1,303 1,949 1,382 1,516 1,388 1,897 0,377 0,829 1,702 1,105 0,45 0,917 1,295 0,118 1,763 0,483 1,625 0,902 1,977 0,571 0,334 0,438 0,666 0,356 1,581 1,641 0,759 1,760 0,320 0,442 1,88 0,3 0,771 0,346 0,593 0,842 1,399 1,504 1,232 0,717 0,127 1,502 1,290 1,296 0,745 0,639 1,814 1,218 1,270 1,903 0,61 0,373 1,123 0,586 0,139 1,520 0,76 1,911 1,300 1,301 1,872 1,488 1,306 1,422 1,506 1,949 1,264 0,175 0,263 0,319 0,848 0,369 1,435 1,81 1,926 1,746 1,814 1,467 1,25 1,514 0,172 0,102 1,65 1,54 1,488 1,854 0,188 0,869 1,967 1,228 1,505 1,352 0,462 0,601 0,259 0,892 1,844 0,128 1,450 0,840 0,426 1,929 1,179 0,440 0,576 0,983 0,578 0,755 0,791 1,608 0,457 0,904 1,107 1,643 0,443 1,213 0,61 0,299 0,406 1,464 0,675 1,902 1,856 1,144 0,685 1,222 0,297 1,868 0,554 1,119 1,79 0,12 1,305 0,274 0,88 1,686 0,814 0,552 1,700 1,232 1,39 1,142 1,718 1,33 1,226 1,437 0,550 1,53 0,946 0,37 0,344 0,965 0,480 1,757 1,887 0,974 0,589 0,884 0,689 0,980 1,822 0,376 0,776 0,187 1,471 1,7 1,988 0,509 1,109 1,342 0,956 0,842 1,276 0,382 1,964 0,837 0,844 1,114 1,902 1,792 0,745 0,285 1,671 1,105 1,752 0,787 1,662 0,964 1,947 1,926 1,944 1,549 1,436 0,29 0,614 0,305 0,500 0,578 1,282 1,784 0,964 1,31 1,333 1,944 0,919 1,162 0,43 1,385 0,145 0,384 1,722 0,441 0,126 1,841 0,975 0,734 0,532 1,809 0,184 1,676 0,860 1,570 0,586 1,674 0,941 0,144 1,678 1,912 1,624 1,246 0,719 1,607 1,37 1,658 1,990 0,293 1,413 1,358 0,837 0,569 0,488 1,213 0,934 0,0 1,578 0,383 0,294 0,399 0,886 0,345 0,22 0,304 0,535 0,505 1,416 0,62 0,404 1,508 1,801 0,789 0,597 1,731 0,896 1,790 1,229 1,162 1,408 1,757 0,954 1,185 1,776 0,737 0,178 0,483 1,902 1,645 0,740 0,853 0,240 0,22 1,706 0,75 0,542 1,854 1,608 1,186 1,142 1,896 1,648 1,344 1,976 1,92 1,733 0,760 1,590 0,304 0,261 0,12 0,951 0,58 0,239 0,528 1,179 1,456 0,371 0,318 0,119 1,651 1,882 0,686 1,338 1,613 1,538 1,786 0,392 0,873 1,715 1,357 0,74 0,978 0,228 0,697 0,398 0,137 1,677 0,958 0,160 0,154 0,144 1,433 0,526 0,553 0,344 1,152 0,663 0,912 0,550 0,605 0,539 0,528 1,641 0,234 0,953 1,965 0,646 0,867 1,786 0,962 0,37 0,156 0,553 0,825 0,181 0,672 1,382 1,659 1,510 1,160 1,190 1,142 0,532 0,810 1,236 1,202 1,535 0,883 0,537 0,198 0,521 0,26 0,321 0,735 1,811 1,230 0,25 1,691 1,534 1,598 0,417 1,759 1,18 0,573 0,678 0,805 1,847 1,514 1,423 0,11 1,53 0,367 0,533 0,327 0,106 0,165 0,31 1,977 1,364 1,743 1,299 0,869 1,960 0,387 0,844 1,248 1,992 1,906 0,920 0,299 0,206 0,682 1,374 0,518 1,236 1,123 1,962 0,650 0,374 1,888 1,824 0,203 0,419 0,231 0,203 0,215 0,626 1,70 1,712 1,422 1,172 1,462 0,978 0,433 1,833 1,737 0,318 0,577 0,518 0,367 1,255 1,857 1,561 0,258 1,300 1,526 1,137 1,607 1,370 0,766 0,731 0,25 0,429 0,394 1,877 0,77 1,91 0,941 0,171 0,738 1,615 0,181 0,137 0,617 1,604 0,77 1,453 1,838 1,159 1,522 1,377 1,208 0,157 0,142 1,303 0,779 0,845 1,937 1,923 1,854 1,618 0,971 0,7 1,655 0,819 1,167 1,73 0,926 0,191 0,744 0,394 1,258 1,461 1,162 1,204 0,249 0,413 1,437 0,691 0,759 0,359 0,616 0,826 1,729 0,816 0,996 1,659 1,697 0,372 0,112 0,730 1,148 0,594 1,3 1,575 0,859 0,797 0,142 0,434 0,842 1,997 0,819 1,805 1,932 0,925 1,422 1,960 1,505 0,784 0,371 0,706 0,619 0,971 1,543 1,949 1,71 0,554 0,160 0,966 0,481 0,289 1,948 0,269 0,606 0,314 1,327 1,159 0,908 1,530 1,177 0,159 1,521 1,123 1,301 1,344 0,851 1,103 1,528 1,424 1,233 0,771 1,627 1,539 1,74 0,386 1,169 1,183 0,140 0,672 1,584 0,63 0,197 0,711 1,248 1,550 1,574 1,608 1,907 0,971 1,626 1,191 1,792 1,516 0,699 1,141 1,901 1,25 1,889 0,236 0,777 0,580 0,668 0,235 0,189 1,301 0,887 1,976 0,608 0,696 0,707 1,212 1,121 1,32 0,895 0,327 0,61 1,209 0,773 1,944 1,337 1,784 0,321 1,387 0,965 0,683 0,486 0,992 0,681 0,111 1,625 1,590 0,18 0,923 0,723 1,686 0,741 1,584 1,437 0,625 0,351 0,346 0,381 1,109 1,573 1,8 1,54 1,151 1,873 0,349 0,131 1,804 0,582 0,178 0,881 1,888 1,609 0,783 1,719 1,739 0,627 0,837 1,298 0,43 1,397 1,653 1,868 1,96 1,120 0,126 1,232 1,607 0,251 1,375 1,389 1,61 0,311 1,825 0,751 0,175 0,433 0,418 0,868 0,661 1,724 1,597 0,893 1,896 0,399 0,747 1,909 0,303 0,847 1,709 1,913 1,651 1,894 1,846 1,380 0,578 0,285 1,527 0,835 1,339 0,626 0,482 0,972 1,639 0,295 1,604 1,501 1,377 1,105 0,922 0,599 1,851 1,756 1,725 1,665 1,747 0,611 0,119 1,419 0,712 1,11 1,199 0,458 0,171 1,87 0,320 1,654 1,344 0,227 0,526 0,30 1,676 1,552 1,967 0,8 1,531 0,264 0,844 0,421 0,836 0,837 0,870 1,661 0,370 0,121 0,73 1,262 0,106 0,287 1,276 1,227 1,605 1,236 1,790 0,994 1,955 1,540 0,549 0,720 1,620 1,425 1,486 1,482 1,198 0,511 0,339 0,484 0,41 0,332 1,311 0,381 1,29 0,325 0,755 1,514 0,902 0,249 1,806 1,38 0,702 1,642 0,16 0,823 0,917 0,454 0,143 1,530 0,971 0,58 1,481 1,516 0,243 1,722 1,115 1,565 1,577 1,819 0,853 1,123 0,758 0,753 0,25 0,869 1,15 0,544 0,74 0,929 0,490 0,857 0,817 0,293 1,455 0,229 1,543 1,350 0,634 1,268 1,586 1,352 1,69 1,735 1,789 1,477 0,747 1,196 1,484 0,134 0,536 1,528 0,760 1,635 1,964 1,618 0,158 1,261 1,375 0,690 1,262 0,98 0,976 0,423 0,335 0,901 0,753 0,316 1,497 0,445 0,698 1,800 0,516 0,181 0,531 0,327 0,943 0,905 0,336 1,399 0,886 0,624 0,79 1,510 0,351 0,409 1,375 1,329 1,701 0,529 0,194 1,883 1,827 1,516 0,136 0,774 0,655 1,948 0,454 1,770 0,388 0,222 1,455 0,13 0,655 0,916 0,642 1,140 1,987 0,720 1,78 0,190 0,296 0,556 1,271 1,884 0,445 0,842 1,954 0,936 0,924 1,58 0,44 0,691 0,312 1,925 1,506 1,569 0,133 1,389 1,761 1,73 1,91 1,523 1,599 1,230 1,845 0,989 0,137 0,435 0,509 0,298 1,254 1,812 0,213 1,561 1,575 0,998 0,848 0,885 0,275 1,573 0,133 1,597 0,809 1,797 0,253 0,933 0,934 1,814 1,310 1,41 0,644 0,315 0,323 1,34 0,74 0,784 1,183 0,574 0,728 0,456 1,942 0,244 1,469 1,260 0,352 0,236 1,166 1,467 0,798 1,701 1,769 1,309 0,218 0,566 1,821 1,973 1,271 1,437 1,904 0,454 1,272 1,127 1,50 0,399 0,412 1,387 0,387 1,617 0,871 1,137 1,999 1,703 0,239 1,306 0,907 1,693 1,67 1,10 0,682 0,512 0,731 1,62 0,93 1,253 0,632 1,786 1,982 1,249 1,332 0,394 1,58 0,146 0,134 1,6 0,953 0,491 0,473 0,968 1,934 0,891 1,861 1,729 0,523 1,891 1,299 0,154 1,227 0,852 0,987 0,663 1,638 1,947 0,350 0,48 1,446 1,978 0,567 1,120 1,599 0,479 1,47 1,262 0,6 1,855 1,68 1,441 0,510 1,525 0,960 1,231 0,522 0,104 0,710 1,152 0,108 1,770 1,885 1,502 1,725 1,330 0,403 1,268 1,975 0,48 1,781 0,523 0,921 0,507 0,632 1,790 0,535 0,742 0,737 0,177 0,619 0,42 0,676 0,148 1,844 1,195 1,95 1,743 0,790 1,764 0,12 1,517 1,5 1,206 1,206 0,145 0,929 1,443 1,64 0,438 1,469 0,931 1,489 0,667 0,453 1,159 1,108 0,899 1,772 1,916 1,859 0,551 0,928 1,715 1,231 0,251 1,93 1,756 1,465 0,41 1,572 0,951 0,482 1,437 0,344 0,139 1,43 0,806 1,363 0,994 0,537 0,550 0,573 0,798 0,586 0,106 1,468 0,510 0,396 1,26 0,791 1,411 1,322 1,61 0,868 1,606 1,380 0,213 0,69 1,654 1,398 1,813 0,441 0,102 0,20 0,354 1,982 1,112 1,58 0,260 0,517 1,138 1,308 0,334 0,599 0,966 0,391 1,494 0,85 0,286 0,944 1,626 1,416 1,485 1,338 1,845 1,825 0,612 1,202 1,96 0,780 1,309 1,399 1,580 1,183 1,457 1,898 1,793 1,644 0,278 1,211 0,805 1,615 0,279 0,685 1,67 0,583 1,324 1,658 1,438 1,200 0,257 1,926 1,433 0,154 0,966 1,617 0,275 1,768 0,403 1,723 1,744 0,615 0,524 0,500 1,782 0,84 0,206 1,788 0,686 0,123 1,469 1,849 0,457 0,262 0,605 0,678 0,753 1,60 1,695 0,238 0,917 1,311 0,309 1,998 1,293 0,745 0,575 0,615 0,886 0,770 0,513 0,573 1,152 1,199 0,671 1,269 0,175 1,574 0,30 0,518 1,293 1,795 0,735 1,756 1,557 0,46 1,699 1,296 1,692 1,508 1,837 0,281 1,732 0,294 0,813 0,11 1,961 1,273 0,511 1,273 1,98 1,862 0,326 1,804 1,365 1,111 1,287 0,163 1,779 0,889 1,27 1,224 1,296 1,608 1,397 0,638 1,50 0,146 0,126 0,48 1,321 0,64 0,62 0,756 0,437 1,584 1,310 1,667 1,191 1,21 0,631 0,810 0,204 0,979 0,129 1,129 1,951 0,477 0,40 1,96 1,816 0,569 0,306 0,368 0,819 1,173 1,467 0,915 1,500 0,649 0,896 0,798 0,818 0,512 1,112 0,857 0,632 1,855 1,28 0,269 1,337 1,518 1,276 0,499 0,180 0,521 0,828 1,9 0,687 0,516 0,376 0,146 0,288 0,690 1,206 0,911 1,268 0,125 1,977 0,991 1,470 0,665 0,75 0,626 1,821 0,493 1,546 0,533 0,477 1,725 0,478 0,158 0,981 0,934 1,374 1,403 0,624 1,466 0,168 0,749 0,205 0,705 1,320 1,528 0,692 0,764 1,572 0,931 1,537 1,943 0,22 0,685 1,603 1,583 1,915 0,169 0,976 1,86 0,732 0,99 1,77 1,244 1,865 0,657 1,650 0,403 1,82 0,4 1,365 0,556 1,796 1,752 0,444 0,2 0,207 1,860 0,346 0,851 0,484 0,450 0,544 1,123 1,265 0,877 1,672 1,674 0,955 1,314 0,147 1,28 1,574 1,672 0,383 1,80 1,80 0,572 1,290 1,351 1,208 1,218 1,27 0,595 0,922 1,525 0,742 0,266 0,548 0,149 0,344 0,282 0,762 0,125 0,739 1,429 1,582 1,778 1,563 1,931 1,520 1,715 0,15 0,294 0,39 1,961 1,856 0,19 0,987 0,692 1,638 0,742 0,888 1,29 0,477 0,801 0,439 0,902 1,249 0,487 1,568 0,74 0,664 0,694 0,125 1,766 0,137 1,390 1,345 1,276 1,52 1,920 1,316 0,161 1,44 1,453 1,278 0,733 1,105 1,180 1,497 0,576 0,921 1,943 0,707 0,258 0,152 1,628 0,126 0,328 1,140 0,93 1,342 1,40 0,698 1,734 0,683 0,736 1,529 1,385 1,533 0,563 0,739 1,976 0,433 1,687 0,130 1,812 0,835 1,692 1,283 1,55 0,288 0,793 1,915 0,25 1,202 1,203 0,101 0,411 0,61 1,467 1,148 1,129 1,365 1,774 0,173 1,526 0,379 0,987 0,730 1,687 1,996 0,142 0,214 1,522 0,746 1,808 0,531 0,260 1,144 1,243 0,598 1,562 0,157 1,792 1,889 0,984 0,726 0,966 0,656 0,362 0,63 0,835 0,919 0,325 0,18 1,534 1,795 0,653 1,108 1,15 0,498 1,146 0,266 1,401 1,844 1,753 1,57 1,511 0,702 0,707 1,452 1,90 1,340 1,215 1,705 1,339 0,969 1,636 0,106 1,405 0,151 1,542 1,831 1,416 1,558 0,535 0,500 1,321 0,469 1,989 0,355 0,485 0,640 1,590 0,395 0,366 0,748 0,396 0,162 0,785 1,648 1,384 1,923 0,469 1,633 1,896 1,603 1,749 0,790 0,431 0,429 0,874 0,562 1,688 0,67 0,230 0,166 0,952 0,229 0,290 1,782 0,656 0,848 0,283 1,872 0,743 0,235 0,845 1,508 0,230 0,321 1,36 1,960 1,814 1,692 1,957 1,879 0,913 1,490 0,491 0,373 1,600 1,392 0,343 0,960 1,628 1,769 1,990 0,756 1,454 0,338 1,14 1,239 0,59 0,518 1,725 0,626 1,894 0,384 1,61 1,917 0,375 0,930 0,60 0,315 1,335 0,638 1,313 1,610 1,99 0,215 0,477 1,994 0,39 1,908 0,687 0,199 0,568 0,763 0,196 1,745 0,284 1,299 0,829 0,929 1,593 1,845 1,894 0,314 0,218 0,232 0,917 0,317 0,411 1,343 1,87 1,269 0,571 0,483 0,114 0,152 1,581 0,748 1,623 0,979 1,582 0,216 1,165 1,617 0,410 0,922 0,573 0,105 0,692 0,259 1,709 1,636 1,804 1,106 1,224 0,31 1,258 1,261 1,560 1,619 1,343 1,664 1,676 0,211 1,796 1,688 1,647 1,655 1,838 1,702 1,763 0,813 0,857 0,850 0,303 0,130 0,500 0,618 1,619 0,573 0,750 0,490 1,627 1,529 0,211 1,345 1,450 0,547 1,410 1,931 0,386 1,745 0,706 1,697 1,351 1,954 0,847 0,390 1,188 0,839 1,380 1,392 1,681 0,231 0,399 1,181 1,374 1,317 1,790 0,592 0,55 1,710 0,42 1,870 0,750 0,168 0,72 1,809 1,687 1,432 1,886 1,682 0,777 0,830 0,534 1,759 0,149 0,717 1,694 0,889 0,611 0,576 1,716 1,141 1,15 0,552 0,459 1,650 0,682 1,0 1,852 0,742 1,707 1,851 0,294 0,511 0,650 0,886 0,324 1,238 0,886 0,574 1,235 0,651 1,734 1,88 1,455 1,123 0,132 0,550 0,126 0,433 1,296 0,633 1,922 1,595 0,259 0,649 1,430 0,528 0,577 1,217 0,764 0,351 0,814 1,505 0,324 1,860 0,733 1,362 0,718 0,348 1,979 1,780 0,466 1,892 1,9 0,923 0,364 0,352 0,20 1,209 0,450 1,585 0,374 0,671 1,531 0,549 1,72 0,595 0,389 0,665 0,145 1,270 1,344 0,431 0,130 1,727 0,148 1,64 1,611 1,838 1,228 0,866 1,683 1,92 0,480 0,851 0,755 1,438 0,80 0,414 1,477 0,923 0,66 1,839 1,725 0,181 0,400 1,553 1,197 0,157 1,831 1,394 1,292 1,210 0,731 1,313 0,157 0,789 1,486 0,306 0,414 1,982 1,378 1,916 1,337 0,810 1,451 0,778 1,712 1,907 0,569 0,125 0,92 0,346 1,811 1,430 0,192 1,878 1,228 1,933 1,864 0,876 1,794 0,783 1,998 0,684 0,918 0,104 0,442 1,987 0,166 1,252 1,406 1,539 0,770 0,581 0,563 0,567 1,948 1,52 0,115 0,920 0,90 1,476 1,875 1,522 1,47 0,925 1,888 0,814 1,288 0,579 0,817 0,478 0,300 0,57 1,110 0,82 1,242 1,271 0,523 0,253 1,21 1,446 1,165 1,13 0,121 1,711 1,254 0,981 0,227 0,557 1,659 0,297 0,363 0,599 0,189 1,143 0,512 0,949 0,974 1,323 0,578 1,941 0,132 1,522 1,468 1,3 0,400 1,765 1,74 0,17 0,139 1,27 0,712 1,541 1,191 1,538 1,351 0,459 1,327 0,347 0,844 0,694 1,409 1,658 0,868 0,578 0,795 1,940 0,124 0,988 1,37 0,715 0,47 0,798 0,820 1,622 0,529 1,334 0,955 1,626 1,350 0,294 0,763 0,251 1,36 0,141 0,820 0,673 1,451 0,182 0,54 0,761 1,934 1,376 1,387 1,924 1,334 1,417 1,809 1,635 0,316 0,215 1,828 1,247 1,235 0,603 1,260 1,468 0,578 1,726 1,92 1,847 0,934 0,486 1,929 1,86 0,477 1,943 1,301 1,27 1,865 1,126 0,353 0,678 1,230 1,472 1,968 1,713 0,496 0,699 1,474 0,963 0,35 0,517 0,6 1,454 1,696 0,791 0,141 1,46 0,894 1,874 1,536 1,546 0,685 0,525 1,419 0,182 0,385 0,639 1,959 1,569 1,420 0,872 0,950 0,979 0,558 0,624 0,598 0,127 1,553 0,234 0,789 1,967 0,123 0,185 1,869 1,891 0,139 0,742 1,657 1,200 1,255 1,621 1,710 0,336 0,524 1,225 0,993 0,132 0,715 0,747 0,297 0,591 0,578 0,205 1,455 1,3 1,438 0,851 0,813 1,324 0,236 0,353 0,914 0,702 0,389 0,177 0,345 1,114 0,168 0,570 0,607 0,232 1,556 1,669 1,983 0,94 1,463 1,832 1,837 0,218 1,163 1,827 1,812 0,322 1,330 1,744 0,109 0,118 0,368 1,962 1,237 0,61 1,649 1,353 1,246 0,842 1,12 0,630 0,605 1,595 0,41 0,307 0,477 1,473 0,121 0,190 1,537 0,18 0,865 1,28 1,288 0,777 0,372 0,701 0,377 0,375 0,998 0,109 1,981 1,716 0,672 0,294 0,613 0,129 1,960 1,841 1,820 1,641 0,413 0,702 1,986 1,641 0,427 0,407 0,835 0,120 1,786 1,564 0,820 0,844 1,189 0,644 1,722 1,45 0,876 1,406 1,262 0,910 1,435 1,796 1,103 1,347 1,806 0,981 1,142 0,197 1,189 1,914 1,614 0,146 1,216 1,631 0,394 0,432 1,912 1,802 0,203 0,909 0,668 0,296 0,522 0,116 1,67 0,183 0,480 1,144 0,305 1,532 0,361 0,562 1,456 1,254 1,185 1,831 0,863 1,101 0,867 0,395 1,893 0,30 1,555 0,478 0,231 1,434 0,306 0,77 1,634 1,884 1,787 0,81 1,257 0,604 0,712 1,54 0,915 1,112 1,636 0,993 0,525 1,444 1,992 1,829 1,992 0,701 1,859 0,810 1,108 1,321 0,105 1,414 1,31 1,888 0,60 1,876 0,865 0,833 0,288 0,998 0,166 1,994 0,127 1,444 1,626 1,284 0,570 1,474 1,7 0,344 0,593 1,38 1,208 1,433 0,67 0,213 1,867 0,992 1,590 1,70 1,565 1,874 0,501 0,151 0,313 0,423 0,796 1,744 1,790 1,266 1,888 0,914 1,545 1,607 1,606 1,846 0,853 0,850 1,136 1,786 1,145 1,300 1,299 0,900 0,61 1,219 0,176 1,89 1,290 1,750 0,578 0,947 0,954 0,846 1,708 0,703 1,701 0,608 1,913 0,724 1,162 0,844 1,179 0,414 0,502 1,15 1,341 0,30 0,197 0,494 0,106 0,172 1,963 1,124 0,776 1,22 0,87 1,683 1,936 0,487 1,585 0,132 0,278 1,291 1,682 0,884 1,285 0,225 1,378 1,184 1,929 0,348 0,327 0,795 1,249 0,531 1,921 0,489 1,509 1,773 1,401 1,718 1,535 1,44 1,661 1,70 1,64 0,525 0,20 0,506 1,438 0,617 0,784 1,253 0,226 0,833 0,611 1,541 0,410 1,438 0,79 0,831 0,352 1,360 1,293 0,820 1,262 0,617 0,771 1,328 1,25 1,299 1,775 0,380 0,915 1,783 1,467 1,735 0,217 1,617 1,744 1,487 0,164 0,344 0,383 1,530 0,31 0,825 0,967 0,162 1,893 0,683 0,169 0,540 0,231 0,336 1,127 1,768 0,634 0,865 1,977 0,11 1,583 0,577 1,807 1,156 1,262 1,8 0,168 0,512 1,30 0,842 0,630 0,482 1,315 1,768 1,232 0,649 0,471 0,938 1,604 0,325 0,362 0,748 1,81 0,227 0,792 0,781 0,746 1,759 0,125 0,711 1,37 0,309 0,516 1,460 0,49 0,690 0,148 1,281 0,946 1,303 1,466 1,161 1,617 1,927 1,903 1,256 0,694 1,999 0,265 1,934 1,757 1,767 1,922 0,352 1,339 1,797 1,868 1,737 1,68 0,951 1,539 0,683 1,571 0,75 0,321 1,697 1,441 0,492 1,410 0,628 1,692 0,434 1,272 1,736 1,904 0,589 1,524 1,34 0,119 0,371 0,85 1,374 1,85 0,743 0,616 0,938 1,163 0,628 1,653 1,437 1,782 0,713 1,678 1,659 0,810 0,613 0,518 1,128 1,651 1,66 1,351 1,882 1,195 0,670 0,756 0,144 0,905 0,437 1,825 1,410 1,534 1,478 1,157 0,463 0,132 0,991 0,626 0,866 1,762 0,689 0,758 0,274 0,452 1,226 1,725 0,539 0,433 0,17 1,190 1,199 1,400 1,974 1,616 0,574 1,533 1,646 1,22 0,97 1,727 1,259 1,984 0,714 1,673 1,282 0,151 0,840 0,270 1,525 0,423 1,898 1,799 0,907 1,548 0,74 1,835 0,184 0,712 1,963 1,324 1,519 0,301 0,16 0,379 0,663 0,284 1,514 0,346 0,35 1,527 1,87 0,266 1,276 1,97 0,595 0,574 1,591 1,769 0,263 1,938 1,341 1,96 1,778 0,149 0,292 1,783 1,685 0,163 0,99 0,570 1,211 1,822 1,486 0,750 0,651 0,474 1,939 0,962 1,393 0,287 1,252 0,182 0,454 0,961 0,944 1,811 0,213 1,92 0,160 1,452 0,736 0,30 0,985 1,925 1,601 0,920 0,445 0,145 0,269 1,730 0,972 1,709 0,71 0,600 1,996 0,965 1,115 1,671 0,917 1,163 0,572 1,228 0,360 1,89 0,456 1,78 1,809 1,475 0,825 1,630 0,136 1,476 1,245 1,442 1,948 0,825 1,976 0,638 1,728 1,629 0,855 1,317 1,526 1,156 0,117 1,621 1,970 1,707 0,624 1,201 1,918 0,189 1,780 0,598 0,917 0,742 0,271 1,5 0,420 0,921 0,393 1,838 0,561 1,839 0,480 1,216 0,344 0,410 1,848 1,46 1,423 1,174 1,660 1,624 1,546 1,441 0,136 1,705 0,232 0,421 1,456 1,377 1,808 1,906 0,604 0,667 0,607 1,330 0,96 1,92 0,659 0,437 1,463 0,786 1,698 0,372 0,431 1,707 1,523 0,528 1,978 0,429 1,111 1,62 0,227 0,500 1,275 0,499 0,517 0,206 1,477 0,128 0,539 0,600 0,437 0,842 0,319 0,313 0,480 1,85 1,488 1,699 1,195 1,448 1,297 1,899 1,474 0,322 1,146 0,20 0,96 0,685 1,255 1,827 0,895 1,463 1,450 1,538 1,232 1,499 1,650 1,73 0,556 0,749 1,917 1,817 0,271 0,163 0,485 1,106 0,855 0,934 1,816 0,559 1,165 1,548 0,269 0,211 0,635 1,524 0,77 0,467 0,88 1,114 0,307 0,344 0,843 0,835 0,871 0,930 1,440 0,996 0,882 0,240 0,231 0,869 0,579 0,504 1,371 0,336 1,634 0,971 0,453 1,639 1,84 0,382 1,600 1,961 0,410 1,304 0,985 1,258 0,478 1,888 1,260 1,598 1,97 0,500 1,803 1,879 1,556 1,436 1,147 1,871 1,537 0,237 1,284 0,150 1,996 1,637 1,310 1,612 0,732 0,481 0,201 1,314 1,427 0,157 1,857 1,504 0,776 1,707 1,264 1,144 0,787 1,296 0,373 1,50 1,751 0,61 1,133 0,783 0,251 0,240 0,869 1,151 0,280 0,631 1,79 1,800 0,433 0,810 1,178 1,501 1,344 1,939 0,43 1,978 0,221 0,32 1,584 0,390 0,286 1,422 1,572 0,494 1,230 1,370 0,550 1,694 1,50 1,596 1,55 1,734 0,598 1,523 1,583 1,506 0,975 1,65 0,9 0,644 0,629 0,173 1,725 0,579 1,935 1,346 0,275 0,580 1,427 0,708 0,146 0,258 1,57 0,501 0,774 1,33 1,747 1,803 1,987 0,52 1,583 0,28 1,997 0,431 1,998 1,536 1,946 0,747 1,587 0,578 0,544 0,63 1,499 1,549 0,79 1,522 1,858 0,44 1,215 0,275 1,170 0,60 1,796 1,951 1,971 0,280 0,508 1,920 0,616 1,73 1,267 0,205 0,886 1,585 0,459 1,358 0,29 0,74 1,739 1,6 1,663 1,977 0,688 1,198 1,881 0,712 0,330 0,743 0,597 1,340 1,69 0,967 1,825 1,136 0,396 1,121 0,311 0,618 0,14 1,936 1,933 1,958 1,592 0,49 1,416 1,358 1,759 1,52 0,376 0,422 1,6 1,214 0,61 0,563 1,808 0,394 1,695 1,618 0,845 0,979 1,776 1,765 0,915 1,736 1,839 1,256 1,418 1,171 1,383 1,352 0,756 0,750 1,948 1,76 1,919 1,250 0,642 1,508 1,936 1,124 1,949 0,367 1,364 1,629 1,511 1,353 0,258 1,458 1,657 1,189 1,539 0,299 0,748 1,268 1,744 1,629 1,324 1,162 0,240 0,897 0,640 0,951 1,482 1,767 1,781 0,273 1,731 1,865 0,724 1,62 1,576 1,840 1,983 0,580 1,268 1,154 0,77 1,505 0,936 0,813 0,187 1,43 1,855 1,49 1,839 0,500 0,483 1,909 0,277 1,562 1,805 1,251 0,597 0,426 1,99 1,808 1,870 1,512 0,724 1,847 0,818 1,632 1,833 0,122 1,642 0,83 1,460 1,963 0,383 1,296 1,50 0,213 1,965 1,479 0,269 1,191 0,293 0,496 1,396 1,492 0,979 1,30 1,209 0,833 1,296 0,242 1,484 0,975 0,650 0,78 0,11 0,562 1,362 1,956 0,76 0,54 0,346 1,897 0,672 0,895 1,228 1,939 0,765 1,581 1,43 0,216 0,840 1,155 1,737 1,451 0,292 0,660 1,0 1,473 0,105 0,805 0,381 1,853 0,339 0,595 0,958 1,376 0,586 1,280 1,240 1,500 1,868 1,512 0,48 1,907 0,727 0,641 1,736 0,342 0,179 1,489 0,792 0,93 1,657 1,703 1,506 1,622 1,367 1,249 0,676 1,976 0,259 0,707 0,840 1,821 1,144 1,326 0,370 1,858 0,851 1,678 1,118 0,312 0,848 1,310 0,198 0,870 1,999 1,499 1,573 0,556 0,820 0,313 0,92 1,513 0,479 1,103 0,345 1,771 0,855 1,730 1,696 1,20 1,827 0,264 1,184 0,537 0,491 1,789 1,254 0,917 0,462 0,208 0,676 1,650 0,645 1,104 0,626 1,608 1,878 1,39 0,97 0,373 1,444 0,380 1,380 0,379 0,71 1,461 0,382 1,617 1,743 0,667 0,474 0,261 1,767 0,885 1,419 0,864 0,117 0,799 1,675 1,79 0,222 1,628 1,132 1,227 0,300 1,429 1,856 1,159 1,293 0,492 1,341 1,251 1,843 1,112 0,480 1,319 0,27 1,145 1,451 0,737 1,123 1,84 1,439 1,314 1,122 1,380 1,743 0,901 0,997 1,518 1,302 1,868 0,91 1,917 0,396 0,683 0,21 0,776 0,750 0,345 1,385 0,761 0,610 1,669 1,462 0,362 0,159 0,533 1,981 0,62 0,754 1,680 1,483 1,849 0,56 0,315 0,740 1,916 0,323 1,548 1,812 1,656 1,530 1,185 0,42 0,689 1,259 0,138 0,305 1,710 1,823 0,548 1,311 1,83 1,995 0,457 1,810 0,248 0,940 1,8 1,53 0,468 0,808 0,176 1,242 1,14 1,449 0,735 0,470 0,824 0,394 1,671 0,340 1,541 1,69 1,110 0,92 0,550 1,782 1,786 0,933 0,351 0,20 0,603 1,568 1,537 1,385 1,118 1,810 1,503 0,320 1,429 1,352 1,265 1,627 0,357 0,970 1,739 1,20 1,797 0,858 0,215 0,382 0,265 0,965 1,765 0,288 0,487 0,409 1,724 0,545 1,50 0,103 1,678 0,381 0,662 1,393 0,607 1,327 1,294 1,127 1,305 1,189 1,395 1,187 0,751 1,962 0,130 0,612 0,86 1,551 1,363 1,332 0,703 0,583 1,740 0,87 1,559 1,461 1,984 0,512 0,865 0,827 1,239 0,720 0,617 1,705 1,218 1,482 0,531 1,296 0,646 0,564 0,853 0,454 1,306 0,210 0,167 1,875 0,636 1,521 1,529 1,113 1,269 1,758 0,588 0,131 0,792 1,118 1,114 0,483 0,248 0,57 0,535 1,30 1,140 0,824 1,417 1,429 1,414 1,890 1,106 1,553 0,361 0,507 0,965 0,994 1,896 0,660 0,631 0,630 0,375 1,492 1,480 0,68 1,562 1,565 1,981 1,196 0,558 1,926 0,561 0,213 0,905 0,349 0,194 1,539 1,187 0,775 0,79 1,989 0,295 1,602 1,411 1,745 0,153 0,531 1,886 1,954 1,461 1,603 1,305 1,782 1,973 1,298 0,402 0,53 0,109 1,813 1,598 1,909 0,147 0,725 0,708 0,421 0,444 0,963 1,486 1,915 0,801 1,929 0,320 1,565 1,498 0,535 1,195 0,333 1,781 1,828 1,270 1,231 0,230 0,973 0,364 1,873 0,966 0,711 0,49 1,583 1,381 1,922 0,397 0,687 0,85 1,302 0,892 1,553 1,985 0,645 0,537 0,644 0,744 1,622 1,606 0,255 0,48 1,656 1,797 0,250 0,983 0,462 0,185 0,378 1,500 1,512 1,320 1,259 0,449 1,632 1,494 0,489 0,56 1,671 0,833 1,471 0,735 1,447 0,892 1,833 0,926 0,787 1,362 1,809 1,798 0,569 0,29 0,713 1,931 0,356 1,67 1,72 0,972 0,387 0,366 0,74 1,807 0,197 0,306 1,145 0,91 0,785 1,376 0,115 0,677 0,194 1,536 1,887 0,363 0,186 1,121 0,6 1,698 1,864 1,105 0,653 0,252 0,939 0,950 1,372 1,994 0,616 0,995 1,105 1,152 1,504 0,887 1,217 1,572 1,960 0,339 0,702 0,44 0,811 0,425 1,52 0,509 0,534 0,660 1,829 1,767 0,270 0,83 0,330 1,332 0,383 0,697 1,625 1,29 1,827 1,251 1,644 0,468 0,89 0,279 1,613 1,888 1,490 0,436 0,347 0,573 0,145 1,39 0,362 1,717 1,13 0,360 0,54 0,160 1,188 0,804 0,263 0,815 0,705 1,823 0,182 0,841 1,369 0,989 1,8 1,990 0,213 0,60 1,132 1,931 1,716 0,684 1,327 1,106 0,40 1,609 1,257 1,895 0,273 1,826 1,908 1,393 1,87 1,301 1,235 1,22 1,267 0,689 0,575 1,555 0,408 1,645 1,784 0,202 1,92 0,718 1,188 0,915 1,150 1,93 1,276 0,564 0,638 1,784 1,842 1,361 1,958 0,956 0,177 0,437 0,389 1,464 0,871 0,582 1,332 0,192 0,109 0,78 0,782 0,726 1,493 0,109 1,379 1,413 1,545 1,696 1,582 0,249 0,511 1,398 1,707 1,268 1,401 0,158 1,124 0,622 0,135 0,674 1,812 0,372 1,594 1,38 0,246 0,920 1,349 1,984 0,824 0,794 0,965 1,818 1,889 0,715 0,487 1,714 0,62 1,615 0,613 0,988 1,421 0,362 1,501 1,268 1,459 0,547 1,541 0,148 0,209 1,191 0,910 0,792 0,473 0,897 1,52 0,901 0,793 1,471 1,602 0,330 0,731 0,744 1,788 0,397 0,94 0,829 0,286 1,50 1,975 1,254 0,522 1,345 1,398 1,870 0,799 0,311 0,953 1,269 0,913 1,145 1,289 0,731 1,913 0,575 0,45 1,578 1,151 1,331 1,508 1,126 0,778 0,734 0,111 1,201 1,909 1,762 0,835 0,289 1,778 0,194 0,332 0,74 0,218 0,870 1,200 0,108 0,330 1,846 1,513 0,220 0,833 1,862 1,822 1,270 0,188 1,529 1,691 0,705 1,78 0,335 1,404 1,282 0,498 1,33 1,993 1,219 0,268 0,802 0,546 0,562 1,291 0,20 1,565 1,649 0,857 1,85 1,754 1,392 1,831 0,91 1,1 1,53 1,797 0,373 1,942 1,224 1,243 1,707 1,223 0,76 0,19 1,276 0,713 0,196 0,809 1,714 1,630 1,960 0,287 0,696 0,842 0,135 0,309 0,735 0,22 1,346 0,317 1,489 0,975 1,921 1,481 0,797 0,539 1,845 0,112 0,583 1,943 0,112 0,944 1,43 0,819 1,743 1,105 0,139 1,230 1,453 1,123 0,931 0,436 1,62 0,347 1,901 1,84 0,857 0,611 1,503 0,96 1,532 1,952 1,684 1,820 1,931 1,373 1,678 0,195 0,313 0,168 1,344 0,450 1,147 1,529 1,373 1,364 0,801 1,289 1,805 1,641 1,727 1,336 1,897 1,159 0,431 1,990 1,843 1,35 0,437 1,892 0,270 1,562 1,455 0,555 1,698 1,868 1,799 0,631 0,758 0,402 0,0 0,70 0,969 0,210 1,86 1,554 1,22 1,186 0,180 0,593 1,251 1,17 1,219 1,803 1,34 1,963 0,307 0,702 0,809 0,774 1,799 1,690 1,1 1,75 0,812 0,659 0,388 1,828 0,530 1,368 0,294 0,186 0,437 1,316 1,207 0,64 1,455 1,890 1,223 1,30 0,261 1,492 0,549 0,142 0,915 0,675 0,946 1,83 0,747 1,650 1,938 0,789 1,136 0,981 1,523 0,76 0,36 0,785 1,674 1,672 0,913 0,305 0,15 1,377 0,263 1,161 0,767 0,744 1,151 1,357 0,586 0,843 1,623 1,504 1,177 1,249 1,722 0,125 0,454 1,521 0,168 1,885 0,401 1,817 0,255 0,291 1,238 1,138 1,560 0,596 1,451 0,725 1,166 0,174 1,436 0,460 1,640 0,944 1,177 0,776 0,326 1,946 1,939 0,343 1,524 0,255 1,553 1,399 1,702 0,257 0,882 0,36 1,892 1,196 0,495 1,598 1,233 0,245 1,251 0,847 1,114 0,126 1,437 1,760 1,311 1,539 0,330 0,147 1,324 0,895 1,11 0,168 1,12 1,519 1,449 1,460 1,710 0,347 0,846 0,552 0,295 0,960 0,612 1,10 1,564 1,45 0,34 1,552 0,414 0,787 1,599 1,17 0,937 1,767 0,257 0,608 0,745 1,90 0,346 1,138 1,482 1,583 1,594 0,896 1,883 0,866 1,258 0,466 0,751 1,703 0,579 1,336 0,6 1,861 0,358 1,309 0,578 0,355 1,477 1,12 1,66 0,389 1,80 0,252 0,668 0,967 1,497 0,442 1,630 1,329 0,859 0,237 1,502 1,69 1,172 0,272 1,373 0,565 1,289 1,937 1,489 1,769 1,691 0,79 0,39 1,414 0,684 1,754 0,561 1,697 0,881 0,285 1,266 0,223 1,892 1,56 0,542 1,824 0,562 0,806 0,285 1,874 1,745 0,936 1,248 0,887 0,131 1,166 1,702 0,528 1,319 1,872 1,111 0,623 0,787 0,122 0,101 0,702 0,292 1,884 1,893 1,569 0,44 0,723 1,415 1,218 0,957 0,766 1,169 1,287 1,803 1,682 0,31 1,153 0,213 0,146 0,31 1,884 1,113 0,130 1,436 0,499 1,113 1,677 0,268 1,957 1,14 0,226 0,933 1,473 1,248 1,837 1,315 1,257 0,296 0,779 1,484 1,157 1,152 0,363 0,407 0,690 1,324 0,880 0,809 1,428 1,486 1,727 1,117 1,430 1,159 1,811 1,639 1,595 0,353 1,12 0,177 1,557 1,33 1,451 1,121 1,157 0,123 1,671 1,445 1,737 1,245 1,117 1,896 1,283 1,992 1,777 0,4 0,474 0,939 0,887 1,45 0,635 1,300 0,94 0,627 0,188 1,94 1,488 0,32 0,944 1,792 1,471 0,124 0,821 1,366 0,944 0,58 0,889 1,786 0,800 0,264 0,704 0,952 0,965 0,456 1,723 0,300 0,937 1,438 1,125 1,400 1,132 1,589 1,477 0,43 1,698 1,655 0,123 1,429 1,316 0,108 0,941 1,30 0,159 0,941 0,925 1,730 0,600 1,571 1,721 0,681 0,26 0,318 1,706 1,58 0,558 1,818 0,103 0,625 1,784 1,932 0,151 1,708 1,394 0,487 1,341 0,756 1,494 0,276 1,613 1,655 1,578 0,993 0,858 1,272 1,850 1,271 0,847 0,879 0,231 0,176 0,222 1,724 1,22 1,530 1,400 1,505 1,72 0,223 0,825 1,290 1,808 0,404 0,740 0,809 1,462 0,403 1,680 1,166 0,240 0,860 1,996 0,571 1,862 0,270 1,584 0,55 0,168 0,285 1,171 1,952 0,152 0,789 0,564 0,418 1,568 1,542 1,46 1,426 1,865 0,485 1,548 1,709 1,22 0,694 1,237 0,393 1,574 0,661 1,131 1,210 1,0 0,935 0,614 0,852 1,947 1,716 0,601 1,854 1,939 0,723 0,757 0,800 0,524 1,171 1,800 0,148 1,212 1,124 0,935 0,34 1,470 0,815 1,9 1,838 1,792 1,381 1,344 1,88 1,633 0,24 1,370 0,79 1,316 0,813 1,112 0,674 0,788 1,658 1,499 0,161 0,764 0,334 1,234 1,162 1,774 0,281 0,337 0,266 0,470 0,630 0,325 1,486 0,391 0,283 1,37 0,829 0,428 1,313 1,872 0,265 1,372 1,960 1,259 1,580 1,736 1,633 1,332 0,571 0,471 1,765 1,726 1,53 0,781 0,474 1,570 1,156 1,691 1,356 1,32 0,963 0,154 1,907 0,273 1,982 0,404 1,125 0,223 0,746 0,85 0,627 1,999 0,991 1,193 0,65 1,977 1,244 0,133 0,352 1,113 0,11 1,559 1,481 0,394 1,742 0,672 0,605 1,291 0,912 0,327 1,175 1,584 0,720 0,596 0,8 0,975 0,94 0,889 1,749 0,925 1,57 0,885 0,318 0,652 1,105 0,6 0,553 1,191 0,956 1,61 1,965 0,189 0,233 0,552 1,942 0,190 1,576 0,520 0,900 0,606 1,537 0,475 1,756 0,518 1,699 0,119 1,312 0,358 1,739 1,452 1,223 1,835 0,903 1,229 0,695 1,342 0,583 0,311 0,695 0,253 1,160 1,425 0,372 1,345 1,777 1,416 1,885 1,553 1,331 1,156 1,40 0,104 1,800 0,347 1,773 0,755 1,955 1,912 1,441 0,104 1,245 0,818 1,970 0,474 0,439 1,23 0,176 1,820 0,802 0,509 0,420 0,700 1,542 1,345 0,26 0,416 0,858 1,728 1,231 0,223 1,853 0,99 0,103 0,932 1,760 0,548 0,955 0,234 0,561 0,320 1,933 0,569 1,878 0,756 0,186 0,969 1,778 1,533 0,485 0,423 1,135 0,125 0,644 0,953 0,943 0,437 0,759 0,897 1,424 0,957 1,985 0,918 0,604 0,386 0,452 1,776 0,782 0,684 1,770 0,646 1,820 0,867 1,818 0,189 0,125 0,896 1,920 0,166 0,854 1,613 0,532 1,858 1,179 1,731 0,903 1,927 0,603 0,414 0,753 1,339 0,556 0,261 1,30 0,76 1,740 1,262 1,260 1,115 1,582 1,7 0,801 0,210 1,176 1,183 0,364 0,165 0,214 1,199 1,164 1,176 0,125 1,129 0,5 1,374 1,189 0,86 1,398 0,397 0,360 0,850 0,815 1,954 0,226 0,302 1,233 0,284 0,68 1,947 1,383 0,408 0,91 0,322 1,738 0,907 0,718 0,551 1,724 1,796 0,247 1,568 0,666 0,585 1,821 0,640 0,138 0,969 1,383 1,884 1,930 1,220 1,765 0,42 1,654 1,571 0,307 0,963 0,79 0,672 1,429 1,180 0,364 1,5 0,466 1,907 1,147 0,822 1,225 0,387 0,320 0,712 1,497 1,607 0,744 0,801 1,770 1,174 0,765 0,571 1,223 1,123 1,590 0,802 0,36 0,137 1,459 0,986 0,358 1,613 1,817 1,913 0,875 1,344 0,843 0,436 0,251 0,500 0,244 1,660 1,686 0,78 1,752 1,240 1,816 1,419 0,111 1,760 0,316 0,977 1,754 0,98 1,408 1,410 1,893 0,343 0,417 0,22 0,378 1,538 0,901 1,963 0,122 1,218 1,950 0,918 1,921 1,259 1,842 0,353 0,859 1,847 0,360 0,819 1,791 0,193 1,236 1,924 0,930 1,612 0,642 0,932 0,880 0,49 1,136 0,645 1,168 0,491 1,832 0,772 1,926 0,471 0,97 1,833 1,881 0,817 1,350 1,12 0,624 0,507 0,476 1,247 1,812 0,393 1,214 0,872 0,424 0,496 1,263 1,115 0,599 1,4 0,338 0,93 1,70 1,946 0,41 0,504 1,912 1,166 0,192 0,655 0,142 1,820 1,868 1,636 0,461 0,278 0,136 0,746 1,885 0,784 1,692 1,864 1,649 1,915 1,104 0,926 1,550 1,313 1,61 0,21 0,413 0,317 1,512 1,844 0,552 0,620 1,521 1,735 1,204 1,310 0,355 1,421 1,529 1,850 0,733 1,813 0,615 1,358 0,973 1,195 1,319 1,206 0,692 1,682 1,286 1,857 0,763 0,415 0,121 0,809 1,97 0,978 0,306 0,508 0,369 1,867 0,777 1,870 0,755 1,18 0,44 1,267 1,156 1,743 1,643 1,740 0,522 0,969 0,255 1,367 1,254 1,457 1,409 0,161 1,478 1,911 1,759 1,735 1,150 1,796 0,80 0,515 1,586 0,762 0,199 0,314 0,53 0,644 0,822 1,548 1,885 1,540 1,663 0,699 0,893 1,149 0,711 1,954 1,624 1,567 1,114 1,957 1,172 1,942 1,344 1,188 0,498 1,146 1,511 1,30 0,240 0,14 0,239 0,683 1,239 1,166 1,585 0,962 1,6 1,948 1,969 1,923 0,688 0,303 0,239 0,638 0,5 0,970 1,700 1,861 1,86 1,820 1,18 1,815 0,152 1,49 0,139 1,864 0,954 1,429 0,600 1,372 0,300 0,470 0,183 0,469 1,955 0,472 0,607 1,648 0,720 0,678 1,867 1,448 0,839 1,663 0,161 0,816 1,837 0,825 0,436 1,85 1,613 1,261 0,460 0,109 1,354 1,671 0,165 1,479 1,937 0,735 1,865 0,392 1,246 0,911 0,777 1,486 0,413 0,191 0,464 1,112 0,144 0,651 0,741 1,468 1,787 0,85 1,686 0,475 0,36 0,402 1,750 0,644 0,902 1,275 0,533 0,92 1,351 0,348 0,784 1,759 0,334 1,554 0,825 0,451 0,722 1,818 0,22 1,712 1,905 1,141 0,641 0,210 1,884 0,842 0,663 1,685 0,184 0,70 0,935 1,277 1,496 0,118 0,409 1,619 1,933 0,723 1,193 1,629 0,386 0,554 0,843 1,767 1,344 0,638 0,869 1,818 0,706 1,670 1,463 0,329 0,586 1,741 1,216 0,187 0,165 0,510 0,871 1,351 0,166 0,767 0,989 1,209 0,712 0,796 1,859 1,18 0,924 1,758 0,816 1,114 1,381 1,14 0,817 0,17 1,55 1,320 0,770 0,690 1,367 1,810 0,345 1,689 0,90 0,494 1,62 1,667 1,236 1,859 1,734 0,253 0,948 1,201 0,579 1,526 0,818 1,317 0,234 0,952 1,751 0,0 1,41 1,508 1,978 0,219 0,900 1,388 1,293 1,118 0,291 0,75 1,776 0,652 0,113 0,560 1,421 0,430 0,861 1,665 0,822 1,458 1,551 0,398 1,837 1,667 1,95 0,848 1,845 0,201 0,489 1,262 0,861 0,149 0,177 0,282 0,468 1,537 1,316 0,355 0,917 1,448 0,262 1,306 1,72 0,946 0,82 0,383 0,709 1,739 0,330 1,4 0,167 0,57 0,900 1,536 1,739 1,125 0,302 1,480 0,535 0,217 0,604 1,812 1,451 0,523 0,179 0,373 0,138 0,666 0,433 0,768 1,434 1,31 0,348 0,97 1,709 0,133 0,932 0,138 1,232 0,335 0,27 1,931 0,677 0,407 1,476 1,908 0,174 1,421 1,697 0,595 1,72 1,189 0,107 1,886 0,326 0,635 1,156 0,589 0,421 0,451 0,11 0,842 1,434 0,661 1,810 0,988 1,846 1,40 1,920 0,277 1,382 1,0 0,676 1,208 1,93 0,935 1,465 1,966 1,495 0,525 0,825 1,894 1,191 0,80 1,764 0,754 0,387 1,184 1,173 1,839 1,692 0,78 0,345 0,498 1,334 1,223 0,425 0,698 0,29 0,92 0,836 1,896 0,73 1,139 1,967 0,919 0,327 1,23 0,36 1,996 0,654 1,961 0,760 0,134 0,219 1,907 0,989 0,278 0,877 1,80 1,478 1,716 0,622 1,83 0,865 1,163 1,265 1,920 0,523 0,95 0,756 1,802 1,361 0,941 0,960 1,744 0,623 1,678 1,795 0,684 1,729 1,220 1,814 1,243 1,359 0,659 1,145 0,472 1,381 1,448 0,414 1,349 0,91 1,490 0,270 0,784 0,443 0,452 1,140 0,639 1,857 0,883 0,755 1,903 1,90 0,704 0,442 0,418 0,670 1,850 0,820 1,695 0,765 1,252 0,935 0,227 0,8 1,993 1,816 0,136 0,827 0,848 1,900 1,282 0,173 1,915 0,184 1,953 1,108 0,857 0,376 0,287 1,826 1,657 1,747 0,105 1,738 0,125 0,560 1,820 0,549 0,236 1,229 1,139 0,886 1,840 1,676 1,787 0,596 0,454 0,762 1,631 0,389 0,679 0,352 1,209 1,440 0,425 1,417 0,186 0,302 1,944 0,789 1,493 0,276 1,216 1,64 0,210 0,154 0,670 0,959 0,641 1,796 1,17 0,804 0,151 1,22 1,463 1,262 1,454 1,648 1,754 0,857 0,801 0,755 1,291 0,654 1,514 0,152 1,661 0,632 0,92 0,332 1,670 1,261 1,594 1,628 1,310 1,514 1,330 0,488 0,353 1,408 0,492 0,180 0,501 0,402 0,893 0,60 1,530 1,438 1,57 1,887 1,25 0,24 1,559 0,450 1,261 1,248 1,345 0,453 0,602 1,949 1,580 0,101 1,270 1,932 0,476 1,674 1,985 1,398 1,741 1,718 1,602 0,760 1,387 1,917 1,995 1,995 0,200 0,492 0,245 0,12 1,542 0,111 1,241 1,573 0,287 0,904 1,888 1,972 1,309 1,696 1,255 1,276 1,987 1,363 0,946 0,752 1,447 0,265 0,233 1,484 1,231 0,30 0,927 1,232 0,477 0,720 1,431 0,802 0,140 1,197 0,358 0,431 0,629 1,667 0,930 1,210 1,951 1,820 1,329 0,288 1,972 1,460 1,919 0,783 0,546 1,727 1,955 0,754 1,90 0,398 0,350 0,372 1,645 1,43 0,406 0,201 1,122 1,830 0,350 0,259 1,163 1,699 1,971 0,943 0,218 0,907 1,899 1,315 0,121 1,34 0,495 1,278 0,532 1,989 0,873 0,948 0,645 1,958 1,617 0,378 1,508 1,117 0,686 1,972 0,881 0,413 1,88 0,694 0,571 0,553 1,594 0,653 1,578 0,180 1,163 1,27 1,210 0,698 1,250 1,375 1,249 0,167 0,928 1,349 0,569 0,494 0,631 0,280 1,591 1,128 0,507 0,197 1,979 0,43 1,695 1,478 0,245 1,397 0,485 1,595 0,61 1,245 0,860 1,563 0,244 0,915 1,807 0,544 0,304 1,516 1,963 1,997 1,969 0,974 0,767 0,505 0,504 1,403 0,990 0,19 0,582 1,692 0,470 1,110 1,336 0,632 1,617 1,944 1,60 0,128 1,370 0,871 0,320 1,352 1,334 0,989 0,64 0,631 1,224 0,846 0,813 0,473 1,666 1,41 1,535 1,617 0,232 1,622 1,509 0,962 1,799 0,3 0,633 0,467 0,201 1,562 0,876 0,999 0,885 1,981 0,952 1,648 0,815 1,873 0,540 1,165 1,868 0,766 0,164 1,438 1,259 0,371 0,701 1,787 0,304 1,882 1,432 1,204 1,125 0,614 1,475 0,596 0,512 1,616 0,729 0,463 0,273 1,571 0,133 1,978 0,123 0,315 1,543 1,414 0,626 0,376 0,625 1,352 1,458 0,907 1,368 0,324 1,360 1,566 0,972 0,504 0,371 0,194 0,235 0,744 0,114 1,132 0,23 0,809 1,15 1,279 0,757 0,583 0,612 0,348 1,839 1,34 1,796 1,843 0,119 1,673 1,589 1,418 1,586 0,885 0,309 1,604 1,851 0,164 0,959 1,579 0,376 0,664 1,629 1,898 1,504 1,128 1,852 1,839 1,698 0,999 1,439 0,80 0,203 0,663 0,21 0,990 1,143 1,216 0,597 0,187 1,408 0,658 1,822 1,652 0,671 1,749 0,541 0,356 0,125 0,499 0,97 0,499 1,603 0,830 1,795 1,327 1,894 0,380 0,502 0,61 1,746 1,825 0,658 1,536 0,278 1,30 1,876 1,44 0,234 1,629 0,145 0,221 1,36 0,217 1,385 1,996 1,129 1,346 1,319 1,312 0,309 1,793 1,164 0,585 1,973 0,621 1,116 0,639 0,237 1,378 0,562 1,387 0,658 1,853 1,636 0,215 1,256 1,111 0,89 0,125 0,637 1,118 0,442 1,844 0,230 1,386 1,298 0,887 1,586 1,8 1,928 0,158 0,391 1,685 1,844 0,448 0,846 1,337 0,394 1,767 0,156 1,725 0,980 1,655 1,542 1,66 0,761 1,878 0,789 1,81 0,659 1,96 1,473 0,496 1,579 1,252 1,302 1,924 0,180 1,805 1,638 1,425 0,153 1,986 1,54 1,420 0,558 1,693 0,403 1,531 1,910 0,744 0,132 0,370 0,478 0,897 0,662 1,510 1,910 1,672 0,273 0,72 0,309 1,198 1,307 1,420 0,253 0,565 1,973 1,690 0,812 1,136 1,939 0,902 0,837 0,766 1,527 0,183 0,222 1,281 0,267 1,729 0,760 0,127 0,446 0,894 1,546 0,370 1,654 1,89 1,296 0,919 0,41 0,15 1,158 0,369 1,848 0,449 1,220 1,810 0,804 1,166 1,125 1,353 1,682 0,115 0,944 1,983 1,660 0,366 0,762 0,740 1,244 1,280 1,8 0,614 1,336 0,127 0,12 1,374 1,564 0,266 0,255 0,621 0,673 0,382 0,786 0,420 0,535 1,729 1,110 1,163 1,606 0,883 0,716 1,110 0,544 0,716 0,981 1,333 0,989 0,973 1,762 1,134 0,524 1,751 1,336 0,530 1,940 0,847 0,928 1,769 0,434 0,689 0,293 0,509 0,10 0,665 0,217 1,753 0,342 1,292 0,656 1,348 0,611 1,464 0,600 0,173 1,823 0,778 0,542 0,489 1,762 0,18 0,953 0,662 0,362 0,626 1,896 0,29 1,877 0,67 1,612 0,556 1,580 1,599 1,731 1,145 0,22 1,974 0,482 1,547 0,351 1,52 1,270 1,852 0,560 0,239 0,119 1,548 1,760 1,112 1,9 1,45 1,305 0,706 1,134 1,878 0,777 0,591 0,393 1,312 1,830 0,308 0,679 0,83 1,468 0,771 0,476 0,295 1,533 1,943 0,437 0,938 0,603 0,757 1,770 0,956 0,657 0,431 0,106 0,788 1,554 0,632 1,507 0,250 0,240 1,462 1,3 1,945 1,292 1,122 1,987 0,96 1,188 1,34 1,111 0,9 0,123 1,700 0,233 1,492 0,240 0,189 0,818 1,383 0,955 1,15 1,41 1,541 0,838 1,158 0,291 1,346 0,636 0,600 0,532 0,920 0,483 1,793 0,589 0,579 0,311 0,156 1,995 0,441 1,721 0,757 1,113 0,85 0,689 1,192 1,111 1,878 0,779 0,692 0,420 1,743 1,498 0,763 0,911 0,158 0,831 0,312 1,180 1,552 0,482 1,900 0,180 1,19 1,292 0,908 1,467 0,870 1,418 0,652 1,435 1,469 0,234 0,911 1,464 1,295 0,235 1,866 1,511 1,682 0,505 0,189 0,132 0,105 0,694 1,514 1,172 1,426 1,876 1,540 1,996 0,790 1,26 1,725 1,865 0,167 1,195 1,86 1,244 1,144 0,208 0,882 0,118 1,638 0,467 1,691 1,546 1,746 1,510 0,234 0,147 0,874 0,667 1,195 1,740 1,918 1,275 1,428 1,24 0,554 0,948 1,216 1,213 1,628 1,380 0,930 1,707 1,23 0,813 1,518 0,100 1,751 1,816 0,802 1,760 1,685 1,738 1,412 0,911 1,675 0,839 0,804 0,943 1,264 1,632 1,189 0,561 1,398 0,479 0,790 1,546 1,320 0,263 1,174 1,973 0,424 1,179 1,791 0,124 1,507 1,678 1,944 1,987 0,565 1,428 0,853 0,396 1,857 0,751 1,434 1,142 0,661 0,334 1,147 1,378 0,358 0,450 1,599 0,560 0,92 0,378 0,186 1,39 1,727 0,982 0,898 0,400 1,474 0,773 0,364 1,264 0,340 0,319 0,46 1,340 0,158 1,896 0,418 1,958 0,327 0,183 0,67 0,968 0,416 1,411 1,564 0,370 1,218 0,122 0,440 1,531 1,309 0,186 1,320 0,817 1,166 1,165 0,415 0,334 1,401 1,119 0,476 1,506 0,784 0,600 1,84 0,10 0,847 0,123 0,491 1,160 1,810 1,986 1,200 1,525 0,139 0,796 1,617 1,241 0,946 1,818 1,131 0,639 0,230 1,134 0,808 1,565 0,614 1,187 1,2 1,857 0,396 1,298 1,693 0,555 1,333 1,590 0,986 0,171 0,557 1,120 0,700 0,125 1,703 1,952 0,268 1,534 1,231 0,269 1,762 0,474 1,748 1,484 0,586 0,787 1,820 0,211 0,597 0,993 0,343 0,880 0,496 1,248 0,316 1,781 1,595 0,529 0,692 1,253 0,694 1,565 0,287 0,708 0,468 0,211 0,89 0,457 0,426 1,667 1,431 0,99 1,31 1,176 0,710 1,896 1,189 0,35 1,495 0,218 1,141 0,585 1,74 1,774 0,981 1,286 1,619 1,27 1,144 0,918 1,466 1,403 1,835 0,568 1,478 0,408 1,530 0,954 0,400 1,589 1,803 1,298 0,119 0,800 0,952 1,714 0,490 0,615 0,936 0,737 0,627 0,942 1,701 0,540 0,490 1,17 0,919 1,784 1,216 1,395 1,762 1,556 1,585 1,357 0,69 0,687 1,349 0,947 0,542 1,444 1,133 0,881 1,121 0,715 1,515 0,32 0,951 0,605 0,522 1,205 1,917 0,117 0,230 0,262 1,912 1,164 0,681 1,605 1,506 0,190 0,161 1,185 0,43 1,186 1,949 1,18 0,351 1,572 0,403 1,769 1,572 1,11 0,175 0,29 1,933 1,189 1,554 0,101 0,597 1,700 0,904 0,577 1,656 1,204 1,375 1,473 0,622 1,777 1,857 1,924 1,683 1,838 0,307 0,359 0,878 1,282 0,975 1,49 1,863 0,947 1,835 0,570 1,989 1,838 0,974 0,109 1,980 1,298 0,514 1,846 1,956 1,709 0,15 0,597 1,555 1,708 1,575 0,447 0,2 1,138 0,574 0,732 0,839 0,634 0,676 1,501 0,770 1,592 1,155 0,622 1,445 0,160 1,550 0,772 1,244 0,133 0,174 1,807 1,611 0,608 1,525 0,827 1,157 0,719 1,741 1,737 0,147 1,702 0,585 0,585 0,21 0,646 1,306 1,811 0,248 0,655 1,867 0,155 1,372 0,890 1,915 1,379 0,752 0,760 1,651 0,321 0,932 0,157 1,262 0,203 1,563 0,335 0,90 0,17 1,606 1,271 1,201 0,831 1,364 1,569 1,354 1,894 0,739 1,368 0,510 0,631 1,223 1,302 0,163 1,397 1,435 1,526 0,520 0,389 1,107 0,547 0,811 1,805 0,259 0,713 0,575 1,176 1,551 0,389 0,206 0,179 1,972 1,252 0,770 1,104 1,241 0,86 0,446 1,255 0,580 0,131 0,775 1,262 0,431 1,284 0,0 1,131 0,929 1,194 0,218 0,756 0,172 0,397 0,996 1,818 1,577 1,626 0,971 1,99 0,234 1,237 1,18 0,819 1,73 1,493 0,365 1,266 1,872 1,19 0,878 0,451 1,839 1,86 1,521 0,659 0,317 0,880 0,987 1,178 0,66 1,949 0,205 1,949 1,558 0,889 0,515 1,469 1,572 0,694 1,648 1,351 1,537 0,538 1,904 1,406 1,436 0,588 0,796 0,640 1,893 0,73 1,374 0,946 0,466 0,439 1,681 1,894 1,289 1,567 1,337 1,665 0,367 0,699 1,21 0,806 0,544 1,869 1,705 1,123 0,927 0,260 1,150 1,481 0,384 1,754 1,444 0,378 0,354 1,685 0,603 1,443 1,675 1,309 0,593 0,615 0,422 0,965 0,316 1,282 0,617 1,499 0,372 1,260 0,753 0,140 0,95 0,386 0,633 0,138 1,614 1,265 1,740 0,96 1,25 1,36 1,303 0,711 0,499 0,78 0,938 1,907 1,1 0,325 1,973 0,467 0,227 1,757 0,984 1,448 1,764 0,41 1,81 1,211 1,70 0,406 0,616 1,290 0,966 1,490 1,502 0,281 0,958 1,972 0,199 1,278 1,257 0,109 1,652 0,313 0,238 1,146 1,839 1,747 0,720 1,243 0,539 1,388 0,787 1,947 0,481 1,879 0,86 0,48 0,782 0,817 1,441 1,833 0,465 1,78 0,15 1,359 1,84 0,474 1,436 1,349 0,147 1,943 1,805 0,938 0,156 0,512 1,488 0,686 0,549 1,632 1,298 0,110 1,59 0,213 0,422 1,244 0,690 1,562 1,818 1,602 0,760 0,75 1,914 1,226 1,471 1,954 0,416 1,878 0,42 0,290 0,734 0,939 0,760 1,412 0,861 1,215 1,100 1,993 0,838 1,460 0,85 0,512 1,375 1,709 1,254 1,77 0,830 0,848 0,860 0,145 0,57 1,946 0,552 0,477 1,824 1,49 0,906 1,726 1,456 1,381 1,777 0,232 1,941 1,161 1,898 0,378 1,804 0,880 0,350 1,63 1,595 1,898 0,531 1,227 1,14 0,858 1,845 1,769 0,711 0,492 1,722 1,99 1,571 1,243 0,771 1,401 1,565 0,169 1,233 0,554 0,190 1,932 1,39 0,534 0,716 1,281 1,116 1,118 0,64 1,607 0,417 0,434 1,860 1,795 0,680 0,532 1,680 1,574 1,5 0,214 0,258 1,39 1,426 0,172 0,396 0,775 1,840 1,134 0,162 1,385 0,176 0,585 0,148 0,860 1,497 0,803 0,955 0,276 1,510 0,754 0,586 0,933 0,201 0,570 0,779 1,607 0,720 1,920 1,539 0,538 0,659 1,169 0,990 0,394 1,495 1,455 0,536 0,102 1,288 0,828 0,861 0,91 0,293 0,151 0,480 1,56 1,828 0,49 0,573 0,472 1,336 1,588 0,775 0,498 1,596 1,995 0,339 0,50 0,850 0,743 0,389 0,777 1,624 0,985 1,137 1,10 0,658 1,807 0,802 1,244 0,621 1,374 0,988 0,639 0,100 1,666 1,992 0,264 0,935 1,532 1,422 1,69 0,493 1,655 0,610 1,569 1,597 0,158 1,474 0,278 0,831 0,461 1,354 0,258 0,270 0,792 1,91 1,770 0,300 1,300 1,757 0,468 1,181 1,794 0,392 1,990 1,918 1,423 1,539 1,300 1,372 1,828 1,408 1,496 1,276 1,929 1,497 0,934 0,125 0,43 1,74 0,163 1,304 0,548 1,630 1,869 1,378 0,437 1,254 1,159 1,88 0,447 1,689 0,660 1,461 0,317 1,683 1,963 1,573 0,204 1,959 0,846 1,385 0,19 1,99 0,393 1,41 1,471 0,654 0,119 1,599 1,174 0,446 1,254 1,598 0,306 0,188 1,810 0,43 0,587 0,793 0,964 1,428 1,216 1,689 1,177 1,177 1,464 1,393 1,872 1,83 1,456 0,297 0,553 0,857 1,289 1,372 1,339 1,459 1,908 1,674 0,285 1,241 0,57 0,637 0,118 1,309 1,252 0,279 1,743 0,69 0,531 1,443 1,769 0,553 1,573 1,452 1,795 1,522 0,539 0,735 0,359 1,591 0,505 0,562 0,646 1,730 0,520 1,161 1,985 0,615 1,364 0,903 1,23 1,384 1,564 1,776 0,26 0,43 1,800 0,40 1,505 0,374 1,191 0,58 0,979 0,737 0,928 1,856 1,960 1,955 1,470 1,329 1,613 0,699 1,911 0,503 0,423 1,492 0,256 1,487 1,63 1,40 1,972 1,466 1,422 0,556 0,917 0,120 0,305 1,437 1,611 0,276 0,254 0,574 1,328 0,216 1,610 0,788 0,760 0,120 1,790 0,234 0,914 0,522 1,468 1,518 1,844 1,666 0,858 0,293 0,430 0,238 1,52 1,777 1,855 0,83 1,610 0,114 1,570 1,662 1,635 1,978 0,331 1,650 0,652 0,296 1,627 0,191 0,859 1,582 1,305 1,22 1,495 0,944 0,632 1,588 0,864 0,885 0,150 0,529 0,570 1,744 0,802 1,802 1,895 0,138 1,918 0,424 1,279 0,276 1,271 1,638 1,434 1,11 0,175 1,132 0,170 1,412 0,614 1,890 0,172 1,589 0,622 1,235 1,630 1,302 0,249 0,350 1,672 0,539 0,658 1,463 0,790 1,839 1,701 0,132 0,791 1,439 1,598 0,208 1,314 1,304 0,419 1,391 1,255 0,219 0,295 1,922 1,774 1,157 1,317 0,517 0,418 1,802 0,628 0,500 0,389 0,887 1,517 1,509 1,124 1,101 1,566 1,486 0,695 1,53 1,124 0,642 0,288 1,340 0,677 1,107 1,712 0,868 0,296 1,565 0,229 0,652 0,709 1,236 1,30 1,478 0,53 0,775 0,326 1,101 0,764 0,89 0,902 1,761 0,631 1,295 1,467 1,364 1,785 0,667 0,640 0,144 1,564 1,752 1,332 1,187 1,397 1,309 1,847 1,458 0,501 1,469 1,375 0,797 0,80 0,143 1,474 1,52 0,838 0,470 1,564 0,530 1,819 1,486 0,488 1,49 0,393 1,219 0,467 1,683 0,931 0,526 1,740 0,488 0,231 0,277 1,123 1,124 1,870 1,317 0,493 1,251 1,810 1,412 1,114 0,984 1,781 1,574 0,878 1,758 1,57 1,724 0,831 1,196 1,746 0,951 0,828 1,492 0,622 0,374 1,511 0,133 0,136 1,620 1,353 1,484 1,872 0,415 0,858 0,841 0,4 0,114 0,182 0,132 0,450 1,371 1,716 1,803 0,575 0,527 1,12 1,612 1,877 0,542 0,530 0,804 1,356 0,726 1,151 1,850 1,628 0,897 0,890 1,130 1,941 1,63 0,825 1,525 0,800 1,924 1,307 0,974 1,86 0,997 0,919 1,778 1,767 0,136 0,604 0,266 0,317 0,682 1,755 1,583 0,16 0,534 1,809 0,531 1,543 0,912 1,756 0,720 0,240 0,693 1,82 0,950 0,517 0,416 1,810 0,443 0,976 0,570 1,886 1,741 1,277 0,35 1,442 1,676 1,362 1,261 1,853 1,934 0,199 0,419 0,210 1,1 1,417 0,416 0,784 1,386 1,738 1,232 1,588 0,186 0,947 1,788 0,914 1,267 0,645 0,411 1,648 0,839 0,29 0,289 1,121 1,198 0,835 1,151 0,815 1,20 0,767 0,744 1,790 0,973 0,499 0,444 0,48 1,966 0,89 0,12 0,803 1,494 0,471 0,432 1,593 0,157 1,584 1,696 1,363 1,424 0,849 0,768 1,410 1,607 0,181 0,493 1,572 0,840 1,971 1,310 1,625 0,856 0,689 1,630 0,662 0,412 1,357 1,964 0,527 0,712 0,379 1,880 0,656 1,482 1,100 0,673 1,859 0,866 1,397 0,254 1,302 0,111 1,537 0,208 0,991 0,412 0,228 0,573 1,617 0,144 1,598 1,473 1,245 0,803 0,728 1,364 1,473 1,504 1,140 1,361 0,793 0,261 1,828 0,695 0,562 1,631 1,592 0,258 0,40 1,706 0,122 1,63 1,181 0,963 0,215 1,572 0,463 0,200 1,533 1,431 0,516 1,355 0,182 1,47 1,401 1,998 1,836 0,152 0,456 1,743 0,963 0,980 0,565 0,889 1,928 1,937 1,989 0,242 1,868 0,0 0,480 1,206 0,16 1,819 1,49 1,473 0,87 0,630 1,627 0,471 1,14 0,501 1,279 0,798 1,68 0,942 0,297 1,146 1,885 0,809 1,327 0,209 1,262 0,486 1,377 0,58 1,760 1,399 1,514 1,165 1,273 0,23 1,920 1,579 1,857 0,630 0,787 1,796 0,587 1,951 1,225 1,935 0,660 0,231 1,624 0,68 1,61 1,333 1,85 0,817 0,174 1,872 0,749 1,577 1,728 0,402 1,693 0,33 1,707 0,208 0,937 1,782 1,183 1,75 0,420 1,339 0,772 1,371 1,87 1,550 1,302 1,975 1,528 0,288 1,311 1,900 1,805 1,377 0,161 1,740 1,370 1,503 1,877 1,367 1,192 1,728 0,916 0,574 1,719 1,722 0,816 0,818 1,401 0,307 0,334 1,894 1,108 0,767 1,575 0,609 1,508 1,291 1,466 0,640 0,520 0,5 0,147 1,842 0,856 0,508 1,148 1,673 0,53 1,482 0,696 0,746 1,544 1,644 0,838 1,965 0,616 1,467 0,274 1,821 1,373 0,366 1,30 1,102 0,300 1,794 0,366 0,712 1,134 0,744 0,852 0,764 1,280 0,820 0,108 0,238 0,194 0,159 1,61 1,233 0,30 0,461 0,774 0,876 0,189 0,622 1,540 0,385 0,294 0,714 1,514 1,303 1,667 1,483 0,978 0,482 0,387 1,928 0,182 0,237 1,813 0,780 1,623 1,245 0,630 0,278 1,159 0,443 1,507 1,722 1,473 0,533 0,290 1,968 0,308 1,182 0,967 0,740 0,340 1,781 0,24 0,895 0,314 1,935 0,919 1,52 0,879 0,167 0,730 0,868 0,194 1,64 1,101 0,459 1,523 0,555 0,598 1,165 0,923 1,256 0,238 1,834 1,184 1,564 0,544 0,300 1,995 0,185 1,599 1,379 0,959 0,797 1,514 1,307 0,201 0,189 1,884 1,543 0,144 0,38 0,478 1,198 1,137 1,128 0,672 0,658 1,746 0,643 0,174 0,565 0,316 0,819 0,970 0,790 0,559 1,983 0,263 0,240 1,452 1,994 1,488 1,317 0,159 1,190 0,992 0,129 0,51 0,852 1,505 0,752 0,826 0,291 1,56 0,53 0,653 1,62 0,363 0,416 0,731 0,101 0,362 1,174 0,261 1,345 0,289 1,474 1,87 0,899 0,166 0,418 1,982 1,692 0,9 1,759 0,835 1,389 1,5 0,738 0,821 1,524 0,174 0,161 1,684 1,641 1,802 0,53 1,665 0,502 0,386 0,81 1,954 0,732 1,128 1,558 0,778 0,276 0,151 0,412 0,23 0,882 0,666 0,924 0,684 0,976 0,204 1,974 1,510 1,787 0,959 0,355 1,957 1,268 0,866 1,276 0,504 1,139 0,744 0,748 1,523 0,924 0,184 1,283 0,619 1,672 0,531 1,4 1,672 1,233 0,329 0,633 1,794 1,271 1,687 0,558 1,691 1,877 1,831 1,860 0,636 1,994 0,918 0,34 0,928 0,974 0,861 0,825 0,914 0,892 0,668 0,330 1,438 1,35 0,547 1,508 1,551 0,982 0,18 1,15 1,81 1,906 1,620 0,449 0,692 1,109 0,905 1,47 0,938 0,155 0,611 0,958 1,419 0,43 1,471 1,14 0,229 1,191 1,996 1,882 1,887 1,747 0,204 1,853 1,18 0,707 0,819 0,672 1,165 0,491 1,941 0,96 0,44 1,214 1,609 0,91 0,465 0,640 0,175 0,625 0,822 0,177 1,343 1,972 0,809 0,84 1,161 0,922 0,960 0,469 1,110 0,181 1,668 1,61 1,714 1,763 1,777 0,230 0,40 0,193 1,403 0,164 0,66 1,795 0,664 0,182 0,543 0,592 0,760 1,562 1,94 0,637 0,921 0,366 0,835 1,968 0,551 1,83 0,494 1,410 0,845 1,607 1,777 0,350 1,865 0,842 0,861 0,178 1,395 1,98 0,44 1,433 0,645 0,387 0,171 0,998 0,589 1,819 1,683 0,968 1,964 1,645 0,301 1,562 1,3 1,198 0,731 1,983 1,779 0,134 1,102 1,879 0,354 0,672 0,569 0,570 0,947 1,676 1,905 0,715 0,409 1,428 1,54 0,86 1,266 1,978 0,159 1,883 1,297 1,420 0,238 1,226 1,856 0,469 1,151 1,664 1,709 1,345 1,975 0,439 1,822 0,650 1,155 0,998 0,827 0,918 0,192 0,894 1,532 0,806 0,198 1,777 1,265 0,320 0,732 0,795 1,114 0,106 0,241 1,884 0,81 1,414 1,781 0,287 1,600 1,953 0,938 1,542 1,21 0,304 0,278 1,688 1,49 1,400 0,735 1,628 0,910 1,489 0,196 0,326 1,784 0,932 0,510 0,279 0,319 1,674 0,572 1,916 0,969 0,877 0,261 0,549 1,57 1,988 0,108 1,413 1,664 0,214 1,31 1,818 1,319 0,781 1,458 1,611 1,856 0,306 1,946 1,897 0,690 0,468 1,527 1,584 0,727 0,944 0,501 1,430 0,908 0,47 1,33 0,443 0,20 0,145 1,753 1,119 0,686 0,226 0,354 1,646 0,756 1,382 0,79 0,361 0,280 1,939 1,706 0,819 1,924 1,852 0,393 0,19 0,659 1,136 1,806 1,461 1,233 0,470 1,90 0,260 0,697 0,331 1,717 1,68 1,142 0,34 1,751 0,573 1,504 1,277 0,58 0,762 1,800 1,126 0,791 1,696 0,121 0,339 0,436 0,618 1,105 0,967 0,854 1,516 0,155 0,428 0,626 1,165 0,872 1,428 1,589 0,446 1,876 0,942 0,429 1,572 0,630 1,478 1,772 0,731 1,292 1,928 0,810 0,520 0,837 0,919 0,298 0,345 0,832 0,850 0,405 0,690 0,715 0,288 0,833 0,397 0,579 1,238 1,45 1,462 0,363 0,878 1,384 0,431 0,686 1,900 1,21 1,931 0,770 1,656 1,900 1,659 0,431 1,458 0,527 0,963 0,944 0,474 0,253 0,555 0,290 0,378 1,971 1,814 0,772 0,410 1,352 1,342 0,273 1,542 0,216 1,710 1,301 1,776 1,759 1,371 1,473 1,472 0,589 0,953 1,488 1,574 0,126 1,831 1,273 0,791 1,840 0,148 1,47 0,50 1,686 0,387 1,624 1,341 1,91 0,909 1,472 0,897 0,812 0,552 0,502 0,320 1,310 0,228 0,332 1,82 0,49 1,136 0,534 1,41 1,597 1,408 1,499 0,177 1,288 0,411 0,191 1,853 0,945 0,931 1,529 0,905 1,408 1,741 1,506 0,135 1,488 1,146 1,185 0,192 1,885 1,522 1,586 0,517 0,976 0,136 0,650 0,292 1,164 0,694 0,597 0,45 1,488 0,219 1,978 1,347 0,655 1,785 0,528 0,440 1,656 0,91 1,404 1,164 0,963 0,278 0,521 0,532 1,488 0,723 1,109 0,978 0,690 1,921 1,399 0,71 0,265 0,30 0,261 1,648 1,212 1,516 0,474 0,287 0,357 0,52 0,332 1,570 0,728 1,105 0,464 0,460 1,251 1,2 0,786 1,768 0,527 1,591 0,979 0,653 1,750 1,647 1,193 0,682 0,945 1,225 1,127 0,60 0,873 0,950 0,486 0,126 0,647 0,713 1,387 0,124 1,362 0,91 1,242 1,16 1,926 0,569 0,712 1,719 0,651 1,736 1,890 1,160 1,861 0,502 1,699 1,40 0,641 0,906 1,25 1,840 0,407 0,388 0,231 0,653 1,764 0,271 1,448 1,846 1,920 0,676 0,457 0,8 1,794 0,974 1,794 0,765 1,568 0,633 0,911 1,33 1,984 0,184 1,519 0,918 0,231 1,949 1,348 0,978 0,348 0,254 0,540 0,184 0,847 0,633 0,241 1,227 0,583 0,314 1,695 1,730 1,873 1,157 0,377 0,850 0,139 0,679 1,632 1,525 1,833 1,813 0,503 1,408 0,655 1,969 0,311 1,160 1,908 0,737 0,483 1,130 0,407 1,785 1,606 0,295 1,997 0,513 1,522 0,367 1,665 1,191 0,192 1,242 1,42 0,292 0,569 0,560 1,963 1,181 1,426 1,538 0,946 1,185 0,105 0,302 1,527 1,775 1,82 0,55 0,100 0,424 1,315 0,59 0,202 1,798 1,343 1,654 1,790 0,735 1,516 0,639 0,750 1,385 1,788 0,177 1,993 1,812 1,106 0,18 1,803 0,801 0,349 0,754 1,7 0,222 0,995 0,855 1,253 0,636 0,622 1,359 1,998 0,658 1,699 1,768 0,528 1,365 0,917 1,177 0,666 0,267 1,366 1,322 1,171 1,265 0,538 0,711 0,482 1,156 0,913 1,419 0,935 1,139 0,81 0,843 0,408 0,197 1,109 0,730 0,33 1,55 1,287 1,472 1,305 1,17 1,398 1,520 0,774 1,928 0,299 0,903 1,157 0,463 1,50 1,100 0,453 1,851 1,90 0,237 0,879 1,777 1,697 0,704 0,390 0,888 0,880 1,368 0,850 0,977 0,969 1,713 1,874 0,729 0,901 1,802 0,68 0,828 0,732 1,409 1,980 1,667 1,121 0,766 1,642 0,726 0,492 1,671 1,578 0,11 0,570 1,334 1,548 1,857 0,90 0,824 1,787 0,659 1,65 0,237 1,263 0,23 0,335 1,761 1,264 1,818 0,886 1,554 0,975 0,953 1,730 0,766 0,875 0,820 1,636 1,642 1,981 0,327 1,234 1,270 1,594 0,343 1,940 0,2 1,733 1,455 0,956 0,243 0,713 1,58 1,653 0,895 1,867 0,990 1,13 1,771 0,802 1,110 1,556 1,565 0,809 0,855 1,920 0,896 0,358 1,813 1,820 1,859 1,136 1,320 1,564 1,762 1,8 0,399 0,788 1,753 0,441 1,797 0,38 0,701 1,243 1,34 1,253 1,569 1,936 0,952 0,87 0,121 1,939 0,346 0,474 1,911 0,797 1,330 0,879 0,525 0,480 0,968 0,681 1,787 0,618 1,573 0,774 1,404 1,890 0,675 0,446 1,883 0,451 1,918 0,522 0,207 0,279 1,318 1,930 1,606 1,811 1,31 0,222 0,736 1,527 0,417 1,474 0,874 0,877 0,3 0,648 1,180 0,787 0,806 1,2 0,96 0,872 0,500 0,55 1,819 1,539 0,903 1,265 0,870 1,520 1,991 1,871 1,927 0,464 0,994 1,341 0,873 0,584 0,519 1,457 1,852 1,46 1,771 0,649 0,360 1,726 1,71 1,109 1,663 1,686 1,864 1,39 1,756 1,589 1,347 1,221 0,3 1,444 1,903 0,554 1,571 1,979 0,72 0,516 0,617 0,145 0,879 1,993 1,127 1,506 1,205 0,759 1,344 1,380 1,702 1,435 0,240 0,958 1,308 1,302 0,37 0,520 1,969 1,996 0,204 0,47 0,900 0,303 1,900 0,87 1,935 1,81 1,705 1,614 1,141 1,234 1,753 0,843 0,164 1,121 0,931 0,667 0,493 1,507 1,484 1,344 1,304 0,924 1,555 0,652 0,117 0,319 0,792 0,209 0,257 0,401 1,871 1,887 0,117 0,267 1,755 0,269 1,579 1,771 1,56 1,461 1,835 1,399 1,210 1,106 1,721 0,717 1,223 0,988 0,945 1,24 1,362 0,699 1,914 1,378 1,730 1,608 0,649 1,970 1,31 0,401 1,58 1,899 1,277 0,255 0,180 0,490 1,905 1,365 1,476 1,799 0,896 1,662 0,514 1,476 0,740 1,331 1,541 0,985 0,85 0,764 0,814 0,918 0,994 0,573 0,837 1,494 0,329 0,769 0,143 0,385 0,988 1,899 0,240 1,651 1,395 0,890 0,785 1,450 0,699 1,621 1,308 1,518 1,689 1,710 0,489 1,707 0,495 1,189 0,666 0,973 0,598 1,871 0,351 1,851 1,792 0,720 0,47 0,813 0,643 0,90 0,971 1,988 1,883 0,426 1,986 1,340 0,23 1,855 1,390 0,999 1,463 0,889 1,872 1,960 0,852 0,505 1,482 0,111 0,718 1,875 0,238 0,29 0,938 0,549 0,218 1,95 1,43 0,610 0,713 1,955 0,411 0,614 0,137 1,65 1,510 0,629 1,180 0,293 1,3 0,828 1,663 0,588 0,742 0,758 0,507 1,45 1,548 0,147 0,421 0,313 1,651 1,140 0,438 1,488 1,480 1,945 0,456 1,137 0,914 1,463 0,111 1,630 1,679 1,512 1,669 1,305 1,918 0,198 0,356 0,430 1,2 0,797 1,956 1,271 0,639 0,911 1,293 1,763 1,512 0,108 0,715 0,699 0,824 0,338 0,493 0,749 0,577 0,981 1,325 1,368 0,99 0,456 0,990 1,366 1,637 1,39 1,202 1,836 0,418 1,444 1,545 0,369 0,576 0,724 1,74 0,271 0,984 0,610 1,975 0,730 0,10 0,89 1,843 0,723 1,331 0,386 1,643 0,136 1,564 0,232 1,158 1,549 0,24 1,852 1,3 0,65 1,374 1,805 1,133 1,740 1,911 0,380 1,411 0,261 0,215 1,562 0,815 1,965 1,990 1,585 1,450 0,268 0,135 0,276 0,734 1,533 0,669 1,950 0,142 0,971 1,290 1,969 0,162 1,473 0,196 1,677 0,392 0,721 1,627 0,757 0,104 0,443 1,887 0,81 0,487 1,44 1,827 0,263 1,452 0,124 0,364 0,807 0,359 1,206 1,462 1,934 1,555 1,56 0,528 1,633 1,561 1,611 0,831 0,731 0,55 1,966 1,939 1,950 1,681 0,979 1,942 1,458 0,769 1,727 1,478 1,378 0,601 1,303 1,15 0,546 1,802 0,365 0,881 1,291 0,261 1,304 0,213 1,767 1,972 1,195 1,17 1,62 0,796 1,466 0,83 1,447 0,616 0,499 0,498 1,207 1,981 1,551 1,427 0,496 0,213 0,184 1,166 0,942 0,951 1,962 0,124 0,960 1,320 0,535 1,505 0,523 1,453 1,113 1,542 1,461 1,98 1,711 1,794 1,535 1,872 1,736 1,512 0,50 0,710 1,517 1,845 0,34 0,521 0,489 1,721 0,804 0,462 0,473 1,120 1,206 1,0 0,805 1,576 0,807 0,609 0,899 0,864 0,276 0,801 0,671 1,791 1,293 0,179 1,295 1,789 0,216 0,201 0,358 1,263 1,807 0,672 1,322 0,199 0,268 0,871 0,106 1,246 0,860 0,635 1,333 0,802 1,979 1,155 1,169 0,814 1,624 0,540 0,273 1,909 0,49 1,645 1,740 0,464 0,986 1,818 1,479 0,246 0,633 0,452 1,992 0,741 1,295 1,485 0,381 0,280 0,954 0,287 1,758 1,782 1,301 1,383 0,499 0,45 1,352 1,352 1,596 0,231 0,741 0,165 1,900 1,944 1,647 0,437 1,61 1,578 1,173 1,806 0,631 1,44 1,371 1,164 0,804 0,760 0,651 1,397 0,718 1,361 1,873 0,685 1,366 0,370 0,846 1,617 1,498 1,271 0,879 1,21 1,248 0,703 0,172 0,567 1,238 0,150 0,64 1,687 0,178 0,959 0,116 1,672 1,656 1,157 1,204 0,880 0,575 0,470 0,450 1,463 0,489 1,625 1,837 1,906 1,39 0,434 1,556 1,682 1,701 0,719 1,735 0,725 0,732 1,604 0,166 1,35 1,867 1,976 0,66 0,732 0,210 0,392 1,526 1,662 0,179 0,712 1,80 0,338 0,710 0,83 1,490 0,128 0,676 0,470 1,264 0,893 0,332 1,639 1,974 0,200 1,169 1,775 0,70 1,273 0,627 0,927 0,915 0,355 0,748 0,708 0,830 0,980 0,265 0,791 0,66 0,36 0,88 1,196 0,676 1,221 0,340 0,268 0,880 0,664 1,986 0,289 0,692 0,134 0,104 0,332 0,948 0,177 1,568 0,373 0,173 0,770 0,942 0,257 1,541 1,268 0,985 1,904 0,945 0,685 0,664 0,189 1,467 0,411 1,283 0,664 1,931 1,503 0,992 1,338 0,153 0,931 1,983 0,469 1,843 0,439 0,440 1,973 1,532 0,749 0,187 1,553 1,207 0,778 1,795 0,962 1,983 1,946 1,198 0,243 0,66 1,870 1,15 1,183 1,811 0,996 1,946 1,560 0,962 0,639 1,496 0,693 1,993 0,158 0,472 0,772 0,910 1,865 0,222 0,124 0,607 1,45 0,786 0,904 0,226 1,229 1,974 0,0 0,199 0,100 0,567 0,139 1,805 0,145 0,780 0,458 0,518 1,558 1,762 1,483 0,106 0,256 1,12 0,773 1,304 0,588 1,998 1,140 1,20 0,97 0,974 1,137 1,241 1,766 0,458 1,369 0,420 1,771 1,292 1,339 1,328 1,336 0,376 0,54 1,153 1,706 0,97 0,600 0,928 1,5 1,530 0,417 0,399 1,323 0,75 0,34 1,944 0,162 0,367 0,374 0,213 1,71 1,201 1,997 0,437 1,356 0,737 1,65 1,363 0,890 1,87 0,858 0,784 0,568 1,956 0,693 0,192 1,757 0,623 1,401 0,562 1,37 0,529 1,433 1,769 1,814 1,746 1,75 1,802 0,506 1,549 1,748 1,858 1,140 0,874 1,159 1,77 0,772 1,643 1,491 1,997 0,486 1,952 0,640 1,372 1,465 1,678 0,665 1,853 1,660 0,113 0,887 0,666 0,772 0,971 1,605 1,115 0,272 0,272 0,36 1,838 0,512 0,418 1,158 1,324 1,575 0,411 1,528 0,540 0,843 0,618 0,49 0,272 1,372 0,844 1,183 1,373 0,984 0,948 0,338 0,314 0,179 1,92 0,409 0,103 1,806 0,685 1,838 0,277 0,538 0,13 1,885 1,756 1,435 1,22 1,15 0,324 0,305 0,41 0,323 1,335 1,176 0,707 0,741 0,467 1,770 0,568 1,945 0,667 0,311 1,896 1,529 1,540 0,124 1,230 0,170 0,159 0,446 0,764 0,901 1,337 0,566 0,534 1,403 0,566 0,912 1,458 0,148 0,815 0,253 0,932 0,162 0,609 1,475 1,958 1,614 0,646 0,352 0,493 1,351 1,283 0,214 0,592 0,527 1,401 0,179 1,918 0,777 1,846 1,368 1,857 0,58 0,906 0,109 0,898 1,723 0,488 1,66 1,664 1,146 0,570 0,673 0,399 0,803 0,923 0,640 0,574 1,187 0,526 0,187 0,256 0,836 0,831 0,107 0,586 1,766 0,891 0,720 0,560 1,904 0,652 1,741 1,533 0,402 1,955 0,457 0,937 1,329 1,432 1,926 1,846 0,669 1,692 1,845 0,43 1,940 0,475 1,290 1,36 0,620 0,399 0,585 0,941 0,269 1,197 0,435 0,139 1,368 1,930 1,798 1,993 0,382 1,685 0,163 1,363 1,847 0,916 1,562 1,146 0,798 0,451 0,752 0,388 0,414 1,579 1,532 1,938 1,645 0,278 1,733 1,867 0,793 1,566 1,490 1,981 0,181 0,863 1,720 0,782 0,309 1,514 1,437 0,165 0,35 1,363 0,715 0,818 0,476 1,545 1,404 1,637 0,961 0,772 0,896 1,590 0,376 1,209 1,859 0,951 1,505 1,850 1,888 1,259 0,777 1,207 1,179 0,573 0,173 1,407 1,508 1,378 1,314 1,988 0,356 0,387 1,534 1,429 1,758 0,855 1,918 1,818 1,238 1,124 1,120 1,870 1,56 1,103 1,541 0,60 1,580 0,437 1,635 0,46 0,451 0,5 0,207 1,151 0,888 1,274 0,489 0,171 0,126 1,251 1,570 1,254 1,974 1,316 0,57 1,804 0,406 1,238 1,143 1,783 0,681 1,662 0,482 1,561 1,401 0,154 1,399 1,940 0,852 1,847 0,841 1,310 1,889 1,897 0,111 1,356 0,9 1,768 0,626 0,864 0,639 1,340 1,147 1,371 1,425 1,377 1,779 1,357 1,236 1,760 0,543 0,893 0,143 0,700 1,440 0,436 0,605 1,695 1,360 1,745 0,981 1,905 1,375 1,111 1,200 0,952 1,228 1,676 0,966 0,504 1,474 1,25 1,353 1,977 0,494 0,433 1,15 0,580 1,305 0,821 1,988 0,517 0,328 1,78 1,707 0,327 0,820 0,262 1,181 1,950 1,930 1,19 0,717 0,443 1,781 0,539 0,562 0,557 1,275 1,590 1,230 0,830 0,328 0,359 0,923 0,940 0,490 1,753 0,858 0,942 0,299 1,148 1,793 1,333 0,499 0,240 1,121 1,123 1,571 1,1 1,9 0,108 0,873 0,978 1,336 0,550 1,505 0,468 0,925 1,193 1,405 1,794 0,755 1,260 0,618 0,968 0,71 1,455 0,271 1,381 1,132 1,570 0,347 0,59 0,780 1,87 1,403 1,305 0,731 0,26 1,94 0,543 0,925 0,757 1,685 0,89 0,710 1,886 0,955 0,26 1,310 1,782 1,695 0,413 1,801 0,985 1,6 0,513 0,607 0,396 1,248 0,189 0,291 0,482 1,613 0,933 1,928 0,937 0,562 0,560 1,724 0,954 1,493 1,794 1,784 0,68 0,871 1,555 1,358 0,740 1,574 1,656 0,184 1,598 0,924 0,30 1,883 0,750 0,519 1,752 1,333 1,600 0,600 0,683 1,493 1,969 0,832 0,85 0,290 0,938 0,116 1,299 0,413 1,4 0,895 0,195 0,886 1,426 0,781 0,293 0,757 0,453 0,415 1,883 1,575 0,516 0,56 1,236 1,404 1,607 0,792 0,466 0,287 1,215 1,390 1,152 0,4 1,773 1,885 1,367 0,372 0,845 0,308 0,417 0,18 1,306 0,916 1,59 0,936 0,399 0,123 0,819 0,103 0,322 1,590 1,618 0,543 0,807 1,492 1,932 0,42 0,403 1,18 0,244 1,34 0,77 0,769 1,67 1,676 1,460 1,113 1,679 1,986 0,203 1,761 0,798 1,23 1,879 0,607 0,0 0,471 0,888 0,852 0,313 1,284 1,451 1,965 1,741 0,691 0,664 0,417 1,70 0,137 1,857 0,898 1,864 0,27 0,523 1,481 1,975 0,608 1,602 1,223 1,528 0,677 0,989 0,940 0,928 0,484 0,547 0,23 1,340 0,864 0,163 0,282 0,624 1,300 0,288 1,543 1,151 0,663 1,177 0,7 0,570 0,773 1,826 0,646 1,666 0,228 1,71 1,46 0,732 0,637 0,608 0,830 1,628 0,845 0,31 0,62 1,131 0,465 1,480 0,891 0,284 0,508 1,528 0,77 0,137 0,182 1,59 1,902 0,175 1,392 0,814 1,956 0,26 1,979 1,686 1,169 0,8 1,619 1,629 0,204 1,127 1,961 1,953 0,59 1,808 1,16 0,649 1,205 0,411 1,631 0,496 0,310 1,102 1,948 1,168 1,204 0,901 1,498 1,53 0,846 0,870 1,207 1,112 1,224 0,605 1,312 1,987 0,272 1,338 1,732 1,986 0,728 0,495 1,582 0,725 0,576 0,209 1,300 0,649 0,927 1,715 1,229 0,600 1,380 1,811 0,119 0,957 1,39 1,694 1,485 0,953 0,957 0,42 1,487 1,631 0,240 1,855 0,291 0,157 0,115 0,616 0,661 0,517 0,486 1,799 1,149 0,482 1,991 1,198 1,608 0,476 0,259 1,437 1,462 0,432 0,175 0,363 0,578 0,119 0,331 0,509 0,251 0,707 0,422 1,783 1,849 0,397 1,725 1,774 0,707 1,412 0,56 1,316 0,903 0,492 1,460 1,673 0,836 1,814 1,903 1,417 1,22 0,256 0,359 0,266 0,985 0,302 1,626 0,262 1,196 0,625 0,746 0,691 0,453 1,536 1,851 0,906 1,291 1,864 0,112 0,400 0,110 0,709 1,394 0,422 1,958 1,525 1,757 1,640 1,845 0,766 0,339 1,382 1,333 1,721 0,896 0,5 1,840 0,248 0,141 0,293 1,418 0,862 0,68 1,292 0,480 1,657 0,550 0,931 1,464 0,299 1,937 1,112 1,742 0,153 0,818 0,698 0,527 1,691 0,338 1,79 0,579 0,846 0,352 1,630 1,48 0,643 0,289 0,797 0,217 1,433 0,769 0,202 1,462 1,915 0,559 0,481 1,818 1,686 1,101 1,269 1,745 1,12 0,454 0,112 0,201 0,442 0,693 0,625 0,722 1,448 0,754 0,123 1,73 1,623 1,14 1,35 0,931 1,89 0,957 1,130 0,733 1,551 1,346 1,436 0,658 1,143 1,247 0,281 0,494 1,338 0,745 1,49 1,282 0,69 1,442 0,587 1,118 0,150 1,1 0,3 1,983 0,410 0,15 0,601 1,179 0,241 1,906 1,94 0,209 1,448 0,70 1,130 1,467 0,18 0,663 1,460 0,447 1,390 1,578 0,159 1,477 1,947 1,20 1,13 1,200 1,199 0,385 0,34 1,340 0,919 0,230 0,940 1,745 0,305 0,258 0,877 1,607 1,852 0,850 0,249 1,334 0,25 1,44 1,100 1,759 0,647 0,395 1,236 1,236 0,270 0,771 1,468 0,94 1,656 1,6 0,806 1,724 0,32 0,559 1,934 1,518 0,549 0,729 1,825 1,728 0,324 1,478 0,932 0,458 0,638 0,257 0,530 1,755 1,955 0,569 1,307 1,172 1,364 1,607 1,996 1,184 1,181 1,844 1,886 1,209 1,524 0,773 1,413 0,737 0,663 1,459 1,903 1,284 1,603 0,101 1,173 1,884 1,8 1,360 1,593 0,15 0,108 1,608 1,416 0,783 1,967 0,957 0,939 1,95 1,460 1,952 0,885 0,5 1,911 1,226 1,772 1,32 1,441 1,763 1,747 0,176 1,390 0,604 0,328 0,973 0,769 0,723 0,810 1,276 0,828 0,101 1,140 1,204 1,660 0,423 1,568 0,412 1,478 0,47 1,177 0,523 0,237 0,344 0,252 1,512 1,457 1,306 0,938 1,851 0,762 1,784 1,909 0,17 0,178 0,365 0,769 1,800 0,511 1,70 0,107 1,775 1,149 0,592 0,87 1,229 0,247 0,643 0,17 0,427 1,725 0,85 0,796 1,380 1,782 0,44 1,974 0,858 1,55 0,334 1,960 1,73 0,158 0,437 1,686 0,678 0,875 0,932 0,868 1,969 0,417 1,775 0,357 1,733 1,434 1,880 0,621 1,56 1,406 1,428 1,752 0,237 1,786 0,720 0,288 1,135 0,59 0,753 1,306 0,552 0,379 0,864 0,751 1,483 0,526 1,193 0,906 1,523 0,584 0,433 1,132 1,823 0,699 1,9 1,423 0,539 1,714 1,362 1,875 1,66 0,996 0,97 0,301 1,807 0,903 1,425 0,671 0,465 1,259 1,35 1,24 1,818 1,730 1,894 1,75 1,258 1,816 0,51 1,266 1,720 1,663 1,486 0,705 0,606 1,329 1,874 0,404 1,956 0,52 0,503 0,386 1,866 1,932 0,522 0,92 1,838 1,858 0,269 1,554 1,218 0,942 0,476 1,726 1,730 0,369 1,711 1,534 1,858 1,788 1,794 0,34 1,577 0,441 1,58 0,712 0,536 1,803 1,596 0,446 1,105 0,169 0,680 0,131 0,249 0,160 0,330 0,86 0,726 1,257 0,13 0,132 1,472 0,67 1,117 1,973 1,880 1,450 1,773 1,147 0,448 0,749 0,410 0,880 0,71 1,90 0,17 1,528 1,984 1,639 1,669 1,609 0,508 0,934 1,317 0,265 0,392 1,626 1,790 0,559 0,893 0,619 1,806 1,778 0,92 1,615 1,297 1,758 0,923 0,91 0,877 1,932 1,794 0,773 0,634 0,469 0,328 0,165 1,744 0,976 0,177 0,702 0,60 1,305 1,86 0,378 1,411 0,196 0,880 0,889 0,139 0,910 1,634 0,580 1,930 0,860 1,696 0,194 1,341 1,824 0,333 1,913 1,724 1,119 0,842 1,578 0,273 1,751 0,989 1,179 1,23 1,448 1,489 0,661 1,525 0,537 1,315 1,849 1,971 1,487 1,865 1,310 1,492 0,889 0,674 0,918 0,921 0,904 1,462 0,8 1,426 1,562 0,525 1,210 0,989 0,176 1,300 0,288 1,622 1,586 0,127 1,589 1,946 1,663 0,664 0,43 1,604 1,500 0,157 1,772 0,572 1,853 0,483 0,883 1,17 1,309 1,579 0,582 0,740 1,861 1,358 0,561 1,813 0,641 0,982 0,658 0,82 1,253 0,277 1,840 0,349 1,490 0,968 1,488 0,316 0,931 1,16 0,682 0,267 0,598 0,264 1,947 0,996 0,96 1,730 0,805 1,35 0,318 0,410 0,422 0,822 1,65 1,960 0,392 0,174 1,84 1,582 1,167 0,66 1,256 1,715 1,21 1,3 1,511 1,345 0,629 1,133 0,887 0,619 0,600 1,781 1,858 0,172 1,416 0,492 0,254 1,437 1,681 0,439 1,392 1,339 1,424 1,393 0,60 1,327 1,115 1,678 1,171 1,201 1,55 1,228 0,388 1,800 0,73 1,713 0,391 1,623 0,272 1,260 1,105 0,2 1,192 1,900 1,406 0,413 0,190 1,861 0,706 0,889 1,325 0,117 0,730 0,710 0,979 1,61 0,804 1,77 1,500 1,874 0,473 0,797 0,352 1,968 0,494 0,965 1,976 0,418 1,685 0,865 0,497 1,43 1,551 1,443 0,606 0,210 1,540 0,689 1,355 1,716 1,874 1,628 0,130 0,28 1,317 1,41 1,42 0,396 1,174 0,562 1,541 1,238 0,257 0,772 1,446 0,565 1,508 0,117 1,241 0,876 1,752 0,355 0,892 1,923 0,847 1,335 0,191 1,244 1,329 0,303 0,944 1,373 1,554 0,170 1,290 0,179 1,550 0,346 1,648 1,185 0,332 1,135 0,207 1,995 0,498 0,138 0,464 1,650 1,351 1,908 0,513 1,168 1,991 0,884 1,987 0,745 1,803 1,740 0,645 1,611 0,21 0,374 0,93 1,217 0,3 1,639 1,320 1,660 1,146 1,339 1,509 0,317 0,836 0,845 0,861 1,390 0,233 0,404 0,119 1,107 1,978 0,434 1,439 0,509 1,759 0,632 0,241 1,80 0,219 0,244 0,91 1,344 0,296 0,660 1,869 0,935 0,612 1,947 1,515 0,389 0,276 0,234 1,495 0,360 0,989 1,414 0,641 1,988 1,279 0,854 1,255 1,889 1,34 1,143 1,624 1,839 0,416 1,705 0,365 0,282 0,894 0,642 0,537 0,733 1,757 1,206 0,810 1,697 1,847 1,448 1,781 1,242 1,779 1,882 1,509 0,37 1,973 1,895 1,608 0,847 0,674 1,123 1,680 0,336 1,896 1,295 1,269 0,46 1,268 0,886 0,848 1,196 1,151 1,198 1,677 1,132 1,234 1,680 0,763 0,790 1,910 0,150 0,475 1,266 0,937 0,270 0,18 1,202 0,833 0,460 0,250 0,860 0,46 0,17 1,725 0,189 1,157 0,963 0,157 0,449 1,809 1,920 1,58 1,464 0,763 0,685 1,4 0,568 0,975 0,421 1,549 1,686 0,888 1,214 1,625 0,529 1,67 1,751 0,998 1,706 1,304 0,726 1,815 1,26 1,758 0,811 0,659 0,94 0,104 0,337 0,203 0,282 0,378 1,939 1,278 1,181 1,428 1,61 1,1 0,859 1,277 0,811 1,737 1,459 1,366 0,876 0,380 0,390 0,362 0,19 0,245 0,529 0,209 0,828 1,945 1,788 1,50 1,469 1,783 1,991 1,591 0,275 1,437 1,837 0,759 1,130 1,104 1,24 1,127 1,334 0,336 0,489 1,132 1,177 0,191 0,187 0,97 1,996 1,487 0,117 1,8 1,843 1,763 1,290 1,122 1,998 0,56 1,162 0,617 0,954 0,264 1,504 1,153 1,731 0,848 0,440 1,978 1,533 1,526 0,822 0,546 0,957 0,897 1,303 1,260 1,672 0,675 0,868 1,496 0,134 1,936 0,747 1,513 1,58 1,906 1,247 1,444 1,765 0,652 1,812 1,939 0,665 0,727 0,264 1,661 0,755 1,365 1,118 0,253 0,529 1,81 0,202 1,852 0,982 1,128 1,998 1,923 1,413 0,159 0,684 1,592 0,705 0,906 1,936 0,995 1,519 1,822 1,409 0,174 1,653 1,954 1,69 0,673 0,680 0,414 1,613 0,643 1,529 0,502 1,241 1,787 1,820 1,996 1,919 0,751 1,156 1,756 0,730 0,485 1,575 1,283 1,599 0,840 0,59 0,902 0,579 1,108 1,533 1,980 0,211 1,418 0,69 1,110 1,775 1,600 0,790 1,45 0,283 0,862 0,953 1,559 1,663 1,177 0,64 1,789 1,711 1,206 1,206 0,151 1,185 0,985 0,246 1,724 0,364 1,754 0,667 0,220 0,892 0,779 1,141 0,326 0,600 1,207 0,463 0,439 1,426 1,144 1,801 0,333 1,967 1,55 1,625 0,819 1,403 1,734 0,476 1,7 1,561 0,683 0,159 1,310 0,501 0,98 1,178 0,199 1,352 1,177 1,646 1,140 0,280 1,979 1,796 0,873 1,996 0,586 0,981 1,197 1,43 1,540 0,230 0,455 0,322 1,438 1,79 0,988 0,999 1,41 0,200 1,4 0,956 0,792 0,103 1,599 0,197 1,811 0,331 0,777 0,357 1,107 0,297 1,891 0,875 1,991 0,624 1,438 0,150 0,0 0,358 1,946 0,376 1,302 1,249 1,548 0,924 1,387 1,199 0,571 1,404 0,667 0,614 0,939 1,112 0,535 1,41 0,263 1,27 0,285 0,928 0,384 1,236 1,946 1,609 0,122 0,856 0,561 1,591 0,960 1,793 1,946 0,158 0,605 1,685 1,530 1,56 1,573 1,185 1,696 1,391 0,515 1,92 0,753 0,599 0,227 1,667 1,256 0,874 1,89 1,216 1,49 0,28 0,189 1,220 1,98 1,791 1,385 1,888 0,66 0,343 0,895 1,386 0,294 1,389 1,92 0,791 1,943 1,565 1,78 1,313 0,46 0,665 0,238 0,392 0,407 1,311 0,715 1,226 1,487 1,617 0,515 0,44 0,240 0,207 1,788 0,944 0,272 0,16 1,158 1,569 1,691 0,816 1,142 1,717 1,219 0,300 0,236 1,100 1,40 0,430 1,984 1,684 0,810 1,854 0,978 1,147 0,942 0,551 0,292 1,111 0,297 1,238 1,579 0,894 1,136 1,895 0,898 1,564 1,805 0,129 0,441 0,494 0,882 0,536 1,304 0,910 0,778 1,648 0,99 1,398 1,36 0,753 1,429 0,752 0,657 0,669 0,183 1,208 0,622 0,556 0,442 0,658 1,25 0,434 0,518 1,733 0,337 1,922 0,137 0,398 1,261 0,47 0,411 1,893 1,728 0,568 0,372 0,253 1,149 0,221 0,710 1,618 0,473 0,476 0,963 1,425 1,595 0,804 0,1 0,495 0,239 1,547 1,260 0,906 0,675 0,742 0,915 1,849 0,916 0,752 0,512 0,715 1,415 1,605 0,116 1,162 1,231 1,152 1,273 0,14 0,407 0,89 0,960 1,917 1,786 0,680 0,806 1,142 0,833 0,742 0,192 0,284 0,683 0,742 0,713 1,401 0,424 1,605 1,188 0,593 0,525 0,981 0,213 1,747 0,377 1,385 0,880 0,247 0,196 0,239 0,65 0,530 0,942 0,799 1,776 1,922 0,777 1,181 1,847 1,578 1,393 0,639 1,532 0,582 0,178 0,927 0,612 0,524 1,493 0,727 1,356 1,728 1,425 1,97 1,749 1,1 0,866 0,304 0,285 1,294 0,173 0,796 0,794 1,355 1,677 1,249 1,140 1,464 0,713 1,837 1,625 0,161 0,946 0,351 1,283 0,618 1,453 1,406 1,313 0,125 0,136 0,510 1,316 0,254 1,962 1,229 0,464 1,596 0,967 0,50 0,98 1,327 0,166 1,508 1,811 1,146 1,682 1,6 0,971 1,878 0,10 0,729 0,428 0,390 0,589 1,884 1,616 1,92 0,295 1,587 0,459 1,186 1,880 1,739 0,455 1,65 0,708 0,684 0,196 1,654 0,37 1,182 1,919 0,648 0,414 1,132 0,335 0,639 0,415 0,269 0,91 0,323 1,160 0,120 0,562 0,890 1,40 0,569 1,458 0,359 0,483 1,981 1,78 1,562 1,457 1,122 0,467 1,579 0,133 0,616 1,917 0,740 1,69 0,161 1,766 0,560 1,697 1,338 0,459 1,291 1,677 0,914 0,113 0,304 0,914 1,732 1,584 1,668 0,46 1,239 0,952 1,612 0,831 1,166 1,151 0,4 0,287 1,907 0,954 0,2 1,823 1,413 1,894 1,255 0,975 1,822 0,475 0,546 0,159 0,142 0,748 1,788 1,402 1,197 1,18 0,790 0,888 0,576 1,253 1,964 1,910 1,914 1,939 1,556 0,172 0,48 1,662 0,840 0,953 1,111 1,770 0,497 0,886 1,173 1,392 0,577 1,625 0,162 0,534 1,983 1,133 1,903 1,758 1,914 1,238 1,592 0,925 0,839 0,122 0,354 0,140 1,554 1,715 1,669 0,391 1,489 1,104 0,270 1,650 1,338 0,778 0,994 1,12 1,73 1,978 0,453 0,276 1,760 1,912 0,952 1,148 1,619 0,510 1,701 1,596 0,259 0,798 1,431 1,49 0,664 1,201 1,577 0,600 0,102 0,893 0,567 1,601 1,777 0,966 0,522 0,475 0,303 0,242 1,458 1,684 0,948 1,805 1,831 0,990 1,41 0,94 1,760 0,890 1,558 0,216 0,971 1,650 0,610 0,504 0,866 1,763 0,751 1,781 0,844 0,400 0,192 0,493 0,981 1,510 1,452 0,870 0,954 1,242 1,962 1,883 1,985 1,3 1,743 0,334 1,452 0,276 0,164 1,579 0,779 1,404 1,928 1,706 0,993 0,998 1,10 0,38 1,287 0,332 1,741 0,686 0,796 1,515 1,64 0,634 1,363 1,702 1,904 1,542 0,81 0,789 1,242 0,458 1,564 0,455 1,968 0,798 1,776 0,190 1,165 1,970 0,121 1,420 1,968 0,545 1,992 1,32 1,241 0,913 0,635 1,245 1,106 0,941 0,398 1,485 1,93 0,167 0,967 1,318 1,677 0,429 0,316 1,570 0,975 0,621 1,251 1,95 1,409 1,526 1,985 1,42 1,780 1,944 1,379 0,420 0,392 0,314 1,676 1,147 0,901 0,537 0,122 0,502 1,413 0,245 0,629 1,244 0,655 0,489 1,246 1,571 0,779 1,227 1,968 0,715 1,277 1,969 1,461 0,346 0,393 1,464 1,778 1,926 1,528 1,839 0,78 1,217 1,390 0,635 0,226 0,410 0,281 1,167 0,364 0,501 0,465 0,11 0,128 0,766 1,470 0,995 1,477 0,749 1,188 0,712 0,494 1,179 1,129 1,866 1,502 1,527 1,779 1,400 0,603 1,220 0,912 0,147 0,699 0,368 1,699 0,479 1,856 0,628 1,735 1,181 1,730 1,329 0,758 0,426 1,586 1,377 1,97 1,552 1,407 0,643 1,768 1,901 0,52 0,588 0,203 1,771 0,966 1,878 1,266 0,916 0,909 0,632 1,412 1,696 1,245 0,60 1,553 1,159 1,783 1,482 0,818 1,369 0,677 1,320 0,231 0,123 0,522 0,63 1,488 1,582 1,671 0,620 1,751 0,848 1,747 0,628 0,952 0,968 0,66 0,560 0,457 1,97 0,208 0,240 0,828 0,629 1,76 0,223 0,716 1,86 1,625 0,941 0,895 1,884 1,68 0,718 1,345 1,762 1,832 1,117 0,689 1,239 0,263 0,83 1,600 0,95 0,966 1,445 1,789 0,487 1,535 1,226 0,545 1,245 1,820 1,116 0,403 1,597 0,51 0,707 0,946 1,38 0,81 0,995 1,386 0,835 0,814 0,442 1,610 1,644 0,761 0,854 0,76 0,176 1,757 1,879 1,348 0,883 0,28 1,726 1,41 1,472 0,740 1,791 0,748 0,207 0,888 1,618 0,996 1,774 1,148 0,762 0,886 1,693 1,187 1,686 1,524 0,967 0,84 0,873 1,548 1,186 1,964 1,34 1,701 1,587 0,177 1,562 1,155 0,447 0,552 0,88 0,507 1,243 1,628 0,726 0,426 0,593 0,579 1,135 1,864 0,728 0,579 0,808 0,599 1,237 1,632 1,969 0,588 1,776 0,249 1,90 0,169 0,662 0,641 0,573 1,384 1,835 1,329 1,19 1,108 1,414 1,789 1,534 1,24 0,503 1,98 1,884 1,216 0,110 1,791 0,346 1,145 0,987 1,497 1,956 0,799 1,694 0,0 0,706 0,145 0,947 0,930 1,755 1,690 0,287 0,823 0,242 1,468 1,678 1,51 0,207 0,298 0,444 0,624 0,338 1,147 0,232 1,923 1,272 0,245 1,129 0,505 1,850 1,291 1,140 1,663 1,95 1,127 0,524 0,435 0,661 0,855 0,662 0,152 1,412 1,391 0,791 1,334 1,591 1,902 0,436 1,816 0,425 1,170 1,810 1,560 0,905 1,771 1,968 0,740 1,916 1,56 0,420 1,582 0,974 1,956 0,823 0,660 0,608 0,949 0,804 0,679 0,547 1,419 1,274 1,324 1,289 1,444 1,792 0,578 1,296 1,121 0,985 0,386 0,217 0,913 0,249 0,954 1,728 0,943 1,15 0,510 1,281 0,506 0,227 0,918 1,598 1,340 1,640 1,558 0,933 1,912 1,169 0,630 1,548 0,92 1,812 1,529 1,662 0,648 0,832 0,741 0,987 1,948 0,452 0,479 0,965 0,382 1,908 0,494 0,251 1,973 0,451 0,748 0,770 1,15 1,965 0,733 1,613 0,857 1,78 0,979 1,600 0,504 1,167 1,254 1,421 1,787 0,487 0,864 1,833 1,987 0,274 1,548 1,511 0,123 1,688 1,767 0,950 1,342 0,832 0,494 1,714 0,912 1,95 1,785 0,263 0,182 0,552 1,640 0,856 1,281 1,42 1,42 0,653 1,161 0,329 1,345 0,782 1,739 1,25 1,146 1,18 0,821 0,684 0,836 1,277 1,682 1,615 0,743 1,503 0,402 1,751 0,24 1,710 1,861 0,90 0,69 0,504 0,818 0,116 0,742 1,985 0,986 1,18 0,955 1,97 0,192 1,447 1,252 1,783 0,627 0,964 1,242 0,426 1,176 1,262 1,989 1,579 1,241 0,308 0,792 0,683 1,357 0,438 0,252 0,434 0,874 0,550 0,840 1,297 0,337 0,329 1,267 0,897 0,862 0,340 1,818 0,153 0,356 0,583 1,5 0,697 0,181 0,986 1,305 0,145 0,658 0,781 0,888 0,745 1,639 1,517 0,39 1,764 1,685 1,275 1,271 1,728 0,92 1,695 1,917 0,935 0,647 1,313 1,718 1,292 1,491 1,212 0,891 1,250 1,104 0,637 1,941 0,859 0,937 1,996 1,433 0,840 0,907 0,568 0,320 1,512 0,598 1,372 1,134 1,58 1,712 0,291 1,810 0,579 0,470 0,876 1,795 1,146 0,549 1,166 1,482 0,657 1,809 0,690 1,141 0,325 0,80 1,243 0,840 1,465 0,58 0,655 1,610 0,258 1,166 1,740 0,491 1,180 1,742 1,647 1,681 0,391 1,552 1,106 1,434 1,862 0,480 1,383 1,974 1,270 0,995 0,396 1,288 0,383 1,704 0,496 0,697 1,573 0,12 1,38 1,211 1,741 0,901 0,942 0,445 1,366 0,762 1,350 1,543 1,918 1,425 0,251 1,916 1,494 1,831 0,461 1,367 1,239 0,679 1,569 0,826 0,853 0,412 0,210 0,701 0,54 1,365 0,876 0,738 0,368 1,457 1,231 1,624 0,267 1,122 1,770 0,737 0,35 1,576 1,607 0,232 1,378 1,171 0,531 0,397 1,977 0,363 0,648 1,718 1,143 0,510 0,454 1,807 1,502 1,252 0,166 0,188 0,699 1,228 0,665 1,896 1,320 0,402 0,685 1,600 0,197 0,526 0,866 0,660 1,691 0,148 0,391 1,541 1,85 1,327 0,879 1,438 1,325 1,870 0,890 0,858 1,358 1,721 0,98 0,878 1,613 0,433 0,65 1,111 0,456 0,189 1,81 1,556 0,0 1,122 0,227 1,96 0,729 0,721 0,18 1,436 1,286 1,611 1,949 1,213 1,863 0,457 1,398 1,587 0,283 1,691 1,786 0,204 1,945 1,548 1,385 0,251 1,677 0,237 1,174 1,670 0,365 0,968 0,175 1,888 1,161 0,749 0,113 0,585 1,31 1,721 0,248 1,218 1,68 0,928 1,582 1,133 1,613 0,566 1,825 0,838 0,955 1,437 0,902 1,439 1,611 0,391 1,328 1,202 1,242 1,129 0,137 0,725 1,845 0,623 1,346 0,539 1,37 0,256 0,621 1,400 0,35 1,476 0,518 0,901 1,740 1,104 0,141 0,919 1,470 0,384 0,415 0,256 0,955 1,713 1,516 0,193 0,447 0,449 1,419 1,237 0,770 1,642 1,791 0,961 0,90 0,885 0,945 0,930 1,268 1,395 1,56 1,132 1,251 1,158 0,582 0,614 1,315 1,111 1,260 0,780 0,602 1,60 0,984 0,464 0,155 1,851 1,292 0,903 0,179 0,12 0,664 0,962 1,754 0,226 0,191 0,798 0,414 1,824 0,723 0,249 1,698 1,400 1,915 1,517 0,663 0,148 1,164 0,60 1,626 1,450 1,331 1,543 0,743 1,543 1,593 1,750 0,541 1,508 0,267 1,855 0,926 1,127 0,548 0,45 1,941 1,213 1,783 1,88 1,903 1,835 0,0 1,889 1,300 1,603 1,438 1,877 0,399 0,591 1,458 0,360 0,397 1,969 0,5 1,311 0,159 1,747 0,578 0,142 1,685 1,705 0,611 1,910 1,299 1,510 1,437 0,263 0,672 1,503 0,564 0,91 1,957 1,89 0,678 0,852 1,104 0,535 0,553 0,193 1,771 1,903 1,192 0,516 1,876 1,299 1,443 0,853 1,944 0,456 0,950 0,575 0,397 1,803 0,667 1,759 0,93 1,529 1,434 1,9 1,529 1,605 0,680 1,668 0,796 1,126 1,114 0,814 0,730 0,558 1,674 1,993 1,85 0,344 0,983 1,507 0,269 1,712 0,167 0,968 0,961 1,235 0,144 1,829 1,365 0,157 0,972 0,180 0,871 0,242 1,299 1,122 1,375 1,176 0,250 1,80 1,596 1,975 1,664 1,449 1,30 1,150 0,465 0,918 0,121 1,616 1,784 1,171 0,556 0,481 1,267 0,610 0,41 1,979 0,670 0,485 0,396 1,248 0,407 1,744 1,328 1,432 1,8 0,434 1,147 1,427 1,155 1,431 0,272 0,83 1,66 0,903 1,181 0,972 1,161 0,328 0,26 0,306 0,878 1,668 1,720 0,286 0,267 0,784 1,130 1,111 1,552 0,83 1,916 0,169 1,930 0,34 1,576 0,806 1,851 0,6 0,588 1,547 1,854 0,229 0,626 1,512 1,209 0,351 0,859 1,510 0,876 0,12 0,291 1,286 0,902 0,877 1,324 0,678 1,243 0,223 0,926 0,197 1,862 1,597 1,774 1,917 0,896 0,617 0,783 1,893 0,957 0,529 0,582 0,23 0,174 1,124 0,387 0,667 0,312 1,312 1,855 0,513 0,463 0,89 1,474 1,380 1,451 1,623 0,751 0,470 0,753 0,508 0,475 0,769 0,119 0,452 0,392 0,549 1,193 0,171 1,102 1,425 0,333 0,916 0,875 1,698 0,20 0,845 1,6 0,216 0,613 0,260 1,431 1,459 1,23 0,893 0,991 1,190 0,373 0,589 1,796 0,95 0,865 0,208 0,383 0,800 1,582 0,726 0,316 0,346 0,690 0,728 1,790 0,22 1,25 1,606 1,390 1,464 0,792 1,781 0,856 0,488 0,150 1,420 0,193 0,773 1,999 1,103 1,559 0,624 1,191 0,922 0,707 1,464 1,234 1,950 1,148 1,215 1,776 0,226 1,154 1,147 0,710 1,742 1,586 0,826 0,375 0,209 1,712 1,660 0,495 1,194 1,614 0,166 1,675 0,405 1,523 0,249 1,898 1,897 0,279 0,267 1,446 0,803 1,89 1,326 0,101 1,189 1,932 0,129 1,697 0,296 1,623 0,576 0,47 1,178 1,747 1,289 1,402 1,468 1,388 0,245 1,360 1,622 1,77 1,77 1,799 0,416 1,145 1,851 0,514 1,105 0,748 1,585 0,494 1,721 0,143 0,848 0,959 1,92 0,336 1,578 1,169 1,615 0,555 1,800 0,972 0,339 0,868 1,68 0,362 1,36 1,633 1,531 1,947 1,407 1,448 0,113 1,323 1,224 0,675 0,964 0,355 0,256 1,727 1,583 1,223 1,883 1,499 0,857 1,808 1,260 1,106 0,451 1,83 1,338 1,526 1,479 0,454 1,979 1,130 1,813 1,685 1,851 0,826 0,262 1,648 0,587 1,830 1,587 0,234 0,973 0,277 1,202 1,96 1,588 1,774 1,923 0,842 0,325 1,673 0,809 1,244 0,693 0,666 0,884 1,513 0,319 0,127 0,634 1,73 0,606 1,724 1,187 1,264 0,584 1,670 0,392 1,658 0,1 0,43 1,444 1,40 1,345 0,91 0,207 1,136 1,270 0,399 0,234 1,357 0,748 0,940 1,348 1,328 1,2 1,467 0,761 1,400 1,330 0,457 1,529 0,289 0,115 0,892 1,764 0,870 0,647 1,263 0,301 1,19 0,510 1,564 0,542 0,373 1,936 0,219 1,293 1,729 0,601 1,9 1,648 1,190 1,232 1,216 0,295 1,811 1,211 1,885 1,159 0,832 0,474 1,404 1,0 1,551 1,296 1,255 1,741 0,693 0,537 1,774 1,700 0,953 0,857 1,725 1,162 0,747 0,313 0,658 1,807 0,373 1,283 1,133 0,967 1,895 1,905 0,993 0,769 0,872 0,504 1,655 1,594 1,628 0,292 0,108 1,248 1,612 0,323 1,581 0,277 0,326 0,596 0,883 0,755 1,522 1,402 0,354 0,770 0,10 0,986 1,874 1,66 0,138 0,14 0,831 0,484 0,269 0,424 1,793 0,550 1,681 0,329 1,261 1,461 1,48 0,54 0,941 1,767 0,819 0,961 1,450 0,633 1,544 0,564 0,328 0,153 1,60 1,513 1,782 1,30 0,66 1,93 0,791 0,526 0,816 1,886 0,688 1,830 1,487 1,366 1,465 1,173 1,255 1,642 1,263 1,814 0,352 0,700 0,551 1,670 1,816 0,644 0,510 0,814 0,713 0,359 0,95 1,577 1,800 0,878 0,420 1,972 1,841 1,85 0,842 1,816 1,980 1,984 1,939 0,838 0,159 1,547 0,103 1,762 1,353 1,876 1,24 0,987 0,236 1,56 0,453 0,859 1,118 0,604 0,485 1,675 1,753 0,843 0,358 0,300 0,394 1,238 1,862 1,262 1,977 0,575 1,220 0,702 0,222 0,523 0,135 0,424 0,505 1,659 1,907 1,178 0,987 0,342 1,426 1,165 0,875 0,420 1,446 0,685 1,576 1,608 1,893 1,851 1,175 0,224 1,449 0,444 0,580 0,648 0,3 1,898 1,558 1,138 0,433 0,373 0,531 1,595 0,890 0,566 0,920 0,819 0,991 0,368 0,207 0,287 0,601 0,983 1,181 1,817 0,232 0,695 1,355 0,938 0,834 1,995 1,512 0,243 0,497 0,625 0,876 1,884 1,507 1,514 0,445 0,126 0,823 0,344 1,62 0,279 0,717 1,660 0,859 1,339 1,633 1,586 1,93 1,780 0,521 0,288 1,675 0,323 0,986 0,345 0,677 0,275 1,578 1,27 0,9 1,277 0,16 0,89 1,780 1,836 1,800 0,381 1,297 1,330 1,600 1,907 0,972 0,15 0,733 1,811 1,546 1,584 1,142 1,178 1,35 0,602 1,366 0,592 1,483 0,893 0,313 0,299 0,85 1,798 1,614 0,55 1,786 0,148 1,707 0,223 0,875 1,582 1,557 0,839 1,307 0,325 0,978 1,571 1,237 0,957 1,820 1,450 1,510 1,359 1,424 1,546 0,932 1,441 1,167 0,561 0,401 0,952 1,963 1,431 0,266 0,899 0,949 1,844 0,276 0,669 1,96 0,253 0,379 1,61 0,866 0,740 0,639 1,635 0,89 1,34 1,531 1,440 1,635 1,343 0,845 0,233 1,738 0,433 0,628 1,127 0,39 0,705 0,797 1,561 1,523 1,538 0,931 1,89 0,157 0,720 1,364 1,652 0,151 1,249 0,442 0,581 1,804 1,445 1,925 0,22 1,920 0,472 1,826 0,167 1,905 1,508 0,127 0,325 0,439 1,393 0,922 0,199 0,248 1,880 1,206 0,217 1,295 1,214 0,347 1,94 0,677 0,548 1,568 0,14 1,620 0,595 0,917 0,495 1,408 0,724 0,462 1,785 1,859 0,897 0,110 1,899 0,519 0,652 1,134 1,501 1,710 1,581 0,636 1,748 0,480 0,729 0,580 1,326 0,828 1,22 1,566 1,126 1,796 1,558 1,879 0,944 1,897 1,662 1,520 1,103 0,514 0,347 1,475 0,982 1,711 0,501 0,676 0,35 0,163 1,903 1,453 0,474 0,278 1,354 1,181 1,127 0,942 0,225 1,935 0,131 1,603 0,884 0,540 0,182 1,830 0,300 0,85 0,795 1,463 1,46 0,184 0,156 1,232 0,399 1,303 1,864 1,455 0,639 0,557 1,981 1,352 1,10 1,673 1,565 1,711 0,256 1,736 0,458 0,344 0,888 1,934 1,610 1,968 1,235 1,458 1,697 1,39 0,968 1,881 0,639 0,733 0,671 0,442 0,556 1,335 0,39 0,543 1,491 1,710 0,988 1,249 1,817 1,187 1,223 0,677 0,240 0,660 0,306 1,941 0,564 0,328 0,584 1,486 0,898 0,399 0,660 0,728 0,87 1,57 1,972 0,421 1,170 1,323 0,65 1,622 1,922 1,261 0,869 0,402 1,904 0,228 0,498 1,316 1,63 0,86 0,78 0,360 0,970 0,370 0,64 0,340 0,160 0,892 0,502 1,808 1,656 1,819 0,712 0,360 1,833 1,394 1,832 1,852 1,911 0,782 0,196 1,399 1,990 0,567 0,614 0,892 0,494 0,731 0,110 1,455 0,144 0,602 0,789 0,437 1,565 1,764 0,349 0,606 0,183 0,839 1,403 0,938 0,154 0,502 0,909 1,482 1,422 0,690 0,382 1,717 0,56 0,493 1,67 0,861 0,648 1,469 1,589 0,576 1,290 1,70 0,976 1,760 0,770 0,886 0,986 0,770 0,330 0,277 1,848 1,845 1,407 1,497 1,321 1,929 0,839 1,233 1,684 1,292 0,957 0,634 0,735 1,650 0,613 0,326 0,867 0,388 1,591 1,645 0,615 0,5 0,39 0,725 1,962 0,819 0,330 0,942 0,383 1,705 0,774 1,193 0,685 1,316 1,897 0,231 1,692 0,974 1,483 0,625 1,63 1,930 0,82 1,619 0,748 0,515 0,883 0,245 0,139 0,17 0,190 0,493 0,481 1,90 1,207 1,360 0,462 1,616 1,760 1,967 1,891 0,522 1,251 1,304 1,940 1,515 0,449 1,463 1,83 0,393 1,409 0,732 0,73 1,678 1,163 0,896 0,931 0,843 1,975 1,905 0,921 1,461 0,550 0,304 0,164 0,139 1,484 0,364 0,490 0,575 1,663 1,710 1,346 1,38 0,672 1,523 1,973 1,346 0,685 1,74 0,970 0,772 0,676 0,487 0,61 0,375 1,502 1,622 1,823 1,413 0,444 0,180 0,506 1,164 0,348 0,976 1,336 0,762 0,600 0,647 1,777 0,194 1,433 0,746 0,502 0,635 1,690 0,300 0,670 1,211 0,749 0,903 1,48 1,377 1,503 0,6 0,424 0,387 1,584 1,262 1,96 1,335 0,123 0,802 1,359 0,312 1,776 0,775 1,85 1,347 1,551 0,627 1,393 1,789 0,985 0,575 0,320 0,23 0,945 1,680 0,950 0,785 1,970 1,499 0,965 0,649 1,687 0,896 1,40 1,407 0,210 1,930 1,773 1,49 1,306 0,424 0,476 0,311 0,389 1,801 0,345 1,851 1,526 0,781 0,839 1,792 1,824 0,943 0,325 1,577 1,507 0,748 0,92 0,975 1,931 1,106 1,930 1,371 1,506 1,596 0,6 0,344 1,77 0,875 0,856 1,506 1,207 0,310 1,247 1,70 0,382 1,222 0,819 0,600 1,596 0,908 1,841 0,857 0,1 1,601 0,131 1,768 0,631 0,574 0,769 1,883 0,892 0,680 0,902 0,272 1,857 0,488 0,630 0,882 0,984 0,13 1,267 0,311 0,209 0,613 1,789 0,158 1,616 1,441 1,941 1,502 0,118 0,271 1,432 1,813 0,48 0,395 1,361 1,234 1,708 0,650 0,983 0,333 0,339 0,233 1,614 1,375 1,507 1,783 0,424 1,645 1,250 1,531 1,941 1,919 0,456 0,195 0,765 0,389 1,421 0,820 0,953 1,559 0,830 0,656 0,619 1,416 0,983 0,358 1,982 1,247 1,760 1,350 0,483 0,901 1,471 1,197 0,731 1,31 1,613 0,265 0,255 1,185 1,449 0,555 1,232 1,138 1,198 0,614 0,700 0,87 0,189 0,793 1,263 1,420 1,787 1,181 1,79 0,550 1,371 1,221 1,259 1,681 0,512 1,718 1,352 1,201 0,933 0,182 0,536 0,901 1,35 0,457 0,322 1,951 1,735 0,980 1,323 1,351 0,305 1,278 0,716 1,853 0,74 1,267 0,253 0,74 1,668 1,117 0,228 1,102 0,146 1,515 1,262 1,490 0,82 0,558 1,558 1,562 0,27 1,73 0,749 0,625 1,356 1,23 0,131 0,880 1,281 0,258 0,166 0,156 1,703 1,433 0,667 0,892 1,982 0,656 1,509 1,65 1,689 0,945 1,499 0,614 1,954 0,147 1,459 0,469 0,517 1,533 1,232 1,131 1,784 0,721 1,199 1,610 1,232 1,730 0,92 0,373 0,852 0,172 1,896 0,654 0,949 1,749 0,789 1,428 1,613 1,382 1,297 1,412 1,719 1,635 0,520 0,888 0,978 0,267 0,555 1,179 0,58 1,414 1,609 1,424 0,341 0,238 1,472 1,199 1,639 1,912 1,33 1,593 1,586 0,835 0,337 1,474 0,854 0,100 0,506 1,631 0,544 1,89 0,299 1,192 0,374 0,799 0,973 0,471 1,89 0,461 1,379 0,107 1,360 0,831 0,327 1,540 0,521 1,234 1,264 0,481 1,32 1,416 1,408 0,502 0,510 0,768 1,963 0,835 0,472 0,356 0,561 0,591 1,573 0,920 0,176 0,909 1,80 1,884 0,229 0,326 1,492 1,255 1,990 1,722 1,536 0,419 1,878 0,622 1,283 1,213 1,7 0,813 0,688 0,26 0,348 0,927 0,982 0,309 1,797 1,303 0,620 0,376 0,645 1,263 0,443 0,867 0,711 0,141 0,485 1,806 0,971 0,536 0,24 1,838 0,859 0,646 1,495 1,333 0,407 1,414 0,517 0,993 1,143 1,230 0,557 1,941 0,899 0,145 0,26 0,333 0,201 0,163 1,880 0,855 1,957 0,760 1,273 0,334 0,32 1,300 1,652 1,758 1,750 0,237 0,455 0,830 0,536 1,160 0,698 0,199 0,10 0,28 0,639 0,873 1,979 0,141 1,324 1,608 0,734 1,486 0,859 0,121 0,236 0,419 0,229 1,955 1,359 0,862 1,39 0,932 1,151 1,553 0,496 0,28 1,413 1,174 1,638 1,46 1,442 0,904 1,615 1,912 0,773 0,363 1,799 0,118 0,628 0,441 0,521 0,6 0,734 1,830 0,144 0,656 1,462 1,66 1,278 1,890 0,718 0,283 1,993 0,377 0,457 1,225 0,215 0,373 0,823 0,367 1,129 1,720 1,34 1,304 1,708 0,650 0,556 0,290 0,124 1,609 0,744 0,193 1,625 0,905 0,714 0,162 0,866 1,836 1,561 0,283 1,573 0,865 1,329 0,439 0,208 1,585 0,81 1,649 1,717 0,519 1,380 0,765 1,390 0,582 1,175 1,37 0,713 1,934 1,468 0,606 0,335 1,130 0,694 1,357 0,810 0,726 0,426 1,426 1,702 1,148 0,6 1,895 1,964 0,825 1,586 0,843 0,979 0,468 1,426 0,296 1,937 1,547 1,590 0,324 0,477 1,457 1,851 0,693 1,25 0,218 0,230 1,60 0,565 1,723 1,663 0,386 1,917 0,723 1,949 0,704 0,369 0,145 1,698 1,593 1,321 1,226 1,795 1,842 0,177 0,0 1,453 1,129 1,333 1,134 0,157 0,67 1,688 0,751 1,806 1,113 0,718 1,451 0,0 0,835 1,206 1,280 0,97 0,870 1,945 0,529 1,57 0,607 0,289 1,906 0,134 0,647 1,653 1,884 0,586 1,212 1,836 1,928 1,315 0,358 0,1 1,566 1,577 0,997 1,544 0,464 1,571 0,421 0,67 1,378 1,784 0,33 1,746 1,733 1,115 1,662 0,663 1,437 1,377 0,692 0,320 1,878 0,117 0,953 1,804 0,611 1,594 1,107 0,493 1,516 1,136 1,922 0,520 0,870 0,577 1,88 1,721 1,21 0,758 1,250 0,407 0,838 0,863 1,222 0,234 1,722 1,867 0,806 1,352 0,786 1,999 0,240 0,968 1,333 0,748 1,371 1,111 1,910 0,807 1,704 1,508 0,598 1,75 1,354 0,530 1,218 1,202 0,553 0,847 1,851 0,762 0,311 0,680 0,371 0,446 0,2 1,487 1,719 0,392 0,203 1,303 1,879 1,790 0,669 0,430 1,327 0,707 0,227 1,201 0,540 0,972 0,324 1,511 1,458 0,745 0,220 0,85 0,583 0,636 1,350 1,8 0,137 1,290 1,441 1,161 1,163 0,376 0,349 0,600 1,286 0,350 0,20 0,292 1,329 1,195 0,606 0,766 1,23 1,9 1,828 0,659 1,379 0,816 1,871 0,115 1,980 0,788 0,106 1,533 0,838 0,743 0,983 1,612 0,335 1,539 0,274 0,699 0,438 1,345 0,949 1,41 0,630 1,132 0,267 1,422 0,545 0,383 0,688 1,690 1,955 1,293 1,228 0,952 0,424 0,690 1,273 1,290 1,352 1,631 0,494 0,922 1,259 1,329 0,131 1,916 0,23 1,964 0,284 0,403 0,242 0,936 1,157 0,9 0,830 1,248 0,70 1,565 1,39 1,178 1,717 1,819 0,816 0,427 0,544 0,516 0,535 1,831 0,15 0,88 1,110 1,882 1,983 0,964 1,79 1,385 1,395 1,919 1,93 1,844 0,848 0,990 0,75 0,75 1,727 0,343 0,488 1,21 1,774 0,347 1,69 1,493 1,205 1,543 1,240 1,936 1,143 0,647 1,706 1,366 1,729 1,728 0,848 0,572 1,329 1,198 1,517 1,991 0,925 1,606 1,323 0,274 1,946 0,971 0,466 0,292 1,817 0,969 1,593 0,732 0,488 1,912 1,176 0,283 0,664 1,224 1,877 0,325 1,110 1,204 1,255 1,549 1,810 1,105 0,80 0,419 1,459 1,916 1,548 0,517 0,772 0,737 0,848 1,39 0,935 0,180 0,808 1,188 0,47 1,355 0,279 0,717 1,90 1,251 0,357 0,658 1,859 0,249 1,10 0,773 0,979 0,867 1,266 1,338 0,450 0,396 1,841 0,734 1,346 1,232 1,362 1,576 0,435 1,263 0,169 0,668 0,762 0,114 0,131 1,208 1,300 1,338 1,335 0,698 1,260 0,744 1,37 0,604 1,83 0,262 0,869 0,764 0,769 1,484 1,12 1,820 1,846 0,275 1,29 1,687 1,907 0,726 1,978 0,75 1,670 1,654 1,270 0,281 1,846 1,156 1,602 1,232 0,357 1,377 1,151 1,202 0,26 1,270 1,22 1,380 0,942 0,92 1,685 1,554 1,238 0,103 1,631 1,427 1,902 1,56 0,181 0,684 0,251 0,866 1,400 0,846 1,848 1,597 1,384 0,495 1,311 0,618 1,884 0,369 0,812 0,295 1,518 0,636 1,577 0,528 1,788 0,389 0,9 0,324 1,800 0,998 1,558 1,397 0,605 1,960 0,820 0,524 0,578 0,187 0,926 1,229 1,405 0,29 1,43 0,279 1,777 0,349 0,629 0,700 1,513 0,45 0,446 1,955 1,901 0,311 1,884 0,747 0,591 0,927 1,807 0,671 1,518 1,757 0,30 0,148 1,279 0,867 0,458 0,717 0,282 1,894 0,716 0,815 0,427 0,488 1,860 1,635 0,141 0,662 0,84 0,670 0,374 0,271 1,248 0,78 0,786 0,115 1,953 0,410 0,777 1,622 0,317 0,925 0,715 1,777 0,616 0,595 0,247 1,964 0,971 1,883 0,442 0,231 1,291 1,585 0,440 1,486 0,587 1,609 0,432 1,7 1,71 1,137 0,220 1,5 1,367 0,479 0,827 1,842 0,14 1,302 0,977 1,450 1,448 0,95 0,119 0,784 1,671 1,132 1,154 0,218 0,246 0,602 0,549 1,935 1,440 1,315 0,551 0,516 1,419 1,98 1,726 0,984 1,961 0,850 1,128 0,348 1,675 0,627 1,491 1,889 0,292 1,162 1,439 0,998 0,971 1,835 0,717 0,108 0,909 0,225 1,738 0,661 0,960 1,801 0,458 1,472 1,72 1,720 1,740 1,539 1,679 0,573 1,896 0,51 1,797 1,651 0,135 1,443 0,576 1,505 0,511 0,558 0,387 0,588 0,777 1,623 0,221 0,646 1,12 1,587 0,148 1,987 1,286 0,627 1,574 1,885 1,761 0,212 0,242 1,665 1,873 0,139 0,187 0,869 1,378 0,108 1,192 0,839 0,459 0,421 1,821 0,411 0,811 0,246 1,660 1,741 0,645 1,402 1,392 0,515 1,627 0,692 0,907 1,408 1,290 0,813 1,705 1,876 1,814 0,249 1,932 1,147 0,483 1,500 0,248 0,623 1,33 1,600 0,722 1,121 0,632 1,966 1,755 0,213 0,282 0,839 1,441 0,45 1,688 1,671 1,264 1,213 0,845 1,532 1,276 0,928 0,614 0,177 0,331 1,823 1,795 0,791 0,477 1,103 0,37 0,892 0,944 1,131 0,381 1,659 1,466 1,451 1,21 0,725 1,961 1,978 0,508 1,402 1,789 0,777 1,666 0,108 0,901 1,903 1,639 0,806 0,970 0,554 1,371 0,466 1,357 1,871 0,969 1,833 1,854 0,133 1,960 0,380 0,268 0,423 0,763 1,445 0,307 0,443 1,401 0,815 1,957 0,982 0,225 0,960 1,531 0,359 0,346 0,82 0,160 1,185 0,76 1,294 1,823 0,641 1,895 0,906 0,460 0,962 1,30 0,157 1,439 0,397 1,234 1,82 1,114 0,302 1,677 1,646 0,119 0,932 1,652 1,848 1,388 0,683 1,217 1,840 1,771 1,708 0,468 0,900 1,243 1,103 1,857 0,910 1,781 1,811 1,443 0,437 0,678 0,885 0,873 1,790 1,775 0,249 1,15 1,858 1,726 1,870 0,479 0,295 1,472 1,987 0,390 0,265 1,636 0,109 1,958 0,361 0,187 1,707 1,370 0,429 0,858 0,109 0,5 1,81 1,712 0,621 1,381 1,949 0,48 1,227 1,958 0,622 1,253 1,136 0,661 0,730 0,14 1,165 1,659 1,790 1,357 1,301 0,905 0,17 0,131 1,106 1,826 0,49 1,952 1,11 1,427 0,232 1,957 0,925 1,803 0,681 1,454 0,894 0,202 1,258 1,683 0,40 1,151 0,253 1,858 1,217 0,401 1,387 1,981 1,453 1,806 0,759 0,268 0,750 0,558 1,360 1,909 1,31 0,123 1,342 0,217 0,374 1,865 0,665 0,515 1,313 1,715 1,161 0,775 0,770 0,988 1,196 0,581 1,937 0,509 0,838 0,849 1,703 1,447 1,911 0,223 0,608 1,880 1,133 0,437 0,984 0,991 1,144 1,342 1,414 1,859 0,884 1,676 0,107 1,370 1,351 1,676 1,477 1,665 1,600 1,609 0,62 0,368 0,951 0,589 1,581 1,539 0,542 0,55 0,932 0,465 1,365 0,253 1,332 0,944 1,901 1,785 0,220 0,587 1,666 0,750 1,346 1,997 1,523 1,927 1,758 1,988 1,586 1,345 0,561 0,865 1,285 1,293 0,960 1,230 0,123 0,145 0,107 0,619 1,418 0,629 0,35 1,15 1,354 1,962 1,622 1,178 1,363 0,971 0,610 0,376 1,258 0,872 0,166 0,675 0,793 1,627 0,729 1,255 0,179 0,437 0,72 1,140 0,823 1,462 0,607 0,375 1,149 1,650 1,725 0,930 1,222 0,482 0,129 0,499 1,817 0,810 0,381 1,578 1,440 1,5 0,60 1,70 0,275 1,758 1,237 1,161 0,150 1,271 1,508 0,750 1,833 1,840 0,695 1,638 0,108 1,867 0,654 0,719 1,337 0,930 1,535 0,122 1,805 1,666 1,563 0,45 0,65 1,440 1,389 1,152 0,407 1,739 0,102 1,457 0,600 1,579 0,987 0,599 1,626 1,951 1,615 0,959 0,852 0,152 1,753 0,479 0,564 0,599 0,895 0,193 0,985 0,968 1,68 0,511 0,276 0,6 1,530 1,178 0,908 1,706 0,343 0,974 1,989 1,656 1,899 1,734 1,559 1,879 1,842 0,898 1,330 1,967 1,538 0,916 0,633 0,34 1,649 0,742 1,462 1,529 0,760 1,46 0,489 0,165 0,305 1,757 0,103 0,679 1,623 0,359 0,610 1,704 0,811 0,432 1,831 0,808 0,413 1,662 1,984 0,42 0,332 0,498 0,580 0,536 1,366 0,347 1,448 1,165 0,988 0,608 1,892 1,156 1,930 0,839 1,717 0,232 0,364 1,419 0,447 0,661 0,975 0,468 0,572 1,378 1,878 1,505 0,919 0,939 0,271 0,894 0,498 1,217 0,247 1,198 1,873 1,569 1,196 1,772 0,425 0,32 1,48 0,174 1,710 1,662 1,722 0,142 0,19 1,484 1,966 1,25 1,86 1,564 1,616 0,256 0,609 1,901 1,257 1,690 0,295 0,229 1,903 0,460 1,628 0,830 0,920 0,411 1,489 1,434 0,288 1,601 1,885 0,74 0,438 0,684 0,569 1,678 1,211 0,828 0,703 0,635 1,738 1,229 0,894 0,167 1,819 1,483 0,124 0,716 1,720 0,264 1,253 0,866 0,809 1,808 0,561 1,250 0,154 0,680 1,475 1,350 0,482 1,72 1,601 0,825 1,247 0,206 0,872 1,709 0,671 0,702 0,769 1,695 1,167 1,526 1,714 1,835 1,237 1,603 0,68 0,288 0,959 1,104 0,48 1,182 1,198 1,693 1,310 1,173 1,215 1,84 0,180 0,626 0,380 0,250 1,463 1,61 0,98 1,309 0,260 1,901 0,462 0,974 0,371 1,948 0,373 1,338 0,860 1,106 1,828 0,1 0,326 1,322 0,739 0,450 1,112 0,388 1,41 1,122 0,759 0,630 0,32 1,686 0,810 0,92 0,75 1,998 0,142 0,780 0,597 0,343 1,83 1,60 1,920 1,549 1,941 1,236 1,959 0,508 1,379 0,12 0,62 0,525 0,34 0,423 1,438 1,300 0,163 0,839 0,227 0,180 0,292 1,818 0,416 1,686 0,631 0,760 0,496 0,344 1,530 0,996 1,70 0,928 1,287 1,755 0,139 0,434 0,268 1,739 1,986 1,96 1,948 1,267 0,841 0,329 1,329 0,507 1,948 0,659 0,335 0,507 0,722 1,57 0,188 0,346 1,456 1,957 1,624 1,309 1,564 1,319 0,824 1,678 0,573 1,520 1,974 0,595 1,787 1,317 1,970 0,229 1,770 0,788 0,371 1,691 1,600 0,606 0,177 1,741 1,979 0,165 1,524 0,315 1,850 0,21 0,58 0,529 1,697 1,302 0,194 0,386 1,207 0,146 1,774 1,677 0,647 0,888 0,692 0,287 1,271 1,954 1,556 1,157 0,441 1,83 0,445 0,901 1,562 0,976 1,794 0,604 0,537 0,333 0,127 0,384 1,326 1,106 1,752 1,444 0,277 0,288 0,465 0,94 0,50 1,39 0,664 1,670 1,507 0,379 0,828 0,340 0,157 1,740 0,337 1,660 1,40 0,99 0,404 1,418 0,510 0,512 1,368 0,391 1,845 0,47 0,936 1,657 0,864 0,203 1,491 1,355 0,292 0,314 1,562 0,70 1,503 0,501 1,118 1,371 0,61 0,255 1,199 0,586 0,221 0,203 0,847 0,532 0,255 1,51 0,897 0,989 1,568 0,863 1,870 1,271 0,911 0,255 1,695 1,118 1,305 0,82 1,330 1,843 1,204 0,768 0,455 0,345 0,608 1,195 0,468 1,614 1,647 1,352 1,329 0,185 0,899 1,862 1,327 0,186 0,532 1,65 0,57 1,522 0,308 0,136 0,607 1,917 1,311 1,557 1,80 1,229 1,203 0,654 0,373 0,457 0,678 1,173 0,577 1,992 1,197 1,993 0,440 0,22 0,845 1,145 1,136 0,830 0,584 1,103 1,443 1,13 0,720 0,708 0,266 1,548 0,225 1,465 0,944 0,534 0,330 1,381 1,759 1,884 0,26 0,791 0,934 0,559 0,811 0,448 0,996 0,379 1,702 1,273 1,361 0,838 0,304 1,559 1,971 0,238 0,355 1,64 0,636 1,101 0,144 0,791 1,756 0,711 1,89 0,469 0,924 0,263 0,94 0,201 1,994 1,80 0,733 0,282 0,549 1,971 1,915 0,52 0,924 1,803 0,213 1,680 1,658 1,413 0,371 0,267 0,840 1,797 0,724 0,640 1,175 0,213 1,898 0,550 0,506 0,249 0,319 1,342 0,6 1,292 0,375 0,534 0,351 0,740 1,581 1,295 0,860 1,852 0,864 0,479 0,850 1,355 1,672 0,10 0,158 0,74 1,932 1,526 0,646 1,424 1,468 1,89 1,278 0,772 1,495 1,60 1,735 1,564 1,319 1,6 0,90 0,569 1,449 1,189 0,209 0,607 0,946 0,969 1,198 0,61 1,971 0,642 1,550 0,940 0,217 1,442 1,854 0,848 0,913 0,899 0,526 1,217 0,378 0,154 0,553 1,82 1,798 1,734 0,179 0,194 1,755 0,241 1,413 0,17 1,491 1,768 0,282 1,200 0,61 0,369 0,440 0,547 1,603 0,832 1,131 1,184 0,555 0,523 0,19 0,643 0,123 1,859 0,66 0,46 0,404 0,222 0,915 1,750 1,580 0,114 0,743 0,713 1,669 1,49 0,71 0,705 1,781 1,18 1,748 0,980 0,188 1,570 0,251 1,6 1,460 1,342 1,291 1,449 1,611 1,443 0,346 1,619 0,585 0,688 1,2 0,262 1,713 1,709 1,77 0,146 1,295 1,352 0,571 1,963 1,865 0,62 1,724 0,542 0,572 1,618 0,37 0,502 1,104 1,553 1,235 0,92 1,931 0,226 0,458 1,737 0,177 1,467 1,615 0,684 0,151 0,524 0,874 0,2 1,435 1,425 1,735 0,543 1,492 0,605 1,108 1,462 0,269 0,894 0,862 0,687 0,657 1,291 1,258 0,474 0,775 1,789 1,492 1,272 0,878 1,319 1,913 0,820 0,278 1,954 0,410 1,487 1,64 0,919 0,468 1,42 0,758 0,235 1,501 1,705 1,436 1,788 1,760 0,561 0,398 1,625 0,976 0,94 1,978 0,408 0,328 1,362 0,772 0,556 0,285 1,73 0,924 0,955 1,795 1,450 1,429 0,97 0,833 0,194 0,112 0,476 0,507 0,706 0,617 1,294 0,485 1,212 1,748 0,742 1,15 0,799 1,915 0,870 1,811 0,493 1,552 1,866 1,519 0,258 0,812 0,561 1,215 1,498 0,781 1,101 1,231 1,166 1,678 1,303 0,760 1,190 1,56 1,132 0,154 1,819 1,612 1,159 0,271 1,630 1,628 1,353 1,494 0,634 1,763 1,847 0,826 1,210 0,776 1,701 0,732 1,580 1,295 1,466 1,846 0,84 1,664 1,541 1,832 0,920 1,519 1,319 1,430 1,129 0,35 0,537 0,596 1,354 0,86 0,681 0,332 1,369 0,586 1,876 0,811 1,698 1,437 0,108 0,19 1,312 0,166 1,377 0,393 0,182 0,258 0,583 1,660 1,758 0,286 0,169 1,255 1,688 1,357 0,221 1,481 1,458 1,775 1,988 0,749 1,459 1,646 1,318 1,651 1,404 0,97 0,565 1,398 0,850 0,429 1,768 1,879 0,331 0,910 1,317 1,2 1,798 0,967 0,413 1,261 0,551 0,63 0,485 1,858 1,398 0,393 1,715 1,596 1,209 1,67 1,261 1,342 1,172 0,604 0,956 1,675 1,261 1,970 1,18 1,612 1,783 0,274 0,249 1,481 1,939 0,12 1,508 1,226 1,125 0,201 1,932 0,802 0,417 0,102 1,200 0,401 1,674 0,554 0,404 0,967 0,24 1,371 0,523 0,499 1,337 0,451 1,794 0,41 0,443 1,456 0,946 0,905 1,716 1,107 0,75 1,759 0,444 1,113 0,5 0,349 0,964 1,41 1,672 1,313 0,154 0,761 1,665 1,386 0,958 1,821 0,119 0,717 0,279 0,776 1,551 1,124 1,434 1,339 1,951 0,263 1,908 0,716 0,779 0,820 1,534 0,532 0,241 0,522 1,799 0,905 1,383 1,461 0,159 0,535 0,901 1,538 0,544 0,278 1,844 0,733 0,106 1,950 1,919 0,619 0,796 0,215 1,586 0,996 1,266 0,556 1,149 1,166 0,470 1,825 1,704 0,792 0,256 1,495 0,589 1,419 0,314 1,320 1,664 1,333 0,395 0,293 1,975 0,612 1,647 0,941 0,405 0,505 0,465 0,352 1,400 1,906 0,287 1,45 1,1 0,817 0,31 0,557 1,633 0,974 1,31 1,24 0,256 0,715 0,283 1,910 0,943 0,821 0,572 1,448 0,445 0,696 0,46 0,928 1,185 1,551 1,738 0,159 0,706 1,493 0,411 1,104 0,44 0,944 0,976 1,794 0,56 0,763 0,863 1,337 1,326 1,926 1,486 0,898 1,79 1,894 1,293 0,779 1,727 1,455 1,519 1,306 0,801 1,625 0,728 0,640 0,736 1,360 0,588 0,753 1,277 1,831 1,488 0,141 0,0 1,701 1,875 1,868 0,847 0,372 0,96 1,726 0,189 1,975 1,682 1,707 0,320 1,290 1,722 1,467 1,307 0,60 0,126 1,783 1,832 0,60 1,520 0,73 0,260 0,933 1,87 0,988 0,664 1,262 1,743 0,752 0,921 1,453 1,673 1,167 1,195 1,461 1,675 0,680 1,724 0,838 0,593 0,46 1,985 1,659 1,701 1,717 1,92 1,308 0,465 0,521 1,926 0,337 1,660 1,801 1,835 1,872 1,993 0,111 1,774 0,469 0,319 1,755 0,291 1,358 0,41 1,287 0,314 0,685 1,3 1,682 0,635 0,703 0,887 0,769 0,505 1,603 0,829 0,644 1,331 1,698 0,869 0,853 0,57 1,180 1,916 0,729 0,77 1,982 1,218 1,573 0,364 1,15 1,72 1,664 1,607 1,31 1,529 1,692 1,939 1,159 0,170 0,716 1,82 1,361 1,190 1,138 0,358 1,774 0,799 1,579 0,462 1,897 0,564 0,766 1,196 1,74 0,49 1,869 1,548 0,226 0,457 0,662 0,664 0,605 0,240 0,98 0,903 0,599 0,98 1,896 0,476 1,606 0,679 1,302 1,688 0,729 1,452 0,995 0,730 1,602 0,11 1,177 0,709 1,920 0,909 0,315 1,966 1,919 0,223 0,865 1,92 1,373 1,599 1,854 0,465 0,625 0,718 0,634 1,223 1,483 1,258 0,917 0,30 0,122 0,487 0,612 0,497 1,655 1,892 0,917 1,777 1,23 0,915 0,98 0,659 1,670 0,275 0,495 0,750 1,937 0,730 1,578 0,150 0,720 1,238 1,236 1,896 0,345 0,817 0,873 0,557 0,577 0,96 0,528 0,77 1,254 0,796 0,683 0,291 0,217 0,885 1,684 0,619 0,507 0,790 0,396 0,596 1,615 1,347 0,831 0,773 0,367 1,650 0,651 1,941 0,394 0,822 1,512 1,810 1,196 0,164 0,748 0,866 1,635 0,133 1,643 0,805 0,299 0,538 0,387 1,906 1,192 1,411 1,517 1,256 0,877 1,64 1,943 0,145 0,958 0,57 0,99 1,728 0,951 1,688 1,792 1,965 1,145 0,928 0,940 1,686 0,288 0,222 0,159 0,806 0,917 1,394 1,733 0,163 1,773 0,302 0,567 0,994 1,757 1,676 0,457 1,588 0,644 1,184 1,220 0,686 0,403 1,219 1,192 0,301 0,14 1,846 0,467 0,172 0,198 1,653 0,86 1,902 0,472 0,746 0,75 0,240 1,288 0,410 0,220 1,887 0,54 0,463 1,383 1,766 0,344 0,274 1,759 0,367 1,515 0,459 0,49 1,611 1,746 1,315 1,119 0,370 1,578 0,543 1,583 0,488 1,361 1,883 0,952 0,85 1,752 1,911 1,388 0,85 0,514 1,534 0,734 0,939 0,738 0,722 1,588 0,611 1,511 0,453 0,508 0,76 1,331 1,255 1,941 0,736 0,932 0,928 1,651 1,61 1,65 1,898 1,75 1,369 1,311 0,312 1,916 1,480 0,123 0,359 0,966 0,725 0,146 1,378 1,410 1,209 0,700 0,131 0,294 0,592 1,638 0,548 1,737 0,66 1,82 1,947 0,120 1,339 0,496 0,196 1,142 0,248 0,836 0,53 1,811 0,528 1,357 1,574 0,803 0,749 0,386 1,546 0,841 0,32 1,222 0,439 1,952 1,732 0,439 1,641 1,893 0,111 0,596 1,821 0,919 1,655 0,965 0,184 0,570 1,54 1,690 1,691 0,87 1,16 1,318 0,99 0,653 1,3 1,832 0,449 1,879 1,319 1,360 1,427 1,105 1,779 1,476 0,961 1,671 1,521 1,279 0,813 0,441 0,347 1,264 1,735 1,582 0,126 1,203 1,60 1,954 1,941 0,236 0,377 0,330 1,754 1,522 1,4 1,54 1,768 1,671 1,213 1,534 0,13 0,900 1,476 0,123 1,797 0,339 1,112 1,565 1,340 0,21 0,186 0,458 1,939 0,66 0,782 0,808 0,863 1,84 1,198 1,96 0,858 0,221 1,921 0,782 1,559 1,270 1,134 1,325 1,543 0,704 0,29 1,238 0,813 1,205 0,735 1,623 1,915 0,257 0,283 0,343 1,278 1,230 0,462 0,92 0,805 0,171 1,745 1,700 0,185 0,724 0,401 1,360 0,942 1,116 0,879 0,91 0,6 1,607 1,826 1,654 0,861 0,871 1,906 1,831 1,584 1,438 0,187 1,702 0,537 1,700 0,4 0,534 0,811 0,554 0,224 0,478 0,326 0,888 0,61 0,42 1,199 1,655 0,322 1,621 1,86 1,436 1,27 0,911 1,499 1,293 0,564 1,310 0,746 0,409 0,110 1,818 0,842 0,76 1,42 1,720 1,996 1,47 1,183 0,497 0,431 0,233 0,874 1,990 1,719 0,702 0,55 0,146 0,547 0,40 1,835 0,780 0,994 0,619 1,651 0,533 1,949 1,849 0,168 0,550 0,446 1,56 0,52 1,477 0,910 0,695 1,331 0,459 0,426 1,750 0,798 0,67 1,696 1,141 0,994 1,576 0,633 0,168 1,135 0,298 0,131 1,568 0,331 0,721 1,28 0,703 1,372 0,100 0,994 0,271 1,521 1,433 1,748 0,518 1,837 0,621 1,104 0,809 1,152 0,184 1,639 0,627 0,938 0,962 1,281 1,910 0,326 1,637 0,723 1,745 0,921 1,680 1,60 1,780 0,51 1,177 0,679 0,998 0,463 0,739 0,615 0,124 1,465 0,844 0,695 1,890 1,522 1,973 0,857 1,22 0,780 0,940 1,508 1,799 1,787 1,113 0,546 1,642 0,97 0,923 1,547 1,56 1,98 0,927 1,713 0,537 1,108 0,647 0,737 0,990 1,91 0,743 1,264 1,138 1,570 0,326 0,555 0,728 1,326 0,587 1,705 0,926 0,502 0,250 1,92 0,833 0,801 0,393 0,638 0,127 1,834 0,678 1,352 1,510 0,68 0,505 0,293 0,108 0,733 0,47 1,791 1,290 1,890 0,252 1,576 0,720 1,805 0,561 0,388 1,752 0,683 1,622 1,627 1,97 1,528 0,155 0,678 0,690 1,633 1,585 0,369 0,917 1,663 0,880 1,140 0,118 0,247 0,661 0,263 1,897 0,523 0,225 0,717 0,463 0,116 1,186 0,502 1,687 1,88 1,886 1,519 1,325 0,902 1,943 1,893 0,565 0,813 0,572 1,229 0,348 0,370 1,268 0,179 1,270 0,843 0,977 0,334 0,841 0,387 0,998 0,285 1,37 0,820 0,964 1,224 0,252 0,44 1,506 0,434 0,360 1,271 0,684 1,636 1,138 1,799 1,384 0,558 0,85 1,157 1,506 0,418 0,279 1,644 1,630 0,719 0,861 1,434 1,207 1,135 1,970 1,997 0,338 0,682 0,334 0,685 1,702 0,815 1,788 0,877 0,587 0,136 1,492 1,555 1,11 0,354 1,561 1,586 0,131 1,510 1,474 1,145 1,820 0,439 1,162 1,766 0,282 1,2 0,165 1,576 1,448 0,558 1,976 1,487 0,717 1,877 0,657 0,931 0,318 0,97 1,58 0,732 1,266 0,66 1,207 0,682 1,202 0,153 1,337 0,797 0,124 1,160 0,942 0,127 1,191 1,992 1,672 1,961 0,0 0,200 0,771 0,605 1,567 0,202 1,590 0,308 1,836 0,740 0,315 1,894 0,839 0,128 0,500 1,101 1,638 1,361 0,457 0,492 0,228 1,984 1,218 0,736 1,165 1,334 0,265 1,706 1,373 1,686 0,8 1,611 0,52 1,478 1,760 0,312 1,776 1,778 1,601 0,435 0,437 0,127 0,38 1,391 0,299 0,476 1,905 0,950 0,659 1,59 1,279 0,330 1,98 1,135 1,408 0,761 0,660 0,186 0,555 1,581 0,913 1,697 1,926 1,488 0,319 1,873 1,600 1,257 0,777 1,609 1,468 0,492 1,511 1,607 1,561 1,45 0,747 1,774 0,290 1,977 1,987 1,22 0,852 1,50 1,537 1,819 0,650 0,279 1,223 0,238 1,438 0,554 1,380 1,828 1,319 0,919 0,137 0,205 1,140 1,234 1,607 0,777 1,592 1,699 0,291 0,635 1,527 0,873 0,548 1,609 1,115 0,648 1,229 0,331 0,413 1,281 0,312 0,65 0,122 0,434 0,970 1,307 1,464 1,875 0,119 1,550 0,650 1,177 0,888 0,407 0,19 0,11 1,993 1,76 0,515 1,896 0,924 1,408 0,513 1,698 1,840 1,849 0,694 0,756 0,221 0,810 1,927 0,870 0,986 0,230 1,514 0,639 0,47 0,911 1,628 0,192 0,593 1,238 1,616 1,879 1,847 0,423 0,928 0,603 0,465 1,824 0,634 1,252 0,681 0,160 0,777 1,824 1,320 1,508 0,385 0,244 0,46 0,111 0,374 1,290 1,470 0,663 1,989 1,670 0,192 1,104 1,700 1,837 0,457 1,60 0,579 1,547 1,218 0,50 0,88 1,816 0,413 1,62 1,130 1,818 1,479 1,633 0,866 1,787 0,669 1,944 1,537 0,635 0,642 0,66 1,47 0,924 1,923 1,213 0,955 1,85 0,991 0,153 0,979 1,463 0,462 0,192 0,123 0,549 0,930 1,435 0,236 1,758 1,202 0,141 1,486 1,296 1,633 1,798 1,959 1,501 0,395 1,136 0,493 1,47 0,625 0,972 0,182 1,404 0,0 1,875 0,538 0,142 1,232 1,720 0,602 0,532 1,45 1,376 0,198 1,302 0,866 0,275 0,307 0,869 1,839 0,553 0,935 0,926 0,821 1,82 1,190 0,500 1,426 0,770 1,384 1,474 1,845 1,370 0,231 1,431 1,319 1,327 1,127 1,80 0,383 0,550 0,534 1,391 0,789 0,949 0,506 0,523 0,979 1,483 0,154 1,106 1,618 1,50 0,628 0,745 1,793 0,326 0,800 0,497 1,972 1,939 1,570 0,638 1,708 1,953 0,590 0,480 1,721 0,895 1,620 0,390 1,800 1,605 1,356 0,438 1,147 1,980 1,601 1,954 1,58 0,419 0,433 0,21 1,853 0,290 1,821 1,844 1,32 1,57 1,749 1,217 0,53 1,275 0,352 0,969 1,677 0,957 1,524 0,481 0,849 1,997 1,710 1,390 0,643 1,917 0,182 0,411 1,427 0,222 1,608 0,132 1,376 1,403 1,445 1,423 1,316 1,211 0,812 0,93 1,777 1,364 0,994 1,701 0,656 1,60 1,831 1,724 0,387 0,581 0,602 0,592 0,347 0,314 1,524 1,855 0,769 0,243 0,213 0,932 1,983 0,798 1,193 0,775 1,310 0,856 1,679 0,126 0,112 0,392 0,916 0,909 0,374 0,727 1,48 0,105 1,282 0,531 0,841 1,266 1,406 1,387 0,821 0,857 1,52 1,114 0,981 1,374 1,23 0,304 0,873 1,605 1,808 1,460 0,294 0,487 0,91 1,971 0,927 1,418 1,407 0,238 1,797 1,907 1,238 0,69 1,138 0,993 1,391 1,595 0,358 1,307 0,95 0,659 1,582 1,862 0,894 0,882 0,553 0,496 1,311 1,8 1,558 1,590 1,755 0,911 1,195 1,748 1,555 1,7 0,281 0,841 0,673 1,942 0,732 0,753 1,945 0,728 0,809 0,437 0,84 0,717 1,92 0,304 0,159 1,311 1,737 0,120 1,303 0,890 0,114 0,328 0,256 1,886 1,34 1,646 1,724 1,267 0,132 1,993 1,583 0,814 1,158 1,61 1,15 0,699 0,976 0,223 1,668 0,712 1,589 0,570 0,80 0,900 0,829 0,831 1,724 0,343 1,338 0,989 1,809 1,275 1,601 0,110 1,182 0,296 1,516 0,398 1,638 0,962 0,795 0,450 1,601 0,814 0,291 1,251 1,288 1,95 0,195 1,549 1,458 0,667 1,247 0,19 0,536 1,289 1,424 0,617 0,888 1,759 1,423 1,805 1,322 1,987 0,48 0,511 1,815 1,22 0,224 0,517 1,485 1,612 1,303 0,117 1,479 0,329 1,563 1,889 1,367 0,214 1,259 0,819 1,926 0,381 1,766 0,854 1,534 1,413 1,774 0,19 1,303 1,791 0,320 0,621 0,404 0,338 0,289 0,817 1,386 1,434 1,214 1,347 0,274 0,992 1,88 0,349 0,194 1,43 0,909 1,788 1,424 1,100 0,475 0,204 1,62 0,784 1,96 0,767 0,508 1,18 0,93 1,499 1,115 0,145 0,712 1,78 0,714 1,668 1,322 0,55 1,52 1,526 0,746 1,499 0,493 0,80 0,858 1,210 0,532 0,474 1,469 1,445 1,676 1,160 0,780 1,388 0,576 0,885 0,264 1,955 1,832 1,606 0,7 1,291 1,935 0,99 0,0 0,348 1,674 1,359 0,106 0,778 1,217 1,847 0,952 1,352 1,567 1,141 1,637 1,225 1,513 1,766 1,625 1,307 0,61 1,189 1,177 0,936 0,266 1,293 1,80 0,713 1,959 1,669 1,716 1,376 1,522 0,601 0,201 0,55 1,538 0,609 1,399 1,379 1,957 0,856 1,133 1,184 0,911 1,547 1,697 0,458 0,757 0,307 1,267 1,305 1,258 0,498 1,945 0,939 1,584 1,531 0,68 1,484 1,84 0,201 0,220 1,270 0,326 0,386 0,578 1,882 1,32 0,161 0,268 0,978 0,934 1,579 1,590 1,314 1,852 0,873 0,737 0,404 1,860 1,104 0,978 0,190 1,843 1,731 1,54 0,145 1,175 1,284 0,49 1,673 1,543 1,230 0,870 0,938 1,199 1,422 1,60 0,79 1,189 1,750 1,94 0,101 1,724 1,728 0,265 1,845 1,930 1,188 0,206 0,472 1,2 0,887 0,628 0,429 0,493 1,49 0,279 1,668 1,506 0,381 0,118 1,188 1,290 1,561 0,618 0,486 0,110 0,73 1,453 0,22 0,290 1,194 0,893 1,932 0,307 1,840 0,125 0,929 0,867 0,279 1,774 1,718 1,23 0,823 1,796 0,590 0,274 0,348 0,584 1,222 1,329 0,578 1,227 1,151 1,309 1,73 0,333 1,327 0,552 0,866 1,174 0,936 1,474 1,76 1,334 1,356 0,462 1,670 0,747 1,254 0,64 0,538 0,166 1,181 0,575 0,410 0,564 0,297 0,885 0,278 0,173 0,679 0,271 0,844 1,368 1,78 1,861 1,4 1,1 0,8 0,427 0,365 0,32 1,703 1,423 0,272 0,687 1,506 1,298 0,321 1,555 1,833 1,976 1,794 0,500 1,524 1,908 0,346 0,999 0,437 1,382 0,307 1,22 0,808 1,133 1,176 1,282 0,677 0,644 0,349 1,706 0,814 1,722 0,339 1,898 1,473 0,985 1,589 1,183 0,34 1,460 0,311 1,523 1,330 0,311 1,889 1,620 1,830 1,955 1,914 0,996 0,242 0,766 1,543 1,15 0,463 1,33 1,914 1,192 1,600 0,410 1,994 1,14 1,402 1,259 0,837 0,373 1,673 1,988 1,330 1,632 0,364 1,402 0,829 1,191 0,441 1,308 1,928 1,946 0,199 0,665 1,546 1,528 1,847 1,271 0,793 0,289 0,565 1,775 1,345 0,820 0,517 1,603 1,271 1,361 0,354 0,748 1,229 0,668 0,991 0,3 0,569 0,549 0,670 0,412 0,307 1,753 1,703 1,780 0,663 0,576 0,925 1,836 1,381 1,505 1,546 1,860 1,864 1,465 0,798 0,590 1,97 0,453 1,518 0,79 0,166 1,719 0,300 1,836 1,579 0,525 1,618 0,282 1,950 1,184 0,194 0,115 1,100 0,877 1,55 1,637 0,883 0,75 1,399 0,943 0,458 0,7 1,637 1,871 1,127 1,455 0,985 0,824 0,799 1,184 1,562 1,372 0,11 0,398 1,851 1,615 0,213 1,146 0,65 0,180 1,945 1,546 1,764 0,700 1,751 1,695 1,516 1,559 1,517 1,876 1,172 0,999 0,993 1,434 1,235 1,596 0,285 1,209 0,212 0,788 0,707 0,178 1,494 1,890 1,153 1,385 1,952 0,688 0,398 0,877 1,431 0,798 1,162 1,303 1,354 0,714 1,523 0,805 0,208 0,554 0,137 1,352 1,822 0,639 1,958 0,36 1,747 1,630 0,829 0,992 0,834 0,585 1,89 1,314 0,24 0,656 1,266 0,936 0,37 0,835 1,658 1,882 0,42 0,459 0,693 0,915 1,19 1,351 1,188 1,812 0,577 0,471 0,178 0,87 0,370 0,193 0,958 0,589 1,462 0,248 1,771 1,198 1,863 0,307 1,822 0,109 1,232 0,25 0,227 1,760 1,882 0,279 1,360 0,139 0,926 1,27 0,634 0,585 1,936 0,945 1,190 1,445 1,410 1,821 0,429 0,704 0,163 1,411 0,877 0,588 0,178 1,26 0,738 0,632 0,812 1,263 1,795 0,646 0,99 1,514 0,24 1,129 1,336 0,839 0,663 0,954 1,529 1,535 0,78 1,517 0,177 0,972 1,968 1,813 1,883 0,509 1,788 1,747 0,890 0,501 0,780 1,783 1,860 0,89 1,936 1,283 0,887 1,706 1,535 0,191 1,528 1,388 1,411 1,747 0,170 1,163 1,241 0,605 1,300 1,288 1,913 0,234 0,990 1,559 1,820 0,92 1,763 0,604 1,529 0,574 0,489 0,787 0,743 1,529 0,439 0,677 0,707 1,243 0,788 1,496 0,70 1,158 1,941 0,586 1,584 1,840 0,509 1,791 1,145 1,402 1,328 0,751 0,641 1,376 0,677 1,709 1,117 1,653 0,658 1,848 1,494 1,883 1,244 1,467 0,772 1,958 1,173 1,377 0,953 0,362 0,289 1,379 1,8 1,333 1,658 0,655 0,967 0,654 0,910 0,685 0,286 0,413 1,783 1,302 1,446 1,173 1,11 1,724 0,687 1,894 0,128 0,500 0,987 1,249 0,400 0,785 0,167 0,831 1,209 1,749 1,562 1,710 1,121 0,538 1,114 0,107 0,935 1,610 0,151 1,197 1,76 1,775 1,285 0,653 1,543 1,476 0,759 0,589 1,35 0,687 0,300 1,633 1,697 1,826 1,270 0,935 1,623 0,863 0,985 0,313 1,69 0,34 0,718 1,238 0,181 1,265 1,513 1,880 0,118 0,736 1,524 0,972 0,763 0,75 1,548 1,511 0,343 0,797 1,288 1,18 0,921 0,237 1,967 1,594 1,965 1,807 0,906 1,713 1,111 1,581 1,13 1,269 0,491 1,219 0,498 1,866 0,869 1,682 0,813 0,508 0,540 1,734 1,305 0,479 0,803 1,833 0,544 1,591 1,434 0,883 0,381 1,752 0,939 0,676 1,356 1,749 0,357 1,782 1,833 1,404 0,76 0,913 0,346 1,938 0,982 0,651 1,855 0,570 0,248 1,174 0,426 0,312 1,757 0,783 0,707 0,733 1,368 0,654 1,265 1,125 1,14 1,180 1,902 1,822 0,26 1,977 0,369 1,428 0,87 1,667 1,172 1,983 1,597 0,24 1,949 0,135 1,667 1,886 1,200 1,729 1,857 1,166 0,226 1,67 1,775 1,978 0,931 1,367 1,859 0,414 0,183 0,758 0,638 0,286 1,951 0,176 0,790 1,31 1,208 0,439 1,607 1,804 0,722 1,729 1,454 0,44 0,534 0,513 0,266 1,741 1,156 1,444 0,151 1,781 0,711 1,875 1,711 0,226 1,96 1,189 0,41 1,340 0,580 0,824 1,368 0,213 1,896 1,148 1,895 1,796 0,723 0,509 1,881 1,162 0,161 1,504 1,996 0,580 0,679 1,259 1,967 0,27 1,837 0,741 1,264 0,121 0,410 0,248 0,788 1,832 0,202 0,981 0,882 1,608 0,322 1,894 1,42 0,535 0,752 0,671 0,591 0,813 1,391 1,477 0,309 1,281 1,362 0,232 0,525 0,786 1,262 1,291 1,773 0,769 1,137 0,925 1,470 0,935 1,789 0,550 1,160 1,956 0,231 1,916 0,667 1,365 1,468 0,561 0,714 1,224 1,188 1,748 0,820 0,583 1,523 0,116 0,844 1,513 0,638 0,134 0,981 0,347 1,212 0,511 1,933 0,992 1,555 0,487 1,634 1,299 1,938 0,756 0,446 1,745 0,252 0,573 0,900 0,940 0,888 1,521 1,326 0,187 0,91 0,453 1,559 1,689 0,998 1,87 1,692 1,64 0,804 1,452 0,293 0,589 0,651 0,704 1,741 0,740 0,573 1,548 1,185 1,643 0,582 1,97 1,146 1,375 1,161 1,147 1,909 1,800 0,913 1,573 0,137 1,985 0,443 1,502 0,330 1,16 0,124 0,98 1,18 0,186 0,142 1,714 0,787 0,729 1,522 1,388 1,627 0,940 1,368 1,49 1,985 0,49 1,721 1,747 1,789 0,258 0,388 1,593 1,257 0,988 1,396 1,610 0,22 1,165 1,685 1,540 1,818 0,677 0,472 1,788 0,220 0,956 0,386 1,975 0,548 0,28 0,616 1,916 0,224 1,690 0,162 0,212 0,916 1,417 1,695 0,481 0,74 0,651 0,358 1,210 1,705 1,465 0,404 0,948 0,349 0,489 1,959 0,120 1,953 1,925 1,412 1,58 1,456 0,631 0,131 1,719 0,14 0,707 1,582 0,873 1,605 0,529 0,262 0,147 1,983 1,21 1,151 1,21 1,328 1,447 0,40 0,581 0,379 1,415 0,489 0,39 1,251 0,230 0,531 1,675 1,285 0,19 1,869 1,56 0,674 0,667 1,590 0,183 0,641 1,281 1,171 1,302 0,762 1,834 1,213 1,797 0,934 0,955 1,272 0,764 1,367 1,184 0,986 0,810 1,939 0,914 0,7 0,71 1,933 1,312 0,984 0,379 1,949 1,263 0,632 1,304 1,118 0,389 1,790 0,241 0,329 0,780 0,863 1,610 0,647 1,64 1,66 1,505 1,960 0,620 1,180 0,262 1,974 1,767 0,792 0,279 0,910 0,85 1,321 1,577 1,34 0,959 1,154 0,350 1,638 0,182 0,891 1,447 1,265 1,638 0,327 1,195 1,460 0,642 1,928 0,614 1,509 1,606 0,217 0,510 1,518 0,889 0,908 0,570 0,382 0,986 1,692 0,439 1,266 1,368 1,27 0,684 1,813 0,522 0,452 0,94 0,473 0,953 0,127 1,914 1,747 0,92 1,53 0,910 1,860 0,516 1,128 0,698 0,84 1,822 1,823 1,385 0,415 1,727 0,567 1,290 1,374 0,375 0,727 1,665 0,880 1,927 1,369 1,11 1,220 1,114 1,598 0,59 1,904 1,633 0,436 1,633 0,678 0,630 0,734 1,235 0,208 0,48 1,27 0,305 0,714 0,306 1,846 1,50 0,686 0,291 0,435 0,542 0,759 0,342 1,62 0,311 0,648 1,746 1,84 1,838 0,982 1,422 1,813 1,570 0,453 1,704 0,899 1,865 0,633 1,766 1,12 1,216 1,681 1,14 1,915 1,926 0,854 1,400 0,267 0,171 0,954 0,870 0,69 0,4 0,734 0,911 1,447 0,109 0,37 0,136 0,911 0,890 1,610 1,682 0,262 1,191 0,35 1,819 1,365 1,581 1,132 1,260 0,173 0,29 1,35 0,339 0,947 0,708 1,783 1,390 1,422 0,537 1,865 0,317 1,297 1,407 1,487 1,149 0,298 0,463 0,894 1,777 1,481 1,531 0,861 0,660 1,307 0,910 1,246 1,730 1,762 0,264 1,595 0,400 0,966 0,817 1,954 0,934 0,600 0,676 1,616 1,85 1,484 0,160 0,503 0,389 0,155 1,671 0,879 0,51 1,461 0,941 1,329 1,893 0,705 1,232 1,995 1,809 1,624 1,353 0,140 0,227 0,844 1,404 0,432 1,477 1,788 0,3 0,455 0,616 0,966 1,786 0,284 0,367 1,264 0,937 1,396 0,534 0,816 1,884 0,274 1,781 1,874 1,243 1,362 0,56 0,623 1,839 1,578 1,875 1,568 0,126 1,140 1,689 1,129 1,357 0,765 0,538 1,51 1,389 0,344 0,589 1,433 1,207 0,968 1,984 1,741 0,730 0,57 0,960 1,57 0,432 0,93 1,201 0,657 0,66 1,366 1,29 0,605 1,109 0,880 1,680 1,848 0,349 0,421 0,214 0,704 0,28 1,469 0,979 0,601 0,839 1,27 0,850 0,737 0,536 0,108 1,337 1,363 0,659 0,687 1,817 0,781 1,165 0,53 0,872 0,596 1,896 1,973 1,28 0,354 1,468 1,642 0,537 0,771 0,493 0,116 0,889 0,78 1,348 1,629 0,689 0,805 0,950 0,160 1,603 0,677 1,140 0,356 1,839 1,447 0,495 0,742 1,867 1,973 0,412 1,539 1,528 1,5 0,527 0,239 0,499 1,637 1,426 0,704 1,245 1,799 0,21 0,278 0,899 0,822 0,985 0,901 1,269 1,908 0,853 0,783 0,395 0,281 0,653 1,540 0,711 0,926 0,283 1,591 1,247 0,395 1,179 1,517 0,212 1,234 1,210 0,104 0,86 0,119 1,186 0,105 1,433 0,944 0,580 1,244 0,845 1,285 0,196 1,738 0,533 0,805 1,938 0,769 1,657 1,692 1,604 1,320 0,367 0,429 1,151 0,617 1,777 0,937 1,843 0,29 0,678 0,96 1,383 1,209 0,540 0,666 0,446 1,499 1,81 1,34 0,159 1,389 1,392 0,330 0,221 0,747 0,747 0,825 0,727 0,30 0,160 0,896 0,726 0,968 1,885 1,846 1,221 0,427 1,859 0,942 0,659 1,897 0,715 0,154 0,183 0,572 0,155 1,125 0,234 1,836 1,357 0,425 0,519 0,589 0,775 1,187 0,914 0,120 0,125 1,438 1,955 0,30 1,490 0,194 0,451 1,572 0,673 1,498 1,402 0,726 0,351 1,824 0,767 1,59 0,28 0,561 1,928 1,247 0,937 0,642 0,985 1,545 1,41 0,947 0,909 1,800 1,266 1,49 1,458 0,228 1,585 1,257 1,622 1,411 0,334 1,803 1,826 1,640 1,336 0,947 0,194 0,992 0,302 0,381 1,705 0,641 1,217 0,32 1,586 1,292 1,111 1,834 1,523 0,151 1,154 0,367 0,903 1,392 1,253 1,932 1,137 1,713 1,362 0,106 0,545 0,806 0,138 0,458 1,429 1,982 1,195 0,894 0,520 0,305 0,394 1,614 1,931 0,800 0,706 0,297 1,227 0,16 0,719 1,115 1,402 0,406 0,540 1,277 1,871 1,777 1,918 0,426 0,476 1,197 0,541 0,317 1,125 1,499 0,884 0,478 0,999 0,510 1,180 1,8 1,159 0,372 1,417 0,107 1,413 0,793 0,706 1,540 0,825 1,513 0,276 0,393 0,71 1,89 0,171 0,390 1,139 1,745 0,938 0,409 0,308 0,893 1,500 0,294 1,285 0,438 1,887 1,173 0,477 1,828 0,31 1,582 0,629 1,523 0,365 1,181 1,929 0,457 0,36 0,728 1,185 1,992 0,158 1,109 0,125 0,423 1,566 0,916 0,807 1,534 0,406 0,418 0,861 1,71 0,462 0,669 1,113 1,391 1,441 0,409 1,804 1,170 0,773 1,699 0,391 0,133 1,718 1,476 0,211 0,827 0,718 0,514 0,343 0,783 0,918 1,850 1,196 0,508 0,848 1,346 0,552 1,726 0,55 1,248 0,365 0,403 1,720 1,80 1,366 1,563 1,515 0,714 0,323 1,902 1,130 0,219 0,51 0,833 0,293 0,531 0,484 1,525 0,753 0,926 0,483 1,291 1,602 0,734 0,18 1,343 0,432 0,743 0,615 1,720 1,330 1,329 1,961 1,857 0,522 0,470 1,888 1,44 1,406 0,575 0,223 0,522 0,941 0,920 0,640 0,316 0,421 1,282 1,439 0,106 1,578 1,901 1,310 0,639 1,482 0,487 0,182 0,76 0,347 0,752 0,234 1,247 0,394 1,711 1,388 0,578 0,304 0,786 1,529 0,51 1,873 0,927 1,409 0,784 1,789 0,75 0,597 1,896 1,436 1,233 1,517 1,369 1,247 1,486 0,490 1,897 0,247 0,89 0,585 0,164 1,698 1,150 1,629 0,161 1,31 1,43 1,586 1,750 0,99 1,676 1,913 0,669 1,501 1,778 1,532 0,197 1,262 0,462 1,46 1,80 0,631 1,331 1,775 0,656 0,103 0,942 1,633 0,695 0,453 1,438 0,463 0,824 0,985 0,843 1,588 1,39 1,917 0,942 1,850 1,31 0,845 0,494 0,115 1,763 0,327 1,119 0,793 0,696 1,333 1,406 0,992 1,404 1,393 0,287 1,976 0,399 0,206 1,778 1,748 1,584 1,439 1,666 1,643 0,597 1,532 0,626 1,200 1,817 1,211 1,574 1,220 0,765 0,542 0,728 1,170 0,49 1,463 1,539 0,91 0,226 1,19 1,521 0,257 0,173 0,809 0,533 1,381 1,443 1,877 1,77 0,160 1,861 0,247 1,39 0,218 0,989 0,258 1,143 1,26 1,924 0,235 1,911 0,100 1,237 1,21 1,274 0,3 1,382 1,737 0,640 1,69 0,201 0,63 0,6 1,257 0,91 0,994 1,507 0,929 0,145 1,636 0,516 0,328 1,825 0,199 0,444 1,547 1,552 1,243 0,512 1,234 0,731 0,30 0,867 1,104 0,109 1,984 1,823 0,219 0,628 0,496 1,603 1,748 1,257 0,947 0,959 1,45 0,265 1,606 0,793 1,37 0,440 1,795 1,731 1,428 1,322 1,252 1,277 1,822 0,77 1,622 1,56 0,384 0,19 1,171 1,840 0,201 1,40 1,127 1,906 1,699 1,350 1,928 1,159 0,493 0,4 1,632 1,982 0,242 0,920 1,734 1,209 1,625 1,33 1,787 1,863 0,577 1,349 0,802 0,785 0,275 1,22 1,549 0,842 0,932 1,468 0,284 0,249 1,518 1,806 1,707 0,951 0,180 0,71 0,924 1,561 0,502 1,141 0,578 1,697 0,10 1,772 0,621 0,116 0,619 0,495 0,523 0,489 0,222 0,524 0,540 1,401 1,907 0,600 1,47 0,528 1,642 1,858 0,119 1,675 1,747 1,26 1,758 1,925 1,77 1,669 1,414 1,718 0,242 1,847 1,119 0,577 1,152 1,219 1,435 1,806 0,737 1,117 0,597 0,126 0,337 0,874 1,361 0,263 1,338 0,293 0,516 1,356 1,923 0,794 0,301 0,589 1,323 0,775 1,534 0,555 1,926 0,947 0,777 1,419 0,942 1,243 0,711 1,635 1,124 1,489 1,707 0,718 1,568 0,796 0,895 1,968 0,516 0,296 0,212 1,470 1,600 1,753 1,936 0,715 1,127 0,668 1,889 1,552 0,739 0,180 0,249 0,51 0,584 1,545 0,675 1,247 0,970 1,616 1,96 0,14 1,993 0,433 0,650 0,500 1,443 0,764 0,670 1,213 1,386 0,44 0,52 1,103 0,735 0,998 1,399 1,545 0,224 1,606 1,179 0,478 1,369 1,147 1,89 0,913 1,944 1,857 0,920 1,548 1,365 0,971 0,588 1,668 1,392 1,175 1,183 0,31 0,378 1,969 0,728 1,868 0,784 1,265 1,506 1,298 1,167 0,292 0,966 0,794 0,408 0,650 1,253 0,49 0,113 1,477 1,665 1,290 0,648 0,865 1,955 1,488 1,352 0,957 1,795 1,10 0,455 0,263 1,215 1,761 0,942 1,356 1,838 1,446 0,501 1,907 0,934 0,623 1,418 1,291 1,669 0,613 1,526 0,952 1,540 0,636 1,372 0,689 0,486 0,375 1,309 0,802 0,830 1,898 0,658 1,309 1,660 0,270 0,790 1,612 1,664 1,288 1,385 1,668 1,454 0,422 0,364 0,138 0,886 0,26 0,138 0,501 1,201 1,95 0,147 1,419 1,789 0,289 1,513 1,499 0,687 1,423 1,426 0,808 0,673 1,70 0,174 0,94 1,284 1,457 0,113 1,948 1,973 1,116 0,156 0,940 1,682 1,607 0,734 0,2 1,863 0,87 0,125 1,870 0,946 1,829 0,906 1,872 0,37 0,953 0,520 1,576 0,556 1,349 1,747 1,328 0,138 0,647 1,166 0,323 1,867 0,768 1,632 0,393 1,261 0,795 1,987 0,907 1,857 0,932 1,473 0,416 1,720 0,287 0,686 1,835 1,100 0,79 0,814 0,647 0,549 0,426 1,479 0,827 1,184 0,803 0,570 1,176 0,72 1,625 0,229 1,184 0,417 1,347 0,510 0,233 0,269 1,367 0,142 1,716 0,309 1,707 0,166 0,335 1,294 0,111 1,372 0,326 1,631 1,480 1,349 1,9 0,746 0,231 1,392 1,262 0,589 1,7 1,204 1,198 1,559 1,104 0,282 1,707 0,619 0,75 0,214 1,642 0,173 0,569 0,904 1,408 1,922 0,10 1,604 0,82 1,504 0,971 0,963 1,201 0,381 0,580 0,714 1,275 1,997 0,654 1,67 1,198 0,450 1,147 0,880 1,809 0,566 1,155 0,555 0,183 0,651 0,667 0,391 1,551 0,406 1,569 0,811 1,127 1,272 0,584 1,512 0,233 1,445 0,945 1,886 0,241 0,43 1,32 1,53 1,997 0,841 0,949 0,279 1,320 1,150 1,118 1,707 1,141 0,183 1,401 1,314 1,757 0,369 1,812 0,1 0,658 0,48 1,603 1,846 0,827 0,933 1,45 0,283 1,582 0,642 1,538 1,170 0,849 1,729 0,86 0,580 0,181 1,76 0,440 0,825 0,191 1,263 1,958 0,727 0,479 1,217 0,162 0,450 1,200 0,681 1,444 1,734 0,739 0,291 0,952 0,311 1,317 0,222 1,983 0,201 0,743 1,577 0,271 0,53 0,304 1,568 1,638 1,409 1,220 0,758 0,188 1,501 1,775 0,360 1,52 0,935 1,77 0,621 1,664 1,559 0,94 1,449 0,345 1,814 0,320 0,674 1,671 1,555 1,484 1,914 1,994 1,596 1,169 0,411 0,529 0,695 1,597 0,805 0,416 1,946 1,45 0,368 1,25 0,890 0,623 1,636 0,484 1,479 1,618 0,900 1,590 0,79 1,744 0,217 1,710 0,294 1,409 1,26 0,50 0,114 1,329 0,610 0,617 0,186 0,615 1,804 1,775 0,411 0,38 1,12 0,127 1,939 1,596 1,119 1,264 1,590 1,369 1,990 1,12 1,942 1,802 0,763 1,723 0,130 0,485 1,371 1,845 1,9 0,199 1,508 1,259 1,650 0,368 1,670 0,666 1,133 1,988 0,903 1,495 1,383 0,473 0,333 1,557 0,371 1,457 0,315 0,566 1,549 1,557 1,627 0,458 1,900 0,540 0,26 1,821 0,488 0,867 0,135 0,924 1,470 1,861 0,898 1,497 0,690 0,624 0,709 0,406 0,927 1,739 0,164 0,556 1,145 1,851 1,124 0,333 1,608 1,749 1,914 0,272 0,843 0,917 1,152 1,345 0,546 0,445 0,212 1,555 1,929 0,943 1,765 1,314 0,673 0,319 0,584 0,672 0,303 1,116 0,115 1,813 1,525 1,11 0,195 1,989 1,57 0,489 0,584 0,483 0,192 0,934 0,155 0,373 1,82 0,526 1,655 0,475 0,696 0,407 0,426 1,881 1,351 0,607 0,927 0,637 0,411 1,150 1,613 1,865 0,690 1,877 0,379 0,251 0,429 1,31 1,995 1,450 0,205 0,334 0,36 1,396 0,847 1,820 0,239 1,655 1,266 0,540 1,726 0,242 0,540 1,504 0,631 1,615 0,564 1,696 1,431 0,391 0,333 1,117 0,577 0,983 1,696 0,231 1,200 0,29 1,991 0,258 1,688 0,805 1,425 0,830 1,193 1,843 0,164 0,271 0,299 0,569 1,431 0,922 0,305 0,479 0,571 1,660 0,300 1,522 1,800 0,763 0,905 1,420 1,397 0,997 1,331 1,172 1,839 0,814 1,757 1,494 1,420 1,387 1,376 0,76 1,660 1,891 1,731 0,859 0,960 1,633 0,273 0,30 0,817 0,911 1,456 1,47 0,849 1,367 1,218 0,490 1,566 0,668 1,386 0,779 1,370 1,246 1,160 1,480 0,30 1,842 0,541 1,610 1,429 0,634 1,982 0,914 0,974 1,965 1,712 0,280 1,796 0,699 0,295 0,645 1,982 1,366 0,938 0,259 1,576 0,336 1,609 1,863 0,490 0,421 0,236 0,964 0,924 0,504 0,370 1,25 0,738 0,271 1,106 1,437 0,153 1,5 1,466 1,234 0,370 0,626 1,775 1,343 1,473 0,649 1,282 0,52 1,443 0,899 0,210 1,113 1,616 1,779 1,154 1,648 0,541 1,729 1,597 0,191 1,951 1,671 0,366 1,710 1,252 0,183 0,283 1,708 1,348 1,132 0,773 1,194 0,854 1,549 0,351 1,897 1,263 1,872 1,671 1,896 1,412 1,504 0,398 1,665 0,956 1,417 1,51 0,52 1,131 1,2 0,289 1,350 0,547 1,207 0,987 1,944 0,361 1,190 0,66 1,409 0,407 1,487 0,249 0,416 0,234 1,682 1,614 1,223 1,109 1,247 0,222 0,919 1,625 0,244 1,490 0,465 0,921 1,621 0,611 1,898 1,694 1,437 1,528 0,32 0,199 0,99 1,854 1,500 0,554 0,576 1,336 0,412 1,564 0,476 0,37 0,219 1,428 1,611 0,3 1,940 0,992 0,21 0,60 1,781 0,501 0,964 1,180 0,752 1,610 0,846 0,593 1,619 1,288 1,912 1,407 1,240 0,632 0,645 0,236 1,741 0,91 0,900 1,947 0,110 1,63 1,856 1,563 1,195 1,718 1,353 0,111 0,550 0,858 1,819 0,523 1,773 0,164 1,767 0,580 0,344 0,409 0,495 0,709 0,272 0,98 1,297 0,863 0,240 0,176 0,160 1,362 1,793 0,466 1,474 0,562 1,724 0,175 1,629 0,56 1,406 1,74 0,146 1,504 0,453 0,360 0,21 1,853 1,853 1,107 0,479 0,728 0,529 1,521 0,74 1,120 0,424 0,93 0,407 1,182 1,347 1,212 1,271 1,153 1,106 0,506 1,783 0,661 0,517 0,440 0,619 1,927 0,145 1,256 1,625 1,381 0,644 0,284 1,703 0,882 0,549 0,983 1,965 0,408 0,680 0,673 0,89 0,264 0,156 1,914 0,751 0,251 0,161 1,28 1,47 0,361 1,780 1,276 1,37 0,868 0,788 1,728 0,560 0,944 0,590 0,183 1,725 0,307 1,412 1,634 1,644 1,600 1,435 1,481 0,266 1,583 1,801 1,396 0,688 0,185 1,291 1,403 0,295 0,901 1,230 1,497 0,28 0,632 1,605 0,115 0,971 1,13 0,592 1,763 0,421 0,407 0,28 0,751 1,407 0,164 1,211 1,943 1,595 1,370 0,10 1,428 1,592 0,464 1,813 0,615 1,715 1,688 1,613 1,394 1,636 1,302 1,135 1,837 0,490 0,344 1,925 0,837 0,998 1,756 0,732 1,594 0,766 1,418 1,305 1,821 0,514 1,383 0,309 1,156 0,877 0,66 0,981 0,891 1,532 0,795 1,197 1,961 0,973 1,375 0,415 0,726 0,59 1,357 0,372 1,429 1,352 0,534 0,832 1,497 1,347 0,629 0,493 1,882 1,361 0,130 0,410 1,885 1,721 0,223 1,425 1,180 0,276 1,848 1,812 0,401 1,93 0,240 1,329 0,46 0,247 1,935 1,999 1,257 0,969 0,498 1,672 1,671 1,357 1,27 1,299 0,805 1,601 0,954 0,68 0,761 1,973 1,699 1,515 0,151 0,255 1,527 0,427 0,474 1,344 1,966 1,840 1,174 1,128 1,400 0,471 0,712 1,26 0,213 0,216 1,88 0,132 1,676 1,352 1,685 1,980 0,738 1,920 1,821 0,658 0,799 0,383 0,718 1,484 0,241 0,388 0,80 1,797 0,439 1,225 1,747 1,36 1,1 1,118 0,432 0,407 0,835 0,463 1,96 1,286 1,351 1,717 0,826 1,891 1,712 1,154 0,623 1,378 1,633 1,42 1,50 0,861 0,302 1,773 0,29 0,324 0,346 1,850 0,440 1,308 1,211 0,694 1,847 1,466 0,355 0,636 0,395 1,813 1,997 0,742 1,723 0,749 1,982 1,389 0,178 1,437 1,377 0,404 0,784 0,308 1,978 1,979 1,64 1,828 0,843 0,645 1,191 0,719 1,980 0,634 0,153 0,181 1,664 1,954 1,218 0,983 1,953 1,237 0,418 1,324 1,805 0,445 1,180 1,562 1,936 0,402 0,610 0,255 1,181 0,210 0,194 1,513 1,117 1,667 0,136 1,165 0,623 0,985 1,590 1,413 1,587 1,968 1,667 1,16 1,649 1,430 0,247 0,159 1,511 1,436 1,935 0,359 0,563 1,813 1,92 0,126 1,854 1,103 1,712 1,346 1,953 0,163 1,49 0,537 0,478 1,185 1,839 1,750 0,229 1,77 0,983 0,931 1,290 0,155 0,725 1,791 1,824 0,700 1,697 0,391 0,174 0,563 0,622 1,647 1,508 0,0 0,4 0,615 0,651 0,912 1,604 1,162 0,853 0,441 1,41 0,227 0,546 1,353 1,385 1,113 1,797 1,822 0,890 1,739 1,969 0,263 1,563 1,759 0,208 0,473 1,694 0,156 1,59 0,752 0,766 0,174 1,860 1,86 0,69 0,455 1,432 1,468 0,508 1,712 0,893 0,327 1,387 0,734 1,189 0,190 1,965 1,492 1,717 0,678 0,901 0,934 1,935 0,601 1,413 0,677 1,205 1,843 0,241 0,771 0,120 0,413 1,747 1,382 0,37 1,390 0,533 0,164 0,301 0,911 0,116 0,774 1,162 1,301 1,985 1,866 1,768 0,751 0,935 0,857 0,539 1,6 1,408 0,610 0,541 0,718 1,545 0,344 1,123 0,383 0,68 0,454 1,650 1,671 1,442 1,183 1,79 0,617 1,976 0,810 1,281 1,805 0,960 1,667 0,825 1,866 1,683 1,783 0,612 0,862 1,889 0,412 0,950 1,741 1,523 0,246 1,338 1,167 1,436 1,722 1,198 1,653 0,572 0,887 1,110 0,463 0,957 0,427 1,826 0,431 1,913 1,570 1,963 1,934 0,647 1,501 1,940 0,268 0,889 0,208 0,574 0,248 0,674 0,5 1,135 0,420 0,219 1,986 1,959 0,161 1,67 1,327 0,338 1,129 1,546 1,926 0,725 1,193 1,798 1,836 0,833 1,388 1,390 1,51 0,191 0,605 0,223 0,912 0,366 0,594 0,321 1,896 1,133 0,318 1,68 1,251 0,259 0,336 0,809 1,303 0,765 1,134 1,209 0,719 0,10 1,700 0,361 1,979 1,171 1,763 1,436 0,517 1,890 1,429 1,518 0,118 0,380 1,473 0,925 1,909 1,286 0,818 0,140 1,690 1,914 1,711 0,484 0,98 0,201 0,841 0,318 0,202 0,946 0,970 1,243 1,138 0,748 1,787 0,304 1,18 1,741 0,816 0,591 0,540 0,577 0,129 0,43 0,708 1,457 1,54 1,260 1,282 0,219 0,811 0,707 1,684 0,565 0,501 1,385 1,292 1,234 0,646 0,507 0,55 0,933 1,904 0,109 0,755 0,633 1,677 0,980 1,749 1,615 1,288 1,532 1,859 0,629 1,799 1,494 1,170 0,763 0,688 1,525 0,813 1,399 0,525 0,198 0,502 1,897 0,415 0,353 1,498 0,572 1,507 0,921 0,324 0,397 1,941 1,590 1,594 0,312 1,784 1,275 0,328 1,556 0,666 0,847 0,762 0,499 1,176 0,986 1,468 1,373 0,76 0,362 0,131 0,871 0,707 0,310 0,377 1,772 1,817 1,6 0,286 1,194 0,744 1,494 1,790 1,401 1,204 1,763 1,334 1,500 1,454 0,165 0,616 0,226 0,810 0,815 1,35 0,422 1,126 1,965 1,547 1,492 0,127 1,53 0,389 1,216 1,602 0,636 1,875 0,422 1,641 0,266 0,728 1,748 0,543 0,210 0,225 1,255 1,678 0,166 0,261 0,981 1,646 0,47 1,910 0,192 1,976 1,53 1,626 1,480 0,667 1,642 1,770 1,886 0,246 0,835 0,857 0,871 1,602 1,256 1,557 1,694 1,461 1,329 0,913 0,949 1,935 0,172 0,713 1,727 1,280 0,860 0,992 1,940 0,224 0,808 1,579 1,991 0,661 0,966 0,546 1,677 1,36 0,809 1,926 1,600 0,102 0,555 1,829 0,694 0,302 1,538 1,637 1,598 1,248 0,202 0,324 1,219 0,273 1,991 1,362 1,706 0,426 0,879 0,294 0,757 0,99 1,82 0,694 0,80 1,886 0,471 1,312 0,594 1,212 0,118 0,582 1,647 0,588 1,513 0,419 0,810 1,516 0,157 1,141 1,512 1,192 0,949 0,687 1,257 0,700 1,109 0,225 1,267 0,174 1,808 1,398 0,61 1,193 1,3 0,223 1,744 1,835 1,360 0,498 0,485 0,559 1,797 1,182 0,317 1,264 0,742 0,465 1,18 0,45 0,814 0,283 0,839 1,575 0,549 1,458 0,669 1,907 0,656 1,425 1,330 1,21 0,861 0,66 0,560 1,558 0,346 1,405 0,378 1,989 0,487 1,448 0,700 1,953 1,958 1,491 0,355 1,859 1,144 0,644 1,783 1,729 1,320 0,158 1,635 0,698 1,209 0,951 0,78 1,998 1,290 1,285 1,504 1,725 0,560 0,939 0,757 1,552 1,48 1,95 0,275 1,839 1,901 1,285 0,415 0,264 1,70 1,573 0,981 0,966 0,917 0,904 0,943 1,782 1,507 1,533 0,164 1,820 0,208 0,417 0,455 0,290 1,360 0,736 1,810 0,612 0,106 0,261 1,631 1,852 1,165 1,126 0,5 0,982 0,661 0,197 1,299 1,189 0,142 1,777 1,303 0,436 0,679 1,949 1,409 0,694 1,345 0,590 0,376 0,84 0,408 1,620 0,848 1,749 1,438 0,52 0,713 0,24 0,712 0,219 0,918 0,685 1,693 0,665 0,445 0,929 1,824 1,923 0,332 1,528 0,354 1,116 1,394 1,314 0,604 0,79 0,121 0,94 1,134 0,969 0,72 0,740 0,696 0,403 1,493 0,368 1,854 0,461 1,504 1,308 0,868 0,253 0,461 0,43 1,478 0,265 0,526 0,959 0,87 1,303 1,813 0,646 1,506 0,358 0,107 0,201 0,330 0,226 1,925 1,400 0,196 0,905 1,147 0,199 1,861 1,640 1,175 0,124 1,186 0,924 0,79 0,330 1,583 0,782 1,464 0,777 0,501 1,905 1,127 0,464 1,839 0,64 0,277 0,7 0,23 1,338 0,364 1,11 1,313 0,951 0,76 0,714 1,948 1,459 0,368 1,601 1,992 0,16 1,721 0,858 1,284 0,133 0,362 1,704 0,666 0,842 1,531 0,900 1,864 1,780 1,966 1,805 1,855 0,509 0,505 0,80 1,205 1,365 1,461 0,531 1,677 0,331 1,417 1,739 1,683 0,312 1,664 0,881 1,315 1,874 0,937 0,62 1,488 1,864 0,236 1,770 1,350 1,708 0,764 0,181 0,16 1,205 1,692 0,682 1,778 1,8 1,455 0,339 1,260 0,99 0,881 0,410 0,603 1,335 1,113 1,465 0,171 0,616 1,585 1,220 0,781 0,203 1,216 1,930 1,358 1,145 1,815 0,439 0,386 0,169 1,823 0,647 0,609 1,917 0,198 1,302 1,432 1,342 0,984 1,349 1,450 0,80 0,257 1,965 0,887 1,726 0,781 1,642 0,107 1,854 1,808 1,814 1,224 0,644 0,925 1,532 0,974 0,150 1,457 0,44 1,522 0,257 1,323 0,934 0,939 0,492 1,40 1,40 1,992 0,877 1,772 0,25 0,674 1,937 0,439 1,362 1,873 0,735 0,384 0,516 1,574 0,335 0,276 1,52 1,775 0,387 0,288 0,869 0,367 1,10 1,797 1,847 0,792 0,885 0,919 1,344 1,752 1,571 0,774 1,446 1,65 0,518 0,942 0,883 0,626 1,559 0,655 1,10 0,884 1,925 0,681 1,994 0,240 1,212 0,996 1,403 0,802 0,200 1,272 0,770 0,712 1,952 0,435 0,573 0,212 0,520 1,795 1,492 0,331 1,644 0,312 1,877 1,736 0,996 1,491 1,199 1,591 1,522 1,655 0,823 1,64 1,932 1,822 1,526 1,704 1,41 1,285 1,388 0,333 0,0 0,789 0,389 1,451 1,28 0,885 1,629 1,13 1,214 1,649 0,878 0,786 0,619 0,905 1,133 0,979 0,516 1,175 0,77 1,911 0,252 1,526 1,767 1,723 0,641 0,644 0,60 1,117 0,178 0,919 0,932 0,779 1,160 0,637 1,122 0,681 0,54 0,140 1,721 0,572 0,991 0,335 0,673 1,676 0,469 1,559 1,580 1,150 1,996 1,205 0,604 1,277 1,479 1,31 0,945 0,264 0,724 0,883 0,941 1,524 1,477 1,737 0,529 0,824 1,872 1,633 0,241 0,842 0,587 0,650 1,457 0,673 0,767 1,511 0,853 0,247 0,509 0,981 1,254 0,630 0,56 1,63 0,850 0,751 0,446 1,530 1,164 0,568 1,786 1,953 1,517 1,708 0,561 0,782 1,739 1,7 1,483 0,566 1,524 0,457 0,3 0,298 1,659 1,299 1,580 0,809 0,698 1,144 1,727 0,308 1,2 1,694 0,957 0,132 0,849 0,200 0,146 1,439 0,417 0,101 1,409 0,925 1,954 0,810 1,888 1,639 1,598 0,513 1,602 1,558 0,684 1,88 1,944 1,744 0,58 1,78 0,290 0,862 1,3 1,681 0,423 1,940 1,776 1,125 0,61 1,763 0,766 1,252 0,843 1,353 0,891 0,790 0,673 1,871 0,564 0,864 0,665 0,945 1,802 0,404 0,91 1,804 0,347 1,156 1,840 1,454 1,347 0,613 1,197 0,841 0,624 0,397 1,125 1,882 1,261 1,687 1,681 1,452 1,586 1,736 0,265 0,236 0,155 0,210 1,139 1,465 0,923 0,136 0,79 0,46 1,661 0,464 0,109 1,499 1,322 0,207 0,175 0,516 0,870 1,640 0,245 1,556 1,384 1,104 0,388 1,737 0,95 0,915 0,67 1,502 0,238 1,796 1,199 1,626 0,643 1,739 0,444 0,985 1,686 1,352 0,792 0,658 1,337 1,424 0,3 1,707 0,858 1,16 1,473 1,539 1,444 0,460 0,278 1,390 1,747 0,151 1,998 0,282 0,211 1,184 1,110 0,658 1,11 1,511 1,339 0,900 0,487 0,906 0,381 1,397 0,537 1,571 0,585 1,420 0,634 0,162 0,203 1,763 1,933 1,918 1,7 1,237 1,180 1,825 0,191 0,847 1,87 1,737 0,414 1,156 0,953 0,379 0,411 0,804 1,692 0,34 1,115 0,27 0,917 0,869 0,761 1,223 1,634 1,93 0,678 1,264 1,359 1,956 0,556 0,336 1,249 1,200 1,132 1,249 0,702 0,938 1,76 0,756 0,841 0,60 1,675 1,275 0,753 0,689 0,453 0,280 1,62 0,947 0,922 1,661 0,613 0,938 1,92 1,822 0,894 0,683 1,344 1,888 0,550 1,325 1,40 0,744 1,642 0,228 1,178 0,605 1,53 1,30 0,218 1,380 0,980 0,860 0,186 0,625 1,549 0,285 1,961 1,315 1,272 0,277 1,297 1,222 1,587 1,690 0,436 0,65 0,63 1,476 1,325 1,868 0,120 1,702 0,695 1,424 0,138 0,894 0,945 1,538 0,832 1,755 1,785 1,79 1,482 0,186 1,461 0,104 0,300 1,494 0,292 1,250 0,458 0,735 0,939 1,813 0,789 0,789 1,704 1,909 1,449 0,41 0,17 0,225 0,217 1,931 1,345 0,273 1,711 1,630 1,128 1,378 0,620 1,682 0,955 1,423 0,899 0,466 0,759 0,663 0,203 1,136 0,345 0,325 1,627 0,855 0,229 1,653 0,217 1,318 1,555 0,253 0,704 1,592 1,306 1,599 1,784 1,921 1,944 0,216 0,32 1,914 1,255 0,876 0,775 0,910 1,19 0,260 1,840 1,878 1,581 0,135 0,799 0,144 0,478 0,873 1,766 0,178 1,52 1,998 1,611 1,48 1,384 0,868 0,8 1,544 0,823 0,130 0,161 0,867 0,53 0,395 0,402 0,374 0,562 0,551 1,910 1,175 1,360 0,375 0,990 1,951 0,686 0,167 1,711 1,821 0,907 1,442 1,233 0,361 0,150 1,128 0,338 0,469 0,427 1,338 1,29 1,716 1,151 0,594 0,314 1,795 0,818 0,19 0,559 1,89 0,606 1,813 0,173 0,816 0,259 1,854 1,508 1,158 0,862 1,861 1,686 1,559 0,211 0,824 0,393 0,691 0,580 0,617 1,189 0,172 1,689 1,187 1,685 1,639 1,838 1,818 1,126 1,555 0,996 1,17 1,691 0,712 1,691 0,699 0,283 0,709 1,47 1,794 0,741 1,237 1,856 1,391 1,93 1,1 1,211 1,135 1,176 1,991 0,833 0,640 0,941 0,282 0,528 1,194 1,173 0,690 0,725 1,768 0,955 1,644 0,828 1,603 0,271 1,48 1,989 0,931 1,527 1,895 1,704 0,75 1,26 1,960 1,512 1,708 1,517 0,432 0,69 0,643 0,225 0,133 0,984 0,125 0,387 0,359 0,176 0,243 0,718 1,934 0,298 1,567 1,546 1,462 0,894 0,949 0,704 0,729 1,933 1,81 1,779 0,441 1,561 0,111 0,38 0,394 1,34 1,929 1,759 1,831 0,868 1,40 0,168 0,246 1,568 0,245 0,978 1,498 1,93 0,58 0,836 1,686 0,882 1,835 0,357 1,128 1,173 0,386 0,719 0,447 0,414 0,396 0,110 1,529 1,55 0,275 1,728 1,507 1,617 1,988 0,159 1,762 1,213 0,409 0,886 0,721 1,932 1,157 0,937 1,107 0,674 1,197 0,546 0,780 0,161 0,779 0,681 1,94 1,327 1,584 1,54 1,426 1,356 1,489 0,576 1,315 0,962 0,735 1,766 0,216 1,56 0,203 0,14 0,397 1,25 1,273 1,276 0,248 0,950 0,95 0,20 0,546 0,276 1,665 0,720 1,409 0,854 1,719 0,81 1,52 0,326 1,71 1,367 1,868 1,207 0,307 1,101 1,705 1,402 1,77 1,226 1,940 0,956 1,713 0,557 1,710 1,452 1,294 0,395 1,973 0,319 1,611 1,418 1,238 0,624 1,415 0,540 1,590 0,380 0,147 1,207 0,284 0,446 0,401 1,857 1,948 1,10 1,729 1,557 1,950 1,385 1,42 0,453 1,156 0,787 0,233 1,11 0,210 0,875 0,53 0,865 1,87 0,876 1,213 1,723 0,585 1,566 0,341 0,388 1,631 0,559 1,841 0,438 0,542 1,588 0,809 0,270 0,650 0,808 0,463 1,418 0,711 0,592 1,696 1,496 0,51 1,205 1,602 0,650 0,605 0,95 0,581 0,764 1,122 1,483 1,626 0,409 1,202 0,825 0,16 1,355 1,983 0,813 0,227 0,724 0,896 1,154 1,671 0,645 0,10 1,871 0,530 0,681 1,661 1,641 0,0 1,480 0,366 1,821 0,545 1,385 0,541 0,936 0,418 1,584 0,614 0,303 0,584 0,855 0,885 1,270 1,875 0,264 1,962 0,174 0,946 0,873 0,417 1,423 1,45 1,12 1,429 0,783 0,968 1,584 0,879 0,128 1,118 1,272 1,384 1,659 1,800 1,614 0,657 0,908 0,589 1,651 0,258 0,62 1,84 1,809 1,834 1,924 0,289 1,554 0,784 1,227 1,310 1,78 0,589 0,404 0,830 1,458 0,688 0,543 0,460 0,697 0,291 1,143 1,631 1,1 1,132 1,379 0,815 1,539 1,646 1,583 1,764 0,926 0,932 1,594 0,972 1,261 1,708 0,80 0,883 0,452 1,156 0,29 0,127 0,347 0,489 0,633 1,26 0,343 1,637 0,998 1,723 1,20 1,458 0,655 0,927 0,32 1,959 0,300 1,832 0,790 0,218 1,53 1,305 1,790 0,627 0,474 1,945 0,293 0,311 1,563 1,481 1,526 0,508 1,601 1,784 0,118 1,771 0,820 0,565 1,294 0,977 1,48 0,674 1,870 0,360 1,383 0,105 1,427 1,296 1,61 0,876 0,201 0,778 1,933 1,771 0,942 0,761 1,742 0,510 0,761 0,963 0,478 0,952 1,242 1,641 0,669 1,939 0,580 1,392 0,365 1,728 1,518 0,476 1,282 1,430 1,851 1,63 1,700 0,257 1,17 1,642 1,900 1,343 0,88 0,443 1,803 0,324 0,424 0,648 0,120 0,136 1,603 0,704 0,468 0,853 1,642 1,976 1,192 1,180 0,687 0,376 1,933 0,883 0,375 0,167 1,576 0,287 1,906 0,207 0,741 0,82 0,306 1,337 1,418 1,665 1,228 0,416 1,648 1,998 1,836 0,529 1,916 0,39 0,134 0,903 1,572 1,394 0,979 0,920 0,713 1,571 1,220 1,500 0,596 0,441 1,441 1,617 0,843 1,617 1,634 0,897 1,203 1,104 1,314 0,118 0,968 1,719 1,105 1,205 1,277 1,55 1,806 1,721 1,252 0,865 0,228 0,898 1,457 1,900 1,402 0,614 0,221 0,505 0,786 1,478 0,297 0,407 0,924 1,865 1,772 0,822 0,761 1,444 0,903 1,451 1,936 0,134 0,959 1,455 0,327 0,552 1,956 1,388 0,873 0,70 1,774 1,857 1,998 0,675 0,228 0,775 1,336 0,552 1,30 1,480 1,338 0,848 1,47 1,891 1,602 1,865 1,106 0,542 0,784 0,740 1,38 1,783 0,951 0,394 0,699 1,551 0,994 1,349 1,560 0,754 1,499 0,429 1,586 1,737 0,153 0,904 0,225 0,677 1,295 0,627 0,395 0,8 1,453 0,768 1,307 1,643 0,563 1,394 1,538 1,994 1,633 0,567 1,795 1,980 0,705 0,616 1,251 0,457 1,615 1,86 0,319 1,834 1,314 0,913 0,367 0,876 0,499 1,844 0,835 1,28 0,359 1,930 1,717 1,472 0,277 0,2 1,821 1,783 0,281 0,851 1,896 1,8 0,500 0,81 1,759 1,762 1,616 0,959 1,187 0,51 0,318 1,388 0,578 0,344 0,451 0,415 1,391 0,586 1,773 1,174 1,250 1,984 1,37 0,358 0,803 0,419 0,943 1,576 1,944 1,114 0,613 0,796 1,485 1,469 0,919 1,769 0,841 1,400 1,876 1,214 0,321 0,601 1,369 1,62 0,900 1,784 1,826 0,246 1,926 0,118 1,613 0,835 0,782 0,73 1,934 0,379 0,185 0,887 0,175 1,869 0,264 0,31 1,846 0,330 1,254 1,431 0,253 1,237 0,970 0,31 1,579 1,226 0,242 1,387 0,942 0,205 1,436 1,271 0,391 0,757 1,701 1,66 0,729 1,89 0,781 0,583 0,234 0,273 0,924 0,23 0,186 1,585 1,464 0,525 0,86 1,275 0,40 0,26 1,788 0,93 1,854 0,739 0,882 1,193 0,922 1,226 0,4 1,23 0,679 0,923 1,134 0,937 1,376 0,758 0,494 1,414 1,603 0,731 1,240 0,679 0,387 0,389 1,633 1,666 0,257 1,643 0,320 0,18 0,41 1,446 1,289 1,764 1,808 1,869 1,617 1,905 1,953 0,599 0,291 0,969 0,976 1,156 1,149 1,665 1,520 0,189 1,672 1,793 0,77 0,937 0,134 0,898 1,629 0,26 0,519 0,719 1,269 0,909 1,280 1,629 1,139 1,689 1,1 1,628 1,494 1,915 1,45 0,575 1,221 0,427 1,89 0,7 1,285 0,164 0,989 0,557 1,592 1,502 1,107 1,68 1,479 0,848 1,785 0,945 1,983 0,966 0,485 1,395 0,617 0,373 1,106 1,506 1,104 0,52 0,93 1,173 1,528 1,350 0,568 1,731 0,735 1,988 0,629 1,187 0,732 0,579 0,383 1,496 1,985 0,752 0,715 0,246 0,779 1,909 1,356 0,817 0,684 0,190 0,187 0,656 1,278 0,394 1,261 1,606 1,367 0,110 0,850 1,654 0,467 0,616 1,966 0,376 0,67 1,420 1,840 0,179 1,659 1,329 1,252 1,35 0,64 0,177 1,709 1,243 1,18 0,312 0,57 1,824 1,408 0,541 1,760 0,600 1,511 1,831 1,584 1,174 1,949 0,87 0,716 1,380 1,452 1,669 1,595 0,478 1,420 0,727 1,923 0,975 0,63 0,465 1,34 1,240 1,525 1,815 1,973 1,876 1,176 0,791 1,179 0,829 1,762 1,988 1,984 0,859 0,622 1,107 0,738 0,965 1,924 1,11 0,274 0,735 0,294 1,222 0,98 0,460 1,344 1,850 0,697 0,11 0,663 1,737 1,683 0,869 1,658 0,789 1,800 0,550 1,603 1,981 0,421 1,691 1,915 1,635 0,163 0,273 1,652 0,919 0,806 0,310 0,700 1,415 1,325 0,873 1,374 1,247 1,522 1,108 1,51 0,455 0,695 1,259 0,252 0,695 1,120 0,374 0,450 1,877 0,417 1,642 1,420 1,942 0,390 1,43 0,603 1,938 1,103 1,465 0,38 1,386 1,485 1,363 1,103 1,984 0,884 1,842 1,633 1,295 1,641 0,380 1,710 1,782 0,592 0,942 0,405 1,510 0,530 1,666 1,986 0,648 0,366 1,752 0,373 1,866 1,453 0,566 1,543 0,477 0,920 1,362 1,369 1,540 1,155 1,676 1,66 0,55 1,813 1,280 0,346 0,892 0,732 0,512 0,80 0,62 0,178 0,727 1,592 1,907 1,701 1,670 1,320 1,172 1,20 1,657 1,685 0,339 1,343 0,428 1,625 0,226 0,258 0,143 1,641 1,159 0,395 1,17 1,303 1,989 1,188 1,898 1,689 1,115 0,913 0,566 0,328 1,175 0,822 0,364 0,65 0,102 1,628 0,964 0,552 1,742 1,652 0,180 1,477 1,55 1,806 1,644 0,251 0,278 0,508 0,537 0,762 1,889 1,268 1,188 1,924 1,304 0,49 0,331 0,659 1,827 0,145 0,737 1,745 1,736 0,281 0,187 0,245 1,127 1,682 1,444 1,504 0,429 0,548 0,522 0,161 0,87 1,498 1,225 1,976 1,950 1,680 1,117 0,15 0,680 1,951 0,359 1,110 1,175 0,92 0,747 1,151 1,719 0,736 0,875 1,741 1,84 1,327 1,136 1,981 1,935 0,337 0,624 1,841 1,723 1,64 1,103 0,787 1,637 0,627 1,201 1,95 0,420 1,110 0,316 1,232 1,999 0,706 0,830 0,46 0,305 0,507 0,376 1,919 0,553 1,933 0,235 1,34 0,573 0,905 0,247 1,257 1,903 1,76 0,257 1,880 0,699 1,487 0,821 1,86 0,543 1,746 0,529 0,393 1,516 0,504 0,214 1,631 0,144 1,447 0,232 0,979 1,115 0,269 1,762 0,370 1,325 1,512 0,810 0,838 1,979 0,31 1,79 1,525 1,936 1,722 1,388 1,829 0,912 1,533 1,184 0,631 0,933 1,196 0,882 1,904 0,394 0,198 1,239 1,975 0,22 1,71 1,89 1,122 0,843 0,291 1,887 1,835 0,973 1,602 1,60 1,893 0,262 1,771 1,69 1,613 1,545 1,806 0,322 0,501 0,605 0,650 0,951 1,791 1,903 1,793 0,203 1,914 1,411 1,164 1,126 1,384 1,717 1,990 1,871 0,177 0,509 0,490 1,544 1,679 1,69 1,584 0,397 1,631 1,278 0,247 0,171 0,473 1,891 1,725 1,471 1,268 1,160 0,481 0,924 0,413 0,122 1,571 1,161 1,713 0,112 1,248 1,939 0,464 1,719 0,795 0,181 0,310 1,586 0,29 1,287 0,323 0,762 0,551 0,472 0,491 1,121 1,674 0,665 0,409 1,70 1,556 1,772 1,221 1,764 1,266 1,965 0,615 0,422 1,498 0,481 0,486 0,829 1,690 1,103 0,398 0,23 1,522 0,765 1,940 1,120 1,794 1,703 0,360 1,276 0,774 0,8 0,297 0,275 1,204 1,884 0,156 1,9 1,736 0,97 1,605 1,468 0,952 0,171 1,208 1,292 1,909 0,537 1,840 0,394 0,898 0,49 1,850 0,842 0,920 1,978 1,537 0,147 1,740 1,161 0,398 0,642 0,21 1,914 0,625 1,238 1,953 1,644 0,517 1,184 1,890 0,6 0,473 1,747 0,393 1,76 0,659 1,819 1,688 0,815 0,327 0,752 0,303 1,361 0,123 1,363 1,45 0,996 0,284 1,846 1,293 1,82 0,422 1,958 0,64 0,542 0,494 0,198 0,873 0,109 1,833 1,480 1,892 1,558 1,732 1,406 0,647 0,68 1,693 0,829 0,581 1,42 1,303 1,444 0,455 1,694 0,145 0,812 0,313 0,749 0,120 0,456 0,95 1,781 1,228 0,225 1,806 0,949 0,882 1,292 1,967 0,385 1,25 1,810 1,690 1,738 0,243 0,735 0,285 0,974 1,5 0,830 1,174 1,996 0,408 1,840 0,737 0,559 0,239 0,11 1,397 0,997 0,83 0,890 1,760 0,604 1,686 0,379 1,687 0,518 0,194 1,671 0,141 0,446 0,245 0,451 1,604 0,270 1,776 0,1 0,554 0,124 1,763 1,864 1,509 0,254 0,133 0,488 0,966 1,575 0,518 1,275 0,711 1,817 0,12 1,862 0,275 1,159 1,337 1,764 0,769 0,309 1,648 1,793 1,615 0,877 0,291 1,961 1,96 0,343 1,869 1,637 1,306 0,301 1,186 0,268 0,941 1,750 1,561 0,337 0,592 0,430 1,984 0,132 0,250 0,240 1,770 1,920 0,983 0,535 0,103 1,591 1,990 0,848 1,740 1,28 0,43 1,940 0,898 1,233 1,627 0,508 0,188 0,413 0,107 0,446 0,213 0,990 1,786 1,77 0,585 1,929 1,942 0,236 0,951 1,922 1,499 1,125 0,326 1,719 1,811 0,502 0,990 0,821 0,299 0,509 0,725 0,622 1,571 0,956 0,353 0,675 0,289 1,609 1,276 1,364 1,850 0,219 1,652 0,457 1,60 1,187 1,680 0,709 1,580 1,174 0,592 1,5 1,206 1,7 1,375 1,715 0,293 0,616 0,879 1,360 0,909 1,502 0,323 0,981 0,920 0,502 0,375 1,498 0,5 0,80 0,537 0,903 1,336 1,903 0,462 1,102 1,789 1,742 0,744 0,715 0,458 0,698 1,461 0,101 1,593 0,312 1,503 0,988 0,825 0,79 0,37 0,9 1,528 0,839 0,433 1,183 0,16 0,649 0,787 1,819 0,705 0,670 0,117 0,640 1,260 0,246 1,642 0,845 1,807 0,735 1,54 0,776 1,139 1,200 0,630 0,380 1,589 1,578 1,91 0,597 1,795 1,249 1,371 0,617 0,832 0,897 1,593 0,34 1,251 1,969 1,883 0,396 0,778 1,483 1,682 1,446 0,604 0,809 1,76 1,163 0,528 1,220 0,717 1,143 0,629 0,345 1,996 1,879 1,407 1,670 0,687 1,150 0,198 0,179 0,467 1,402 1,8 1,258 1,573 1,689 1,814 0,775 0,732 1,284 1,716 0,976 0,673 0,595 1,318 1,178 1,309 1,174 0,554 1,151 0,935 1,114 1,515 0,171 0,338 1,136 0,872 0,760 1,9 1,554 0,617 1,289 0,453 1,286 1,18 0,215 0,975 1,716 0,695 0,536 1,217 1,166 0,302 0,929 1,853 0,560 1,909 0,154 0,973 0,327 1,898 1,379 0,442 0,400 1,199 1,436 0,477 1,73 0,915 1,659 0,133 0,113 1,98 1,389 0,14 0,993 1,853 1,148 0,781 0,343 1,861 1,230 1,93 0,404 1,599 1,867 1,866 1,438 0,698 0,973 1,298 0,716 0,715 1,747 0,315 1,999 0,401 1,606 0,301 0,607 1,692 0,864 1,299 0,630 1,125 0,699 0,872 1,843 1,978 1,523 0,237 1,108 0,841 0,896 0,102 0,348 1,913 0,559 1,972 1,751 1,242 0,224 1,483 1,591 1,666 0,693 0,143 1,891 0,74 0,997 1,234 0,687 0,861 0,787 0,876 1,287 0,434 1,471 0,510 0,798 0,935 1,343 1,355 0,815 0,794 1,972 1,844 0,17 1,40 1,664 1,396 1,69 1,215 1,517 0,387 1,913 1,148 1,901 0,177 0,321 1,800 1,63 1,529 0,341 1,247 1,552 0,146 0,669 0,702 1,961 0,915 1,920 0,7 1,70 0,619 0,968 0,424 0,388 0,812 1,57 1,195 1,145 1,923 0,267 1,281 0,826 1,514 1,649 1,542 0,674 1,507 1,105 0,3 0,924 1,28 1,872 1,687 0,786 0,58 0,965 0,736 1,138 1,375 1,372 1,232 0,601 0,212 0,519 0,459 1,585 1,305 0,847 1,273 0,207 0,346 1,371 0,92 1,709 1,33 1,983 1,670 1,588 0,824 0,814 0,348 0,41 1,445 1,692 1,79 1,108 1,349 1,842 1,915 1,181 1,852 0,450 0,878 0,192 0,410 0,84 1,903 0,150 1,502 1,916 0,523 0,671 1,315 0,686 0,848 1,913 1,60 0,772 1,874 1,954 0,103 0,711 1,391 1,673 0,372 1,286 0,52 1,42 0,252 0,414 1,85 1,71 0,373 0,774 1,69 0,569 0,938 1,900 0,346 1,93 0,432 0,270 0,859 1,569 0,318 0,666 0,858 0,972 0,144 0,346 1,145 0,682 1,898 1,16 0,762 0,439 1,881 0,188 1,596 1,655 1,956 0,822 0,195 0,951 0,674 0,149 0,694 1,550 1,472 0,419 1,679 1,628 1,486 0,914 0,420 1,500 1,383 1,260 1,515 0,750 1,363 1,339 0,453 1,300 0,324 1,70 1,52 0,176 1,880 1,258 0,465 1,310 0,579 1,95 0,138 1,950 0,171 1,93 1,504 0,964 0,835 1,748 1,267 0,176 1,574 1,989 1,103 1,726 0,131 1,550 1,47 0,845 0,755 1,311 1,152 0,874 0,695 0,372 0,328 1,591 1,152 1,831 1,323 0,428 0,674 1,630 0,764 1,283 0,654 1,806 1,732 1,813 0,468 1,443 0,408 0,585 1,47 1,900 1,275 0,125 1,145 1,368 1,578 0,584 0,157 0,952 1,26 0,715 0,75 0,304 1,671 0,592 0,334 1,519 0,107 1,733 1,20 1,804 0,739 1,931 1,530 0,162 1,223 1,905 0,952 0,567 0,211 0,517 1,438 0,881 0,277 1,460 1,250 1,688 0,43 0,304 0,949 1,132 1,583 1,578 0,365 1,220 0,685 0,164 1,695 1,495 0,851 0,552 1,417 1,92 1,83 0,195 1,401 0,949 1,132 0,115 1,762 1,742 1,760 1,416 1,531 0,305 1,798 1,910 0,976 1,93 0,935 0,283 0,574 1,615 0,837 1,613 1,134 1,863 1,501 0,824 1,160 0,572 1,19 1,835 1,564 0,990 1,783 0,990 0,235 0,74 1,635 0,588 0,754 0,446 0,388 1,962 1,129 0,708 0,195 0,883 0,775 1,99 0,146 1,645 1,158 1,138 1,194 0,691 1,901 0,264 1,992 1,693 0,118 1,81 1,361 1,733 0,229 0,942 1,616 1,156 0,670 0,369 0,802 0,607 0,621 0,869 1,295 1,363 0,719 1,94 1,451 1,819 0,709 0,56 1,813 1,441 1,946 1,762 1,524 0,583 0,520 0,922 1,160 0,978 0,240 1,399 1,686 0,900 1,6 0,647 0,729 1,540 0,164 1,130 0,825 1,284 0,22 0,603 1,315 1,66 1,473 1,866 0,853 0,437 0,929 0,291 1,473 0,466 1,481 1,80 0,605 0,597 0,883 1,363 1,555 1,559 0,643 0,712 1,243 1,114 1,385 0,713 0,781 0,40 1,188 1,74 1,70 0,753 1,163 0,416 1,854 0,350 0,579 1,120 0,544 0,452 0,584 1,351 0,922 1,537 1,618 1,209 0,311 0,27 0,135 1,538 1,193 1,605 1,380 1,25 0,363 0,549 0,864 1,514 1,804 0,888 0,410 0,934 0,22 1,79 1,480 0,418 1,174 0,323 0,700 1,838 1,390 0,654 0,168 1,178 1,211 1,46 1,524 1,438 1,160 1,678 1,752 0,157 0,202 1,539 0,238 1,823 0,379 1,284 1,819 0,619 1,283 1,643 0,225 1,583 0,313 0,367 0,763 1,396 1,413 1,418 1,257 1,872 0,246 0,402 1,743 1,557 1,519 1,275 0,219 1,307 0,684 1,593 1,749 0,619 1,792 0,728 0,176 0,17 0,478 0,191 1,457 0,802 1,631 0,906 0,185 0,575 0,417 1,768 0,476 0,217 0,378 0,12 0,529 0,955 0,142 0,490 0,647 0,262 0,360 0,499 1,502 0,519 0,53 0,235 1,707 1,189 0,187 1,52 1,647 0,311 0,651 1,148 0,98 1,222 1,588 0,62 1,6 1,692 1,32 0,764 0,904 1,656 1,499 0,957 0,690 1,543 1,463 0,979 1,548 1,242 0,201 0,80 1,360 1,393 0,153 1,884 1,324 1,697 0,224 0,556 0,473 0,476 1,363 1,237 0,122 1,256 1,838 1,699 0,991 0,798 1,562 0,565 1,711 1,661 1,828 1,902 1,165 0,823 1,127 0,60 0,382 1,544 1,91 0,498 0,344 0,982 0,313 0,794 1,96 1,530 0,187 1,525 1,351 0,346 1,589 0,876 1,466 1,400 1,281 0,618 0,121 1,275 1,321 0,607 1,978 1,273 1,323 1,690 1,141 0,893 0,356 0,712 0,508 0,289 1,917 0,61 1,183 1,688 0,553 0,304 1,440 1,669 0,913 0,429 1,751 0,858 0,7 0,878 0,161 0,261 0,905 1,785 1,863 0,637 1,41 0,652 0,815 1,46 0,109 0,17 0,899 0,763 0,202 1,63 0,911 0,735 0,835 0,130 0,199 1,75 1,508 0,542 1,91 0,990 1,762 1,615 1,952 0,478 1,118 1,891 1,43 1,608 1,951 0,372 0,637 1,867 1,937 0,773 0,759 0,135 1,458 1,382 1,74 0,172 0,677 0,724 0,989 1,437 1,119 1,704 1,44 1,602 1,877 1,714 0,312 0,825 1,289 0,163 1,809 0,335 0,28 0,408 0,552 1,338 0,307 0,162 0,218 0,612 0,309 1,346 1,965 1,471 1,127 0,8 0,125 0,872 1,920 0,540 0,672 0,505 1,150 0,194 0,402 1,628 1,943 0,582 0,977 0,408 0,610 0,642 1,764 1,356 1,848 0,227 0,298 1,91 1,770 1,768 0,235 0,908 1,606 0,623 1,884 1,989 0,826 0,202 1,959 0,680 1,772 1,628 1,925 1,757 0,947 0,428 1,662 1,221 1,8 1,233 1,216 1,859 1,106 1,628 0,781 1,707 1,854 0,83 1,438 1,183 0,638 1,823 0,740 0,35 1,600 0,275 1,915 0,979 1,597 0,273 1,664 1,872 0,963 1,949 1,958 1,557 0,889 1,733 1,538 1,505 0,303 0,266 1,1 1,765 1,980 0,924 0,611 1,85 1,221 1,642 0,532 0,227 1,507 0,962 1,384 1,547 0,387 0,881 0,107 1,439 0,697 0,996 0,286 0,627 1,789 0,950 1,739 1,46 0,94 0,654 1,560 0,311 0,292 1,881 0,852 1,232 1,58 1,84 0,822 1,979 1,976 1,410 1,6 1,224 1,508 0,693 0,537 0,825 1,913 0,552 0,891 1,668 1,933 1,908 1,420 1,263 0,517 1,704 1,77 0,779 0,951 0,467 1,290 1,147 1,704 0,867 0,991 0,162 1,521 1,947 1,854 0,773 1,50 1,6 0,895 1,659 0,185 0,28 0,171 1,513 0,681 1,875 1,234 0,714 0,246 1,54 0,996 0,752 1,133 0,895 1,539 1,587 0,444 1,145 1,216 1,460 0,285 0,491 0,908 0,396 1,399 1,147 1,711 1,213 0,194 1,283 1,118 1,428 1,537 0,670 1,116 0,48 0,586 1,329 1,88 1,910 0,212 0,889 1,488 0,427 0,794 0,693 0,180 1,763 0,301 0,558 0,229 1,52 0,361 0,801 0,506 0,556 1,142 0,908 1,394 0,790 1,928 1,206 0,933 0,190 1,257 1,260 0,423 0,635 1,859 0,420 0,179 0,299 0,343 1,147 1,819 0,114 1,83 1,785 0,841 0,485 1,190 0,271 1,215 0,922 0,220 0,572 1,9 1,59 0,91 0,445 1,538 1,939 1,446 0,500 0,971 0,393 1,702 0,166 1,837 1,460 0,199 0,831 1,230 0,485 0,613 0,711 0,614 1,652 1,967 0,265 1,584 0,96 1,506 0,62 0,923 1,716 0,851 1,830 1,522 1,276 1,332 0,846 0,774 0,196 0,190 1,159 0,348 1,65 1,396 0,285 0,410 0,667 0,895 0,277 0,986 0,129 0,935 1,947 0,581 1,756 0,805 1,900 1,519 0,871 1,711 0,69 0,149 0,147 1,233 0,670 1,583 1,305 1,226 0,220 1,250 1,151 0,271 1,640 1,469 0,382 1,510 1,643 0,537 0,731 1,581 0,997 0,470 1,159 1,746 1,386 0,142 1,995 1,138 1,379 0,979 0,775 0,448 1,660 1,792 1,448 0,241 1,192 0,748 0,261 0,851 1,271 0,893 1,931 0,770 0,394 0,773 1,662 0,445 0,385 0,302 1,52 0,296 0,560 1,53 1,458 0,277 0,508 1,186 0,731 0,551 1,704 0,53 0,787 1,804 1,595 1,255 1,614 0,593 1,248 0,713 0,535 1,142 1,210 0,898 0,292 0,698 0,265 1,553 1,172 0,261 1,355 0,740 1,752 0,436 0,535 1,804 1,441 1,26 0,929 0,59 1,570 0,751 1,509 0,206 1,858 0,376 0,663 0,258 0,530 1,360 0,796 0,962 0,253 1,652 0,558 1,905 0,985 0,680 1,579 1,728 1,331 1,306 0,605 0,544 1,405 0,326 1,334 1,768 1,245 1,117 0,165 1,47 0,553 1,722 1,830 0,245 1,962 0,441 1,367 1,870 0,952 1,511 1,262 1,521 1,450 1,85 1,570 0,153 0,225 1,575 1,630 0,106 0,776 1,609 0,468 0,88 1,346 1,769 1,622 0,195 1,124 1,710 1,183 0,200 0,136 0,432 1,610 1,832 1,841 1,433 0,534 1,296 0,335 0,630 0,68 1,977 1,200 1,712 0,575 0,190 0,21 0,582 0,515 0,424 1,999 1,468 0,697 1,979 1,856 0,514 1,34 1,665 0,904 1,58 0,867 1,56 0,315 0,924 0,833 0,536 0,96 1,605 1,563 0,509 0,503 1,139 1,123 1,828 1,437 1,453 0,46 0,678 1,479 0,895 1,101 1,719 1,785 1,3 1,210 0,875 1,986 1,808 0,99 1,115 0,152 0,232 1,52 1,120 0,256 1,610 0,838 1,496 0,773 0,119 0,439 0,430 0,914 0,561 1,275 0,919 1,950 0,633 0,993 1,43 0,70 0,8 1,242 0,445 0,425 1,980 0,66 1,977 1,571 1,300 1,271 0,411 0,77 1,483 1,244 0,81 1,599 1,196 0,920 1,599 1,353 1,664 1,468 1,708 0,230 1,514 0,863 0,985 1,550 0,467 0,979 0,205 1,54 0,177 0,305 0,996 0,159 0,463 0,166 1,481 0,613 0,660 0,456 1,665 0,951 0,647 1,588 0,325 1,926 0,989 0,700 1,414 0,616 0,834 1,505 0,306 0,665 1,250 0,610 1,670 0,429 0,398 1,964 0,768 0,902 0,821 0,794 1,538 0,67 1,781 0,126 1,617 1,310 0,414 1,54 0,448 1,754 1,158 0,267 0,928 0,438 1,42 1,776 1,774 1,70 1,7 0,495 0,141 0,377 0,726 1,193 1,558 0,225 0,527 1,86 1,801 0,131 1,834 1,509 0,325 1,996 1,332 1,523 0,833 0,774 1,529 0,570 0,952 1,771 0,341 1,923 0,53 0,127 1,347 1,578 1,739 1,670 1,236 0,740 1,69 0,385 1,547 1,418 0,966 0,995 0,936 1,32 1,835 0,185 0,726 1,30 1,755 0,443 1,73 1,763 1,690 1,791 1,325 1,549 1,162 1,883 0,611 1,212 1,213 0,713 0,728 0,136 0,507 1,89 0,601 0,439 0,539 0,430 1,267 0,680 0,395 1,887 1,926 1,414 1,278 0,542 0,946 1,622 0,870 1,427 1,505 0,952 1,848 0,266 0,120 0,853 0,926 1,764 1,515 1,201 1,395 1,81 0,581 1,679 1,74 1,105 1,446 0,565 1,327 0,478 0,663 1,940 0,256 1,736 0,57 1,807 1,337 0,864 0,41 1,346 1,161 1,305 0,757 1,570 1,226 1,425 1,849 0,26 1,127 1,5 0,531 1,976 1,138 0,354 1,247 1,705 0,813 0,42 0,271 0,551 0,49 0,690 0,970 1,492 1,838 0,655 1,577 0,200 0,67 1,922 1,188 1,167 1,784 1,745 1,321 0,900 0,65 1,884 0,360 1,485 0,148 1,976 0,91 0,121 0,734 0,708 0,562 1,524 0,889 1,391 1,985 1,762 1,810 0,23 0,370 1,210 1,396 0,190 0,542 0,419 1,927 0,122 1,485 0,202 0,727 1,654 0,701 1,956 1,211 1,832 1,699 1,410 1,644 0,57 0,153 1,665 1,989 0,769 0,655 1,390 1,136 0,170 1,621 0,983 0,620 0,628 0,474 1,690 0,990 1,897 0,508 0,644 0,219 1,867 0,382 0,906 1,423 0,91 1,370 1,650 1,573 1,413 0,506 0,64 1,576 1,397 1,803 0,318 1,962 0,702 1,264 0,316 0,68 0,397 0,440 0,147 1,250 1,25 0,899 0,180 1,974 0,421 1,65 0,749 1,320 1,286 1,79 1,981 0,981 0,311 1,370 1,355 1,413 1,318 1,294 0,494 1,291 1,210 0,727 1,451 0,62 1,345 1,894 1,282 0,286 0,2 1,4 1,548 0,206 1,622 1,996 0,550 1,246 1,332 1,171 0,573 1,346 0,867 1,488 0,341 0,584 1,474 1,129 1,785 1,916 1,13 1,871 0,287 0,692 0,528 0,744 0,882 0,787 0,981 0,996 0,195 0,39 1,437 0,194 0,985 0,403 0,461 1,314 0,848 0,427 0,528 0,632 1,691 0,89 0,741 0,501 0,589 0,995 0,763 0,430 1,861 1,289 0,633 1,231 0,36 0,355 1,609 0,943 1,908 0,102 1,684 0,548 0,656 0,166 0,134 0,362 1,309 1,920 1,243 1,126 0,981 1,955 0,289 0,162 0,278 1,247 0,957 1,429 0,404 1,733 1,609 0,644 1,713 1,788 1,700 0,915 0,703 1,484 0,570 0,339 0,695 0,712 1,363 1,74 1,904 1,951 1,280 0,995 1,804 0,357 1,286 1,236 1,670 1,631 0,920 0,288 0,899 1,154 0,589 1,948 0,381 1,675 1,491 0,484 1,27 0,833 0,18 0,112 1,660 0,285 1,693 1,346 0,404 1,498 0,254 1,107 0,975 1,527 0,991 1,607 0,326 0,864 1,400 1,929 1,775 1,976 0,802 0,195 0,472 0,277 1,749 1,958 0,890 0,368 0,78 1,294 1,345 0,960 1,580 0,707 1,491 0,270 0,893 0,841 0,988 0,720 1,473 0,606 0,358 0,823 1,788 0,261 1,825 1,742 0,86 1,142 1,822 0,717 1,719 0,814 1,202 1,284 1,634 1,169 1,128 1,548 0,360 0,880 0,666 0,21 0,160 0,435 0,470 1,939 0,951 0,389 1,85 0,190 0,114 1,891 1,408 1,863 0,316 1,948 0,170 0,247 1,161 1,274 0,399 0,613 1,405 1,650 1,95 0,706 1,824 1,400 1,572 0,646 0,500 0,255 1,503 0,3 0,94 0,61 0,0 0,778 0,390 1,942 0,751 1,98 1,267 0,147 0,459 1,288 0,371 1,232 1,0 1,529 0,788 0,692 0,100 0,716 1,619 0,942 1,361 1,531 0,791 0,158 1,787 1,131 0,840 1,182 0,92 0,913 0,191 0,932 1,993 0,403 0,738 1,28 0,769 1,436 0,143 0,365 0,288 1,896 1,580 1,307 1,334 0,530 1,764 0,839 1,162 1,890 0,484 0,270 1,28 1,570 0,956 0,180 0,398 1,561 1,499 0,100 0,90 1,264 0,669 1,760 1,767 0,777 1,645 0,821 1,736 0,883 0,205 1,466 0,673 0,912 1,721 1,57 0,803 0,746 0,512 1,663 1,253 1,864 0,122 1,727 1,795 0,95 1,816 0,338 1,749 0,961 0,218 0,369 1,981 0,931 1,476 0,765 1,743 1,130 1,511 0,12 1,413 1,68 0,164 1,735 1,252 0,234 1,492 0,127 0,842 0,13 1,89 0,676 1,941 0,739 0,948 1,569 1,865 1,294 0,46 0,551 1,410 1,19 0,254 0,148 0,574 1,792 1,105 0,774 1,328 0,219 1,249 1,232 0,671 1,41 1,40 0,827 0,328 0,970 1,826 1,540 1,625 0,946 0,465 1,568 1,582 0,88 0,24 1,59 1,405 0,954 0,915 1,975 0,925 1,245 0,752 0,816 1,864 1,260 1,129 0,520 0,674 0,420 0,19 0,142 1,365 0,972 1,110 1,821 0,772 0,386 1,411 0,601 0,842 1,556 1,819 1,553 0,243 0,854 1,758 1,522 1,187 0,89 0,403 1,1 0,695 0,626 1,99 1,714 0,934 1,746 1,946 0,830 0,429 0,148 0,86 1,895 1,407 0,392 0,759 1,390 0,883 0,280 0,516 1,1 1,36 0,768 0,153 1,123 0,768 1,694 0,523 0,751 1,534 0,62 0,655 0,387 0,818 0,720 1,453 1,548 0,365 1,217 1,905 0,431 1,986 1,862 1,238 1,635 1,660 0,47 0,814 1,659 0,696 1,79 1,716 0,621 1,706 1,542 1,771 1,430 0,727 0,727 0,945 0,721 0,353 0,135 0,962 0,556 0,332 1,802 1,734 0,728 1,996 1,170 0,412 1,533 1,143 0,188 1,75 1,123 0,464 1,608 1,580 0,510 0,263 1,174 1,975 1,669 1,391 1,725 1,162 0,647 1,430 1,350 0,938 0,508 0,84 0,956 1,610 0,954 1,356 0,618 1,181 1,863 1,115 1,442 0,611 1,724 1,504 0,763 1,427 0,170 0,647 1,263 0,933 0,274 0,715 0,441 0,47 0,631 0,901 1,962 1,903 1,965 0,841 1,707 0,516 1,447 0,736 1,173 0,27 0,529 0,851 0,615 1,443 0,567 0,614 0,294 0,660 1,28 1,996 1,475 0,662 1,845 1,220 1,863 0,503 1,523 1,714 0,185 0,79 1,764 0,223 1,378 1,622 0,960 1,866 0,719 0,620 0,962 0,800 0,974 0,303 0,40 0,562 1,336 1,373 1,769 0,792 1,565 1,465 1,495 0,893 1,409 0,650 1,6 0,695 1,226 0,864 1,48 0,661 0,451 1,6 0,460 1,268 0,912 1,681 1,278 0,872 1,604 1,706 0,554 0,727 0,972 1,661 0,911 0,51 0,807 1,743 1,882 1,574 1,952 0,606 1,149 0,35 0,151 1,395 0,627 1,635 1,399 0,64 0,966 1,186 0,200 0,168 0,136 0,110 1,470 1,762 0,698 1,630 1,814 0,463 1,935 1,264 0,934 0,851 1,958 1,408 1,33 1,59 1,94 1,980 0,372 0,751 0,240 0,782 1,894 1,818 0,508 1,890 0,234 0,626 0,10 1,166 0,645 1,603 1,391 0,763 0,794 0,574 1,131 1,797 0,495 0,757 0,924 1,591 0,818 1,368 1,526 0,170 0,796 0,654 1,509 1,97 0,729 0,675 0,853 1,178 0,134 0,893 0,483 1,976 1,283 1,568 0,148 1,721 0,965 0,580 0,293 1,61 0,593 1,808 1,597 1,64 0,684 0,343 0,82 0,328 0,897 0,556 1,733 1,166 0,651 1,753 1,991 1,32 1,313 0,971 0,938 1,889 0,837 0,547 1,538 1,484 0,311 0,18 0,591 1,16 0,585 0,388 1,150 1,961 1,497 0,718 0,992 1,212 0,86 1,853 0,42 0,321 0,131 0,39 0,69 1,221 0,93 1,559 1,91 0,280 0,845 1,769 0,717 0,63 0,950 1,221 1,169 1,179 1,804 1,620 0,615 0,295 1,404 0,358 1,516 1,535 1,291 0,788 0,920 1,361 0,868 1,524 1,990 1,715 0,427 1,224 1,354 1,716 1,307 1,197 0,401 0,809 1,813 1,113 0,476 1,126 0,145 1,529 0,237 1,265 0,585 0,446 0,672 0,366 1,975 1,482 0,849 0,40 1,494 0,381 0,240 0,615 0,616 1,791 0,630 1,308 1,572 1,347 1,962 0,184 0,865 1,524 1,531 0,997 1,496 0,536 1,84 0,266 0,438 0,102 0,388 0,593 0,799 1,347 0,925 0,614 1,697 0,965 1,436 1,330 0,389 0,750 0,363 1,196 0,245 1,982 0,742 1,985 0,917 0,227 1,820 0,518 1,406 0,830 0,572 0,203 1,616 1,364 1,265 0,637 0,733 0,837 0,278 0,450 1,182 0,464 1,220 0,520 0,993 0,789 0,121 1,26 0,112 0,768 0,920 1,306 0,118 1,550 1,792 0,758 1,331 0,630 0,16 1,741 1,502 0,159 1,242 1,542 1,596 1,68 0,159 1,800 1,523 0,17 0,830 0,766 1,408 0,995 1,900 1,698 0,373 0,866 1,933 1,834 0,464 1,454 0,896 0,381 1,312 1,32 1,872 0,280 0,379 1,446 0,679 0,557 0,466 1,481 1,880 1,74 0,579 1,564 1,200 1,868 1,139 1,421 1,146 0,937 1,907 1,296 1,386 0,678 0,289 0,686 1,305 0,251 0,116 1,311 0,823 0,473 1,862 1,91 1,774 1,948 0,751 1,100 0,147 0,611 0,465 0,165 0,183 0,954 1,161 0,425 1,690 0,492 1,10 0,163 0,215 0,480 1,560 0,230 0,267 1,973 1,715 1,504 1,514 0,692 0,804 1,978 1,231 1,657 0,68 0,771 0,190 1,713 0,611 0,760 1,427 0,530 0,206 0,673 0,199 1,733 1,396 0,586 1,38 1,950 0,899 0,716 1,630 0,631 1,350 0,236 0,167 1,90 1,408 1,578 0,978 1,114 1,196 1,936 0,317 0,434 0,497 1,135 0,439 0,426 1,340 0,961 1,351 1,182 1,424 1,77 1,647 1,554 0,522 0,375 1,792 0,193 0,305 1,92 1,803 1,462 1,540 0,549 0,734 0,892 0,776 0,639 1,31 1,602 1,401 0,356 0,445 1,399 1,199 1,924 0,301 1,195 0,65 1,440 1,659 0,380 0,565 1,125 0,658 1,358 0,639 0,202 1,665 1,96 0,702 0,482 1,557 1,272 1,652 1,885 0,369 1,83 0,442 1,73 0,45 1,625 0,259 0,963 1,409 0,978 0,969 1,736 0,181 0,449 0,178 1,889 1,916 1,464 0,826 1,19 0,454 0,691 0,801 0,760 1,358 1,179 0,607 0,939 0,77 0,603 0,768 0,459 0,440 1,6 0,883 1,117 0,158 0,956 0,988 1,334 1,532 0,981 1,631 1,630 0,349 1,484 0,4 0,916 1,512 1,644 1,490 0,766 0,978 1,740 1,914 1,880 1,619 1,589 0,563 1,575 1,933 1,933 0,859 1,929 0,106 1,941 1,837 0,296 0,775 1,834 0,354 1,704 1,199 1,660 1,872 1,550 0,941 0,485 0,871 1,544 1,880 1,899 0,675 0,348 1,224 0,869 0,907 0,325 0,274 0,478 1,721 1,153 0,136 0,530 1,582 0,576 1,595 1,289 0,863 0,524 0,930 1,566 0,592 0,674 0,728 1,200 1,972 1,181 0,441 0,420 0,772 1,585 0,429 1,998 1,168 0,387 0,262 1,196 0,822 0,530 1,985 0,108 1,917 1,287 1,608 1,362 0,778 1,45 0,850 1,345 1,514 1,642 0,141 1,605 0,662 1,619 1,274 1,419 1,381 0,925 0,757 1,34 1,635 0,190 0,348 0,554 0,656 1,207 1,817 1,581 0,382 1,205 1,788 1,528 1,638 0,938 1,524 1,777 1,395 1,728 1,955 0,25 1,59 1,679 0,119 1,526 1,195 1,593 1,711 1,728 1,518 0,310 0,44 0,509 1,473 0,402 1,939 0,32 1,359 0,9 1,907 0,350 1,525 0,198 1,711 1,467 0,708 1,364 1,458 1,375 1,823 1,46 0,131 0,211 0,634 1,629 0,396 0,990 0,992 1,789 1,844 1,927 1,200 0,588 1,44 0,910 0,180 1,733 0,166 1,85 0,730 1,764 0,686 0,242 0,170 0,835 1,921 0,792 0,352 0,288 0,251 1,38 0,185 0,556 0,559 0,390 0,628 0,133 1,382 0,116 0,668 0,461 0,164 0,677 1,117 1,596 1,533 1,381 1,767 1,499 0,954 1,508 1,717 0,312 1,349 0,365 1,698 1,536 0,327 1,324 1,984 1,613 1,496 1,988 0,693 1,455 0,355 0,942 0,902 1,663 1,123 1,473 0,183 1,200 0,2 0,538 1,797 0,455 0,647 1,243 0,21 1,875 1,723 0,974 0,587 0,174 1,471 1,243 1,470 1,541 1,6 0,995 1,625 0,944 0,87 1,866 1,750 1,31 1,49 1,968 0,182 1,745 1,77 1,217 0,522 0,391 0,387 0,560 0,318 0,965 1,726 1,60 1,802 1,671 1,785 1,328 1,196 0,169 1,43 1,583 0,244 1,2 0,524 0,542 0,487 1,526 0,124 0,361 0,690 1,243 1,562 0,568 1,79 0,893 1,556 1,519 0,384 1,389 0,241 1,415 1,396 0,589 0,9 1,622 1,185 1,769 0,379 1,104 0,159 0,268 1,887 0,902 1,420 1,996 0,70 0,644 1,973 1,471 1,548 1,968 0,245 0,205 1,725 1,227 0,222 1,123 1,121 1,849 1,178 0,373 1,499 0,655 1,386 1,49 1,212 1,528 1,22 1,420 0,542 0,65 1,965 1,828 1,486 0,393 1,156 1,502 0,628 1,700 1,176 1,220 1,436 1,175 1,864 1,274 0,538 1,700 1,310 1,579 1,302 0,210 1,979 0,812 1,430 1,300 1,292 0,872 1,920 1,691 0,339 1,701 0,149 0,997 1,843 1,975 0,805 0,386 0,754 1,378 0,428 0,848 0,161 1,994 0,791 0,847 1,653 0,272 0,305 1,494 0,446 1,978 0,835 0,674 0,833 0,888 0,887 0,20 0,562 0,32 0,367 1,925 0,390 1,520 1,154 0,3 0,942 0,618 0,41 0,260 1,605 1,831 1,837 0,754 1,688 0,200 0,519 0,407 0,375 0,653 0,920 1,623 1,190 0,765 0,910 1,623 1,513 0,552 1,117 0,657 0,856 0,971 0,610 1,573 1,776 0,375 1,673 1,661 1,909 0,99 1,562 1,182 0,796 1,463 0,596 0,348 0,114 0,287 1,195 1,568 1,888 1,589 0,180 0,123 0,739 0,649 0,66 0,125 1,562 0,178 1,685 0,423 0,567 1,365 1,899 1,187 0,673 0,151 1,110 1,555 1,34 0,666 1,307 1,762 0,965 0,921 1,872 1,58 0,220 0,277 0,980 1,114 0,25 0,795 1,575 1,343 1,110 0,36 1,943 0,829 1,792 0,371 1,11 0,899 1,501 1,223 1,478 0,199 1,915 1,827 0,238 0,222 0,410 1,761 0,21 1,932 1,928 0,726 0,46 1,482 0,333 1,828 0,651 0,483 1,369 0,242 0,775 1,589 0,201 0,281 0,876 1,861 0,428 1,663 0,538 1,204 1,171 1,193 1,600 0,524 1,176 0,514 1,241 0,43 1,586 1,757 0,221 1,380 0,853 0,757 1,286 1,87 0,384 1,110 0,414 0,830 1,935 1,478 0,816 1,752 0,786 0,431 0,989 0,866 0,914 0,422 0,99 0,185 0,131 0,554 1,710 0,705 1,270 1,817 0,552 1,185 0,321 0,968 0,358 0,102 1,217 1,859 0,101 1,347 1,721 0,665 1,101 1,419 1,603 0,446 1,627 0,983 1,919 1,530 1,687 0,518 0,165 0,635 1,558 1,457 0,452 0,877 1,87 1,131 1,908 0,911 0,446 0,190 1,368 1,462 1,83 0,25 1,608 0,981 1,520 0,883 0,920 0,427 1,149 1,315 1,676 0,746 0,820 0,279 0,289 1,301 0,929 0,0 1,793 1,508 0,521 0,135 1,144 0,406 0,431 1,925 1,136 1,400 0,113 1,316 0,228 0,570 1,526 1,998 1,166 1,415 1,872 1,125 0,484 1,845 0,570 0,717 1,660 1,721 0,972 0,695 0,880 1,163 1,228 1,274 1,977 1,656 0,300 1,318 1,186 0,432 1,884 0,746 0,141 1,310 1,531 0,685 1,164 1,600 1,193 0,71 0,90 1,722 0,780 1,846 1,683 0,728 0,514 1,601 0,219 1,91 1,816 1,446 0,474 0,488 1,832 1,824 1,681 0,437 1,534 1,110 0,464 1,183 0,565 1,833 1,286 1,590 1,369 0,136 1,614 0,693 0,841 0,169 0,357 1,266 1,465 1,676 1,663 0,660 1,192 1,176 0,366 0,761 0,198 1,12 0,643 0,685 0,509 1,698 0,110 0,414 1,157 0,986 0,372 1,914 1,113 0,348 1,599 1,593 0,544 0,274 1,657 0,963 1,831 0,875 1,768 1,541 0,831 1,164 0,873 0,584 1,607 0,37 0,36 1,111 0,12 1,57 0,380 1,685 1,563 0,100 0,430 0,566 0,760 1,303 0,392 0,507 1,266 0,962 0,710 0,662 0,673 1,928 1,300 1,313 0,187 0,405 0,871 0,267 0,640 0,879 0,413 1,558 0,194 0,56 1,954 1,850 0,742 1,239 0,328 0,961 0,530 0,774 0,119 1,319 1,441 1,731 1,2 1,738 0,64 1,676 1,881 1,244 0,288 1,258 1,58 0,682 1,396 0,454 1,427 0,251 1,235 0,995 1,818 1,286 0,540 0,557 1,744 1,713 0,469 1,821 0,113 0,64 0,419 0,976 0,872 1,421 0,86 0,750 0,722 1,220 1,454 0,658 0,543 1,999 0,196 1,231 1,740 1,830 1,732 0,735 0,413 1,139 0,412 0,425 0,42 1,938 0,566 0,808 0,882 1,754 1,888 0,552 0,676 0,215 0,881 1,96 0,139 0,613 1,411 1,610 1,392 0,958 0,343 0,347 0,857 1,45 1,446 0,76 1,887 0,144 0,516 1,676 1,514 1,831 0,529 1,782 0,134 0,775 0,267 0,787 1,542 0,415 0,466 0,296 0,352 0,741 0,254 1,455 1,673 0,450 0,239 0,513 0,723 0,197 0,964 0,21 1,581 1,93 0,267 0,771 0,382 0,259 0,275 0,311 0,130 0,199 1,116 0,255 1,141 1,919 0,604 0,428 0,998 1,407 1,44 1,392 0,49 0,219 0,592 0,637 1,85 1,62 1,108 1,84 1,489 1,178 0,434 1,984 1,768 0,586 1,273 1,953 0,109 1,674 1,730 0,582 1,25 0,695 0,805 1,313 1,363 0,510 1,287 1,141 0,888 1,608 0,913 1,905 0,515 1,85 1,462 0,122 1,333 0,948 0,558 1,315 0,771 0,771 1,4 0,753 1,231 0,918 0,602 1,98 1,293 0,618 1,241 0,955 0,770 0,145 1,40 1,593 1,438 0,815 0,168 1,153 0,317 0,855 0,71 0,793 1,764 0,7 1,456 0,574 1,39 1,520 1,400 0,548 0,73 1,451 1,851 0,293 1,586 0,384 1,683 1,568 0,757 0,374 1,253 1,629 1,300 0,201 0,248 0,176 0,906 0,378 1,741 1,851 1,450 0,452 1,990 0,64 1,221 0,306 0,115 1,778 0,656 0,291 1,372 1,468 1,545 1,530 0,578 0,257 0,345 0,966 0,22 1,2 1,822 1,456 0,852 1,711 1,842 1,864 0,929 0,768 1,46 0,324 0,641 0,29 0,947 0,958 0,403 0,129 1,951 1,317 0,167 1,315 0,479 0,163 1,169 1,229 0,589 0,302 1,2 0,78 0,352 0,941 1,905 1,122 0,179 0,97 1,794 0,662 1,295 1,208 0,365 1,819 1,948 0,152 0,518 1,305 0,908 1,783 1,411 1,650 1,548 0,641 1,373 1,865 1,257 0,266 1,253 0,139 1,522 0,915 1,879 1,210 0,745 0,742 1,70 1,459 0,160 1,463 1,320 1,522 1,923 0,892 0,130 0,401 0,140 1,382 0,849 1,83 1,113 1,784 1,647 1,648 1,343 0,900 1,132 0,722 0,388 1,398 1,101 0,989 1,977 1,425 0,228 0,330 0,168 1,270 1,242 0,694 0,33 0,507 0,528 0,716 0,965 1,720 0,80 1,342 1,387 0,723 0,459 0,714 1,305 1,211 1,408 1,924 1,638 0,834 1,488 1,964 1,265 1,937 1,127 0,327 1,909 1,826 1,686 1,487 1,720 1,667 0,93 0,758 0,732 0,951 1,507 0,813 0,299 1,834 1,638 0,648 0,615 1,861 1,751 1,649 0,73 0,930 1,513 1,379 1,300 0,200 0,704 0,239 1,636 1,768 0,529 0,500 0,181 0,483 0,990 0,977 1,134 1,339 0,312 0,48 0,812 0,928 0,241 0,238 1,274 1,113 1,139 1,648 0,566 0,313 1,779 1,942 1,996 0,234 0,442 1,503 1,620 1,163 0,76 1,821 0,694 0,908 1,851 1,467 0,495 1,133 1,368 0,63 0,500 0,221 0,432 1,393 1,899 0,420 0,203 1,536 1,363 1,555 1,21 0,309 1,752 0,196 1,851 0,935 0,443 1,394 0,433 1,58 0,108 1,403 1,451 1,91 0,932 1,485 0,561 0,406 0,820 0,585 0,589 0,213 0,760 1,285 1,347 1,994 1,705 0,216 0,87 0,90 1,534 1,44 0,641 0,232 0,325 0,125 0,710 0,818 0,465 1,458 1,604 0,768 1,529 1,131 1,260 1,900 0,725 0,654 1,969 1,601 0,794 0,803 0,294 1,705 0,341 1,612 0,315 1,6 1,454 0,994 0,216 1,175 0,619 1,651 0,266 0,512 1,901 1,841 1,598 1,342 1,979 1,741 1,939 0,68 0,978 1,628 1,319 0,933 1,919 0,172 1,572 0,546 1,77 0,251 0,542 1,633 1,352 1,332 1,154 0,287 0,796 0,694 0,725 1,451 0,18 0,480 1,125 0,777 1,299 0,264 0,338 0,985 1,538 0,155 0,433 0,280 0,607 1,139 1,428 1,299 0,942 1,991 1,434 0,281 1,850 0,932 0,757 0,443 0,160 1,556 1,641 1,636 0,601 0,922 0,77 1,350 1,756 0,608 1,520 0,967 0,175 1,921 1,490 0,543 1,844 1,586 1,369 0,675 1,475 1,784 0,135 1,72 0,330 0,207 0,95 1,132 0,479 1,460 1,806 0,428 1,795 1,178 1,28 1,926 1,745 1,256 1,586 0,991 1,119 0,598 0,160 1,678 0,579 0,694 0,111 1,779 1,847 1,513 0,330 1,555 1,903 0,809 1,996 1,859 1,203 1,948 0,713 0,983 1,723 0,744 0,235 0,957 0,203 0,940 1,255 0,357 1,834 1,370 1,624 0,926 0,203 1,916 0,130 0,237 1,495 1,169 1,649 0,283 0,632 1,287 0,642 0,9 0,673 1,758 1,632 1,672 1,463 0,506 1,654 1,850 0,88 0,170 0,731 1,258 0,304 1,24 1,362 0,312 1,374 1,141 1,123 0,172 1,335 1,658 0,527 1,53 1,585 0,65 1,874 0,783 0,397 1,958 0,988 0,628 0,705 1,458 0,885 0,247 1,599 0,322 0,472 0,241 0,307 0,174 0,717 1,763 1,377 1,524 1,521 0,218 1,91 0,488 0,474 1,354 0,899 1,141 0,201 0,949 0,848 1,101 1,255 0,245 1,142 1,646 0,209 0,596 0,665 0,109 0,586 0,34 0,44 0,486 0,584 1,342 1,569 1,966 1,158 1,108 0,971 0,832 0,897 0,158 1,918 0,746 0,102 0,192 1,460 0,966 0,268 1,708 0,775 0,707 1,465 1,670 1,840 0,337 0,888 1,676 1,610 0,480 0,966 1,945 1,346 1,481 0,973 0,754 1,84 1,38 1,102 1,876 0,955 0,227 1,24 0,221 1,428 0,378 1,764 0,804 1,176 1,452 0,261 1,334 1,503 0,236 1,913 0,979 1,91 0,106 1,425 1,161 0,750 0,472 0,926 1,914 0,165 0,70 0,804 0,131 0,702 1,625 0,356 0,967 1,895 0,728 1,703 1,973 0,518 1,372 0,856 0,574 1,392 0,735 1,287 0,211 0,992 1,266 0,437 1,140 0,724 1,353 0,85 1,925 1,170 0,257 0,994 1,739 0,118 1,106 1,921 1,419 0,397 1,406 0,925 1,255 1,672 0,594 0,352 1,192 0,238 0,288 0,975 0,730 1,647 0,756 0,251 1,100 1,612 1,696 1,896 0,742 0,873 0,392 0,32 1,568 1,658 1,387 1,387 0,41 0,263 1,511 0,52 0,514 0,475 0,667 0,365 0,368 0,763 1,798 1,573 1,544 1,264 1,306 1,406 0,214 1,929 1,79 1,565 1,851 1,401 0,798 1,347 0,101 1,129 0,719 0,715 1,617 1,938 1,179 0,13 0,545 1,155 0,83 0,44 0,888 0,131 0,127 1,532 1,536 1,800 1,569 0,30 0,481 0,860 0,442 0,526 0,866 0,658 1,359 0,338 0,130 0,409 1,48 1,952 1,30 0,956 1,938 0,65 0,968 1,990 0,731 0,413 0,13 0,65 0,83 0,750 1,994 1,915 1,160 1,257 1,278 1,96 1,495 1,381 1,620 1,123 0,596 1,54 1,895 1,198 1,511 0,821 0,684 0,644 0,552 1,478 0,351 0,197 1,259 0,963 0,261 0,154 0,817 0,452 0,923 0,309 0,365 0,983 1,869 0,739 1,507 0,532 0,745 0,996 1,295 0,890 0,399 1,644 0,269 0,75 0,590 1,280 1,865 0,248 1,349 0,979 0,377 1,231 1,240 1,167 1,187 1,297 0,957 0,911 0,139 0,241 0,712 0,700 0,396 0,129 1,742 1,72 0,276 0,392 0,935 1,302 0,901 1,360 1,200 1,851 1,406 1,305 1,561 1,297 1,345 0,58 0,465 0,122 1,304 1,825 0,183 1,629 0,173 0,433 1,464 0,411 0,191 1,556 1,979 1,338 1,158 1,454 0,75 1,99 0,314 0,191 0,301 1,226 0,720 1,778 1,988 0,661 1,47 0,404 0,31 0,451 1,364 1,924 1,241 1,371 1,784 1,604 1,242 0,122 1,517 0,624 1,614 1,46 1,778 1,517 1,372 0,656 1,89 1,327 0,314 0,958 0,89 1,824 0,396 1,143 1,72 0,172 1,385 1,445 1,695 1,583 0,200 1,869 0,213 0,393 0,18 0,487 0,877 1,257 0,222 1,769 1,577 1,566 0,834 1,823 0,458 1,990 1,55 1,819 1,835 1,355 0,526 0,818 0,673 1,81 1,755 1,889 0,256 1,961 1,617 0,485 1,947 1,997 0,97 1,752 0,686 1,327 1,617 1,245 0,625 1,545 1,87 0,641 0,995 0,24 1,935 1,204 0,543 0,356 0,272 1,993 0,369 0,435 0,23 1,356 1,542 0,1 0,286 0,377 0,858 0,806 1,945 0,437 0,358 1,663 1,277 1,840 0,799 0,704 0,916 0,119 0,45 0,484 0,192 0,659 0,51 0,515 0,91 0,141 0,637 1,378 1,558 1,197 0,254 1,20 1,782 0,990 0,625 1,86 1,760 0,937 1,271 0,115 1,114 0,361 0,219 0,197 0,875 1,663 0,278 0,335 0,241 1,188 1,393 0,990 0,491 1,484 0,911 1,75 1,26 0,465 1,574 1,764 0,390 0,154 1,463 0,140 1,506 1,252 1,11 1,50 0,202 0,211 0,235 0,816 0,436 1,145 0,538 0,365 1,215 1,301 0,965 1,532 1,335 0,489 1,70 0,988 1,696 1,910 0,133 0,820 1,70 1,924 1,40 0,225 1,488 1,366 0,99 0,748 1,447 0,354 0,559 0,566 1,235 0,586 1,482 1,414 0,730 1,229 0,916 1,636 1,233 1,409 1,387 0,246 0,238 0,418 0,499 1,139 0,560 0,942 0,171 0,459 1,408 0,317 1,596 1,545 1,566 1,682 0,154 0,311 0,270 1,481 1,15 1,400 1,936 0,319 1,948 0,823 1,554 1,663 0,68 0,645 1,431 0,429 1,538 0,157 0,180 0,251 1,941 0,253 1,207 1,811 1,212 1,457 1,930 0,754 0,1 0,282 1,279 0,766 1,492 0,254 1,234 1,816 1,782 1,217 0,942 1,559 1,209 1,341 1,712 0,534 1,633 1,507 0,729 1,384 1,3 1,980 1,881 1,810 1,101 1,576 0,23 1,140 1,589 1,157 0,216 1,453 1,234 0,377 1,92 1,584 0,224 1,104 0,321 1,650 1,757 1,65 0,114 1,330 1,154 0,921 0,122 0,962 1,822 0,827 1,620 0,477 1,649 1,821 0,114 1,645 0,924 1,65 1,409 0,409 0,223 0,111 1,319 1,461 0,639 0,147 0,404 0,640 1,648 1,321 0,631 1,58 0,587 0,743 0,313 1,77 1,89 0,766 0,746 1,752 0,376 1,259 1,863 0,639 0,21 1,28 0,772 0,271 0,99 0,523 0,300 0,929 0,297 0,137 0,265 0,632 0,477 0,760 0,253 0,476 0,68 1,908 0,3 1,606 1,245 1,313 1,644 0,63 1,743 1,563 0,526 0,954 1,130 0,9 0,587 1,824 1,479 0,334 1,517 1,271 1,258 1,130 0,644 0,717 1,886 0,339 0,0 1,951 1,731 1,689 0,672 0,44 1,235 1,414 0,261 1,886 1,717 0,515 0,913 1,537 1,797 1,873 1,436 1,127 1,105 1,379 0,955 0,684 0,211 1,60 0,631 1,871 1,410 1,602 0,586 1,867 1,520 1,160 1,122 1,574 1,965 0,446 1,186 1,641 1,92 0,476 1,487 1,55 0,279 0,611 0,490 0,385 0,881 0,571 0,949 1,264 0,401 1,307 0,835 0,109 1,456 0,782 0,737 1,731 0,530 0,219 0,882 1,623 0,264 0,769 0,331 1,726 0,514 0,314 0,937 0,180 0,498 1,741 1,688 0,678 1,949 1,599 1,519 1,102 1,212 1,527 0,921 0,175 1,603 0,815 1,345 1,165 0,22 1,552 1,82 0,691 1,258 0,696 1,793 0,620 0,833 1,458 0,686 0,531 1,125 1,814 1,639 0,955 0,952 1,974 1,852 1,609 0,694 1,554 0,693 0,572 1,105 1,792 0,710 0,529 1,951 0,328 0,0 0,169 1,880 1,418 0,742 1,318 0,54 1,379 0,525 0,569 0,760 1,981 1,571 1,275 0,776 0,301 0,926 0,997 0,44 1,161 0,11 0,15 1,404 1,919 1,896 1,561 1,918 0,839 1,167 1,721 0,115 1,339 0,360 0,777 0,929 1,929 0,473 0,548 1,85 0,198 0,664 0,541 1,965 1,46 0,597 1,947 0,127 1,264 1,95 0,203 0,790 1,189 1,665 0,968 1,764 0,526 0,479 1,350 1,333 1,101 0,690 0,780 1,971 0,497 1,747 0,629 0,288 1,957 0,971 1,334 0,862 0,457 0,406 0,49 1,489 0,856 0,751 0,946 0,807 0,707 0,753 1,243 1,502 1,326 0,180 1,169 1,179 1,606 1,821 1,341 1,31 0,448 1,502 0,455 1,906 1,885 1,133 0,71 0,224 1,616 0,201 1,217 0,737 1,4 1,857 1,851 1,437 0,890 1,896 0,793 0,683 0,808 0,706 0,148 0,268 0,82 1,703 1,730 1,891 0,836 1,533 1,329 1,990 1,54 1,823 0,291 0,529 0,542 0,604 0,791 1,19 1,304 1,124 0,340 1,644 0,290 0,117 0,598 0,240 0,854 1,856 0,14 0,943 1,989 1,489 0,202 0,820 0,326 0,457 0,418 0,633 0,982 1,92 1,888 0,17 0,469 0,614 1,884 0,885 0,915 1,383 1,123 1,132 1,877 0,422 0,126 0,39 1,382 0,770 1,912 1,413 1,607 0,590 1,562 1,501 1,666 0,775 1,485 0,290 0,89 0,692 1,984 1,145 0,511 1,409 0,54 0,611 1,155 0,358 1,925 0,158 1,921 0,967 1,847 0,745 1,327 1,933 0,326 0,307 0,636 1,516 0,369 0,100 0,28 1,597 0,181 0,889 1,812 1,538 1,675 1,317 1,218 0,268 0,53 0,703 1,185 0,56 1,566 1,141 1,522 0,674 0,663 0,296 0,297 0,905 1,720 1,768 1,434 0,349 0,992 0,335 0,249 1,795 1,445 1,419 0,569 1,368 0,564 0,419 0,350 1,946 1,965 0,845 1,1 0,492 1,984 0,775 1,378 0,577 0,19 1,739 0,647 1,753 1,548 1,282 0,483 1,647 0,89 1,91 0,120 1,506 1,522 1,230 1,834 0,199 0,589 0,945 0,127 1,224 1,754 0,821 1,180 1,814 0,618 1,192 0,624 0,867 1,610 0,825 0,831 0,22 1,743 0,428 1,957 0,24 1,4 1,895 1,391 0,556 0,102 1,828 1,7 0,533 0,50 1,662 1,973 1,689 0,686 1,871 1,157 1,475 0,125 1,788 0,583 1,346 1,587 0,12 1,855 0,337 0,294 0,729 1,692 1,313 0,795 0,265 1,69 0,664 1,762 0,804 0,974 0,316 1,269 0,575 0,412 1,961 1,420 0,923 1,407 0,814 0,91 1,984 1,275 0,942 0,763 1,113 1,272 0,711 0,977 1,888 1,658 1,664 0,593 1,650 0,910 0,391 0,76 0,648 0,220 0,135 1,266 1,467 0,498 0,662 1,732 0,144 1,356 0,175 1,621 1,353 0,633 0,494 0,21 1,687 1,265 0,178 1,319 1,1 1,113 1,983 1,102 1,792 0,954 0,35 0,216 0,941 1,999 0,586 1,287 0,810 0,418 0,900 0,696 0,714 0,204 0,661 1,662 1,658 1,41 1,907 0,960 0,688 0,248 1,555 1,188 0,188 1,745 1,589 1,119 1,272 1,524 1,871 1,385 0,25 1,451 1,501 0,231 1,317 1,93 0,367 1,306 1,565 1,171 0,684 1,765 0,62 1,308 0,53 0,84 0,395 1,556 0,696 1,149 0,734 0,23 1,671 1,580 0,608 1,637 1,989 1,825 0,126 1,749 1,647 1,530 1,656 1,53 1,708 1,421 1,124 1,715 1,461 1,550 1,143 0,349 1,548 1,307 0,634 0,124 1,60 0,287 0,739 0,377 1,854 0,397 0,764 0,400 0,645 0,54 0,984 1,902 1,866 0,569 0,199 1,602 1,895 0,862 0,592 1,779 0,972 1,972 1,223 0,983 1,420 1,167 0,496 0,838 0,281 0,178 1,714 1,227 1,966 1,805 0,644 0,281 0,38 0,125 0,439 1,877 0,797 0,349 1,190 0,521 0,158 0,695 1,232 1,372 1,652 1,194 0,857 0,233 0,405 1,979 1,802 1,596 0,281 1,556 0,248 1,474 0,639 1,494 0,668 1,643 1,551 1,608 1,708 0,542 0,727 0,499 1,258 1,128 0,458 0,288 1,888 0,866 1,934 0,57 0,707 0,33 1,824 0,775 0,332 1,476 1,533 0,400 0,651 1,491 0,543 0,929 1,962 1,831 1,499 1,764 1,888 1,369 1,357 1,677 1,979 0,711 1,852 1,986 1,901 1,28 0,221 0,936 1,403 0,628 1,441 0,704 1,454 1,858 0,343 0,417 1,880 1,978 1,37 0,770 1,765 0,971 1,636 1,190 1,955 0,753 1,581 0,464 0,581 1,419 1,40 0,567 1,971 1,859 0,315 1,328 1,230 0,853 0,643 0,904 1,951 0,308 0,220 0,625 1,547 0,761 1,819 1,397 0,629 0,720 0,603 1,481 1,97 0,152 0,100 0,519 0,501 0,800 1,650 0,499 1,523 0,879 0,686 0,768 0,574 1,550 1,509 1,195 1,976 0,212 0,734 1,476 1,342 0,750 1,583 0,937 1,563 1,418 1,315 0,519 1,819 1,524 1,791 1,135 0,857 0,797 0,833 1,107 1,703 1,861 0,557 0,651 1,626 1,634 0,628 1,356 0,936 0,27 1,403 0,836 1,195 0,230 1,322 0,254 1,673 1,197 1,606 0,487 0,368 1,884 0,871 0,289 1,135 0,73 1,62 1,701 1,430 1,466 0,410 1,34 0,826 0,451 1,224 0,897 1,992 1,870 1,449 1,402 0,885 1,487 1,874 0,492 1,881 1,776 1,386 1,139 0,558 0,671 0,454 0,166 0,375 1,278 1,59 1,298 0,593 0,222 0,232 0,676 1,320 0,227 1,12 0,585 0,563 0,705 0,456 0,294 0,171 0,983 1,925 1,756 0,741 1,818 0,423 0,658 1,772 1,695 0,899 0,840 0,179 1,992 0,230 1,157 1,471 1,356 0,669 1,817 1,931 1,356 0,861 1,107 0,377 0,785 1,195 0,544 0,117 1,527 0,965 0,849 1,140 1,102 0,478 0,639 1,614 1,384 0,126 0,220 1,192 0,212 1,276 1,187 1,103 1,767 1,231 1,166 1,329 0,940 1,408 0,82 0,367 0,945 1,379 1,901 1,420 0,536 1,102 1,705 0,323 0,334 0,385 1,237 1,161 0,135 1,476 0,671 0,393 1,831 0,949 1,971 0,589 0,470 1,283 0,1 0,292 0,858 1,717 1,680 1,166 0,968 0,616 1,698 0,347 1,194 1,502 1,462 1,946 0,665 1,211 1,885 1,438 0,311 1,846 0,687 0,390 1,646 1,222 1,612 1,829 1,501 1,864 0,184 0,391 0,748 0,180 1,551 0,599 0,174 0,436 0,823 0,811 0,777 1,569 1,954 0,221 0,789 0,270 1,299 0,304 1,718 0,622 0,39 0,479 0,215 0,810 1,658 0,83 1,208 1,393 1,488 0,581 0,73 0,502 0,963 0,916 1,314 0,511 0,531 1,515 1,226 1,672 0,816 0,801 1,66 0,471 1,202 0,51 0,159 0,566 0,326 1,537 1,777 1,682 1,254 0,341 0,169 0,600 1,800 1,282 0,593 1,898 1,423 1,88 0,114 0,223 0,836 0,919 0,501 0,742 1,810 1,312 1,536 1,323 0,19 1,633 1,120 1,477 1,332 1,31 0,492 1,610 0,654 0,999 0,951 1,78 1,90 1,264 1,826 1,167 1,627 0,562 1,274 0,867 0,604 1,552 1,862 1,660 0,297 0,508 0,456 0,275 0,782 1,155 0,443 0,933 0,290 1,33 0,292 1,415 1,680 0,556 0,6 0,589 1,192 0,662 1,315 0,662 1,551 1,964 0,604 1,740 1,364 0,397 0,495 1,100 0,202 0,786 0,196 0,162 0,432 0,278 1,342 0,566 1,136 0,377 0,940 0,377 1,650 0,127 0,601 1,763 1,802 0,908 1,851 1,63 1,217 0,442 1,911 0,659 1,957 1,129 0,482 0,96 0,272 1,534 0,998 0,926 1,485 0,135 1,206 0,64 0,404 1,77 0,234 0,249 1,953 1,220 1,47 0,795 0,610 1,712 0,842 0,524 0,632 0,340 1,144 0,34 0,246 1,138 1,800 1,50 1,417 0,570 0,207 0,744 0,621 1,516 1,893 1,516 1,552 0,720 0,800 1,552 1,663 0,616 1,353 0,661 0,571 1,756 1,369 1,73 1,482 1,608 1,308 0,464 0,237 1,3 0,326 0,778 1,724 1,181 0,750 1,303 1,328 1,655 1,858 0,868 0,655 1,640 0,441 0,14 1,964 1,893 1,32 1,238 1,70 1,19 0,671 1,108 0,211 0,658 0,106 0,69 1,474 1,533 0,819 1,218 1,524 0,432 1,703 1,258 1,776 1,86 0,866 1,746 1,315 1,851 1,293 0,889 1,799 0,813 0,652 0,76 0,13 0,113 0,407 0,111 1,845 1,343 1,484 0,703 0,938 1,131 0,705 1,168 1,670 0,709 0,232 1,880 0,734 1,152 0,877 1,939 1,390 1,772 0,785 0,525 1,741 0,318 1,640 1,862 1,880 0,145 0,589 0,579 0,722 0,935 1,357 0,480 1,37 1,969 0,574 1,867 1,743 0,887 0,136 1,740 0,798 1,245 1,373 0,927 0,671 0,726 1,594 1,207 1,804 1,896 0,291 1,474 1,564 0,481 0,482 0,821 1,677 1,574 1,257 1,801 0,163 0,917 0,825 1,567 0,861 0,909 0,255 1,889 0,224 1,12 0,73 1,394 1,14 0,178 0,265 1,97 0,106 0,379 0,88 0,131 0,815 1,374 1,782 1,337 1,569 0,828 1,800 1,853 0,232 0,636 1,733 0,301 0,138 0,754 0,220 0,291 1,179 1,977 0,990 0,929 1,819 0,285 0,124 1,605 1,188 0,297 1,181 1,434 0,542 0,171 0,997 0,268 1,476 0,254 1,701 1,604 0,511 0,468 1,505 0,512 1,659 0,446 1,203 1,764 1,115 1,449 1,111 0,502 0,856 1,57 1,241 0,741 0,482 1,946 0,779 1,342 0,90 1,670 1,290 0,803 0,197 1,273 1,762 1,448 1,86 0,261 0,2 0,519 1,764 1,75 1,394 0,42 0,549 1,412 1,460 1,288 0,45 0,289 1,488 1,187 1,104 1,115 0,817 0,973 0,400 0,106 1,101 1,479 1,937 1,287 1,885 1,292 1,220 0,238 1,253 1,387 1,649 1,686 1,908 0,490 0,530 0,825 1,483 0,131 1,142 0,379 1,190 1,264 0,620 1,563 1,444 1,909 1,228 1,451 1,719 1,193 0,984 1,760 1,757 1,587 0,768 1,466 1,529 0,779 1,434 1,289 0,567 1,475 1,652 1,1 0,499 1,527 1,817 0,537 1,833 0,976 0,343 0,538 1,392 0,85 0,116 0,108 1,682 0,163 1,644 0,807 1,405 1,538 1,971 1,807 1,780 1,860 1,249 0,64 1,959 0,161 0,821 0,222 1,167 0,615 0,863 0,918 1,923 1,757 1,872 0,150 0,47 0,551 1,617 1,650 0,517 0,764 1,284 0,670 0,281 1,712 1,773 0,442 1,931 1,661 1,943 0,722 0,770 0,24 0,21 0,859 0,89 0,330 1,540 0,251 0,979 0,202 0,447 0,459 0,439 0,116 0,382 0,924 0,526 0,227 1,607 0,493 0,453 0,776 1,764 1,781 0,517 1,667 1,65 1,374 0,987 0,508 0,694 0,326 1,915 1,688 0,930 0,849 0,888 0,701 0,753 1,949 1,876 1,91 0,334 1,720 0,416 1,185 1,947 1,355 1,664 1,698 1,120 0,843 1,111 1,531 0,649 1,961 0,260 1,25 1,449 1,235 1,86 0,525 0,75 0,945 0,959 1,641 0,423 1,586 1,365 1,628 1,415 1,968 0,440 1,184 1,411 0,299 1,117 0,698 0,651 1,158 1,623 0,889 0,650 1,519 0,220 1,81 1,186 0,751 1,42 1,568 1,96 1,692 1,593 0,86 0,906 0,777 0,609 0,44 0,509 0,989 1,159 0,843 1,61 0,421 0,791 0,713 0,191 0,283 0,91 1,415 1,218 1,829 1,49 0,239 1,367 0,685 0,750 1,698 1,510 0,480 0,764 1,107 1,854 0,360 0,734 1,514 0,946 0,448 1,793 1,582 0,115 0,792 1,123 0,365 0,177 1,289 1,84 0,175 0,407 1,618 1,327 1,229 0,363 1,143 1,963 1,409 0,469 1,780 1,263 0,282 0,666 0,628 0,414 1,442 1,669 1,359 0,346 0,953 1,584 1,870 0,509 0,283 1,285 0,194 1,326 0,990 0,655 0,944 1,839 0,273 0,489 1,35 1,202 1,765 1,616 1,805 0,792 0,303 1,732 1,251 0,476 1,601 0,427 1,49 1,660 0,238 1,588 0,524 0,780 0,804 1,466 1,335 0,216 1,358 0,649 1,685 1,70 1,274 0,8 0,264 1,786 0,714 1,685 1,537 1,997 1,134 0,306 1,163 0,568 0,786 0,764 1,908 1,420 0,466 0,757 0,404 0,724 1,834 1,704 1,541 1,667 1,547 0,304 1,563 1,807 0,899 0,714 0,441 0,589 0,846 0,827 0,988 1,840 1,896 0,664 1,55 1,334 0,668 1,699 0,640 0,258 0,893 0,821 0,66 0,207 0,929 1,745 1,404 1,785 1,66 0,424 0,806 1,403 1,424 1,894 1,383 0,827 1,274 0,951 0,210 0,91 0,177 1,596 1,141 1,306 1,586 0,18 1,551 1,662 1,303 0,213 0,11 0,612 1,327 0,252 0,574 1,356 0,205 0,394 0,263 1,404 0,206 1,251 0,396 0,837 1,584 0,309 1,94 0,993 0,602 1,492 1,776 1,713 1,604 1,506 1,28 0,274 1,619 0,871 0,413 1,84 0,549 1,451 0,804 1,769 0,280 1,745 0,190 1,699 1,686 1,912 0,29 1,99 1,687 1,347 0,699 0,761 0,586 0,238 0,953 1,653 1,387 1,109 1,167 0,771 0,378 1,197 1,856 0,362 0,827 1,562 1,196 0,254 1,683 1,819 0,299 1,162 0,836 1,664 0,659 1,1 1,29 1,246 1,387 1,987 1,244 0,108 1,175 1,478 1,346 0,974 0,537 1,833 1,421 0,897 0,609 0,568 0,287 1,0 1,418 0,362 1,400 0,978 1,345 1,951 0,217 0,997 0,27 1,232 1,661 0,349 0,706 1,713 0,324 1,451 1,941 0,120 0,356 0,924 1,403 0,560 1,97 1,522 1,932 0,705 1,799 1,494 0,95 1,316 0,532 1,764 1,883 1,355 1,522 0,746 0,926 1,675 0,822 0,286 1,349 1,987 0,502 0,796 0,340 1,755 0,419 1,225 0,320 0,771 0,371 1,935 1,145 1,131 1,873 0,236 0,234 0,578 0,559 1,667 1,924 0,77 1,589 0,110 1,407 1,208 0,38 1,403 1,456 1,84 1,797 0,406 0,726 1,639 0,221 1,170 0,255 1,357 1,492 0,335 0,25 0,129 0,68 0,999 1,550 1,496 0,387 0,482 1,947 1,722 1,89 1,496 1,185 0,916 1,603 1,890 1,274 0,74 1,295 1,59 0,628 1,388 0,547 1,154 1,885 1,235 1,563 0,640 0,953 0,129 0,908 1,465 0,475 1,949 0,723 0,336 1,978 0,759 0,235 0,291 1,688 0,962 1,544 1,829 1,848 1,249 0,340 1,987 1,582 0,886 0,264 1,41 1,98 1,987 1,550 0,923 1,622 0,479 0,759 1,266 1,996 0,254 1,389 1,990 1,557 1,446 1,931 1,211 1,6 0,464 1,695 0,957 0,442 1,0 0,269 1,707 0,658 0,947 1,528 0,175 1,667 1,744 1,566 0,406 1,723 0,442 0,449 1,326 1,584 0,303 0,106 1,550 1,879 0,14 0,236 0,907 0,299 0,622 1,975 1,762 0,422 1,120 0,686 0,187 1,517 0,533 0,512 1,233 0,699 0,240 0,973 0,931 0,621 0,837 0,888 0,294 0,474 1,480 0,755 1,845 1,807 1,600 0,607 0,202 0,310 0,35 0,563 1,310 1,437 1,131 0,786 0,10 0,861 0,903 1,138 1,767 0,163 0,538 1,73 1,513 1,293 1,6 0,978 1,989 1,71 0,923 1,486 1,249 1,183 1,40 0,902 0,774 1,248 0,334 1,507 1,433 0,697 1,329 0,17 1,570 0,572 1,291 0,406 1,192 0,234 1,863 0,882 1,421 1,183 1,355 0,436 1,27 1,176 1,99 0,424 1,866 1,541 1,528 1,747 0,521 0,49 0,596 1,416 1,126 0,951 1,790 1,622 1,443 0,104 0,297 0,646 1,705 1,540 0,871 0,848 0,425 0,688 1,700 0,899 1,912 1,145 0,344 1,648 0,233 1,16 0,756 0,791 1,589 1,5 1,873 1,83 1,959 1,510 1,782 1,793 0,967 0,700 1,137 0,846 0,756 0,392 1,312 0,413 1,454 0,855 1,875 1,658 0,890 0,418 0,982 1,148 0,774 0,222 0,375 0,556 1,121 1,978 1,341 0,860 1,490 1,298 0,725 0,320 0,667 1,890 0,609 1,994 1,874 0,372 0,399 1,66 0,280 1,205 1,164 1,817 1,833 0,623 0,631 0,205 1,986 1,121 1,78 1,50 1,119 0,3 0,736 0,27 1,953 1,489 1,776 0,26 1,44 0,852 0,668 1,32 0,208 0,295 1,30 1,126 0,710 1,410 1,348 1,854 0,983 1,95 1,262 1,946 1,758 0,196 1,72 0,486 1,993 0,51 0,921 0,247 1,712 1,4 0,71 1,942 0,451 0,591 0,839 0,109 1,452 1,510 1,893 0,622 1,184 1,397 1,605 0,849 0,259 0,666 0,0 1,510 0,19 1,195 0,6 1,793 1,150 0,661 0,683 1,35 0,842 1,426 0,946 1,681 1,518 0,849 1,981 1,123 1,782 0,985 1,199 1,629 0,677 0,133 1,861 0,609 1,999 0,789 0,127 1,492 0,696 1,230 0,776 1,575 0,89 0,534 1,569 0,67 0,526 1,263 0,278 1,880 1,590 1,405 1,369 0,188 0,488 1,379 1,372 1,105 0,452 1,665 1,760 0,456 0,200 0,804 0,183 1,983 1,564 0,526 1,39 1,426 0,104 0,342 0,600 1,715 0,621 1,813 1,639 0,544 1,775 0,577 1,570 0,845 1,122 0,503 0,7 0,202 0,198 1,771 1,121 0,64 0,893 0,997 0,859 0,211 1,475 0,554 1,308 1,153 1,492 1,378 1,42 1,793 0,933 0,371 0,950 0,455 0,714 0,714 1,486 1,202 1,559 1,299 1,646 1,172 1,332 1,853 0,467 1,816 0,507 0,122 0,644 0,714 1,229 1,790 0,748 0,871 1,675 1,682 0,494 0,105 0,355 1,718 0,957 1,765 0,268 1,789 0,475 1,97 1,713 0,836 1,728 0,324 0,356 0,518 0,545 1,15 1,841 1,434 1,467 0,816 0,644 1,372 1,55 1,873 1,574 0,412 1,836 0,251 0,984 1,454 1,982 1,575 0,543 1,768 0,848 1,320 0,338 1,985 0,29 1,889 0,865 0,889 0,574 1,842 0,404 0,769 0,321 1,662 0,793 0,770 1,298 0,54 1,425 0,137 1,564 1,775 1,447 0,77 1,98 0,314 1,820 1,413 0,345 0,661 1,387 0,842 0,315 1,285 1,780 1,405 0,304 0,255 1,694 1,841 0,289 0,682 1,220 1,66 1,248 0,725 1,911 1,815 0,483 0,640 1,234 0,395 0,987 0,782 0,841 1,648 0,83 0,767 1,888 0,69 1,983 1,878 0,4 0,48 0,719 0,253 0,596 0,654 1,262 0,388 1,258 0,214 0,53 0,304 0,47 1,904 1,980 1,321 0,255 1,520 0,224 0,851 0,977 0,362 1,236 0,971 0,242 0,952 1,942 1,889 1,225 1,80 1,623 1,386 1,522 1,640 0,648 0,38 1,921 1,574 1,770 0,146 0,655 1,930 1,169 0,45 0,317 1,550 0,662 1,679 0,146 0,903 1,987 0,503 0,117 1,332 1,129 0,345 0,285 0,817 1,216 0,274 0,806 0,560 1,72 0,454 0,63 1,26 0,881 0,740 0,902 1,359 0,358 0,668 1,764 0,313 1,556 1,651 0,202 0,526 1,265 0,85 0,566 0,201 1,79 1,810 0,877 0,854 1,432 0,119 0,508 1,105 0,34 0,997 0,298 0,856 1,491 0,287 0,394 1,538 0,901 0,169 1,836 1,508 1,390 1,62 0,262 0,662 0,327 1,376 0,109 1,792 0,568 1,169 1,429 1,709 0,977 0,913 1,645 1,782 0,985 1,596 0,835 0,242 1,70 1,917 1,764 1,766 0,468 1,8 0,810 0,895 1,383 1,109 0,86 0,93 1,339 1,56 1,194 0,338 1,56 0,499 1,671 0,719 0,589 1,336 1,755 1,265 0,461 0,425 0,569 0,234 1,444 0,300 0,800 1,795 0,814 1,1 1,970 1,753 0,666 0,538 0,457 0,890 0,335 0,430 0,419 1,411 0,547 1,957 1,567 0,839 0,239 0,108 0,113 1,656 1,834 1,664 0,490 1,455 0,663 1,902 0,76 0,859 0,342 0,125 0,918 1,895 1,223 1,616 0,366 1,366 0,456 1,605 0,808 0,217 1,715 1,558 1,492 1,340 0,829 1,583 0,169 1,416 0,875 1,345 0,465 1,477 0,737 0,580 0,340 1,333 1,289 0,186 0,388 0,601 1,550 1,788 0,552 1,18 1,135 0,965 1,784 1,716 0,609 0,592 0,589 1,577 0,389 1,364 0,308 1,434 1,364 0,773 1,214 1,981 1,340 0,736 0,276 1,162 0,135 1,137 1,813 0,925 0,476 1,700 0,175 1,106 0,916 0,902 0,783 0,724 0,872 1,492 1,969 1,913 1,718 0,624 0,953 0,281 1,427 0,800 1,349 1,255 1,279 0,542 1,419 0,654 1,385 1,517 1,637 0,223 1,14 0,325 1,176 1,740 0,821 1,897 1,151 0,901 1,374 1,815 1,238 1,771 1,436 1,848 1,870 1,958 0,189 0,723 1,426 1,17 1,296 1,632 0,807 0,622 0,53 0,37 1,609 1,914 1,84 1,506 1,846 0,348 0,333 0,993 0,592 0,204 0,861 0,198 1,564 0,405 1,452 1,903 1,461 0,458 1,579 0,247 0,562 1,489 0,124 1,944 1,704 1,537 0,574 1,266 0,347 0,117 1,43 0,754 1,568 1,860 1,922 0,35 0,617 1,386 1,758 0,488 0,387 0,426 0,925 1,96 1,447 0,60 0,728 1,227 0,869 0,331 1,607 0,836 1,394 0,154 0,565 0,373 0,511 0,622 1,488 1,439 1,286 1,966 0,856 1,447 1,601 0,640 0,690 1,129 1,538 1,173 0,453 0,231 0,839 1,198 0,163 1,820 0,847 1,817 1,119 0,992 0,444 1,341 0,468 0,124 0,267 1,326 0,474 1,884 1,649 1,870 0,59 1,625 0,444 0,537 0,640 0,34 0,732 1,86 0,338 1,681 1,781 1,74 0,284 1,566 1,33 0,264 1,477 1,530 0,951 0,248 1,613 0,797 1,520 0,431 1,951 1,655 0,897 0,46 0,288 1,452 0,515 0,917 0,337 0,874 1,97 0,182 0,658 0,984 1,714 0,704 0,534 0,678 0,437 0,393 1,960 1,173 1,682 1,827 0,597 0,712 1,291 0,282 1,188 0,110 0,984 0,130 0,86 0,650 1,810 1,310 1,517 0,880 0,412 1,311 1,341 0,729 1,692 0,486 1,888 0,958 0,802 1,951 0,889 0,406 1,502 0,365 1,390 1,874 1,594 0,425 0,8 0,396 0,133 0,127 0,355 0,847 0,978 1,663 1,924 0,780 0,743 0,980 0,228 1,226 1,622 0,947 0,466 0,313 1,895 1,143 0,86 1,328 0,499 1,550 0,547 1,53 1,853 1,448 0,147 1,985 0,367 1,395 0,531 1,149 0,804 1,778 1,645 1,854 1,42 1,367 1,968 1,201 0,279 1,490 0,338 0,180 0,90 1,751 0,508 0,752 1,246 0,407 1,439 0,504 1,224 1,619 1,560 1,554 0,469 0,280 0,147 0,503 0,427 1,687 1,223 0,772 0,474 1,469 0,111 1,780 1,449 1,685 1,763 0,414 1,997 0,341 1,579 0,386 1,321 1,349 1,419 1,263 1,453 0,118 1,280 1,282 0,267 1,584 0,920 0,372 0,612 1,460 1,296 0,529 0,517 0,914 0,565 0,909 0,2 1,257 0,688 0,220 1,439 0,202 0,996 0,827 0,797 0,732 1,40 1,537 0,577 0,584 1,305 0,344 1,650 1,658 0,192 0,819 1,960 1,634 1,766 1,650 0,156 0,982 0,456 1,340 0,64 0,482 0,512 0,516 1,955 0,193 0,584 1,17 0,372 1,784 0,299 0,375 1,943 0,127 1,712 1,30 0,729 0,138 0,510 0,443 1,330 0,375 0,470 1,20 1,774 0,185 0,314 0,812 1,246 0,445 1,864 1,424 0,83 1,880 1,625 1,403 1,238 0,609 1,203 0,309 1,721 0,330 0,664 0,737 0,195 1,195 0,571 0,496 0,915 1,671 1,339 0,777 0,383 1,630 1,142 1,288 0,659 1,281 1,965 1,861 0,507 1,485 0,459 1,253 1,598 1,377 0,996 0,461 1,879 0,6 0,805 0,814 1,383 0,159 0,843 1,578 0,211 1,74 1,609 0,246 0,374 1,877 1,742 0,908 0,795 1,339 0,540 1,356 0,443 1,631 1,739 1,554 0,885 0,728 1,316 0,364 1,925 1,748 0,825 0,904 0,372 1,61 1,91 0,767 1,53 0,16 0,909 1,786 1,585 0,791 1,944 1,792 0,903 1,595 0,472 1,173 0,200 1,480 1,77 1,804 0,477 0,495 1,500 1,342 0,97 0,886 1,932 0,928 0,419 0,283 1,450 1,387 1,956 1,946 0,814 0,522 0,757 0,612 0,814 1,974 0,139 0,568 0,183 1,978 1,693 1,511 1,408 0,163 1,184 0,873 1,555 0,889 0,722 1,498 0,588 1,696 1,628 1,847 0,573 1,142 0,766 1,703 0,235 0,883 0,727 1,670 1,395 1,479 1,236 1,93 0,701 1,402 1,853 1,105 1,129 1,169 1,364 1,776 0,375 1,373 1,146 1,125 0,604 1,246 0,821 0,135 1,679 1,626 0,457 1,234 0,316 0,986 1,726 1,120 1,221 1,962 1,752 0,601 1,16 0,457 0,774 1,397 0,917 0,509 0,199 1,211 0,598 1,554 0,276 1,128 1,331 0,327 0,894 1,3 1,664 0,447 1,579 0,720 1,711 1,575 0,291 1,73 1,864 0,960 1,608 0,294 0,528 1,761 1,751 0,361 0,912 1,306 0,655 1,495 1,21 0,308 0,977 0,0 0,685 1,954 1,626 1,784 0,307 0,589 0,258 0,867 1,951 1,740 1,269 1,364 1,598 0,38 0,597 0,781 1,448 1,505 0,207 1,620 1,539 1,144 0,598 0,12 0,195 0,585 0,278 0,20 0,505 0,837 0,462 1,592 0,275 1,83 0,754 1,253 0,55 0,571 1,351 0,725 0,361 0,54 0,388 0,764 1,200 0,714 1,885 0,227 0,533 1,813 0,795 0,162 0,209 0,314 0,21 1,29 0,928 1,174 0,320 0,537 0,420 1,348 1,155 0,761 0,945 1,911 0,752 1,635 0,996 1,596 0,382 1,725 1,778 1,266 0,116 0,713 0,909 1,657 1,57 1,489 0,251 0,211 0,361 1,807 1,422 0,464 1,372 0,148 1,341 0,407 1,695 0,838 1,461 0,859 1,494 0,622 0,954 0,525 0,845 1,274 0,90 1,803 1,637 0,171 0,112 1,697 0,114 1,801 1,610 1,758 1,573 0,229 0,558 1,410 1,110 1,387 0,614 0,296 0,528 0,209 0,947 1,475 0,220 1,905 1,189 1,990 0,906 0,969 1,333 1,541 0,139 0,357 0,661 1,926 0,163 1,789 0,155 1,52 1,615 0,471 1,945 1,696 1,260 1,330 1,950 1,49 1,888 1,142 1,296 1,853 1,337 0,707 1,334 1,294 0,141 1,700 1,811 1,998 1,632 1,865 1,35 1,850 0,686 0,0 0,102 0,991 1,854 0,870 0,925 0,309 1,348 1,276 1,777 1,767 1,406 1,938 0,215 1,11 1,826 0,280 1,738 1,366 1,7 0,614 1,456 1,855 1,179 0,546 0,117 1,527 0,395 1,939 0,389 1,79 0,332 1,724 1,463 1,337 1,731 1,351 0,919 0,494 1,664 0,87 0,149 1,523 1,411 0,553 0,503 1,198 0,94 0,234 0,440 0,474 1,247 1,940 0,13 1,705 0,187 0,536 1,806 1,79 1,654 0,429 1,760 1,825 0,551 1,143 1,183 0,890 1,647 0,532 1,993 0,724 1,280 1,129 1,757 1,931 1,407 1,159 1,196 1,515 1,479 0,649 1,559 1,644 0,551 0,344 0,345 0,988 0,294 1,551 0,180 1,712 1,674 0,132 1,936 0,768 1,579 1,636 1,891 0,499 0,271 0,512 1,313 1,973 0,405 1,957 1,894 0,502 1,868 0,194 0,301 0,33 0,820 1,829 1,619 1,619 1,494 1,756 0,645 0,190 0,209 1,49 0,403 1,47 1,823 1,966 0,746 1,252 0,495 1,331 1,203 0,599 0,378 0,848 1,206 1,852 1,760 1,336 0,930 0,488 0,566 0,85 0,631 1,238 0,230 0,715 1,549 1,806 0,564 0,750 1,747 0,746 0,602 1,696 1,828 1,895 0,162 0,777 0,121 1,168 1,174 1,206 1,778 0,404 1,332 1,425 0,881 0,406 0,302 1,160 0,514 1,864 1,74 1,48 1,624 1,212 1,692 0,895 1,878 0,300 0,132 1,911 0,665 0,880 1,561 1,840 0,62 1,34 0,110 0,549 1,490 0,311 0,944 1,17 1,32 1,817 1,85 0,964 1,466 0,938 1,927 0,573 1,311 1,88 0,297 0,643 1,457 1,330 0,481 1,249 1,896 0,555 1,57 0,488 0,800 1,528 0,914 1,319 1,632 0,5 0,529 0,889 0,525 1,485 1,896 1,595 1,540 1,254 1,290 1,171 0,918 0,399 0,818 1,481 0,866 0,65 1,645 1,678 1,482 1,36 1,675 1,263 1,948 0,974 1,285 0,141 0,647 0,690 1,639 0,888 0,576 1,149 1,33 0,190 1,508 0,113 0,232 0,22 1,47 1,534 1,771 0,331 1,77 1,267 1,143 1,47 0,971 1,193 1,691 1,167 1,647 1,598 0,890 1,25 1,209 1,303 0,304 0,364 0,408 1,921 1,74 1,112 0,172 0,520 0,415 0,455 1,143 0,423 1,132 1,298 0,90 1,315 1,252 1,462 0,867 1,261 1,902 1,863 1,531 1,996 1,157 0,440 1,46 1,541 0,737 0,551 0,696 0,566 0,28 1,712 1,670 1,140 1,724 1,785 0,688 0,229 0,721 1,159 0,772 0,572 0,923 0,152 1,159 1,775 1,78 1,484 1,163 0,821 1,791 1,716 1,353 1,237 1,360 0,998 0,567 0,233 0,101 1,132 1,427 0,188 1,851 1,689 1,650 0,926 1,545 0,718 1,800 1,441 1,968 1,41 0,752 1,127 0,964 0,668 1,984 1,410 0,337 0,437 0,89 0,843 0,562 1,201 0,444 0,794 1,10 1,248 0,661 1,871 0,567 0,843 0,146 1,41 1,861 1,263 0,606 0,952 0,111 0,710 1,893 1,708 0,586 1,46 0,292 1,763 0,390 1,151 1,128 1,986 0,596 0,910 0,824 0,993 0,602 1,15 0,257 1,157 1,690 1,614 0,552 0,127 0,518 0,880 1,25 0,992 0,717 0,685 1,28 1,767 0,233 1,745 0,224 1,982 0,379 0,448 0,654 1,716 0,949 0,209 1,286 0,541 0,330 0,592 1,803 0,777 0,301 1,304 1,216 0,530 0,349 1,793 0,111 0,22 1,97 1,456 0,293 0,232 0,31 1,680 1,155 0,589 1,977 1,28 0,729 1,827 1,277 1,206 1,205 0,573 0,46 1,256 0,40 1,376 1,685 0,665 0,434 1,309 1,898 1,425 0,60 1,97 1,534 1,139 1,319 1,761 0,994 1,577 1,101 1,389 0,973 0,863 0,384 1,608 1,976 1,586 1,42 1,800 1,847 0,980 1,918 1,905 1,617 0,416 1,823 1,350 0,808 1,229 1,254 1,939 1,185 0,934 0,267 0,467 1,431 1,944 0,432 1,83 0,726 1,882 0,227 0,74 1,593 1,429 1,571 0,889 0,743 1,581 0,548 0,155 1,826 1,358 0,282 0,34 1,583 1,281 1,79 0,897 1,867 1,440 0,153 0,499 0,716 1,552 0,16 1,628 1,93 1,326 0,445 1,198 1,214 1,108 0,903 1,197 1,736 0,81 0,965 1,14 0,264 1,582 0,393 1,815 1,710 0,727 1,24 1,792 0,36 1,247 1,971 0,203 0,564 0,157 0,16 1,293 1,68 1,696 1,813 1,690 1,443 1,543 1,861 1,338 1,933 1,197 1,105 0,65 1,875 1,837 1,134 0,297 0,510 1,106 1,964 0,988 1,74 0,891 1,746 1,595 0,771 1,126 0,339 0,189 1,193 1,705 0,731 0,465 1,424 0,100 1,284 0,562 0,39 0,877 0,54 0,210 1,273 0,964 1,365 1,641 0,806 0,319 1,373 1,993 1,223 1,70 1,977 1,794 1,472 1,951 1,964 1,769 0,189 1,579 1,505 1,362 0,561 1,873 0,950 1,776 1,292 0,113 0,595 1,160 0,349 0,358 1,735 0,691 0,883 0,735 0,863 0,763 0,950 1,597 1,626 0,596 1,631 1,418 0,146 1,827 0,608 0,385 0,614 0,575 1,801 1,531 1,803 1,406 1,692 1,421 1,376 0,636 0,706 0,464 0,731 1,768 0,80 1,419 1,198 1,443 1,530 0,469 0,891 0,89 1,338 0,443 0,19 1,333 0,674 0,952 1,529 1,773 1,519 1,498 1,276 0,254 0,612 1,16 1,855 1,156 1,166 1,420 1,726 1,733 0,246 1,574 0,277 0,284 1,66 0,563 0,963 0,867 0,349 0,989 0,711 1,840 0,305 1,409 1,949 0,29 1,144 0,229 1,463 1,312 0,909 1,301 1,421 1,225 1,650 1,436 1,249 1,303 1,831 1,383 0,300 0,110 0,679 0,628 0,654 1,205 1,982 1,660 0,760 1,572 0,904 1,242 1,574 1,631 0,669 0,945 1,494 0,546 0,327 1,602 0,5 0,866 0,921 1,909 1,831 1,690 1,601 1,276 0,943 1,176 0,552 0,765 0,512 1,579 1,123 0,713 1,820 0,94 1,293 1,255 1,262 0,781 1,69 1,441 1,599 0,847 1,897 1,226 1,224 0,2 0,834 0,670 1,502 0,979 1,753 1,873 0,791 1,602 1,928 1,290 1,346 0,387 0,924 0,177 1,321 1,691 1,956 0,522 0,653 0,352 1,259 1,180 1,224 0,531 0,20 0,603 0,184 1,800 0,98 0,968 0,178 1,323 1,268 1,818 1,259 0,299 1,909 0,810 0,521 0,781 1,691 1,2 0,168 1,929 0,887 0,701 1,47 0,986 1,428 1,614 0,316 1,127 0,534 1,46 0,974 0,763 0,617 0,706 0,293 1,149 0,217 1,809 1,271 0,162 0,746 0,356 0,310 1,19 1,475 0,494 1,28 1,744 0,491 1,857 1,671 0,63 1,323 0,22 1,693 0,911 1,815 1,842 0,575 0,947 1,453 1,631 1,337 1,309 0,381 1,266 0,253 1,294 1,793 0,442 0,57 1,507 1,604 1,123 1,526 0,105 0,117 0,86 1,950 1,193 0,187 1,632 0,635 1,901 0,375 1,174 1,161 1,914 0,588 0,810 0,554 0,485 0,728 0,831 0,860 0,386 0,455 0,513 0,341 0,816 0,985 1,733 0,909 0,524 0,982 0,331 1,770 1,469 1,337 1,527 1,355 1,542 1,490 0,527 0,171 1,310 0,998 1,801 1,202 0,755 0,801 1,22 0,321 0,83 0,918 1,734 1,47 1,921 1,782 0,375 0,535 1,137 1,165 1,675 1,750 1,263 0,361 0,579 1,342 0,505 0,896 1,664 1,501 0,352 0,319 1,264 0,132 0,256 0,140 0,94 1,483 1,33 1,855 0,497 1,645 0,389 0,663 0,296 1,577 1,861 0,873 1,555 0,6 0,722 0,849 1,886 1,615 1,693 1,674 0,963 1,880 1,393 0,413 0,52 0,351 0,926 1,672 0,143 1,785 1,361 0,815 1,752 1,848 0,908 0,12 1,158 0,182 0,220 0,386 1,438 1,455 1,722 1,301 0,315 0,970 1,93 0,339 0,597 1,385 0,349 0,245 0,420 0,984 0,703 0,732 1,942 1,161 1,442 1,565 0,777 1,559 0,255 0,780 1,932 1,662 0,478 0,780 1,684 0,675 1,180 0,232 1,501 0,9 0,944 1,295 1,192 0,902 1,341 0,362 0,717 0,730 0,640 0,571 0,953 1,78 0,931 1,930 1,711 1,490 0,142 0,895 0,613 0,255 1,110 0,872 1,160 0,817 0,948 1,908 1,35 1,284 0,838 0,90 1,208 1,994 0,228 1,857 1,67 0,477 1,319 0,12 0,982 0,399 0,542 1,156 0,197 0,713 0,359 1,33 0,268 0,8 0,824 1,374 0,343 1,875 1,541 0,221 1,602 1,452 1,812 0,218 0,151 0,642 1,24 0,43 0,775 1,529 0,413 0,975 1,982 0,37 1,149 0,362 0,394 0,890 1,878 1,880 1,237 1,727 0,328 0,845 0,295 1,57 1,569 1,944 0,723 1,222 0,342 1,541 1,662 1,849 0,48 0,511 1,657 0,310 0,272 0,344 0,147 1,319 0,126 0,680 1,975 0,341 0,163 1,882 1,595 0,169 1,297 0,421 0,101 1,61 1,418 0,991 1,281 0,470 0,91 1,788 0,963 0,71 0,690 0,572 0,426 1,868 0,861 0,815 0,952 1,230 1,651 1,747 0,86 1,183 0,576 0,943 1,902 1,460 1,700 1,505 1,648 0,736 1,580 0,151 1,559 1,477 1,223 1,580 1,520 0,577 1,468 0,345 1,388 0,81 1,20 1,291 0,378 1,251 1,828 1,733 1,632 1,875 0,475 0,351 0,443 0,911 1,734 0,99 1,506 0,956 1,132 1,344 0,655 0,762 1,797 0,417 0,670 0,250 0,748 1,136 0,900 0,59 1,680 0,647 1,901 1,207 1,270 0,134 0,111 0,18 0,442 0,559 0,583 1,397 1,781 1,529 0,108 0,676 0,834 1,41 0,26 1,262 1,602 0,436 1,613 1,22 1,264 0,665 1,765 1,337 0,246 1,542 0,686 0,413 0,854 0,706 1,128 1,929 1,624 1,171 0,514 0,464 1,972 0,552 0,181 1,195 0,534 1,301 1,789 0,102 0,232 1,901 0,173 1,129 1,38 1,777 0,768 0,447 1,44 1,414 1,516 1,675 1,354 0,84 1,107 1,165 0,582 0,462 1,38 1,58 1,269 1,936 0,161 1,535 1,969 0,753 1,445 1,127 0,365 1,908 1,606 1,632 0,370 0,757 0,874 1,839 0,641 1,2 1,139 0,28 0,529 1,117 0,376 1,151 1,673 1,29 1,258 1,846 1,797 1,296 0,539 1,554 1,315 0,374 1,350 1,829 1,300 0,331 1,733 1,97 0,437 1,787 1,129 1,46 1,559 1,45 1,558 1,858 0,475 1,108 0,346 1,443 0,53 1,905 1,341 0,970 1,700 1,243 1,213 0,108 1,178 1,546 0,81 0,832 1,983 0,160 1,328 1,516 0,555 0,786 0,540 0,790 1,32 1,694 0,964 1,13 1,664 0,734 0,964 1,515 0,835 1,515 0,475 0,212 1,216 1,397 0,984 0,65 0,890 0,986 1,478 0,222 0,994 0,643 1,667 1,801 0,813 1,456 0,47 1,124 0,209 0,861 0,452 1,107 0,648 1,265 0,163 0,283 0,358 0,788 0,759 0,607 1,483 0,66 1,798 1,371 1,444 0,357 1,543 0,253 1,930 0,779 1,28 1,68 0,19 1,831 0,374 1,359 0,749 1,723 1,501 0,794 1,200 1,270 0,890 0,169 0,469 0,650 0,694 0,396 1,228 0,396 1,955 0,876 0,660 0,189 1,208 0,803 0,692 1,377 0,712 0,526 0,215 1,798 1,829 1,565 0,396 1,956 1,781 1,902 1,493 0,231 0,485 1,249 0,610 1,501 0,154 1,979 1,255 1,652 0,102 0,757 0,598 0,722 1,832 0,859 1,56 0,999 1,151 1,215 1,586 0,667 0,303 1,475 1,213 0,882 0,597 1,215 0,64 0,505 0,843 0,273 1,344 1,972 1,744 1,938 0,820 1,935 1,81 0,773 1,739 0,807 1,260 0,23 1,75 1,89 0,511 0,57 1,921 0,277 0,967 1,303 1,964 0,61 1,311 1,903 0,737 0,315 0,153 0,642 0,113 0,988 1,747 0,552 0,314 0,661 1,367 1,221 0,771 0,143 0,707 0,910 1,704 1,455 0,70 0,574 1,658 1,680 0,148 1,75 0,337 1,128 1,619 0,406 0,799 1,756 0,10 1,105 1,969 1,857 1,213 1,588 1,570 0,901 1,80 1,474 1,232 0,928 1,883 0,934 1,74 1,720 1,52 1,730 1,595 1,869 1,977 1,515 1,408 0,71 0,177 1,983 0,880 1,339 1,335 0,244 0,739 1,494 1,894 0,397 1,62 1,168 0,439 1,362 0,399 0,658 1,556 1,104 1,229 0,776 0,132 0,868 1,191 0,91 1,66 0,152 1,197 1,439 0,149 0,809 0,18 0,548 0,209 0,88 0,944 0,241 1,257 0,335 1,39 0,896 0,490 1,973 1,595 1,987 1,695 0,496 1,146 1,684 0,439 1,733 0,681 1,426 1,129 1,325 1,961 0,802 1,598 1,835 0,462 1,205 0,347 0,761 1,274 0,851 1,987 0,185 0,674 1,94 1,642 1,185 1,756 0,19 1,252 0,406 1,916 0,262 1,964 1,613 0,587 0,965 1,262 0,322 1,598 1,175 1,416 0,129 1,165 0,452 1,437 1,782 0,915 1,783 0,10 1,948 1,466 1,374 0,590 0,861 1,363 0,155 0,988 1,158 0,57 1,161 1,446 0,502 1,931 1,982 0,682 0,399 1,416 1,89 0,563 1,665 1,394 0,407 0,806 0,452 1,300 1,563 1,750 1,258 0,251 1,716 1,353 0,5 1,159 0,770 0,896 1,719 0,216 0,519 1,198 1,576 0,660 1,515 1,592 1,812 0,156 0,82 0,688 0,151 0,419 0,381 0,338 0,240 0,167 1,865 0,571 1,930 1,839 1,999 1,719 0,51 1,0 1,250 0,919 0,137 0,684 1,559 1,83 0,984 0,30 1,409 1,41 0,46 0,999 0,477 1,306 1,951 0,714 1,480 0,88 1,447 0,698 1,533 1,188 1,134 0,835 0,889 0,765 1,816 0,467 1,462 1,78 0,311 0,454 1,69 1,536 0,836 0,658 0,275 0,302 0,14 1,207 1,350 0,9 0,86 1,306 0,867 1,780 0,313 1,452 0,473 0,682 0,362 0,116 1,405 1,332 0,452 1,857 0,978 1,757 0,625 0,589 1,360 1,112 1,31 0,953 0,391 0,895 0,320 1,44 0,531 1,132 0,92 0,162 1,800 1,527 0,220 0,784 1,775 0,486 0,652 1,809 0,601 0,42 1,277 1,383 1,971 1,195 1,102 1,784 0,808 1,660 0,826 0,173 1,252 1,264 1,867 0,832 0,795 0,71 0,76 1,473 1,574 0,178 0,344 0,951 0,213 1,0 0,478 0,693 0,736 0,658 0,106 1,197 0,408 0,375 1,907 1,285 0,736 1,534 1,663 1,119 0,227 1,388 0,344 0,872 1,253 1,411 0,243 1,598 0,8 1,794 0,721 0,645 0,845 1,711 1,721 0,106 1,106 1,559 0,590 0,925 1,972 1,880 1,384 0,249 0,34 0,561 0,946 0,620 0,39 0,963 0,525 1,200 0,113 0,798 0,374 1,49 0,717 0,2 0,130 0,948 0,114 1,859 0,239 0,486 1,426 1,295 1,532 1,293 0,750 1,883 1,359 0,884 1,743 0,744 1,814 0,907 1,384 1,45 1,781 1,148 1,316 0,570 1,567 0,985 0,491 1,186 1,183 0,193 0,999 0,514 1,689 1,781 0,578 1,549 1,118 0,895 1,149 0,468 0,206 1,455 1,205 1,102 1,729 0,728 1,256 1,643 1,361 1,851 1,822 0,822 1,36 1,237 0,581 0,796 1,157 0,419 1,32 0,210 0,391 1,696 0,26 0,337 0,350 1,187 0,616 0,19 1,201 1,726 1,449 1,275 0,198 0,599 1,76 1,861 0,496 0,288 1,971 1,731 0,907 0,481 1,491 0,263 1,30 0,93 0,230 1,75 0,52 1,992 1,808 1,233 0,607 1,32 1,265 0,954 1,127 1,885 0,729 0,686 0,870 1,484 0,952 0,620 0,738 0,750 0,61 1,209 1,669 0,820 0,977 1,577 1,910 1,207 0,688 0,943 0,487 1,635 0,498 0,817 1,61 1,632 1,169 1,488 1,985 0,428 0,945 0,939 1,626 0,681 0,261 1,434 1,638 1,68 0,447 0,283 1,171 0,677 1,323 0,844 1,287 0,678 1,31 0,533 1,922 1,606 0,739 0,458 1,294 0,51 0,271 0,473 0,61 0,837 0,940 0,148 1,181 0,746 1,998 1,807 0,780 0,854 0,982 1,737 1,934 0,541 1,216 0,967 1,644 1,536 1,40 0,877 0,497 1,647 0,375 0,119 0,146 0,896 1,987 0,919 0,295 1,552 1,783 1,334 0,648 0,601 1,762 0,97 1,364 0,623 0,994 1,81 1,464 1,4 1,100 1,349 0,68 1,865 0,957 0,688 1,770 1,639 0,811 1,304 1,825 0,668 0,113 1,605 1,826 0,650 1,132 0,195 0,98 0,617 0,572 1,426 0,438 0,851 0,967 1,876 1,70 1,50 0,228 1,43 1,178 0,344 0,474 1,464 1,472 0,168 0,824 1,38 0,516 1,804 1,755 1,249 0,504 0,591 0,67 1,636 0,831 0,404 0,919 0,968 1,869 0,15 1,870 0,250 1,246 1,590 1,609 1,738 0,692 1,756 1,879 1,27 1,375 1,625 1,63 0,711 1,76 1,651 0,503 0,3 1,629 0,319 1,335 0,600 1,899 0,673 1,526 1,500 1,509 1,26 0,596 1,551 0,642 0,170 1,269 1,692 0,335 1,814 1,381 0,876 0,937 1,802 1,408 1,992 1,308 1,96 0,406 1,687 0,250 0,554 1,632 0,969 1,57 0,40 1,282 0,263 1,525 0,427 1,507 1,478 0,197 0,233 1,705 1,824 0,286 0,63 0,377 0,467 1,25 0,595 1,79 1,148 1,318 0,193 0,50 1,718 0,574 0,919 1,757 0,3 0,718 0,902 1,885 1,583 0,368 0,655 0,328 0,733 1,678 0,224 0,702 0,16 1,189 1,52 0,191 1,682 0,121 1,416 1,239 1,759 0,724 0,884 0,864 0,193 0,816 0,739 1,302 0,775 1,731 0,790 0,782 1,660 1,330 1,452 1,983 0,741 1,405 1,118 1,542 0,784 1,897 0,96 1,580 0,119 1,428 0,309 0,817 1,367 0,925 1,888 0,604 1,508 1,658 0,930 1,109 0,36 0,232 0,419 0,759 0,468 0,885 0,948 1,349 0,343 1,422 0,602 0,51 1,381 0,449 1,613 0,142 1,783 0,64 1,71 1,699 0,573 1,551 0,552 0,171 0,142 0,244 1,176 0,490 0,288 0,221 0,128 0,206 1,420 0,940 0,493 0,823 1,417 0,932 1,203 1,525 0,214 0,150 0,620 0,596 1,103 0,162 0,432 0,458 0,343 0,405 1,291 1,121 0,442 0,367 1,614 1,120 1,82 0,257 1,119 0,315 1,167 1,748 1,757 0,41 0,41 1,899 1,173 1,925 0,369 0,888 0,688 0,21 1,244 0,323 0,328 0,474 1,255 0,345 0,928 0,468 0,252 1,362 0,842 1,619 0,791 1,986 0,269 1,428 1,624 1,119 0,989 1,757 0,825 0,606 1,927 0,914 1,80 1,360 1,80 1,101 0,713 0,176 1,160 1,599 1,229 1,368 1,534 0,784 1,578 1,60 0,548 1,858 1,51 0,960 0,116 0,345 0,134 0,162 0,842 0,92 0,961 1,714 0,623 0,553 0,304 1,948 0,870 1,849 0,773 1,592 0,670 1,306 0,3 0,692 1,126 0,453 1,667 1,708 1,330 0,441 1,778 1,229 1,715 1,101 0,708 1,936 1,6 0,56 1,386 0,646 0,665 1,637 0,160 1,13 1,465 0,434 1,867 0,395 1,559 0,494 0,886 1,219 1,824 0,547 0,339 1,513 0,158 1,572 0,147 0,715 1,209 0,657 0,120 1,816 1,316 0,248 0,802 1,898 0,648 0,161 0,204 1,666 0,333 0,514 1,642 0,296 0,843 0,332 1,879 1,46 1,0 0,656 0,849 1,765 0,266 1,85 0,374 0,771 1,266 0,730 1,468 1,396 1,544 1,542 1,869 1,771 1,345 0,337 0,938 0,366 0,449 1,320 0,84 0,444 0,285 0,66 1,353 0,692 0,954 0,215 0,378 0,343 0,893 0,122 1,529 1,355 0,832 1,13 1,768 1,252 0,729 1,696 0,889 1,867 0,548 1,814 1,713 1,51 0,620 1,447 0,415 1,373 1,369 0,402 1,490 1,683 0,22 0,65 0,110 0,889 0,597 1,715 1,416 1,8 1,258 1,561 1,129 0,115 1,978 0,582 0,998 1,793 1,990 0,483 1,545 0,915 1,468 1,287 1,726 0,321 0,166 0,352 0,934 1,666 0,75 0,265 0,677 1,254 0,763 0,57 1,491 0,425 0,457 0,855 1,648 1,509 1,524 0,475 1,814 0,775 0,780 0,762 0,478 1,794 1,479 1,436 1,223 0,230 1,580 1,940 1,18 1,254 1,107 0,532 1,342 0,152 1,942 0,670 0,233 0,744 1,957 1,37 0,738 1,45 0,893 0,159 1,760 0,198 1,574 1,849 1,191 1,881 1,677 1,957 1,609 0,937 1,439 0,324 0,895 1,717 0,260 1,143 1,759 1,288 1,215 0,864 0,517 0,498 1,570 0,642 1,924 0,600 0,910 1,98 0,967 0,811 1,130 0,666 1,225 1,857 1,412 1,581 1,180 0,181 1,768 0,102 1,537 0,940 1,680 0,621 0,946 0,95 0,772 1,992 0,237 0,251 0,452 1,887 1,201 0,388 1,223 1,72 0,959 0,371 0,811 0,481 1,246 1,622 0,378 1,811 0,636 1,917 0,193 0,212 1,752 0,939 0,115 1,398 0,292 0,781 0,159 0,589 1,197 0,564 0,732 1,996 0,934 1,571 1,798 0,187 0,415 0,368 1,505 1,971 1,676 1,701 1,152 1,478 1,192 0,557 0,322 1,326 1,344 0,546 1,629 0,729 1,138 1,697 1,187 1,406 0,707 0,905 0,39 0,804 1,626 1,524 0,868 0,667 0,444 1,419 1,827 1,500 1,407 0,463 0,164 0,343 1,94 0,659 0,209 0,277 1,801 1,581 0,183 0,210 1,90 0,448 1,33 0,244 1,709 1,51 0,683 1,147 0,807 1,800 0,71 1,138 0,553 1,645 1,967 0,633 0,347 0,138 1,259 0,296 0,446 0,281 1,538 1,588 0,196 0,573 0,651 1,702 1,145 1,364 0,309 0,794 1,623 0,677 1,139 1,840 1,812 1,994 1,839 0,374 0,255 1,387 0,429 1,616 0,534 0,166 1,482 0,554 0,80 1,137 1,260 0,438 0,185 1,420 1,824 0,668 0,560 0,643 1,68 0,68 0,773 1,210 0,836 0,795 1,594 1,657 1,866 0,244 1,577 1,569 1,143 0,366 0,574 0,982 0,299 1,998 1,820 0,92 1,910 1,806 0,355 1,781 1,182 1,82 0,687 1,146 1,305 1,323 1,431 1,501 0,410 0,994 1,987 0,682 0,157 0,491 1,92 0,379 0,425 1,569 1,481 1,216 0,137 1,715 0,700 0,422 0,109 0,540 1,583 1,666 0,599 0,84 1,549 0,332 0,227 0,498 0,781 0,172 1,76 1,608 1,560 0,781 1,784 1,212 1,808 0,177 1,513 0,303 0,375 0,639 1,74 0,263 1,387 1,851 1,171 0,657 1,662 0,538 0,957 0,523 1,488 0,415 0,394 0,451 1,41 0,876 0,777 1,985 0,772 0,19 1,98 1,18 1,823 0,61 0,874 1,148 0,727 0,573 1,675 1,567 0,832 1,68 0,418 0,101 1,744 1,72 0,381 0,410 1,474 1,858 1,286 0,103 1,479 1,70 0,511 1,187 1,525 1,523 1,722 1,225 0,636 0,195 0,624 1,709 1,765 0,183 1,817 0,487 1,268 0,357 1,471 1,584 1,314 1,964 0,45 0,37 1,116 1,543 1,652 1,725 1,95 0,58 0,292 1,405 0,860 0,304 1,470 1,474 1,847 0,661 1,649 0,373 0,28 0,780 1,970 1,341 1,226 1,297 0,41 1,837 0,256 0,731 1,248 1,629 0,985 1,901 0,423 0,584 0,569 1,676 0,94 1,560 1,640 1,172 0,669 0,643 0,336 1,620 0,474 1,87 1,3 0,344 1,763 0,12 0,16 0,464 0,512 1,761 0,929 1,346 1,906 0,138 1,886 0,902 0,943 1,211 1,326 0,16 1,314 0,931 1,656 0,669 0,205 1,805 0,677 1,250 1,295 0,144 1,866 0,20 0,869 1,644 1,329 1,499 0,890 1,903 1,891 1,380 1,15 1,584 0,958 1,373 1,645 0,336 0,185 1,79 0,112 1,796 1,891 1,512 0,651 0,608 1,336 0,281 0,451 1,126 1,317 1,183 0,945 0,87 0,448 1,483 0,746 1,485 0,194 0,179 1,397 1,625 1,606 1,614 1,893 1,249 1,246 1,22 0,525 1,642 0,365 1,115 1,660 1,615 1,167 1,867 0,215 0,147 1,447 1,148 0,192 1,154 0,404 1,999 0,350 0,296 1,171 0,332 0,65 1,946 1,812 1,167 1,432 1,219 1,429 1,200 0,344 0,32 1,834 0,327 1,322 0,457 1,6 0,84 0,665 0,348 0,279 0,589 1,502 1,978 1,796 0,496 1,130 0,368 0,625 1,256 0,489 0,358 1,498 0,12 1,24 1,523 0,886 0,884 0,43 0,678 1,133 1,175 0,997 0,549 0,784 0,172 0,579 1,944 1,441 1,416 1,817 0,478 1,716 1,7 1,125 1,287 0,952 1,95 1,1 0,190 1,434 0,785 0,73 0,753 0,614 0,797 1,706 0,523 1,60 1,752 1,391 1,150 0,373 1,575 1,955 0,313 0,382 0,481 1,215 1,879 0,393 0,832 1,210 0,680 0,579 0,816 1,868 0,220 1,58 1,152 0,44 1,135 0,757 0,653 0,255 1,476 0,230 1,473 1,542 1,793 0,429 0,669 0,840 0,612 1,721 1,335 1,409 1,831 0,549 1,839 0,37 1,618 0,651 1,392 1,995 0,806 1,664 0,354 0,493 1,165 1,459 1,58 0,245 0,73 1,903 0,225 0,490 1,986 0,314 1,115 0,727 1,18 1,584 0,920 0,504 1,793 0,184 1,842 1,184 0,179 0,68 0,760 0,78 1,64 1,779 1,799 0,847 1,683 1,535 0,548 1,74 0,427 1,27 0,742 1,54 1,39 1,792 0,997 0,494 1,165 0,630 0,709 1,875 0,48 0,391 0,840 0,254 1,430 0,981 1,265 1,696 0,312 1,53 0,218 1,707 0,126 0,313 1,957 0,925 0,432 1,798 0,381 0,525 0,291 0,891 1,322 0,92 0,365 1,765 0,877 0,388 0,721 1,312 0,304 1,209 1,531 0,382 0,134 1,77 1,857 1,998 1,597 1,408 1,851 1,712 0,600 1,286 0,385 0,729 1,382 1,811 1,936 1,315 1,720 0,636 1,76 1,361 0,44 0,329 1,180 1,466 0,782 0,122 1,191 0,815 1,614 1,582 1,968 1,193 1,603 1,972 0,195 0,297 0,900 0,633 1,88 1,931 1,100 0,514 1,16 0,124 1,530 0,504 1,757 0,61 0,605 1,528 1,935 0,651 0,680 0,514 0,207 0,591 1,390 0,734 0,827 1,712 1,626 1,331 1,846 1,34 0,821 1,821 1,271 0,417 0,463 1,195 0,773 1,183 1,103 1,467 0,705 0,718 0,203 1,8 0,680 0,47 1,617 0,985 0,52 0,582 1,605 1,75 1,878 0,587 1,656 1,262 1,679 0,78 1,421 1,319 1,785 0,721 0,257 0,872 1,275 0,500 0,50 0,731 0,199 0,569 1,558 1,50 1,414 1,659 1,894 1,816 1,451 0,744 1,845 0,311 1,18 0,98 0,286 0,195 0,553 1,632 1,594 0,441 0,564 0,519 1,188 0,615 0,933 0,773 0,923 0,232 0,338 0,220 1,592 0,612 1,821 1,158 1,658 0,53 1,719 0,455 0,508 0,484 1,774 1,813 1,975 1,687 0,959 1,744 1,109 1,890 0,410 0,655 1,723 1,357 0,765 0,708 1,937 0,366 1,441 0,673 1,159 1,63 1,889 0,492 0,521 1,714 0,251 1,994 0,552 1,980 0,809 0,823 0,431 1,727 0,597 0,880 1,59 1,886 1,315 1,266 1,603 1,788 0,102 0,617 0,10 1,513 1,780 0,420 1,716 1,212 0,67 1,55 1,342 0,496 1,299 0,61 1,400 0,596 0,102 1,342 1,416 1,93 1,868 1,540 1,391 0,26 0,708 1,995 0,729 0,426 1,771 1,245 1,27 1,696 0,170 1,811 0,88 1,431 0,170 0,748 1,754 0,38 0,389 0,404 1,62 0,936 0,484 0,530 1,709 0,786 0,109 1,139 1,754 1,280 1,868 1,337 1,307 0,662 1,721 1,629 0,162 1,995 1,944 0,548 0,731 1,159 0,416 0,499 1,676 0,242 1,978 1,408 1,931 1,185 1,220 1,681 0,879 0,604 0,780 0,556 1,389 1,716 1,610 1,459 0,112 1,721 1,317 0,24 1,480 0,500 1,612 0,100 1,293 1,503 1,614 1,397 1,380 1,724 1,254 1,385 0,774 1,444 0,207 1,661 0,255 1,555 0,758 1,871 1,662 0,956 1,905 1,444 1,868 0,373 1,865 0,614 1,225 0,222 1,952 1,533 0,606 0,598 0,653 1,91 1,539 1,391 1,401 1,454 1,338 1,227 0,671 0,437 0,809 1,878 0,152 1,734 0,748 0,881 0,840 1,571 1,139 0,400 0,7 1,539 0,531 1,215 0,803 1,66 1,130 0,321 0,766 1,199 1,312 0,278 0,632 1,729 0,359 1,616 0,782 1,753 0,274 0,706 1,811 0,587 1,521 0,633 0,910 1,521 1,610 0,18 0,704 1,630 1,320 0,301 0,114 0,89 1,87 0,885 1,998 1,932 0,293 0,684 0,132 0,382 1,12 1,60 0,62 0,457 1,528 0,198 1,885 0,537 1,886 0,336 1,788 0,254 1,707 1,7 0,789 1,323 0,147 0,453 0,722 1,472 0,15 0,980 1,102 1,894 1,591 1,583 0,161 1,350 0,606 0,738 1,661 0,439 0,720 0,913 1,214 0,386 0,39 1,411 1,362 1,803 1,184 0,202 1,169 1,308 0,894 1,816 0,788 1,719 0,405 1,201 0,274 1,401 1,387 0,962 0,722 1,140 1,356 1,346 1,294 0,391 1,93 0,935 0,651 0,337 0,372 0,175 0,80 0,913 0,283 0,267 1,8 0,715 0,231 1,660 0,107 0,624 1,105 0,768 1,545 1,522 0,395 1,474 1,668 1,907 0,476 1,661 0,495 0,654 0,731 0,127 1,149 0,609 1,341 1,578 0,164 0,271 0,327 0,205 0,993 1,710 1,449 0,874 1,536 0,378 1,643 1,107 1,671 0,273 1,327 1,820 1,451 0,612 0,400 1,533 1,967 1,380 0,587 1,909 0,183 0,947 0,613 0,55 1,298 0,478 0,590 0,728 1,399 1,9 1,672 1,814 0,385 1,741 0,109 0,456 0,3 1,765 0,532 1,563 0,939 0,535 0,38 1,577 1,756 0,233 1,511 1,194 0,991 0,109 0,29 0,61 1,480 1,69 0,717 0,284 0,774 0,54 0,476 0,618 0,352 1,240 1,318 1,593 1,959 1,101 1,192 1,923 0,126 0,348 1,996 0,804 0,542 1,983 1,167 1,549 1,939 0,785 0,558 1,665 0,834 1,375 0,74 1,891 1,220 1,832 0,427 0,897 1,400 1,856 1,860 0,517 0,963 0,555 0,433 0,34 0,902 0,923 0,486 1,324 1,816 0,390 0,875 0,491 1,32 1,465 0,308 1,207 0,233 1,806 0,298 0,584 0,336 1,515 0,662 1,779 1,812 1,931 0,872 1,972 1,181 1,869 0,800 0,663 0,112 0,303 0,232 1,665 1,204 0,885 0,896 1,499 1,527 0,99 0,210 0,982 0,567 0,732 1,126 0,331 0,117 0,406 1,804 0,625 1,515 1,108 1,263 0,529 1,585 1,379 0,375 1,932 1,966 1,524 1,992 0,384 1,345 0,528 1,243 0,827 1,242 0,11 1,990 1,480 1,575 0,351 1,202 1,396 0,189 1,595 1,91 1,182 0,804 1,221 0,116 0,877 0,809 0,702 1,968 0,614 0,936 1,219 1,306 1,380 0,389 0,231 1,229 0,928 0,379 1,515 1,587 0,3 1,911 0,656 0,881 1,164 1,325 1,632 1,270 1,579 0,658 1,712 1,914 1,660 1,122 0,405 1,894 0,195 1,870 1,667 0,421 0,574 0,269 0,205 0,964 0,987 0,829 1,884 1,99 1,294 1,776 0,441 1,894 1,109 1,433 0,946 1,795 0,277 0,557 0,847 0,783 1,959 1,725 1,666 0,765 0,333 1,926 0,538 1,138 1,293 1,469 0,959 1,67 0,706 0,754 0,461 0,464 1,135 0,158 1,218 0,614 1,231 0,9 1,939 0,155 1,999 1,728 0,989 1,472 0,87 1,526 1,455 0,417 0,100 0,980 1,541 0,80 1,873 0,260 0,593 1,328 1,359 1,765 0,851 1,97 1,937 0,703 0,900 0,556 0,14 0,277 0,512 1,110 0,829 1,783 1,572 0,126 0,333 0,42 0,522 0,38 0,656 1,662 0,115 0,390 0,138 1,743 0,870 1,467 1,510 0,803 1,118 1,440 0,248 0,800 1,29 1,417 0,346 0,832 0,276 0,105 0,692 1,526 1,302 0,586 0,79 0,966 1,398 1,861 1,872 0,697 1,576 1,743 0,963 0,501 0,830 1,782 1,913 1,663 1,397 1,560 1,210 1,620 0,598 1,331 0,507 0,998 1,518 1,185 0,917 1,601 0,994 0,322 0,571 0,271 0,578 1,837 1,489 0,213 1,758 0,938 0,291 0,294 1,74 0,569 1,564 0,773 0,247 1,176 0,209 1,858 1,137 0,669 0,343 0,823 0,860 1,831 1,342 0,113 0,722 0,777 1,150 1,882 0,757 1,322 1,867 0,762 1,263 1,176 0,351 1,974 0,499 1,189 0,426 0,905 0,115 0,3 1,945 0,905 1,686 1,605 0,43 0,724 0,716 1,239 0,759 1,220 1,29 1,906 1,848 1,640 0,218 1,380 1,898 0,711 0,942 0,912 1,523 1,306 1,562 0,724 1,682 1,725 1,84 1,74 1,441 1,292 0,410 1,175 0,767 0,370 0,713 1,598 0,932 0,358 0,260 0,273 1,875 1,388 0,615 1,252 0,312 0,53 1,476 0,706 0,405 1,252 1,691 1,732 0,183 1,702 0,539 1,421 1,496 1,493 1,871 0,458 1,689 1,312 0,249 1,101 1,66 0,324 0,852 0,950 1,749 0,458 0,311 1,989 1,904 1,734 0,77 1,976 0,890 0,876 1,359 0,239 1,697 0,206 0,228 1,193 1,55 1,120 1,623 0,605 1,592 1,571 0,967 1,749 0,6 1,155 0,653 0,712 0,711 0,192 0,363 1,286 1,516 0,260 0,922 1,280 0,485 1,480 1,229 0,951 1,167 1,640 0,862 0,904 0,852 0,424 0,829 0,369 0,322 1,162 1,221 0,440 1,96 1,671 0,635 1,944 1,162 0,540 1,669 1,549 1,755 1,826 0,565 1,265 0,640 1,412 0,970 1,725 0,891 0,700 1,172 0,601 0,24 0,976 1,807 0,690 1,315 0,614 1,283 1,610 1,351 1,312 0,490 0,956 1,742 0,650 1,465 0,961 1,228 0,162 0,88 0,232 1,228 1,912 0,500 0,700 0,599 1,348 0,554 0,359 1,458 0,229 0,754 1,870 1,345 0,801 1,873 1,789 0,791 1,726 0,330 1,981 0,369 0,290 1,311 1,301 0,469 1,253 1,375 0,66 1,189 1,999 0,900 1,604 1,794 1,999 1,229 1,601 1,816 0,51 1,200 1,302 0,542 1,476 1,824 0,329 1,745 1,64 1,575 1,927 1,7 1,13 0,994 0,500 0,291 0,360 0,732 0,309 1,685 0,337 0,355 0,562 1,469 1,949 1,694 1,280 1,790 1,608 1,742 1,39 1,101 0,816 0,708 0,390 0,870 0,975 0,403 0,609 1,20 0,802 1,49 0,146 0,667 0,974 1,770 0,829 1,833 1,862 1,762 1,332 1,102 0,707 1,761 0,919 0,79 0,753 0,999 1,6 1,805 0,513 0,578 1,461 1,4 1,496 0,135 1,388 0,434 0,829 0,249 1,974 0,859 1,547 0,56 0,80 0,425 1,272 0,562 1,111 0,950 1,316 0,446 1,279 1,499 0,467 0,233 0,894 0,488 0,752 0,517 1,25 0,525 1,451 1,489 1,375 1,745 1,502 1,97 1,950 0,781 1,329 1,909 0,383 1,533 1,486 1,506 0,234 0,563 1,321 1,30 1,273 0,702 0,949 1,197 1,428 0,115 1,186 0,789 0,837 1,411 1,253 1,327 0,314 0,974 0,19 1,18 1,191 1,835 0,532 1,66 1,943 1,234 1,709 0,592 0,272 0,528 0,111 1,352 0,601 1,629 1,268 1,46 0,77 1,150 1,408 1,7 0,403 0,119 1,885 1,901 0,877 0,809 1,615 1,864 1,793 1,490 1,404 1,654 1,147 1,608 0,739 0,9 0,846 1,767 1,16 1,984 1,508 1,352 0,167 1,728 1,707 0,161 0,728 0,69 0,18 0,701 0,141 1,110 0,793 0,257 1,721 0,88 0,974 0,374 1,210 0,778 1,168 1,253 0,149 0,176 0,888 0,926 1,970 0,346 0,478 1,431 0,576 0,130 1,862 0,600 1,111 0,720 1,663 0,465 0,401 0,1 1,174 1,589 1,813 0,493 1,742 0,938 1,306 0,486 0,528 0,635 0,749 0,837 0,16 1,406 0,298 0,309 0,229 0,306 0,641 0,671 1,57 0,731 1,617 1,763 1,656 1,919 1,421 1,814 1,368 0,909 1,735 0,227 1,571 0,735 1,296 0,383 0,208 0,378 0,415 0,848 1,426 0,94 1,93 1,734 0,334 1,217 0,431 0,273 1,806 0,755 0,668 1,845 1,758 1,729 1,134 1,980 0,548 0,934 1,467 1,191 0,760 1,76 0,410 1,337 1,867 0,346 1,675 0,190 0,487 1,549 1,928 0,763 1,724 1,626 1,803 1,763 1,264 1,259 1,296 1,14 0,655 0,457 1,645 1,98 0,371 0,84 0,487 1,401 0,531 1,398 1,78 0,478 0,179 0,328 1,29 0,259 1,378 0,47 1,943 0,128 0,796 0,658 1,147 1,158 0,990 0,853 0,163 0,815 1,388 1,970 1,604 1,872 0,565 1,706 1,456 0,49 0,806 1,882 1,985 0,309 1,452 0,534 1,700 1,122 1,908 0,728 1,928 1,257 1,472 1,1 1,322 1,390 0,343 1,482 1,921 0,769 1,398 0,58 1,309 1,136 0,933 1,257 0,569 1,514 0,731 1,813 0,628 1,657 1,817 1,956 1,651 0,161 0,183 0,593 1,34 1,437 0,667 1,876 1,215 0,749 1,4 1,675 0,460 0,382 1,38 1,675 0,544 1,749 0,84 0,311 0,784 0,348 0,227 1,143 1,916 1,195 1,498 0,885 0,145 0,939 1,978 0,586 0,908 1,937 0,704 0,4 0,758 1,11 0,742 1,738 0,385 1,273 1,161 0,763 0,127 1,591 0,234 1,726 1,909 1,604 0,124 0,729 1,341 0,576 1,368 0,127 1,127 0,86 1,183 0,197 1,173 1,746 0,49 0,206 0,305 1,156 1,712 1,607 0,960 0,971 0,719 0,146 0,643 1,122 1,662 0,537 1,521 0,321 0,605 1,864 0,555 1,903 0,980 1,489 0,217 0,448 1,803 1,516 0,883 0,328 0,11 1,572 1,796 1,268 0,544 1,252 0,252 1,666 0,401 0,315 0,924 1,85 0,507 1,495 0,973 1,944 0,540 0,883 0,68 1,133 0,756 1,256 1,871 1,224 0,250 1,82 0,187 0,333 0,567 1,432 1,973 0,863 1,673 0,377 1,208 1,606 0,394 1,163 0,316 1,527 0,741 0,423 0,721 0,569 0,903 0,779 1,754 0,719 0,907 0,859 1,140 1,245 0,334 1,944 1,430 1,875 1,693 1,172 1,918 0,108 1,655 1,133 0,283 0,224 0,45 1,902 1,104 0,42 1,382 1,243 1,505 1,844 0,294 0,967 1,941 0,594 1,497 0,462 1,37 1,972 0,89 1,296 0,187 0,495 1,48 0,184 1,971 1,761 0,597 1,284 0,960 0,564 0,201 0,501 0,652 0,847 1,96 1,698 0,993 0,767 0,903 1,19 0,70 1,294 1,39 0,105 1,684 0,593 1,640 1,814 1,268 1,837 0,391 0,791 0,453 0,608 0,525 0,324 0,941 0,838 1,717 0,67 0,257 1,795 0,298 1,264 0,951 0,971 0,108 1,784 0,176 0,712 1,141 1,636 0,653 0,111 0,363 1,444 1,895 0,24 1,765 1,409 0,775 1,740 0,348 0,826 0,953 1,839 1,808 1,745 0,121 1,712 1,498 0,372 1,618 1,379 1,961 1,146 1,344 0,851 1,795 0,229 0,241 1,784 1,701 1,846 0,117 0,740 1,256 1,219 0,268 1,809 0,787 1,815 1,46 1,598 1,458 0,841 1,319 0,846 1,31 1,366 1,610 1,271 1,113 1,41 0,581 0,361 0,889 0,491 1,124 0,867 0,926 0,374 1,958 0,290 0,579 0,648 1,585 0,861 1,823 0,769 1,340 1,622 1,205 0,78 0,878 1,72 0,790 0,984 0,203 1,208 1,426 1,850 1,298 1,502 0,826 0,774 0,287 1,812 1,284 1,423 0,903 0,726 1,943 0,201 0,445 1,849 0,115 0,235 1,447 0,690 1,309 0,494 1,82 0,540 0,122 0,102 0,872 1,991 0,890 1,40 1,535 1,273 1,267 0,7 1,7 1,803 0,609 0,33 0,166 0,732 1,365 0,980 1,608 1,684 1,317 1,737 0,569 0,559 1,976 1,439 1,946 1,965 0,522 0,864 0,347 1,185 1,690 0,929 1,755 0,387 1,831 0,564 0,377 0,456 0,266 0,601 1,986 0,470 0,389 0,272 1,637 0,788 1,156 1,48 0,840 1,320 0,545 1,221 1,458 1,673 1,79 0,475 0,963 1,366 1,862 0,809 1,27 1,397 0,554 1,503 0,774 1,720 0,994 0,492 0,416 1,242 1,206 0,925 1,625 1,291 1,819 1,474 1,552 1,965 0,376 1,775 1,757 1,872 1,936 0,419 1,326 0,231 1,181 1,160 0,490 0,806 0,525 1,567 1,249 0,111 0,661 1,702 0,750 0,544 0,76 0,656 0,983 1,875 1,519 0,322 1,174 0,212 1,34 0,484 1,355 0,777 0,186 0,911 0,294 1,601 1,322 0,598 1,64 1,245 1,614 0,378 0,98 1,683 0,723 0,411 1,924 0,741 0,458 1,657 0,296 0,31 0,957 1,885 0,465 0,137 1,512 0,848 1,692 0,342 1,630 1,264 0,938 1,283 1,308 1,464 0,645 1,76 1,257 1,394 0,684 1,885 1,529 0,972 0,562 1,849 1,785 0,17 0,840 0,161 0,877 1,487 0,14 1,435 0,307 1,568 1,554 0,594 0,532 0,163 1,443 1,253 1,277 1,556 1,231 0,427 1,45 1,314 0,998 0,246 0,625 0,936 1,649 1,655 0,711 0,940 0,717 0,266 0,27 1,40 1,959 1,373 1,666 1,453 1,947 0,369 1,863 1,801 0,207 1,329 1,646 1,14 1,80 0,201 1,742 1,203 0,928 1,774 0,509 0,941 1,334 1,387 0,914 0,790 1,141 0,566 1,298 1,566 1,418 1,382 0,102 1,309 1,9 0,28 1,555 0,135 0,196 1,304 0,417 1,387 0,581 0,421 0,435 0,943 1,459 1,654 0,117 0,445 0,2 0,645 1,86 0,488 1,288 1,851 0,614 1,445 1,828 0,500 1,601 0,972 1,20 1,893 0,554 1,302 1,530 1,391 0,561 1,805 1,543 1,832 0,819 0,963 0,780 1,358 0,347 1,360 0,275 1,648 0,193 0,479 0,544 0,452 1,267 1,883 0,978 0,996 1,394 1,805 0,285 1,245 0,928 0,195 0,595 1,839 0,927 0,940 1,549 1,177 0,707 1,951 0,25 1,487 0,906 0,698 1,746 1,89 0,989 1,227 1,198 1,725 0,470 1,56 1,552 1,165 0,839 1,998 1,265 0,897 1,408 0,893 1,577 1,246 0,675 0,508 1,780 0,718 0,962 0,434 1,894 0,300 1,930 0,893 0,909 1,382 0,871 1,511 0,679 0,339 0,348 0,868 1,335 0,31 0,666 1,829 0,443 1,173 1,160 1,659 1,249 0,885 1,996 1,114 1,734 1,219 1,332 1,36 1,689 1,169 0,300 0,489 1,704 1,655 1,433 0,214 0,492 1,548 0,758 0,203 0,681 0,43 1,371 0,383 0,456 1,877 0,238 0,906 0,202 1,327 1,381 1,590 0,616 1,947 1,332 0,368 1,165 1,850 0,55 0,402 1,786 0,477 1,500 0,345 0,912 1,908 1,392 1,595 1,780 0,729 0,62 0,473 1,894 1,895 1,984 0,820 0,203 1,84 0,971 0,363 1,366 1,777 0,36 1,434 1,395 1,748 0,111 1,57 0,877 1,809 0,63 0,42 1,837 1,578 0,51 1,321 0,592 0,611 1,268 1,467 0,483 1,261 1,293 0,651 0,919 1,348 1,103 0,159 1,711 0,61 0,260 0,106 1,369 1,898 0,229 0,983 1,312 1,794 0,12 0,104 1,480 1,722 1,435 1,242 0,455 1,72 0,353 1,652 1,573 0,980 0,128 1,449 1,300 0,399 1,113 0,231 1,116 0,516 0,924 0,996 0,622 0,387 1,890 0,168 0,448 0,983 1,883 0,550 1,897 0,974 0,465 0,937 0,319 0,185 0,81 1,574 0,875 0,599 1,675 1,908 1,607 1,834 0,826 1,685 0,455 0,371 1,661 1,264 1,25 1,596 1,555 0,688 0,789 1,481 0,246 0,507 0,78 1,358 0,154 0,810 0,33 0,391 0,870 0,917 1,506 1,222 1,223 0,800 0,308 0,967 1,145 0,350 1,6 0,957 0,173 0,417 1,478 0,924 1,21 1,868 0,95 1,271 0,351 1,978 0,903 1,966 0,491 0,364 0,39 1,307 0,254 1,931 1,342 0,656 1,817 1,899 1,951 0,55 1,233 0,221 1,954 0,324 1,76 0,477 1,959 1,435 0,306 0,312 0,182 1,917 0,961 1,386 1,66 1,224 0,687 1,417 1,408 1,615 0,525 0,58 1,134 0,281 0,897 1,293 0,367 0,671 1,13 0,745 1,543 0,160 0,7 0,593 0,385 0,788 1,113 1,287 0,313 1,736 1,972 0,118 1,807 0,796 1,143 0,493 0,682 0,923 1,65 0,606 1,5 1,111 1,712 1,38 1,321 0,654 1,135 1,482 0,745 1,551 1,506 0,704 0,746 0,469 0,330 1,475 1,941 0,219 1,196 1,590 1,183 0,107 0,693 0,63 1,241 1,350 0,186 0,996 0,202 0,560 0,21 0,320 0,541 0,144 0,158 0,44 0,873 0,84 0,411 1,275 0,37 1,47 0,693 0,113 1,736 1,109 1,168 1,928 0,242 0,70 1,820 1,893 0,858 0,902 1,901 0,221 1,843 0,556 0,349 0,173 1,386 0,333 0,823 1,276 0,246 0,655 1,577 1,70 0,416 1,554 1,904 0,717 1,105 0,796 0,508 0,196 1,869 0,734 0,185 1,569 1,932 1,730 1,431 1,478 1,629 1,819 1,856 1,402 0,129 0,404 1,828 1,770 1,360 1,80 0,260 1,666 0,263 1,64 0,633 0,200 1,646 1,322 1,294 0,675 0,84 1,560 0,790 1,281 0,295 1,235 1,985 1,909 1,756 1,606 1,620 0,789 0,482 0,453 1,0 1,54 1,782 1,388 1,892 1,306 0,772 1,257 0,284 0,728 1,468 0,538 0,50 1,23 0,353 1,601 1,101 1,166 0,947 0,653 0,206 0,762 1,928 1,437 1,13 1,628 0,82 1,24 0,316 1,916 1,402 1,438 1,971 0,338 1,330 1,9 0,505 1,747 0,883 1,514 0,712 0,123 1,151 1,301 1,33 0,103 0,50 1,761 0,706 1,192 1,674 1,502 1,690 1,920 1,290 0,508 1,429 0,258 1,708 0,691 0,593 0,814 0,379 0,635 1,895 1,258 1,268 1,459 0,348 1,435 1,103 0,442 0,58 1,688 1,256 0,414 1,221 1,606 1,303 1,842 1,641 1,596 1,395 0,499 0,166 1,2 0,284 0,470 0,79 1,202 0,582 0,380 0,875 1,561 0,453 1,144 0,559 1,319 1,565 1,312 0,639 0,189 0,336 0,604 0,889 0,198 1,360 1,296 1,898 1,94 0,936 0,169 1,191 0,150 1,977 1,403 0,61 1,793 1,483 0,533 1,928 0,649 0,66 0,321 1,106 0,185 0,580 0,183 0,737 0,438 1,61 0,304 1,706 1,378 1,604 0,886 0,529 1,391 0,391 0,351 1,807 0,934 1,40 1,20 0,353 0,697 0,348 0,283 0,697 1,318 0,883 1,210 0,785 0,724 0,467 0,82 0,56 0,546 1,619 0,932 1,18 0,702 0,90 0,40 1,388 1,204 1,154 0,595 0,641 1,276 0,452 0,779 1,76 0,654 1,355 0,599 0,212 0,214 1,675 0,485 1,566 1,481 1,92 1,444 1,716 0,868 1,113 1,620 0,15 1,875 1,159 1,966 0,588 0,782 1,359 0,70 0,879 1,776 1,804 0,45 1,456 1,820 0,152 0,367 0,762 0,599 0,697 0,421 0,417 1,667 1,870 0,802 0,151 0,447 1,988 1,436 0,198 0,327 1,257 1,304 0,117 1,351 0,127 1,891 0,264 0,895 1,30 0,290 0,152 0,554 1,44 1,287 1,264 1,493 0,371 0,437 0,300 1,413 0,368 1,587 0,277 1,409 1,962 1,413 1,810 0,968 1,355 1,312 1,815 1,136 0,849 0,764 0,949 0,426 0,751 1,623 0,591 0,743 0,383 0,127 0,98 1,904 1,755 0,337 1,831 0,7 1,175 1,364 0,850 1,325 1,372 1,32 0,861 0,763 0,615 0,116 1,614 1,366 1,75 0,402 0,625 0,399 0,922 1,157 0,159 1,793 1,417 0,899 0,923 0,69 0,416 1,593 0,529 0,569 1,278 1,828 1,756 0,508 1,799 0,101 0,118 1,877 1,132 1,112 1,943 0,907 1,561 1,722 1,646 0,309 1,536 1,421 1,205 0,137 1,21 1,148 1,614 1,669 0,135 0,517 1,64 1,402 1,815 0,541 0,930 0,390 1,263 1,54 0,644 1,458 1,163 1,776 1,515 1,723 0,910 0,251 1,983 1,511 1,639 0,425 0,662 1,361 0,228 1,456 1,776 1,993 1,595 1,662 0,791 1,492 0,344 1,256 0,39 1,936 1,430 0,853 0,400 1,76 0,266 1,730 1,291 0,252 0,664 0,920 0,288 1,902 0,370 1,908 0,736 1,17 0,132 1,525 1,242 1,98 0,796 1,224 0,982 1,560 0,836 0,500 1,607 0,854 1,406 1,133 0,206 1,902 1,719 1,653 1,877 1,933 1,927 1,852 0,87 1,153 0,44 1,257 0,550 1,930 0,635 1,907 0,716 0,262 1,399 1,392 1,789 1,873 0,564 1,818 1,103 1,348 0,617 0,698 0,710 1,58 0,614 1,726 1,310 0,221 0,500 1,203 1,770 0,897 0,236 1,880 1,128 1,743 1,211 0,431 0,731 1,659 1,148 0,49 1,537 1,884 1,576 1,175 0,884 1,271 1,929 0,645 1,521 0,409 1,518 0,10 1,42 1,0 1,338 0,530 1,183 1,203 0,774 0,418 0,677 1,32 1,115 1,335 0,767 0,514 0,730 0,297 0,91 0,27 0,718 0,636 1,639 0,917 0,324 1,773 1,800 1,606 1,445 1,745 1,286 0,871 1,974 1,637 1,17 0,923 1,454 1,402 1,668 0,176 0,471 0,320 0,419 0,745 1,824 0,929 0,807 0,292 1,138 1,678 0,780 1,983 0,939 1,984 1,937 1,877 1,216 1,624 0,534 1,880 0,135 1,349 0,906 1,75 0,955 1,840 1,723 1,58 0,430 0,299 1,120 1,247 0,976 0,976 1,205 1,929 0,838 0,765 0,269 0,252 0,127 1,359 0,495 1,179 0,432 0,104 0,409 1,33 1,309 1,894 0,991 1,661 0,273 0,564 1,736 0,65 0,914 0,324 0,889 0,792 0,253 1,483 1,385 1,101 0,745 1,909 0,86 1,542 1,924 1,294 1,412 1,876 0,360 1,362 0,703 0,867 0,666 1,445 0,474 0,383 0,349 1,272 0,355 0,123 0,815 1,183 1,872 1,918 1,713 0,895 0,697 1,209 1,609 1,465 0,278 0,207 0,799 0,796 1,459 0,876 1,822 1,541 1,499 0,568 0,604 1,573 1,448 0,602 0,760 1,553 1,467 1,949 0,602 0,474 1,289 0,22 0,218 0,817 1,705 1,157 0,301 1,216 0,695 0,110 1,837 1,805 1,294 1,24 0,156 0,90 0,588 1,447 0,682 0,414 1,313 1,730 1,586 0,968 1,522 0,106 0,632 0,37 1,912 1,76 0,520 0,878 0,351 0,783 0,185 0,917 0,592 1,995 0,699 1,474 1,947 1,947 0,653 1,784 0,512 1,764 1,333 0,372 0,149 0,675 0,359 1,704 1,886 1,524 1,954 1,179 1,258 0,905 0,592 0,689 1,427 1,239 1,624 0,722 1,512 0,576 0,400 1,492 1,990 1,939 0,610 0,571 0,729 1,39 1,710 1,809 1,47 0,325 1,817 0,621 0,484 1,423 0,757 0,149 1,413 0,235 1,39 1,370 1,980 0,654 0,315 0,233 1,656 1,35 0,772 1,549 0,456 0,259 0,222 0,914 1,618 1,903 1,594 0,342 0,541 0,346 0,665 1,382 0,735 0,823 0,251 1,296 0,418 1,223 1,621 0,587 0,566 1,113 0,937 1,353 1,686 0,63 0,24 1,38 0,635 0,311 0,508 0,502 1,233 1,98 0,457 0,194 0,888 0,676 1,372 1,376 0,542 0,161 0,121 0,111 1,292 1,772 1,876 1,586 0,93 0,512 0,109 0,551 0,728 1,197 0,49 1,578 1,217 0,677 0,369 0,355 0,215 1,996 0,206 0,819 1,924 0,642 0,930 1,348 1,134 0,784 0,358 0,983 1,789 0,181 1,147 1,420 0,327 0,0 1,596 1,510 1,211 0,135 1,352 1,633 1,649 1,85 1,486 0,586 1,171 1,153 0,817 1,612 0,122 0,133 0,628 1,571 0,391 0,636 1,766 1,771 0,65 0,959 0,87 1,201 0,739 1,160 0,383 0,186 1,525 0,976 0,489 0,382 1,951 0,304 1,447 1,898 1,915 0,924 1,696 0,431 1,760 0,135 0,159 0,823 1,819 0,729 0,928 0,249 0,572 0,487 1,143 1,95 1,91 1,180 0,971 0,136 0,822 0,48 0,965 0,338 1,516 1,515 0,115 0,720 1,279 1,964 1,32 0,247 1,111 1,120 0,154 0,854 1,591 0,517 1,924 1,876 1,137 0,642 0,911 1,709 0,538 0,283 0,121 1,794 1,4 0,143 1,37 0,312 0,243 1,346 0,50 0,465 1,259 1,689 1,852 1,267 0,195 1,714 1,8 1,853 1,605 1,353 1,377 0,856 0,661 0,314 0,706 1,341 0,315 1,727 0,233 0,145 1,246 1,15 1,232 1,720 0,533 1,348 1,435 1,285 1,820 1,709 0,941 0,282 0,346 0,708 0,483 1,645 0,686 1,37 1,399 0,919 0,759 0,931 0,97 0,69 1,657 0,293 0,302 1,440 1,617 1,520 0,139 1,606 0,68 1,885 0,358 0,721 0,530 0,316 1,969 0,914 0,39 1,588 0,423 0,218 0,706 1,828 0,763 0,968 0,673 0,94 0,862 1,365 1,529 1,261 0,853 1,726 0,715 1,105 0,915 0,96 0,837 1,190 1,14 0,512 1,134 1,533 1,798 1,188 0,735 0,560 1,203 1,487 0,627 1,888 0,940 0,590 0,413 1,593 1,495 1,244 0,968 0,787 1,560 1,425 1,688 1,54 1,146 0,22 0,283 1,587 0,153 0,588 0,225 1,998 0,530 0,495 1,877 1,151 1,659 0,275 0,271 0,309 1,808 1,716 0,85 1,529 1,750 1,525 1,470 0,108 1,37 1,463 1,195 1,247 0,987 0,435 1,352 0,51 1,336 0,171 1,722 1,776 0,404 1,910 1,821 1,529 0,295 1,809 0,207 1,75 1,903 1,495 0,161 0,815 0,197 0,705 1,907 0,288 0,753 0,115 0,926 0,555 0,409 0,827 1,965 0,717 0,395 0,870 1,379 0,245 1,124 1,392 0,573 1,334 0,372 1,672 1,420 1,274 1,131 0,264 0,228 0,715 1,159 0,188 0,982 0,920 0,466 1,414 0,114 1,758 0,401 0,535 0,147 1,356 1,427 0,298 0,1 1,424 0,939 0,665 1,110 0,300 1,2 1,640 1,283 0,376 1,872 1,561 0,641 0,192 1,501 0,942 0,884 1,997 0,709 0,304 1,145 0,123 0,932 0,162 1,6 0,270 1,609 0,587 0,829 0,693 0,662 0,410 1,864 1,911 1,305 0,681 1,595 1,62 1,812 0,548 1,495 1,266 1,185 1,239 1,901 1,316 1,63 1,496 0,413 1,198 1,86 1,909 1,120 0,71 0,300 0,826 1,234 1,574 1,35 1,48 0,7 0,584 0,753 0,858 0,953 0,38 1,384 1,563 0,361 1,907 0,48 1,335 1,757 0,252 0,598 1,991 1,439 1,78 0,123 1,232 1,717 0,865 1,963 0,214 1,742 0,519 0,884 1,563 1,837 1,67 0,745 1,266 1,280 1,990 1,215 0,378 0,412 1,2 0,64 0,426 0,572 1,219 1,79 0,497 0,972 1,35 1,421 0,157 1,794 0,104 0,430 0,295 1,206 0,529 1,167 0,198 1,774 1,369 1,931 1,672 0,817 0,996 1,978 1,740 1,807 0,514 1,768 1,459 1,621 0,949 0,517 1,920 0,424 0,480 0,655 1,408 0,717 0,788 1,707 0,941 0,789 1,228 1,373 1,372 0,569 1,958 1,558 1,113 1,233 1,833 0,22 0,274 0,260 0,528 0,320 0,828 0,771 1,221 1,958 1,126 0,654 1,828 1,287 1,41 0,515 0,291 0,489 1,221 1,81 1,884 1,163 0,396 1,501 0,557 0,351 0,601 1,210 0,827 1,824 1,879 0,824 0,694 0,858 0,211 0,578 0,546 1,619 0,813 0,535 0,112 1,819 0,896 0,395 1,860 0,878 0,897 1,142 1,387 0,698 1,919 1,470 1,4 1,210 0,720 0,300 1,808 0,125 0,525 0,36 1,397 0,374 0,358 0,463 1,696 1,557 0,859 1,122 0,65 0,355 0,2 0,163 1,584 0,164 0,840 0,768 1,353 0,131 0,125 1,357 0,541 1,9 0,301 1,789 0,142 0,469 1,0 1,864 1,337 0,884 0,843 0,278 1,584 0,436 1,647 1,996 1,55 0,644 0,553 1,833 0,20 0,86 1,519 0,59 1,434 0,29 1,614 0,516 1,830 1,714 0,113 0,635 1,79 0,92 0,795 1,675 0,874 1,613 1,533 1,617 1,56 1,434 0,936 0,37 1,580 1,501 1,745 0,739 0,604 1,346 1,540 1,526 0,831 0,639 0,354 1,853 1,84 0,678 1,437 1,474 1,933 1,858 0,670 0,440 1,587 1,333 0,67 1,328 0,498 1,695 1,324 1,349 1,21 1,155 1,120 0,834 1,676 1,108 1,229 0,230 0,315 1,925 0,890 1,780 0,887 1,117 0,299 0,299 1,372 1,886 1,728 1,415 1,680 0,14 0,632 1,725 0,691 0,389 1,200 0,953 1,822 0,839 0,886 0,651 1,107 0,563 0,824 1,100 0,200 0,691 1,982 1,53 1,340 1,803 0,980 1,137 0,68 1,560 1,328 0,50 1,388 0,458 1,825 0,102 1,691 0,773 1,407 1,942 0,492 0,364 0,635 1,805 1,356 1,914 1,818 1,242 0,180 1,561 1,573 0,740 1,319 1,950 1,558 1,900 1,113 1,134 0,180 1,993 0,488 1,700 1,864 0,81 0,716 1,202 1,405 1,56 0,496 0,642 1,803 0,315 0,691 1,166 0,142 0,958 1,949 0,383 1,811 0,410 0,702 1,323 0,70 1,910 1,24 1,603 1,457 0,269 1,277 0,963 0,905 1,390 0,907 1,99 0,411 0,740 0,484 1,97 0,57 0,585 1,600 1,613 0,212 1,363 0,863 0,656 1,917 1,397 1,749 0,245 0,363 1,873 1,548 0,2 0,738 1,723 0,755 0,55 0,244 0,741 1,368 0,137 0,258 1,636 0,870 1,750 1,546 0,382 0,289 0,559 1,22 0,558 0,66 1,195 0,59 1,445 0,363 1,470 1,106 0,977 0,227 0,758 0,550 1,976 0,273 1,936 0,916 0,794 0,647 0,857 0,289 0,576 1,202 0,683 0,909 0,941 0,69 0,539 1,631 0,91 0,541 0,106 0,599 1,667 1,784 0,838 1,86 0,127 0,949 1,550 1,618 1,495 1,787 1,38 1,715 1,138 0,24 1,401 1,655 0,546 0,420 1,965 0,963 1,700 0,731 1,240 0,705 1,448 1,158 1,854 0,713 1,20 1,525 1,382 1,580 0,498 0,343 0,559 0,812 0,932 0,652 1,29 0,744 0,927 0,68 1,240 0,93 1,707 1,981 0,699 1,737 1,497 1,366 1,948 1,575 0,688 1,130 1,579 0,990 1,205 1,782 0,584 1,289 1,139 1,446 0,383 0,328 1,833 0,799 1,562 0,872 0,90 0,637 1,341 1,12 0,433 0,46 0,781 0,563 1,785 1,591 1,109 1,584 1,204 0,722 1,442 0,852 1,805 0,20 0,182 1,511 1,952 0,333 0,353 1,168 1,80 0,70 1,511 1,291 1,977 0,158 0,665 0,721 1,405 0,515 0,158 0,947 0,69 0,125 1,380 1,468 0,744 0,801 1,386 0,368 0,316 1,688 1,861 0,5 1,799 0,337 0,726 0,757 1,971 1,716 0,933 1,773 0,625 0,367 0,736 1,358 0,411 1,512 0,897 1,650 1,638 1,874 1,36 0,993 1,833 1,368 0,982 0,312 0,873 0,644 0,104 0,469 0,979 0,609 1,587 1,898 0,954 1,99 0,645 1,164 0,47 1,841 1,54 0,242 0,501 1,736 0,691 1,616 0,129 1,760 0,772 1,690 1,574 1,34 0,617 1,736 1,871 0,326 1,828 0,22 0,108 0,890 0,31 0,686 1,554 1,626 0,820 0,120 1,554 1,488 0,551 0,786 1,959 1,404 0,260 1,642 1,111 1,951 1,931 0,231 1,391 0,284 0,231 0,669 1,796 1,345 0,86 0,760 1,418 1,509 1,827 1,185 1,834 0,679 1,710 1,274 0,940 1,943 0,692 0,747 0,554 1,117 1,190 0,427 0,721 0,156 0,500 1,457 0,978 1,952 1,815 0,708 0,795 0,241 0,112 0,47 1,50 0,286 0,87 0,687 1,720 0,922 1,87 0,389 0,481 0,726 1,983 1,550 1,308 1,93 0,247 1,31 0,487 0,251 0,97 0,219 0,404 1,879 1,36 1,962 0,298 0,921 1,399 0,46 1,875 0,13 1,663 0,930 0,171 0,627 0,274 0,993 0,719 0,704 1,931 1,492 0,672 0,800 0,820 0,809 1,174 1,353 0,484 0,23 0,222 1,599 1,702 1,971 0,682 0,176 1,348 1,427 1,443 1,183 1,930 1,160 0,743 0,480 0,492 1,254 1,262 0,756 0,782 1,323 0,846 1,365 0,289 1,321 0,434 1,405 0,85 1,258 0,386 0,163 0,47 0,744 0,959 1,624 1,483 1,610 0,552 1,282 1,430 0,943 0,90 0,863 0,959 1,69 1,914 1,441 1,444 1,433 0,151 0,453 0,172 0,252 1,180 0,757 0,107 1,706 1,251 0,197 1,295 1,132 0,712 0,536 1,261 0,460 1,84 1,431 0,253 1,332 1,895 1,485 1,685 1,904 0,482 1,37 1,468 1,867 0,910 1,587 1,967 1,774 0,270 0,219 0,319 0,816 0,179 0,175 1,244 1,836 1,748 1,872 0,193 0,303 0,8 1,284 1,707 1,188 1,962 0,724 1,616 1,809 0,492 0,60 0,223 0,568 0,982 0,205 1,81 1,90 0,699 1,585 1,644 1,576 1,535 0,341 1,841 0,582 0,431 0,799 0,712 1,391 0,664 0,149 0,930 1,994 1,267 1,358 1,112 1,81 1,371 0,916 0,590 0,959 0,293 0,957 0,466 1,307 0,992 0,983 1,80 0,143 0,62 0,644 0,500 1,480 0,440 0,186 1,744 0,159 0,956 1,389 0,232 1,37 0,418 0,414 0,224 0,49 1,879 1,730 1,773 0,617 0,893 0,639 1,489 0,301 1,664 1,262 0,654 1,4 0,2 0,598 1,424 0,35 1,710 1,199 0,689 0,681 1,792 1,344 0,844 1,168 1,702 1,991 1,573 1,190 0,502 0,193 0,962 1,498 0,32 1,752 0,327 0,768 0,754 1,3 0,795 0,89 0,529 0,370 0,661 0,854 0,142 1,301 0,267 0,6 0,173 0,204 0,183 1,821 0,6 1,233 1,855 0,175 0,130 0,720 0,576 1,245 1,368 0,152 0,740 0,241 1,791 1,663 0,672 1,295 1,793 0,781 1,586 1,90 0,545 0,370 1,969 1,265 1,637 1,661 0,519 0,416 0,618 1,395 0,811 0,40 1,997 1,539 1,317 1,928 1,930 1,937 0,926 1,856 0,275 1,718 1,708 0,684 0,679 0,849 1,881 0,621 0,720 1,628 0,906 1,599 1,207 1,838 1,502 1,819 0,492 1,153 1,878 1,301 0,899 1,391 1,31 1,489 0,432 1,186 0,289 0,540 1,58 1,656 1,967 0,936 1,927 1,824 0,726 0,521 0,211 1,934 1,296 0,132 1,578 1,134 0,191 0,190 1,721 0,975 1,58 0,973 1,240 1,447 1,204 0,982 1,343 1,671 1,540 0,469 1,559 1,424 0,292 0,751 0,500 1,233 0,359 0,556 0,829 1,281 1,422 1,786 1,812 0,805 1,894 1,507 1,695 0,309 1,449 1,403 1,934 1,945 0,286 1,284 0,100 0,778 0,735 1,141 0,364 0,555 1,982 0,787 0,330 0,769 1,464 1,826 0,284 1,891 0,195 1,768 0,92 1,213 0,148 1,587 1,558 1,36 1,775 1,656 1,432 1,766 0,490 1,934 0,110 0,954 0,56 1,307 0,659 0,173 1,330 1,563 0,837 1,940 0,103 1,714 1,862 1,53 1,594 1,444 1,816 0,323 0,538 1,129 1,10 1,620 1,379 1,137 0,603 1,664 0,514 1,600 1,367 0,371 0,667 1,816 0,308 0,202 0,16 1,299 0,643 1,974 0,357 0,841 1,939 0,378 0,757 1,684 1,737 0,517 1,247 1,829 0,327 1,614 0,367 1,665 1,577 1,956 0,114 0,724 1,730 0,615 0,922 0,582 1,154 0,168 1,201 1,844 0,947 0,688 1,884 1,877 1,803 1,536 1,958 0,541 1,999 1,10 1,667 0,516 0,181 1,4 1,228 0,529 0,127 1,455 0,404 0,150 1,955 1,939 0,75 0,389 1,793 0,277 1,606 0,695 0,609 0,237 0,667 1,137 0,74 1,300 0,805 0,411 0,435 1,807 0,819 1,373 1,57 0,364 1,334 1,314 1,863 1,657 0,871 0,497 1,935 0,190 0,128 1,729 1,770 0,847 1,738 1,170 1,72 0,678 1,667 1,728 0,158 1,730 1,315 0,394 0,196 1,419 0,853 0,0 1,637 1,669 0,818 1,727 0,67 0,431 0,273 0,11 0,536 1,509 0,462 1,427 0,545 1,150 1,269 0,349 1,677 0,884 1,559 0,441 1,537 0,763 1,238 0,230 0,846 0,976 0,262 0,364 0,505 0,718 1,570 1,938 0,701 1,349 0,293 1,437 1,32 1,965 1,359 1,55 0,816 1,904 0,775 1,285 1,477 0,955 1,791 0,715 0,774 1,355 0,12 1,494 0,933 0,712 1,289 0,426 1,275 0,89 0,481 0,895 0,799 0,82 0,754 1,802 1,311 0,481 1,567 1,40 1,329 1,626 1,111 0,482 0,453 0,20 0,386 1,605 1,999 0,229 1,293 0,758 0,794 1,761 0,531 0,137 1,664 0,858 1,987 0,346 0,409 0,975 1,804 1,687 1,107 1,178 1,900 0,675 0,62 1,306 0,872 1,672 1,712 1,607 1,904 0,494 0,675 0,502 0,809 0,620 0,572 0,678 1,689 0,896 0,223 0,551 0,919 0,864 0,175 1,726 1,240 1,566 1,742 0,933 0,294 1,274 0,776 0,384 1,49 1,705 0,239 0,417 1,414 0,931 0,177 0,89 0,790 0,123 1,473 1,305 1,366 0,686 0,68 1,256 1,636 1,379 0,278 1,456 0,374 1,599 1,785 0,680 1,360 0,125 0,975 0,301 0,550 0,862 0,777 1,359 0,986 0,976 1,6 1,436 0,105 0,995 0,192 1,489 1,955 1,861 1,845 0,547 0,105 1,683 1,860 0,446 1,254 1,562 0,787 0,854 0,983 1,953 0,821 1,646 1,129 0,724 0,96 0,370 1,944 0,679 0,256 1,155 0,845 0,283 1,50 0,476 0,163 1,232 1,524 0,277 1,243 0,610 0,56 0,614 0,106 1,454 0,82 0,595 1,639 1,580 1,543 1,254 1,357 0,517 0,593 0,736 0,205 0,620 0,576 0,456 0,274 0,28 0,92 0,837 0,790 1,70 1,924 0,114 1,665 0,559 0,796 1,102 1,141 0,718 0,242 0,98 1,978 1,142 0,712 0,8 0,637 0,439 0,774 1,339 0,888 0,468 0,878 1,472 1,267 1,357 0,768 1,52 1,378 0,131 0,494 1,449 1,283 0,115 0,888 0,51 0,928 1,7 1,167 0,744 0,582 0,924 1,926 1,170 1,439 0,196 1,112 0,392 0,598 0,342 1,157 1,538 1,322 1,210 1,678 1,436 1,324 0,86 0,203 0,935 1,633 1,761 1,122 1,663 1,824 1,197 0,960 1,343 0,552 1,275 1,562 1,363 1,539 1,270 0,464 0,821 0,144 1,844 0,175 1,152 0,164 0,90 0,701 1,900 0,181 1,897 1,113 1,973 1,198 0,433 0,378 0,663 0,358 0,432 1,385 1,371 0,370 0,715 1,447 0,502 0,901 0,777 0,683 1,764 1,962 1,906 0,681 0,428 1,375 1,592 1,175 0,58 1,61 0,378 0,393 1,147 1,410 1,343 0,672 1,760 1,659 0,208 1,902 1,691 0,177 0,615 1,206 1,78 0,361 0,4 0,600 0,52 0,727 0,512 0,891 1,55 0,339 1,668 0,392 1,344 0,835 1,535 1,439 0,617 0,584 0,692 1,406 1,460 0,745 0,945 1,121 0,322 0,702 1,84 0,731 1,6 0,370 0,804 0,162 1,448 1,183 0,565 1,117 1,813 1,14 1,933 1,215 0,470 1,81 0,677 0,368 1,967 1,680 0,483 1,868 1,651 0,884 0,366 0,100 0,497 0,274 1,606 1,14 1,427 1,184 1,896 0,481 1,984 1,136 1,538 0,776 0,209 1,956 1,209 0,257 0,798 0,182 0,64 0,847 0,763 1,110 1,434 0,795 0,316 0,586 0,96 0,593 0,845 0,306 1,977 0,574 1,594 0,558 0,762 1,546 1,629 0,759 0,4 0,502 1,986 1,293 0,847 0,117 0,713 1,629 1,730 1,765 0,893 1,271 0,258 1,952 0,808 0,325 1,159 1,200 1,681 0,21 0,464 1,998 1,682 1,103 1,102 0,893 0,206 0,293 1,801 0,630 1,440 1,426 0,906 0,484 1,597 1,839 1,794 0,176 1,773 0,326 1,409 1,91 1,34 1,721 0,820 0,117 1,961 1,387 0,203 1,575 0,233 1,277 0,79 1,572 0,407 1,897 1,655 0,298 1,482 0,920 1,412 1,824 0,828 0,915 1,818 0,64 1,224 1,402 1,719 1,356 1,334 0,883 1,949 1,671 1,525 1,306 1,38 1,198 1,446 1,448 1,875 0,167 0,241 0,648 0,684 0,426 0,283 0,794 0,906 1,595 1,305 0,88 0,739 1,542 0,798 1,479 1,792 1,217 1,92 1,760 0,84 0,14 1,915 0,135 0,743 0,582 0,472 1,50 0,268 0,69 1,53 1,34 1,3 1,241 1,534 1,358 0,546 0,919 1,683 0,350 1,265 1,326 0,606 0,321 0,245 0,459 1,289 1,157 1,913 1,44 0,318 1,493 1,433 0,203 1,863 0,848 0,847 1,549 0,132 1,710 1,963 0,751 1,18 1,615 0,172 1,999 1,514 1,261 0,845 0,249 1,227 1,878 1,96 0,219 1,493 1,478 0,51 1,10 1,774 0,170 0,426 1,1 1,574 1,738 0,915 1,451 1,712 0,321 1,124 1,91 1,363 1,790 0,197 0,848 1,191 1,633 1,672 1,644 1,425 1,141 0,334 0,112 1,121 1,38 1,839 0,434 1,253 1,735 1,327 1,140 0,144 0,680 0,809 0,801 0,960 1,388 0,509 0,6 0,267 1,394 0,211 1,505 1,632 0,462 1,37 0,779 0,25 1,657 0,384 0,427 1,720 0,709 1,586 1,651 0,483 1,278 0,902 1,778 0,394 1,208 0,357 1,831 0,883 0,830 1,202 0,966 0,784 1,193 1,951 0,510 1,179 1,793 1,984 0,36 0,569 0,846 1,592 0,361 1,847 0,240 1,869 0,818 1,250 0,367 0,424 0,227 0,719 1,856 0,169 0,859 1,948 0,396 0,462 0,263 1,86 1,29 1,454 0,539 0,406 0,564 1,413 1,962 1,334 0,389 0,218 1,840 1,139 1,849 1,368 0,298 1,940 0,25 1,121 0,172 1,739 0,605 0,638 0,234 0,383 0,962 1,730 0,688 0,611 0,81 1,321 1,581 0,575 0,376 1,167 1,718 1,181 1,412 1,701 1,326 0,126 1,819 1,940 0,153 1,303 1,131 0,216 0,298 1,781 1,245 1,785 1,615 1,356 0,351 0,55 0,335 1,463 0,901 1,175 1,974 1,144 1,703 0,75 0,625 0,573 0,179 1,147 1,103 1,482 0,955 0,962 0,159 1,439 0,13 0,68 0,158 0,613 0,580 0,0 0,605 1,248 0,515 0,518 0,208 1,816 1,80 1,653 0,567 0,483 1,566 0,439 0,313 0,94 0,185 0,62 0,450 0,187 1,245 1,554 1,798 1,921 1,728 0,282 1,326 1,87 0,949 1,885 0,520 1,152 0,913 0,387 0,598 0,724 1,282 1,271 0,275 1,597 1,389 1,84 1,275 1,299 1,605 1,847 0,516 1,656 1,81 0,956 1,385 1,523 1,745 1,446 0,398 1,495 0,295 0,670 0,518 0,2 1,270 1,981 1,821 0,926 0,878 1,437 0,436 0,960 0,481 1,837 1,917 0,17 1,65 1,122 1,528 0,399 0,24 1,768 1,853 0,407 1,577 1,369 1,693 0,789 1,639 0,155 0,443 1,679 0,363 0,236 0,342 1,411 1,268 0,988 0,441 0,132 1,25 1,543 1,39 1,988 1,313 0,403 0,97 1,333 1,593 1,78 0,753 0,14 0,926 0,839 0,19 1,738 1,512 1,516 0,220 1,951 0,241 0,649 1,490 0,768 0,434 1,132 1,606 0,906 1,26 1,21 0,935 0,53 0,923 1,931 1,392 1,45 0,678 0,53 0,418 1,711 1,96 0,223 0,979 1,756 0,915 1,260 1,785 0,938 1,912 1,222 1,716 1,228 0,167 0,206 1,935 1,559 0,300 1,88 0,193 0,295 1,326 1,836 0,555 1,203 1,79 0,404 1,617 1,665 1,288 0,526 1,227 0,10 0,272 0,546 0,363 0,931 1,741 0,879 1,612 0,599 1,421 1,661 1,417 0,393 0,527 0,746 0,959 0,45 0,182 0,355 1,603 1,833 0,972 0,910 0,261 1,423 0,109 0,360 1,722 0,225 1,696 0,938 0,694 0,690 0,469 0,834 1,819 0,848 0,337 0,84 0,465 1,770 1,956 0,843 1,424 1,536 1,648 0,947 0,997 0,839 0,348 0,655 0,577 0,878 1,370 0,791 0,732 0,476 0,614 1,94 1,982 1,734 0,953 1,980 1,140 1,963 0,12 1,123 1,328 0,493 1,512 1,450 1,526 1,769 0,248 0,718 1,403 0,563 1,728 1,124 1,220 1,720 1,668 0,407 1,119 1,328 0,256 1,560 0,677 0,537 1,83 1,594 0,690 1,895 0,116 0,870 1,254 1,28 1,240 1,357 1,533 0,367 0,462 1,793 0,370 1,570 1,555 1,310 0,886 0,493 1,600 0,267 1,39 0,581 1,949 1,944 1,415 1,284 0,183 0,28 0,973 0,141 0,988 0,157 0,798 0,723 0,127 1,829 1,808 1,30 0,913 1,86 1,246 1,775 1,254 1,813 0,47 0,438 1,82 1,914 0,545 1,913 0,772 0,894 0,584 0,84 1,21 1,642 1,120 0,708 1,375 1,171 0,440 0,31 0,248 0,372 1,219 1,92 0,987 0,477 1,292 1,817 1,697 0,285 1,444 1,886 0,670 0,325 0,749 0,366 1,430 1,311 0,342 0,415 0,70 1,866 0,839 1,163 0,781 1,959 1,531 1,459 0,181 1,490 0,281 1,661 0,884 0,329 1,270 1,284 0,433 0,497 0,819 0,341 1,508 0,417 1,750 0,663 0,80 1,477 0,76 1,342 1,352 1,451 1,970 1,540 1,911 1,671 0,528 0,489 1,468 0,49 0,295 0,474 0,161 1,455 1,360 1,900 1,921 1,561 1,392 1,373 0,584 1,647 0,265 0,424 0,830 1,348 1,683 1,106 1,276 1,392 0,416 0,325 0,826 1,490 0,576 0,154 0,949 1,604 0,30 1,870 0,760 0,85 1,685 0,391 0,445 1,739 0,327 0,209 1,308 0,478 1,911 0,610 1,909 0,222 1,373 1,838 1,516 1,702 0,270 1,922 1,454 0,544 1,888 1,702 1,518 0,719 0,201 1,724 1,471 1,132 1,474 0,895 1,791 0,78 0,526 1,238 0,438 0,573 0,660 0,740 1,661 0,929 0,340 0,721 0,550 1,403 0,151 0,642 1,453 1,122 0,730 0,721 1,516 0,867 1,465 1,544 0,911 1,815 1,862 0,929 1,285 1,973 1,291 0,738 1,26 1,603 0,651 0,15 0,329 1,739 1,620 0,475 1,940 0,609 1,124 0,384 1,837 1,122 0,52 1,372 1,941 0,911 1,262 1,607 0,976 0,97 1,335 0,636 1,128 1,138 0,719 1,169 1,181 0,587 1,112 0,892 0,24 0,453 1,880 0,396 0,441 0,927 1,559 1,760 0,845 1,854 1,974 0,794 0,186 0,435 0,81 1,754 0,4 0,633 1,218 1,611 1,128 1,129 1,749 0,784 0,430 0,171 0,719 1,246 0,591 1,336 1,512 1,787 0,549 0,640 1,123 0,603 0,268 0,813 0,832 1,886 1,478 0,363 0,461 0,292 1,524 1,155 0,397 1,178 1,299 1,569 0,120 1,632 0,848 1,523 1,973 1,943 0,683 1,187 0,297 0,339 1,295 0,592 0,587 1,31 0,282 1,805 1,349 0,859 0,425 0,866 0,657 1,953 1,552 1,180 1,654 0,654 0,881 1,964 1,174 1,515 1,668 0,801 0,242 0,831 0,570 1,466 0,539 0,615 0,60 1,755 1,36 1,693 0,596 1,764 1,423 1,358 0,642 1,368 1,665 1,142 1,885 0,586 0,97 1,703 1,265 0,186 0,990 0,424 1,473 0,37 1,260 0,822 0,364 0,23 1,982 0,236 1,306 0,987 1,857 1,261 1,440 0,397 0,780 1,534 1,134 0,757 1,646 1,98 0,195 0,908 0,301 0,854 0,646 0,181 1,369 0,721 0,21 1,323 1,167 1,5 0,839 0,60 1,552 1,812 1,191 1,783 0,548 1,326 1,970 0,924 1,106 1,472 1,552 1,321 0,945 0,599 0,616 0,918 1,311 1,217 1,674 1,242 0,666 0,126 0,51 0,411 0,305 0,573 0,844 1,861 1,461 1,398 1,368 1,120 0,379 1,339 0,842 1,428 0,643 0,540 1,320 0,251 0,940 0,49 1,266 0,521 0,563 0,911 0,981 0,826 0,256 1,516 0,594 1,401 0,846 0,34 0,496 0,341 0,889 0,675 0,78 1,976 0,831 0,305 1,403 1,859 1,910 0,561 0,437 0,517 0,217 0,496 0,630 1,696 0,95 1,38 0,860 1,123 0,414 0,501 0,89 1,450 0,822 0,572 1,103 0,79 0,978 1,906 1,700 0,462 1,889 1,801 0,654 1,180 1,408 1,443 1,843 1,34 0,779 1,969 0,831 0,538 0,802 0,745 1,524 0,608 0,902 0,53 0,740 1,645 0,78 1,412 0,692 0,819 1,915 1,767 1,662 1,584 0,349 1,126 0,772 0,839 0,589 1,754 1,787 1,22 0,397 1,782 1,389 0,411 0,801 0,470 1,356 1,713 1,422 0,156 1,416 0,140 1,558 1,660 0,336 1,239 1,663 0,772 1,903 0,479 1,396 0,290 0,583 0,971 1,712 0,110 1,93 1,738 1,896 0,247 0,515 1,988 1,602 0,94 1,49 1,943 1,530 1,370 1,31 1,624 1,134 1,245 0,215 1,428 1,993 1,472 0,18 1,0 1,843 0,410 0,618 0,785 1,715 0,251 1,27 0,704 1,937 1,941 0,944 1,228 0,882 0,788 0,402 0,65 0,991 1,520 1,226 1,574 1,740 0,954 1,808 1,944 1,157 1,875 1,913 1,216 0,401 0,351 1,896 1,478 1,466 0,870 0,373 1,934 0,395 1,965 1,244 1,449 0,304 0,423 0,562 0,44 0,456 0,652 1,595 0,858 0,193 0,703 0,824 1,643 1,274 1,420 0,456 0,671 0,842 0,928 0,230 0,224 1,457 1,148 0,947 0,661 1,436 0,419 0,273 1,11 0,643 1,746 0,443 0,341 1,341 1,960 1,717 1,790 0,783 0,482 1,30 0,837 1,637 0,53 0,100 0,382 0,69 1,2 0,711 1,776 1,408 1,118 1,942 1,993 1,906 0,633 1,392 0,126 0,891 1,674 1,263 0,958 0,386 0,876 0,687 0,530 1,444 1,43 1,228 0,360 0,765 0,803 1,433 0,298 1,938 1,852 1,381 0,806 1,407 0,986 1,112 1,99 0,568 1,28 1,166 0,680 0,706 0,224 0,637 0,445 1,389 1,675 1,500 0,414 1,754 0,33 1,694 1,787 1,188 1,413 0,755 1,826 1,780 0,37 0,91 1,509 1,945 0,924 1,19 0,765 0,723 0,686 1,794 1,392 0,855 0,124 0,397 1,598 1,412 1,521 0,578 0,34 1,467 1,302 0,865 1,911 1,365 0,854 0,567 1,164 0,619 0,20 1,353 1,301 0,97 0,724 0,902 1,96 1,442 1,966 0,199 1,753 0,174 0,244 1,990 0,864 1,484 1,287 0,242 1,653 0,314 0,779 0,561 1,899 0,739 1,782 0,602 0,663 0,924 0,799 0,512 1,230 1,944 1,260 1,952 0,837 0,870 0,946 0,222 0,959 0,91 1,1 1,480 0,703 1,440 0,14 1,293 0,445 1,285 0,542 0,567 1,541 0,740 1,176 0,462 0,429 1,781 1,505 0,741 0,739 0,207 1,350 1,431 1,606 0,636 0,62 0,28 1,856 0,456 0,217 0,22 0,76 1,276 1,390 0,141 1,382 1,188 0,77 0,998 1,165 0,957 1,2 0,952 0,777 0,929 1,60 0,806 0,546 1,225 0,300 0,65 0,406 1,125 0,352 1,369 1,681 1,326 1,826 1,719 0,957 0,360 1,262 1,465 1,60 1,834 1,389 1,177 1,225 1,74 1,306 0,972 0,285 0,258 0,464 1,623 1,116 1,193 0,798 1,628 0,606 1,272 0,129 1,563 0,217 1,429 1,615 0,800 0,764 0,966 0,747 0,891 0,65 0,87 0,277 0,595 0,80 1,912 0,85 0,300 0,663 1,921 0,850 0,837 0,208 1,892 0,608 1,220 0,21 0,693 0,271 1,717 1,468 0,502 1,204 1,737 0,722 1,913 1,879 1,921 0,885 0,289 1,73 1,901 0,134 0,635 0,410 1,443 1,500 1,93 0,953 0,187 0,768 1,84 0,723 0,740 0,515 1,506 0,545 1,14 1,944 1,435 1,963 1,580 0,839 0,486 0,99 1,752 1,965 0,545 1,11 1,173 1,127 1,806 1,424 0,376 1,372 1,209 1,750 0,478 1,477 1,737 1,615 0,391 1,654 0,630 1,515 0,863 0,572 0,870 0,193 0,600 0,109 0,510 0,64 0,885 1,343 0,691 1,491 0,513 0,835 1,695 1,910 0,633 1,174 1,388 0,73 1,449 0,439 0,337 1,616 1,475 0,111 1,418 0,527 1,249 1,745 1,705 0,143 0,785 0,506 0,54 0,41 1,559 1,275 0,938 0,859 1,657 1,518 0,821 1,654 1,571 1,847 1,712 0,765 1,329 0,128 0,156 0,395 0,108 0,5 1,381 1,879 0,259 0,698 1,660 0,501 0,795 0,979 0,414 0,853 1,25 1,822 1,880 0,115 0,534 0,734 0,261 1,856 1,805 0,960 0,274 0,294 1,125 1,672 1,441 1,777 0,644 0,492 1,679 0,930 1,855 0,48 1,411 0,772 0,314 0,615 0,964 0,563 0,81 0,766 0,509 1,822 1,725 1,113 0,55 0,709 0,612 1,113 1,678 0,420 0,307 0,389 0,183 1,271 1,8 0,825 0,484 1,44 1,309 1,209 0,570 0,892 1,236 0,800 1,118 0,363 0,419 0,988 1,770 0,41 0,179 1,804 0,567 1,645 0,819 1,324 1,328 1,808 0,10 0,498 1,238 0,161 1,579 0,472 1,965 0,638 1,980 1,601 0,226 1,960 0,571 1,3 1,699 1,470 1,17 0,958 0,422 0,643 1,368 1,472 1,511 0,658 1,572 1,183 0,540 1,577 1,28 0,776 1,209 1,902 1,313 0,890 1,487 1,205 0,987 0,309 1,65 0,319 0,17 0,517 0,875 1,697 1,109 1,271 0,993 1,88 0,418 1,178 0,389 1,306 0,45 0,929 0,581 1,774 0,874 0,725 1,804 0,938 1,659 0,466 0,403 0,86 1,593 0,631 1,939 1,545 1,257 0,186 0,583 0,995 1,131 1,406 0,219 0,636 1,355 0,920 1,211 0,889 1,89 0,703 0,67 1,285 1,904 0,809 0,977 1,152 1,294 0,817 1,622 0,339 0,365 0,476 0,769 1,111 1,936 1,69 1,42 0,714 0,250 0,318 0,854 0,104 1,797 1,562 0,918 0,264 0,937 0,574 0,695 0,737 0,877 0,460 1,432 1,30 0,713 0,654 0,682 0,640 0,862 1,152 1,604 0,968 1,782 0,481 1,834 0,635 1,197 0,456 1,924 0,409 0,254 0,110 0,940 0,984 0,806 1,461 1,713 0,118 0,761 0,438 1,530 1,140 1,551 1,815 0,348 1,838 1,510 0,674 0,969 0,305 0,326 0,223 1,441 1,209 1,211 0,686 1,665 1,983 0,132 1,90 1,354 0,690 1,493 0,829 1,507 1,522 0,947 1,310 0,757 0,935 0,882 1,984 0,724 1,905 1,170 1,812 0,191 1,476 1,45 1,626 0,839 1,925 1,644 0,900 1,411 0,249 0,859 1,887 0,50 0,52 0,871 0,827 1,242 0,831 0,745 1,514 1,728 0,928 0,251 0,800 1,575 1,684 1,371 0,295 1,239 0,240 1,886 1,101 0,391 1,308 1,452 0,18 0,682 1,288 1,51 0,822 0,750 1,503 0,837 1,129 1,768 1,105 0,89 0,348 1,835 1,982 0,523 1,936 0,577 0,664 0,466 1,671 0,677 0,462 0,154 0,428 1,704 0,685 1,192 1,72 0,49 1,821 0,956 1,23 1,802 1,772 1,533 0,775 1,602 0,173 0,224 1,806 1,690 0,80 1,966 1,798 1,500 0,809 1,945 0,713 1,222 0,838 1,755 1,167 1,281 1,308 0,696 0,440 0,236 1,293 1,541 0,349 1,743 0,292 0,443 1,405 0,804 0,608 1,477 1,396 1,68 1,846 0,339 0,87 0,616 0,844 1,980 0,966 0,532 0,358 1,242 1,715 1,571 1,666 0,426 1,593 0,754 0,905 1,468 1,279 0,863 0,745 0,875 0,844 0,448 1,92 0,243 0,927 1,404 0,637 1,575 0,30 0,257 1,297 1,618 0,434 1,548 0,248 1,8 1,182 0,450 1,729 0,355 0,527 0,917 0,469 0,117 0,665 0,103 1,468 1,200 0,871 0,718 1,838 1,794 1,892 0,612 1,601 1,545 0,251 1,38 0,863 0,201 0,927 1,712 0,288 0,116 0,702 0,917 0,175 1,514 1,416 1,542 1,758 0,195 1,739 0,126 1,538 0,127 1,783 0,285 1,751 0,539 1,439 1,327 1,195 1,363 0,606 1,865 0,920 1,178 1,81 1,821 0,967 0,750 0,691 0,745 1,604 1,865 1,468 0,940 0,963 1,809 1,473 1,493 1,967 0,629 1,321 0,846 1,458 1,271 0,444 1,293 1,653 0,713 1,685 0,213 1,557 0,568 0,168 1,455 1,869 0,941 0,98 1,11 1,363 0,577 0,658 1,20 0,97 1,294 1,785 0,766 0,998 0,179 1,600 1,55 1,507 0,630 1,432 1,371 1,845 1,853 0,192 0,693 1,794 1,990 1,181 0,5 1,354 1,828 1,728 0,988 1,89 1,830 1,940 0,792 1,368 0,16 1,733 0,566 1,173 0,961 1,686 1,461 1,720 0,515 0,161 0,579 0,45 0,458 1,891 0,828 1,236 1,630 1,811 1,460 0,224 0,467 0,315 1,38 0,447 0,273 0,152 1,10 1,579 1,718 0,387 0,901 1,926 0,561 1,13 1,643 0,201 0,934 0,212 0,972 1,830 0,737 1,136 0,269 0,375 1,223 0,39 1,849 1,723 0,313 0,159 0,216 0,701 1,472 0,141 1,365 0,119 1,998 1,582 1,924 0,539 1,765 1,226 0,622 0,704 1,299 1,139 0,399 0,554 1,187 1,934 0,340 0,959 1,927 0,295 1,75 0,979 1,404 0,323 0,803 0,858 0,150 1,654 1,29 0,854 1,463 1,55 0,213 1,455 1,27 0,335 1,274 0,438 0,589 1,262 1,457 1,944 0,42 0,12 1,458 1,87 0,982 1,565 0,617 0,332 0,325 1,633 0,394 0,135 0,478 1,867 0,794 0,757 0,93 1,838 1,743 1,125 0,766 0,30 1,437 1,456 1,674 0,48 1,651 0,784 1,891 1,872 0,652 1,276 1,530 1,552 1,322 0,650 1,231 0,97 0,574 1,149 0,824 1,829 1,397 1,345 0,357 0,656 1,612 1,46 1,917 1,690 1,124 0,300 0,880 0,889 0,618 1,747 1,53 0,573 0,132 0,345 1,844 0,851 1,604 0,766 0,891 1,218 0,371 1,655 0,130 0,541 0,398 1,477 1,936 0,710 1,920 1,414 1,190 0,480 1,691 1,884 1,158 1,51 0,622 1,236 0,734 1,772 1,556 0,490 1,127 0,441 1,587 1,762 1,6 1,185 0,554 1,507 1,520 1,675 1,549 0,328 0,723 0,129 1,152 1,201 1,940 1,156 0,316 0,574 1,323 1,538 0,879 1,24 0,962 1,533 1,523 1,176 0,134 0,248 1,625 1,288 1,948 1,195 0,633 1,897 1,628 1,822 1,634 1,635 1,67 0,476 0,405 0,622 1,518 1,341 1,630 1,838 1,826 1,203 0,420 0,989 0,717 0,85 1,206 0,216 0,506 1,645 1,746 1,372 0,635 1,903 1,985 1,752 0,122 0,502 0,90 1,11 1,470 0,276 1,318 1,178 0,299 1,425 1,533 0,380 0,513 1,538 1,89 0,970 1,638 1,557 0,357 0,559 1,503 1,270 0,517 1,528 0,70 1,985 1,190 0,440 0,634 1,819 0,33 0,612 0,134 1,444 1,680 0,987 1,729 1,371 0,117 1,84 1,54 0,519 0,197 0,311 1,894 1,85 0,446 1,920 0,461 0,90 1,773 0,778 1,979 0,922 1,802 1,199 0,490 0,669 1,380 1,941 0,478 1,671 1,514 1,797 0,499 1,498 1,546 1,79 1,991 0,62 0,145 0,25 1,963 1,471 1,973 0,238 0,451 1,897 0,662 0,142 1,797 0,34 1,183 0,705 0,123 0,719 1,242 0,303 1,660 1,192 1,652 1,189 0,630 0,767 0,971 0,589 1,526 1,973 1,548 1,49 1,895 1,0 1,436 1,503 1,140 1,747 1,340 0,108 1,836 0,49 0,712 0,216 0,890 0,892 1,441 1,580 0,382 0,254 0,197 1,100 1,174 0,184 1,780 0,22 0,796 0,384 1,154 0,142 0,639 0,452 0,137 0,761 0,236 1,154 1,740 1,123 1,550 0,142 1,680 1,220 1,867 1,530 0,533 1,147 1,52 1,751 0,970 1,480 0,499 0,544 0,666 1,586 1,85 0,249 0,926 0,568 0,664 1,836 1,509 0,198 1,727 0,896 0,395 1,900 1,309 1,436 1,313 0,847 1,885 1,720 0,31 0,716 1,71 0,140 1,712 1,627 1,606 0,291 0,120 0,18 1,391 1,133 0,52 1,421 1,590 0,982 0,559 0,302 0,459 1,599 0,771 1,381 0,564 1,851 0,488 0,710 1,95 0,368 0,67 0,312 1,663 0,549 0,409 0,403 0,945 1,486 0,626 0,714 0,69 0,635 0,719 1,983 1,412 0,561 0,280 1,228 0,1 0,634 0,305 0,880 0,397 0,65 1,382 0,584 1,873 0,766 1,239 1,315 0,722 0,505 0,472 0,171 0,881 1,197 0,74 0,598 0,57 0,464 0,900 1,433 1,97 1,68 1,709 1,743 0,73 0,829 0,728 0,695 1,649 1,687 0,145 0,389 1,729 1,746 0,481 1,538 0,680 0,70 0,308 0,74 0,320 0,680 0,981 0,678 1,351 1,161 0,788 1,526 1,234 0,200 1,38 0,217 1,822 1,676 1,22 1,699 1,383 1,58 1,593 1,709 1,720 1,959 0,431 0,652 0,703 1,366 1,2 0,45 1,14 1,528 1,99 1,767 0,606 1,161 1,176 1,637 1,222 1,435 1,731 1,677 0,922 1,364 1,370 1,60 1,976 1,9 1,251 0,914 1,545 0,342 0,57 0,654 1,230 1,796 1,826 1,184 0,484 0,556 1,652 1,569 1,854 1,375 1,956 1,367 1,43 0,242 1,490 1,30 0,212 1,873 1,265 0,718 1,794 0,909 1,599 0,490 1,584 0,992 1,607 0,130 1,977 1,142 1,848 0,846 0,677 0,507 1,882 1,6 1,813 0,511 1,813 1,24 1,6 0,927 0,923 0,27 0,611 1,702 0,588 0,919 0,261 0,647 1,559 1,147 1,609 0,108 1,573 1,256 1,67 1,560 1,615 1,184 0,114 1,621 0,405 1,400 0,922 1,483 0,499 0,333 0,140 0,598 0,606 0,354 1,113 0,307 1,274 1,168 1,118 0,599 1,189 0,867 1,346 1,186 1,722 1,567 0,234 1,197 0,678 1,292 0,556 1,680 1,580 0,713 0,420 1,286 1,481 0,629 1,531 0,509 1,813 1,651 0,620 0,230 1,623 0,155 1,865 1,282 1,454 1,685 1,447 0,509 1,214 0,994 0,731 0,596 1,644 1,796 1,994 1,527 1,913 0,574 0,859 1,798 0,721 0,248 1,848 0,273 1,265 0,321 0,450 0,623 0,461 0,870 1,416 0,838 1,935 1,448 0,266 1,436 0,500 1,823 0,660 0,630 1,961 1,740 0,710 1,867 1,488 1,13 1,579 0,969 1,320 0,857 1,860 1,633 0,718 0,551 0,902 1,880 0,248 0,89 0,125 1,0 1,740 0,205 1,949 1,831 0,529 1,173 0,223 1,110 0,519 1,938 0,348 0,940 1,83 1,4 0,408 1,856 0,184 1,798 1,641 1,872 0,523 0,310 1,746 1,665 0,873 1,832 0,280 1,382 1,284 0,688 0,213 0,706 0,942 0,465 0,823 0,504 1,502 1,708 1,637 0,525 1,65 1,383 0,849 1,824 1,273 1,256 1,509 0,147 1,61 1,392 0,881 1,269 1,62 1,376 0,570 0,338 1,494 1,229 0,519 0,898 1,861 0,319 0,134 1,625 0,846 0,974 0,442 1,495 0,268 0,526 1,290 0,609 1,472 1,950 0,262 1,389 0,695 1,899 1,869 1,759 1,523 0,18 0,250 0,717 0,667 1,847 0,606 0,402 1,16 0,171 0,897 1,959 0,197 0,776 0,506 0,355 1,237 0,91 0,102 1,208 0,133 0,918 1,708 1,245 0,619 0,674 1,823 1,593 0,396 1,773 1,587 0,490 0,415 1,10 1,878 0,907 0,80 0,356 0,0 1,773 0,921 1,867 1,133 0,980 1,533 1,393 0,835 1,752 1,740 0,54 0,946 1,4 1,640 0,120 1,468 1,469 0,65 0,567 1,902 1,927 1,97 1,107 0,188 1,498 1,834 0,137 0,680 1,522 1,291 0,646 1,465 1,328 1,605 0,383 0,569 1,328 0,126 1,126 1,89 1,506 1,768 1,318 1,509 0,435 0,207 1,685 1,733 1,550 0,422 0,207 1,152 0,556 0,661 0,240 1,58 0,447 0,938 0,713 0,300 0,413 0,432 1,580 1,447 0,157 0,401 1,368 1,941 0,107 0,915 1,848 1,987 0,612 1,958 0,183 0,389 1,112 1,643 0,873 0,610 1,849 0,260 1,473 1,242 1,847 1,111 0,14 1,1 0,346 1,520 1,104 0,47 1,646 1,180 1,164 0,545 1,776 0,631 0,61 1,214 0,535 0,775 0,953 1,899 1,877 0,106 0,127 0,556 1,679 0,783 0,149 0,955 0,434 0,425 0,375 0,421 0,742 1,559 1,191 1,418 1,496 1,129 1,753 0,390 1,950 1,946 0,948 0,486 0,979 0,583 0,540 0,189 1,646 0,439 0,930 0,657 1,252 1,792 0,818 1,314 0,111 0,168 1,290 1,459 1,572 0,561 0,13 0,255 1,724 0,98 1,271 0,536 1,825 0,429 0,787 1,827 1,519 1,799 0,242 0,738 0,842 0,597 1,741 1,155 1,481 1,537 1,11 1,314 1,437 0,42 1,935 0,779 1,338 0,51 1,663 1,984 0,592 1,97 0,324 1,562 0,765 1,132 1,855 1,579 0,580 0,342 0,978 1,912 1,524 1,532 1,152 1,58 1,477 0,13 0,471 0,143 1,628 0,577 0,263 1,252 0,275 0,647 0,928 1,215 1,410 1,884 1,520 0,455 1,58 0,518 1,885 0,872 0,155 0,710 0,743 1,707 0,369 0,315 1,513 1,42 0,14 0,372 0,138 1,63 0,681 0,297 1,607 1,144 0,740 1,534 1,405 0,761 0,142 0,189 1,962 1,377 0,831 0,680 1,627 1,10 0,570 0,514 1,578 1,146 0,469 1,192 0,720 0,438 1,293 1,819 0,73 1,101 1,575 0,659 0,195 0,401 0,789 1,969 1,347 1,800 1,12 0,240 0,61 0,896 1,649 1,492 0,867 1,356 0,730 0,228 1,101 1,242 1,206 1,840 1,539 0,888 0,873 1,486 1,737 0,977 1,220 1,778 1,823 0,8 1,884 0,105 0,161 0,49 1,737 1,795 1,245 1,811 0,230 1,212 0,771 0,952 0,196 1,856 1,889 0,528 1,296 1,842 1,490 1,652 0,652 0,424 0,942 1,406 1,741 1,662 1,767 1,635 0,440 1,325 1,498 0,545 1,345 1,992 0,715 1,946 0,338 0,648 1,379 1,801 0,225 1,621 0,943 0,187 0,458 1,23 1,3 1,319 0,539 1,501 0,648 1,772 0,822 1,598 0,747 0,597 1,528 0,537 1,100 0,918 1,584 1,782 1,474 1,6 1,106 1,876 1,957 0,997 1,687 0,569 1,521 1,615 0,451 0,50 0,909 0,500 0,286 1,485 0,452 0,18 1,566 0,569 1,52 1,815 0,115 0,279 0,27 1,231 1,47 0,635 0,493 1,797 1,839 1,366 1,878 1,978 0,630 1,363 0,193 0,683 0,526 1,664 0,193 1,584 0,170 1,31 0,240 0,960 0,841 1,478 1,170 1,876 0,127 0,696 1,859 1,699 0,631 1,899 0,443 1,993 0,813 0,200 1,185 1,682 1,536 1,557 0,722 1,516 0,617 0,503 0,629 1,335 0,456 0,112 1,517 1,505 0,857 1,325 0,818 0,902 0,545 1,420 0,102 0,16 0,342 0,171 1,562 0,52 0,877 0,358 0,769 1,712 0,537 0,988 1,333 1,313 1,210 0,428 0,771 1,433 0,653 0,331 0,402 1,484 1,914 0,674 1,110 1,841 1,189 1,71 0,175 1,767 0,683 0,287 0,245 1,679 0,56 0,223 0,366 1,317 0,259 0,673 1,301 0,815 1,502 0,6 0,637 1,414 1,101 1,204 0,697 0,741 0,896 1,224 1,350 1,332 0,891 1,851 0,115 1,489 1,990 0,364 1,206 1,914 1,252 1,420 0,681 0,353 0,36 0,863 1,780 0,69 0,829 0,200 0,776 0,502 0,406 1,961 1,457 0,861 0,964 0,96 1,271 0,69 0,116 1,324 0,179 0,620 0,165 0,24 1,513 0,444 0,359 0,797 1,505 1,703 0,432 0,983 0,941 1,414 1,651 1,211 1,678 1,74 1,513 0,917 0,731 0,323 1,718 0,333 1,431 0,300 0,214 1,305 0,201 1,270 0,194 0,361 0,408 1,943 1,196 0,0 1,671 0,254 0,814 0,463 1,624 1,979 0,46 1,102 1,27 1,515 0,761 1,881 0,483 1,312 1,219 0,298 0,564 1,416 0,760 0,22 1,683 0,207 0,140 0,86 1,891 0,338 0,192 1,375 1,14 0,262 0,526 0,408 1,14 0,236 1,531 0,207 1,101 0,395 1,369 1,7 1,807 0,569 0,832 0,164 1,948 1,737 0,921 1,576 0,751 0,332 1,939 0,948 1,398 1,614 0,640 1,367 0,441 1,574 1,66 0,322 0,228 1,403 0,568 1,127 1,955 0,285 0,160 0,80 1,52 1,173 1,195 1,889 0,728 0,309 0,529 1,843 1,407 1,641 1,576 0,643 0,485 0,626 0,465 0,976 0,248 0,229 0,993 1,804 0,812 0,966 1,622 1,423 0,267 1,589 0,323 0,145 1,410 1,138 1,530 0,295 1,766 1,262 1,387 1,899 1,350 0,409 0,201 1,736 1,639 0,511 0,197 1,717 0,899 1,538 1,627 1,860 1,70 1,814 1,613 0,501 0,347 1,576 1,494 1,482 0,11 0,242 1,90 0,83 1,118 1,658 1,275 0,726 1,148 0,523 0,134 0,452 1,159 0,831 0,451 1,228 1,610 0,356 1,885 0,919 0,853 1,920 1,472 0,191 1,68 0,672 0,704 0,292 1,867 1,701 0,925 0,340 1,840 1,835 1,336 1,628 0,11 0,18 1,652 1,299 0,794 1,752 1,185 0,722 0,767 1,933 1,668 0,480 0,114 0,792 1,703 1,902 1,447 0,869 0,873 1,295 0,165 0,475 0,76 0,732 0,169 0,170 0,86 1,781 0,605 1,955 0,646 0,216 1,345 1,708 1,847 0,821 0,348 1,547 1,863 0,824 1,756 1,326 0,744 1,863 0,571 0,897 0,479 1,260 0,104 0,281 1,630 1,951 1,35 0,629 1,726 1,689 0,712 1,154 0,306 1,226 0,191 0,350 0,640 0,322 1,714 1,601 0,469 0,123 1,277 1,791 1,253 0,448 0,722 1,509 1,880 0,207 0,510 1,959 0,942 1,757 0,592 0,644 0,141 1,967 1,348 0,432 1,771 0,605 0,938 1,401 1,553 0,609 1,441 0,861 0,14 0,10 1,670 1,99 0,385 0,825 1,395 0,337 1,529 1,406 1,280 1,427 1,601 1,987 0,508 0,118 0,492 0,166 0,333 0,488 0,224 1,988 1,910 1,713 0,51 0,593 1,236 1,563 1,285 1,314 0,777 1,438 1,644 0,489 1,839 0,762 0,911 1,262 1,854 0,577 0,365 1,605 0,55 1,413 1,349 0,574 1,460 0,302 0,554 0,924 0,350 1,869 1,368 1,962 1,904 0,416 0,478 1,371 1,497 0,97 0,943 1,382 1,906 0,894 0,149 0,815 1,286 0,148 1,960 1,345 1,25 1,858 1,214 1,928 1,616 1,948 0,229 1,848 0,936 0,413 0,16 1,20 1,263 1,400 1,783 1,264 0,215 1,594 0,485 1,71 0,471 1,0 0,498 0,835 0,948 0,31 1,509 0,396 0,297 0,426 0,303 0,36 1,365 0,432 0,308 0,933 1,287 1,91 0,517 1,477 1,703 0,452 1,273 1,675 0,3 1,985 1,297 1,98 1,688 0,576 1,723 0,941 0,26 0,26 1,736 1,349 0,448 0,160 1,16 1,474 1,233 0,183 1,585 0,1 0,961 0,373 1,201 0,370 1,78 1,594 1,242 1,770 0,521 1,261 1,474 1,383 0,929 0,136 0,781 0,25 1,355 1,30 0,988 0,361 1,838 1,154 1,426 0,60 0,940 0,703 0,846 1,372 1,770 0,189 0,625 1,805 0,105 0,856 1,239 0,344 0,476 0,432 1,411 0,997 1,370 1,259 0,248 1,678 1,615 1,302 0,395 0,574 0,969 1,872 0,214 1,769 0,396 1,257 0,277 0,141 0,766 0,655 0,901 0,805 0,893 1,704 1,775 1,229 1,575 0,984 1,97 1,900 0,442 1,834 0,92 0,695 1,200 0,4 0,839 1,967 0,696 0,187 0,294 0,136 1,238 0,114 0,510 0,753 0,370 0,551 0,244 1,788 1,753 1,171 1,803 0,364 0,85 0,452 0,623 1,481 1,103 1,55 1,598 1,333 0,208 0,774 0,558 1,469 0,781 1,298 0,693 1,850 1,268 0,104 1,427 1,799 0,597 0,997 1,60 1,632 0,497 1,298 0,865 1,444 0,500 1,476 0,729 0,536 1,760 0,105 1,127 0,708 1,901 0,667 0,872 1,303 0,960 0,599 0,260 0,576 1,776 0,511 1,226 0,276 1,154 1,810 1,702 1,222 1,478 0,732 1,541 0,574 0,319 0,509 1,65 1,599 1,909 1,277 0,97 1,759 0,61 0,144 0,607 1,255 1,183 0,537 1,584 0,520 1,98 1,702 1,737 0,138 0,877 1,941 1,218 0,127 1,390 0,751 1,65 1,38 1,850 0,8 1,208 0,508 0,967 0,489 0,639 0,686 0,546 0,24 1,299 0,325 0,40 0,724 0,626 1,34 0,237 1,105 1,171 0,963 0,548 1,341 1,253 1,61 1,946 0,468 1,573 0,168 0,225 1,111 0,685 1,424 1,381 0,262 0,187 1,25 0,574 1,552 1,319 0,940 1,85 0,340 1,136 0,484 0,513 1,295 1,552 0,721 1,177 0,198 0,807 1,670 0,184 0,675 1,981 0,925 0,183 1,822 1,186 1,242 1,856 1,103 1,677 1,254 0,807 1,769 0,516 0,984 0,963 0,897 0,866 1,880 0,141 0,324 0,938 1,936 0,130 0,538 1,34 1,829 1,990 0,908 1,895 1,756 1,71 1,774 1,156 0,940 1,158 0,981 0,541 1,498 1,935 1,187 0,96 1,142 0,351 0,654 0,726 1,991 0,78 0,700 0,559 0,773 0,432 0,24 0,248 1,31 1,465 1,460 1,331 0,952 1,923 0,823 0,156 1,935 0,208 0,8 0,985 1,472 0,399 1,733 0,890 1,247 0,580 1,742 1,329 0,444 0,268 0,526 0,393 0,778 1,5 0,650 1,979 0,33 1,82 0,73 0,521 0,470 1,433 0,353 1,504 0,840 0,557 1,594 0,461 0,602 0,845 1,573 1,147 0,894 0,826 1,741 1,829 0,691 1,168 1,703 0,486 0,826 1,952 1,65 0,479 0,127 1,508 0,166 1,844 1,332 1,350 1,151 1,875 0,225 1,88 0,149 0,966 1,646 0,933 1,681 1,28 1,222 1,990 1,264 0,868 1,482 1,9 1,472 0,104 1,103 0,632 0,104 0,235 1,809 1,319 1,949 1,350 0,448 1,959 1,784 1,387 0,144 1,706 0,185 1,708 1,914 1,805 1,521 1,215 1,270 0,128 1,961 0,456 0,573 1,445 1,318 0,697 0,861 1,371 1,96 0,701 0,23 1,403 1,987 0,749 0,667 1,784 0,744 0,122 0,539 1,773 1,603 1,43 1,292 1,616 1,639 0,697 0,438 0,912 1,241 1,19 1,358 0,528 1,598 0,456 1,354 0,258 0,388 1,672 1,538 1,577 0,894 0,688 1,437 0,447 1,97 0,272 0,342 1,486 1,934 0,963 0,598 0,494 1,475 1,804 0,635 1,412 0,320 0,370 0,377 1,589 1,752 0,415 0,587 0,224 1,979 1,757 1,402 0,228 1,825 0,964 1,256 0,709 0,549 0,462 1,319 1,999 1,195 1,248 0,96 1,699 0,417 0,755 0,559 1,609 1,184 1,905 1,9 1,486 1,730 0,662 1,285 1,573 1,180 0,113 0,780 1,207 1,487 1,517 0,529 0,36 0,485 1,35 1,32 1,998 1,27 0,640 0,360 0,117 0,309 0,838 0,98 0,319 1,893 1,650 1,655 0,725 1,230 0,596 1,571 0,515 1,500 1,858 1,178 0,947 1,141 1,254 0,274 0,938 1,886 1,110 0,210 0,494 1,40 1,115 1,304 1,11 1,276 0,973 1,500 1,250 0,953 1,693 1,220 1,105 1,867 1,239 1,474 0,376 0,623 1,756 0,378 1,393 1,475 1,345 0,494 0,769 0,148 1,705 0,411 1,267 0,866 1,322 0,394 0,181 1,870 0,754 1,799 1,733 1,147 1,221 1,129 0,131 1,197 0,455 0,787 0,252 1,713 0,705 1,404 0,809 1,268 0,802 1,55 1,658 0,948 1,932 0,499 1,947 1,138 0,653 0,552 0,781 1,304 0,912 0,586 0,531 1,495 0,885 0,480 1,147 0,821 1,536 0,673 0,448 0,103 0,286 1,743 0,940 1,539 0,83 1,711 1,103 0,574 1,138 1,163 1,125 1,622 1,737 1,104 1,775 0,511 1,531 0,725 0,10 1,739 0,576 0,517 1,206 1,604 1,124 1,145 0,647 1,193 0,656 0,526 1,648 1,553 1,157 0,48 1,770 1,674 1,345 0,870 0,809 1,217 1,929 1,877 0,661 1,418 1,891 0,153 0,741 1,644 1,120 1,816 1,616 1,199 0,441 1,517 1,36 0,595 1,209 1,311 0,872 1,832 0,944 0,727 1,69 1,658 1,791 0,418 1,665 0,728 1,707 1,526 0,562 0,313 1,269 0,610 0,225 0,374 1,516 1,639 0,801 1,233 1,532 0,527 1,722 1,527 0,722 0,754 0,791 1,776 0,6 0,810 0,263 1,51 1,343 1,67 0,257 0,56 1,815 1,369 0,459 0,392 0,285 1,404 0,869 0,284 1,245 0,437 0,746 0,549 1,754 1,639 0,736 0,972 1,244 0,608 0,771 1,752 0,350 1,225 0,947 1,778 0,490 1,926 1,340 1,119 0,623 1,974 0,991 0,769 1,23 0,911 1,153 1,389 1,460 0,669 1,336 0,14 0,932 1,426 0,932 1,438 0,455 1,445 0,193 1,848 0,580 1,94 1,266 1,95 0,206 0,563 0,375 1,60 1,124 0,275 0,114 0,765 0,147 0,373 0,458 0,280 1,65 0,363 0,29 0,248 1,103 0,131 0,258 0,511 0,797 0,330 0,424 0,990 0,414 1,609 0,900 1,576 0,563 1,515 0,993 1,91 0,73 1,484 1,47 1,870 1,343 0,831 1,120 0,569 0,291 1,206 0,298 0,50 0,923 1,123 1,687 1,153 0,850 0,880 1,229 0,784 1,365 1,307 1,899 0,377 0,363 0,809 0,557 0,109 0,723 1,248 0,345 1,925 0,39 0,995 1,136 1,191 1,508 1,379 1,188 1,218 1,345 1,820 1,525 1,79 1,52 0,728 0,87 0,178 1,661 1,781 1,557 0,248 0,945 1,732 0,102 0,848 1,898 0,234 1,667 1,25 1,666 1,356 0,432 1,603 0,139 1,993 0,238 0,765 1,396 0,673 0,799 0,38 0,435 0,220 1,501 1,185 0,537 1,45 1,611 1,577 0,36 0,809 1,364 1,232 1,766 1,448 0,566 1,339 0,23 0,79 0,81 0,259 1,630 1,409 0,158 0,329 1,377 0,734 1,84 0,159 0,411 0,686 1,469 0,535 1,752 1,221 1,280 1,176 1,966 1,153 1,261 1,729 1,825 1,410 1,100 0,623 0,560 0,84 0,322 1,206 0,739 1,982 1,795 0,631 0,139 1,946 0,995 1,83 0,886 0,544 0,934 0,242 0,539 0,224 0,713 1,90 1,882 0,295 0,819 0,143 1,604 0,464 1,668 0,823 0,654 0,370 1,18 1,80 1,560 1,556 0,152 1,367 1,690 0,789 1,208 1,896 0,921 1,412 0,714 1,533 1,314 0,978 1,391 0,33 0,590 1,644 0,695 0,842 1,719 0,700 0,146 0,101 0,604 1,331 1,976 1,555 0,426 0,292 1,167 0,622 1,971 0,609 1,542 0,616 1,498 0,205 1,557 0,362 1,638 1,708 0,777 0,745 0,616 1,980 0,874 1,644 0,505 1,48 1,472 0,585 0,132 0,39 1,849 0,571 0,352 0,671 1,49 0,72 0,244 1,650 0,252 0,983 1,686 1,372 1,998 0,486 0,383 0,570 0,210 0,3 0,901 0,456 1,394 1,258 1,406 1,868 0,954 1,781 1,138 0,582 1,379 1,413 1,929 1,372 0,51 1,536 1,473 0,180 1,509 1,285 1,499 0,330 0,766 1,400 0,11 0,902 0,479 1,773 0,733 1,288 0,30 1,821 1,114 1,259 1,960 1,481 1,379 0,60 0,416 1,490 0,679 0,565 1,810 1,895 1,299 1,989 0,265 0,25 1,252 0,728 1,52 1,905 1,165 0,128 0,512 1,577 1,920 0,911 1,875 0,322 0,100 0,619 1,724 0,923 0,239 1,791 0,749 0,184 1,571 0,835 0,356 0,653 0,708 0,875 1,103 1,413 1,92 0,808 0,680 1,945 1,415 1,881 1,45 0,642 0,621 1,12 1,366 0,34 0,794 1,980 1,694 1,953 0,91 1,305 1,778 0,533 0,982 0,235 1,313 0,622 1,949 0,208 0,757 1,613 1,214 0,484 1,794 0,551 1,328 0,774 1,373 1,189 0,14 0,559 1,53 0,183 0,348 1,495 1,530 0,5 1,106 1,319 1,971 0,416 0,366 1,243 0,297 0,13 0,176 1,448 0,163 1,824 1,826 0,666 0,68 1,945 0,519 0,868 0,347 1,381 1,763 0,434 0,123 0,695 0,675 0,963 0,769 0,848 0,664 1,737 1,182 1,842 0,729 0,727 0,704 0,671 0,82 0,325 1,616 0,247 1,272 1,438 1,405 1,175 0,74 0,307 0,320 0,97 0,823 0,820 0,243 1,322 0,609 0,913 0,852 0,335 0,339 1,597 1,877 1,281 1,103 1,938 1,256 1,137 1,163 0,92 1,58 1,837 1,355 1,661 1,888 0,945 1,648 0,290 0,350 0,816 0,408 1,286 1,488 1,168 1,44 1,434 0,790 1,889 1,953 0,754 0,762 1,223 0,587 1,874 0,856 0,483 1,425 1,989 0,57 1,386 0,676 1,97 1,957 0,616 1,150 0,463 1,9 0,122 0,217 0,332 0,15 1,43 0,358 0,398 1,494 1,745 1,968 1,569 0,882 1,857 1,195 1,260 0,570 1,988 1,844 1,377 1,533 1,934 0,695 1,101 1,16 0,725 1,673 1,368 1,861 1,216 0,521 0,567 0,308 0,704 0,756 1,726 0,303 0,280 0,633 0,626 0,1 0,325 1,104 1,720 1,84 0,301 0,240 1,368 1,48 0,202 0,16 0,736 0,741 1,151 1,638 0,141 0,221 0,627 1,429 1,780 0,173 0,159 0,35 0,70 1,787 1,468 1,209 1,246 1,658 1,13 0,614 0,84 0,814 0,847 1,636 0,70 0,176 1,681 0,266 0,960 0,73 1,14 0,76 1,174 0,490 1,925 0,284 0,111 0,860 0,886 1,892 1,244 0,783 1,575 0,995 1,856 0,187 0,532 0,514 1,748 1,771 1,176 0,987 1,260 0,524 0,706 1,788 1,272 1,906 0,661 1,652 1,969 0,941 0,346 1,307 0,265 0,826 1,975 0,36 1,224 1,144 0,255 0,711 1,886 1,8 0,429 0,335 0,79 1,877 1,98 0,823 0,483 1,756 0,137 0,323 1,290 1,63 1,482 1,223 0,189 0,434 0,192 0,989 0,996 0,326 0,5 1,17 0,477 0,844 0,856 1,487 0,491 0,489 1,52 1,491 0,520 0,257 0,627 0,142 1,890 0,836 1,356 0,237 0,1 1,393 0,868 1,105 1,500 0,714 0,428 1,749 1,592 0,194 1,76 0,828 1,601 1,980 0,171 0,783 0,138 1,39 1,247 0,405 0,170 1,269 1,510 1,882 1,540 0,504 1,554 0,776 0,986 0,996 1,840 1,659 1,821 1,538 0,683 1,811 1,584 1,331 0,185 0,926 1,316 1,397 1,547 1,685 0,261 1,614 1,340 0,52 1,457 1,614 1,390 0,723 0,124 1,681 0,623 0,336 0,190 0,810 1,362 0,73 0,366 1,200 0,434 1,672 1,288 0,15 1,900 1,485 1,439 1,159 0,725 0,461 1,27 1,853 1,768 0,760 1,221 1,84 1,929 1,366 0,417 1,530 1,934 1,443 1,701 0,283 0,602 1,198 1,483 0,46 1,128 0,78 0,508 1,551 1,76 1,343 0,581 0,176 1,378 1,168 0,192 1,32 0,576 0,281 1,954 1,440 0,224 0,522 0,392 0,16 1,483 1,469 1,5 0,517 0,190 1,712 1,49 1,174 1,605 0,12 1,875 1,978 0,342 1,687 0,365 1,308 0,720 1,352 0,295 1,722 0,598 1,367 0,246 1,57 1,150 0,484 0,502 0,902 0,29 0,458 1,717 1,148 1,684 1,110 0,737 0,642 0,217 1,187 1,615 0,968 0,980 1,551 1,432 1,418 0,725 1,341 0,569 0,483 1,901 1,748 1,701 1,210 1,863 1,139 1,282 0,273 1,543 1,5 0,948 1,785 1,963 1,260 1,229 0,585 0,754 0,364 0,177 0,447 1,621 1,741 0,268 0,358 0,517 1,513 0,428 0,14 1,380 0,243 1,860 1,164 0,306 1,466 0,943 0,967 1,221 1,785 0,731 1,707 1,109 0,989 0,995 1,72 0,963 1,648 1,24 0,497 1,225 0,367 1,371 0,934 0,64 1,695 1,595 1,181 0,56 0,976 0,15 0,568 0,992 1,732 0,534 1,965 0,389 0,153 0,694 1,362 1,934 0,414 0,522 0,64 1,656 1,221 0,988 0,759 1,203 1,361 0,616 0,480 0,329 0,661 0,869 1,237 1,987 0,530 1,765 0,523 1,318 1,34 0,635 1,664 0,504 0,650 0,295 1,851 0,779 1,706 0,550 1,891 1,706 1,175 0,117 1,209 0,319 1,496 1,403 1,481 0,875 1,172 1,739 1,734 0,565 0,789 0,188 1,172 1,792 0,649 0,897 0,449 1,232 1,477 0,994 0,650 1,438 1,117 1,53 1,570 1,63 1,252 1,375 1,87 1,678 0,529 1,338 0,667 1,954 0,779 1,795 0,723 1,817 0,105 1,11 1,668 1,918 0,772 0,146 0,540 1,297 0,90 0,17 0,938 1,239 0,679 1,777 1,229 0,402 0,472 0,774 1,500 1,953 0,137 0,510 1,305 1,518 1,783 1,921 1,539 1,5 1,493 0,955 1,577 1,793 1,766 1,432 1,703 0,173 1,558 0,342 0,522 0,34 1,171 0,508 1,371 0,906 1,815 1,980 0,465 1,597 0,241 0,282 0,215 0,660 1,355 1,670 1,299 1,424 0,838 1,490 1,497 0,536 0,327 1,246 1,196 1,364 1,835 1,777 0,628 0,351 1,529 1,306 0,348 0,695 1,458 0,120 0,410 1,530 0,684 1,187 1,225 0,579 1,229 0,12 1,40 0,54 0,850 1,419 1,344 1,617 1,506 0,324 0,382 1,394 1,393 1,139 0,907 1,242 1,33 0,342 0,692 0,215 0,91 1,356 1,648 1,389 1,560 1,486 0,842 1,474 1,890 1,213 1,557 0,236 0,41 0,983 1,493 0,114 0,421 1,192 1,127 1,788 0,834 1,372 1,437 0,376 0,504 1,649 0,715 0,437 0,511 1,438 1,475 0,230 1,111 1,998 0,50 0,594 1,843 1,681 1,46 1,14 1,467 0,316 0,352 0,672 1,342 0,622 0,133 1,10 0,291 1,269 1,313 1,664 1,29 1,142 0,327 1,20 0,5 0,25 0,716 0,760 0,155 0,858 1,387 0,382 0,505 0,784 1,238 1,123 1,578 0,478 0,505 0,765 1,854 1,197 0,994 1,313 0,858 0,477 1,96 1,329 0,508 1,304 0,798 1,719 1,118 0,922 0,503 0,319 1,585 1,679 1,897 1,942 0,512 1,86 1,452 0,284 1,151 0,628 0,466 1,79 1,926 0,72 1,254 0,67 0,486 1,26 1,178 1,299 1,948 0,26 1,108 1,844 1,202 1,376 1,590 1,389 0,297 1,492 0,485 0,370 0,121 1,397 0,730 1,505 0,56 0,831 1,580 1,835 1,53 0,515 1,272 0,784 1,327 1,756 0,458 1,959 0,955 0,793 1,387 0,585 0,124 1,97 1,393 0,754 0,229 1,172 1,950 0,960 1,801 0,311 0,989 0,204 0,444 0,394 1,393 1,740 0,184 0,511 0,100 0,441 1,706 1,181 0,12 0,161 1,630 0,756 1,915 1,171 1,250 0,434 0,374 1,960 1,336 0,179 1,929 0,794 1,427 1,238 1,791 1,978 0,208 0,644 1,502 0,883 0,354 1,942 0,606 0,6 0,125 0,732 0,716 1,182 0,886 1,904 1,556 0,947 0,397 0,763 0,644 0,833 1,376 1,856 0,707 0,800 1,314 0,871 1,687 0,26 0,889 0,850 0,781 1,258 0,406 0,696 0,107 0,334 0,580 1,716 1,657 0,538 1,34 1,111 1,522 0,385 1,530 0,955 1,650 1,169 1,785 0,180 1,330 1,50 1,713 0,70 1,88 0,766 1,624 1,998 1,737 0,212 1,206 0,763 1,488 0,143 1,576 1,190 1,3 1,491 0,933 1,176 0,659 1,743 0,935 1,388 0,420 0,909 0,167 0,847 1,300 1,405 0,263 0,608 1,934 1,850 1,243 0,593 1,930 1,201 1,790 1,982 1,257 1,30 1,650 0,85 1,480 0,245 0,550 0,306 1,525 1,667 0,826 1,483 1,161 0,644 0,223 0,360 1,959 1,849 0,952 0,574 1,809 1,931 0,494 0,764 1,381 0,897 1,977 1,4 0,912 1,559 1,649 1,296 1,638 1,810 1,156 0,798 1,692 1,24 0,836 1,59 0,78 0,811 0,898 1,637 1,590 0,515 0,716 1,505 0,3 0,68 0,323 1,149 0,818 1,902 1,301 0,662 0,241 0,819 1,752 1,440 1,895 1,112 0,185 1,493 0,266 1,353 0,292 0,171 0,534 0,593 1,933 0,363 0,966 0,179 1,164 0,752 0,364 0,399 0,916 0,747 0,391 0,38 1,130 0,57 0,333 1,141 1,982 0,635 1,256 0,313 1,83 1,729 1,368 1,331 0,429 0,191 0,418 0,563 1,324 0,624 1,999 1,545 1,598 1,417 1,22 0,311 0,485 0,308 0,738 1,859 0,223 1,551 0,875 0,286 1,5 0,316 0,282 0,879 0,493 1,220 1,792 1,24 1,214 1,181 0,80 1,175 0,747 0,553 0,837 1,824 1,785 1,866 0,481 1,138 0,890 1,294 1,476 1,853 0,718 1,994 1,509 1,425 0,878 1,213 0,199 1,525 1,494 1,242 1,39 0,509 1,719 0,64 0,151 0,130 0,624 0,556 0,428 0,65 1,639 0,501 1,17 1,394 1,627 0,51 0,282 0,912 1,103 0,380 1,150 0,920 1,696 0,778 1,288 0,514 1,50 1,142 0,899 1,892 1,796 1,796 0,692 0,147 1,179 1,698 0,654 1,468 1,65 0,404 1,53 0,185 1,675 0,426 1,90 0,880 0,94 1,428 1,41 1,959 1,438 0,187 1,33 0,307 0,163 1,559 0,535 0,773 0,578 0,763 1,882 0,505 1,853 1,399 0,52 1,480 1,192 0,99 1,196 1,311 1,263 0,826 1,134 1,456 0,354 1,593 1,182 1,167 1,829 0,20 1,417 1,54 0,171 0,170 0,614 1,930 1,208 0,710 1,717 1,144 0,288 0,250 1,279 0,393 1,418 1,846 0,401 0,557 1,582 0,173 1,974 1,693 0,988 0,475 1,526 0,921 1,755 0,985 1,51 0,252 0,295 1,184 0,215 1,672 1,56 0,975 0,222 0,349 0,459 1,429 0,24 1,559 1,191 0,639 0,370 0,302 0,697 1,578 0,180 1,602 1,652 1,326 1,429 1,382 1,756 1,750 0,965 0,885 0,873 1,424 0,344 1,760 0,467 1,751 1,459 0,362 1,238 1,7 0,166 1,732 0,603 1,556 0,265 0,20 0,49 0,144 1,268 0,791 0,732 1,796 0,620 1,26 1,393 0,497 1,728 0,623 0,810 1,821 1,822 0,445 1,835 1,730 1,594 1,318 1,433 1,161 1,907 1,156 0,948 0,410 1,610 1,574 1,724 0,725 1,60 1,604 1,68 1,95 1,244 1,53 0,915 0,163 0,661 0,719 1,105 1,191 0,49 1,149 1,600 0,551 0,925 1,363 0,502 0,403 1,570 1,284 0,292 1,846 0,410 0,884 0,469 1,237 1,148 0,479 1,439 1,959 0,631 0,478 0,268 1,783 0,416 1,900 1,18 0,790 0,985 1,794 1,209 1,740 1,159 1,245 1,760 1,242 0,248 0,596 0,294 0,103 1,833 1,539 1,772 0,104 0,295 0,91 0,146 0,396 0,637 0,273 0,462 1,841 0,596 1,926 0,957 1,200 0,300 1,795 1,740 1,869 1,222 0,945 0,867 0,744 1,887 0,458 0,816 1,943 0,636 1,7 0,111 0,707 0,254 1,394 1,401 0,997 0,280 0,996 0,358 0,702 1,927 0,261 0,497 0,272 1,452 1,808 0,679 0,364 1,359 0,220 0,214 1,985 1,344 0,316 0,918 0,675 0,314 1,48 1,599 0,749 0,742 0,80 0,994 0,123 1,389 1,787 0,626 0,56 0,212 0,982 0,88 1,737 0,43 0,826 0,475 0,296 1,232 1,29 1,358 0,137 1,377 0,643 0,701 1,79 0,742 0,193 0,719 1,194 0,127 0,665 0,119 0,852 1,882 0,61 1,247 1,441 0,379 1,439 1,179 0,431 1,892 1,237 0,928 0,897 1,14 0,834 1,56 1,309 0,554 0,476 0,923 1,780 0,573 0,232 0,205 0,427 1,326 0,977 1,887 1,131 1,578 1,276 1,302 0,630 0,443 0,593 0,894 0,649 1,681 1,770 1,313 0,419 0,905 0,111 1,739 1,284 1,545 0,110 0,247 1,413 0,852 1,991 0,972 0,446 1,386 1,268 0,676 1,662 0,572 0,627 0,999 1,140 0,349 0,429 0,205 0,415 1,580 1,135 1,332 1,417 0,61 0,588 1,475 1,800 1,664 0,305 0,451 1,567 0,116 1,638 1,110 1,46 1,205 0,813 0,335 0,541 0,99 0,275 1,144 0,833 1,433 1,300 0,390 1,28 0,85 0,880 1,78 0,128 0,989 1,520 1,150 1,728 0,12 1,886 1,695 1,913 0,663 1,103 1,677 1,880 0,740 1,65 0,493 0,306 1,347 1,129 0,624 0,735 0,296 0,374 1,118 0,199 0,128 1,302 1,99 1,942 0,998 1,346 0,127 1,110 1,845 0,379 0,146 0,438 1,130 1,439 1,588 1,305 0,688 0,945 1,829 0,267 1,657 1,364 1,928 0,706 0,636 0,148 0,786 0,914 1,264 1,164 0,340 0,967 0,589 1,980 0,352 1,55 0,20 0,942 1,377 1,914 1,307 0,903 0,357 0,985 1,717 0,539 1,377 1,182 0,239 0,720 1,362 0,519 0,669 0,773 0,941 0,502 1,214 1,629 0,191 0,849 0,917 1,483 0,919 0,243 0,627 1,861 0,463 0,927 1,933 1,77 0,724 0,399 0,493 1,243 0,467 1,268 0,486 1,806 0,736 0,398 0,250 1,797 0,589 1,38 1,187 1,190 0,752 1,226 1,498 1,20 1,624 0,203 0,47 1,474 0,133 0,742 1,248 1,459 1,847 1,290 1,471 1,869 1,286 1,938 1,35 1,932 1,464 0,753 0,724 0,733 1,115 1,425 0,284 0,118 1,468 0,570 1,432 0,758 1,455 1,846 0,322 1,876 1,641 1,227 0,324 0,863 1,617 1,408 1,569 1,476 0,654 1,785 1,155 1,779 1,646 0,409 0,95 0,57 0,411 1,562 1,646 0,331 1,420 1,998 0,454 1,24 1,938 0,188 1,289 1,780 0,828 1,926 1,222 1,517 0,118 1,794 0,775 0,826 0,6 1,212 0,938 0,502 1,323 0,893 1,919 1,275 1,258 0,160 1,541 1,211 1,930 0,464 0,747 1,702 1,436 0,291 0,164 0,991 1,495 0,284 1,244 1,86 0,237 0,269 0,172 0,284 1,298 1,529 1,344 0,480 0,119 1,811 1,474 1,516 1,519 0,903 0,245 1,531 0,804 0,346 0,773 1,649 0,707 1,891 1,680 0,143 1,280 0,342 1,940 0,672 0,703 0,305 1,983 0,16 1,379 0,674 1,863 0,100 1,21 0,48 0,594 0,758 1,860 0,364 1,482 0,754 1,697 1,206 0,249 0,162 0,361 1,804 1,445 1,743 1,699 0,15 0,689 0,827 1,220 1,948 1,432 1,161 1,736 0,756 0,263 0,236 0,423 0,886 0,69 0,871 0,208 0,975 0,599 0,52 1,70 0,336 0,96 1,965 0,500 0,419 0,433 1,870 1,401 1,71 1,494 1,800 1,184 0,81 1,844 0,819 1,609 1,878 1,481 1,801 1,341 1,401 0,9 1,602 1,557 0,827 0,414 0,21 1,206 0,220 1,723 1,629 1,920 1,972 0,745 0,206 1,965 0,209 0,132 0,693 0,366 1,710 1,368 0,827 0,453 1,662 1,762 0,88 1,217 0,745 0,146 0,238 0,56 0,146 1,893 0,614 1,605 0,506 1,123 0,95 1,543 0,719 1,32 1,132 0,926 0,54 1,677 1,459 0,416 0,324 0,607 0,61 0,304 1,979 0,85 1,227 0,216 1,284 0,197 1,372 0,110 1,149 0,862 0,231 0,470 0,985 1,1 0,475 1,566 0,992 1,8 0,869 0,168 1,326 0,831 1,658 0,575 0,10 1,4 1,895 0,482 1,640 1,255 1,681 0,71 0,221 0,105 0,928 0,957 0,115 0,282 0,913 1,521 1,724 1,36 1,51 0,994 1,241 1,894 0,800 0,886 0,947 1,266 1,911 1,67 0,864 0,56 1,307 1,286 0,635 0,733 0,468 0,876 1,815 0,643 1,368 0,951 0,658 1,476 0,931 0,234 1,313 1,703 0,77 1,892 1,709 1,263 1,343 1,247 1,727 1,610 1,167 0,613 0,261 1,906 1,652 1,390 1,472 0,232 1,500 0,60 0,603 1,274 0,66 0,126 1,985 0,376 1,582 1,818 1,995 0,613 0,542 1,932 1,484 0,21 1,12 0,515 1,604 0,607 0,908 1,805 1,75 0,99 1,870 0,783 1,815 0,224 0,178 1,126 1,174 1,386 1,7 0,294 0,705 0,2 1,193 1,311 0,392 0,947 0,426 1,716 0,294 1,79 0,761 0,419 0,575 0,335 1,51 1,746 1,448 0,419 1,212 1,883 1,391 0,611 0,401 1,213 0,322 1,907 1,445 0,196 1,547 1,927 1,618 0,329 0,906 0,502 1,172 1,934 1,802 1,406 0,534 1,328 0,331 1,270 0,255 0,618 1,144 0,530 1,671 0,777 1,462 1,733 1,669 0,739 1,114 1,227 1,624 1,14 0,108 1,887 0,444 1,153 1,480 0,820 0,401 0,57 0,697 0,616 1,509 1,580 0,852 0,709 1,530 0,339 1,15 0,333 1,614 1,403 1,671 0,473 0,996 1,680 1,780 0,149 0,932 1,203 1,156 0,978 1,983 1,903 0,90 0,558 0,737 0,293 1,504 0,997 0,963 0,407 1,764 1,280 0,697 1,574 0,901 1,984 1,34 0,952 1,988 1,236 0,983 1,80 1,761 1,673 0,67 0,46 1,333 0,626 0,654 0,760 0,593 0,201 1,321 1,678 0,445 1,16 1,360 0,29 1,476 1,248 0,816 0,214 0,174 0,587 0,3 1,793 0,971 0,673 1,690 0,287 1,944 1,934 0,27 0,52 1,637 1,888 1,992 1,54 0,795 0,955 1,741 1,928 0,96 1,941 0,106 0,732 1,781 0,690 0,418 0,315 1,650 1,850 0,570 1,794 0,558 1,239 0,88 0,387 1,102 0,531 1,884 1,163 1,699 0,52 1,489 0,355 0,869 0,607 0,945 1,494 1,153 0,653 0,754 1,760 1,901 1,45 1,496 0,330 1,35 1,671 1,627 1,233 1,382 0,475 0,389 1,840 0,156 1,603 1,392 0,226 1,148 1,544 1,911 0,331 1,230 0,84 1,321 0,304 0,395 1,372 1,820 1,600 0,930 0,434 0,0 1,734 0,995 0,481 0,807 0,648 0,598 1,604 0,881 0,879 1,917 0,295 1,299 1,749 1,128 1,813 0,268 0,374 0,916 0,535 0,694 1,712 0,706 0,72 0,49 0,746 1,93 0,509 1,794 1,450 1,787 0,244 0,663 1,226 0,616 1,726 0,584 0,301 0,695 0,913 1,872 1,802 0,820 1,531 1,247 1,909 1,906 0,52 1,407 1,919 1,144 0,337 0,728 1,380 1,631 1,710 0,996 1,967 1,358 0,279 0,411 1,899 1,937 0,3 1,866 1,178 0,536 1,301 0,718 1,170 0,983 0,367 1,189 0,977 1,219 0,703 1,43 1,897 1,90 1,364 0,432 1,609 0,716 0,398 1,787 0,950 1,502 1,25 1,148 0,473 1,193 1,722 0,928 1,180 0,796 1,529 0,335 0,800 0,367 0,351 0,204 0,18 1,364 1,555 0,660 1,585 0,395 0,185 0,11 0,845 1,379 1,839 1,568 0,698 1,932 1,488 0,583 0,357 1,559 0,749 0,633 1,553 1,576 0,888 0,912 1,530 0,557 1,58 1,907 0,484 0,830 1,306 1,967 1,653 1,885 1,12 1,774 0,812 1,380 1,661 1,659 1,7 0,950 1,858 0,230 0,103 0,384 1,956 0,961 0,725 1,476 0,165 1,721 0,969 0,809 0,789 1,935 0,430 0,594 1,322 1,159 0,489 0,287 1,383 0,977 0,338 0,790 1,259 0,898 0,136 1,10 1,512 1,327 1,38 1,549 0,393 1,890 0,35 1,989 0,515 1,422 0,910 0,893 0,418 0,106 0,574 0,878 0,228 1,508 0,342 0,872 0,854 0,333 0,244 1,353 1,25 0,802 0,754 1,916 1,420 1,628 0,254 0,975 1,793 0,475 0,671 0,494 1,42 1,244 1,27 0,684 1,361 1,719 1,208 0,436 0,149 0,395 0,815 1,662 0,724 1,553 1,72 1,258 1,513 1,641 1,447 1,802 0,830 0,807 0,5 0,691 1,78 1,647 0,684 1,216 1,450 0,437 1,703 1,182 0,222 0,846 0,858 0,395 0,969 0,867 0,51 1,299 1,757 1,253 1,408 1,834 1,398 1,199 1,913 0,984 1,562 1,902 0,989 0,270 0,617 1,738 1,429 1,620 0,35 1,677 0,262 1,645 1,396 0,424 0,236 1,643 0,283 1,416 1,265 0,983 0,820 1,482 0,694 1,9 1,348 0,864 0,996 1,658 1,97 0,359 1,183 1,181 1,487 1,523 0,913 0,39 1,384 1,610 0,153 1,820 1,513 0,611 1,888 1,986 0,522 1,850 0,188 1,838 0,701 0,307 1,814 0,824 0,947 1,495 1,519 0,670 0,626 0,87 0,100 0,749 0,42 1,724 0,777 1,479 1,427 1,226 0,587 1,527 0,644 1,503 1,842 1,527 1,456 0,525 1,361 0,179 1,634 0,339 0,655 0,314 0,230 1,551 0,547 0,50 0,307 1,739 1,173 0,666 0,519 1,966 1,727 1,220 0,52 0,591 0,460 0,403 1,817 0,36 1,923 0,876 1,84 1,996 0,533 0,263 1,681 1,127 0,559 1,649 0,745 1,868 0,459 1,47 1,436 0,710 0,779 0,803 1,78 0,211 0,13 0,277 1,170 0,323 1,354 0,504 1,994 1,265 0,81 1,720 0,19 1,185 0,626 0,155 1,208 1,117 0,338 1,403 0,569 0,404 0,638 0,145 0,350 1,656 0,296 1,283 0,209 0,700 0,10 0,874 1,175 0,266 0,251 1,745 0,9 1,644 1,223 1,655 1,523 1,752 1,473 1,202 0,856 1,700 1,793 1,183 1,440 1,653 1,317 0,650 0,832 0,851 1,425 0,546 1,868 1,294 0,856 1,746 1,641 0,252 0,921 0,390 0,623 0,56 1,566 0,822 0,969 0,583 0,864 1,361 0,998 1,256 1,437 1,23 0,781 1,247 1,117 0,450 0,908 1,833 1,381 1,407 0,965 1,109 0,686 0,580 0,56 0,27 1,983 1,725 1,878 1,886 0,382 1,493 0,255 0,659 0,718 1,15 0,371 1,854 0,104 1,663 0,777 0,347 1,210 1,562 1,957 0,696 0,358 0,350 1,435 1,111 0,981 0,283 1,336 0,960 1,443 0,806 0,653 1,941 1,431 0,616 0,81 1,784 0,829 1,148 1,100 1,957 1,946 0,469 0,387 0,595 0,470 0,486 0,238 0,285 1,464 0,648 0,626 1,713 0,746 0,499 1,477 1,86 0,811 0,599 1,740 1,355 0,652 1,793 0,800 0,891 0,980 1,857 1,743 1,652 0,173 0,401 1,109 0,114 1,747 0,308 1,707 1,111 0,267 1,237 0,45 1,211 1,312 0,922 1,199 1,884 1,35 1,701 0,813 0,575 1,659 0,847 0,810 0,153 1,616 1,416 0,911 1,762 0,689 1,894 1,120 1,845 0,733 0,302 0,700 0,724 1,722 1,25 1,778 0,35 1,801 1,156 1,897 0,304 1,663 1,668 0,970 1,716 1,538 1,621 1,348 0,949 1,899 1,443 0,718 1,673 0,29 0,48 0,756 0,509 0,35 1,37 0,906 0,449 0,666 1,453 0,369 0,198 0,298 0,821 0,198 0,431 1,251 0,365 0,118 1,247 1,299 0,725 1,186 1,114 0,524 0,402 1,882 1,962 0,118 1,877 1,686 1,885 0,306 0,705 0,902 0,999 0,893 0,62 0,861 0,756 0,806 0,920 1,380 0,166 0,741 1,703 1,374 0,798 0,62 1,480 1,199 1,151 1,517 0,889 1,904 0,271 0,539 0,809 1,30 1,683 1,601 1,620 1,940 1,814 1,902 0,958 1,826 0,342 1,292 0,64 1,129 1,434 1,312 1,993 0,686 0,400 1,247 1,918 1,131 1,524 0,9 0,874 0,568 0,217 1,309 0,388 1,658 1,35 0,661 1,448 0,197 1,925 1,8 1,39 0,731 1,31 0,581 0,71 0,341 1,507 1,297 1,141 1,853 1,325 1,938 0,212 1,119 1,493 1,333 1,16 1,275 0,463 1,375 0,41 1,413 1,228 1,301 1,627 1,504 0,148 0,849 0,268 0,739 1,194 0,895 1,112 0,401 0,739 1,862 1,369 1,365 0,160 0,729 1,999 0,641 1,912 0,877 0,628 1,933 1,689 0,111 0,845 0,367 0,918 1,217 1,653 0,613 0,511 1,596 1,35 0,362 0,291 1,494 0,13 1,210 1,961 1,273 0,759 1,898 1,504 0,863 1,119 0,218 0,481 0,688 1,114 0,398 1,588 1,633 1,518 0,452 0,962 1,494 1,457 1,211 0,110 1,648 0,639 1,383 0,793 1,573 0,864 0,393 1,973 0,499 0,924 1,100 0,469 0,763 1,186 1,532 0,330 0,52 1,82 1,780 1,459 0,182 0,543 0,282 1,947 0,510 0,54 0,83 1,922 1,254 0,740 0,693 0,92 1,785 1,337 0,176 0,488 1,831 1,362 1,258 1,947 0,477 0,201 1,46 0,953 1,302 0,967 1,162 1,86 1,349 1,723 1,222 0,492 0,86 0,614 0,211 1,225 1,980 0,757 0,409 0,905 0,232 0,600 1,986 1,996 1,421 1,357 1,739 1,923 1,50 1,715 0,378 0,80 1,276 1,557 0,516 1,723 1,361 0,719 0,444 1,567 0,977 1,57 1,649 1,837 1,694 1,80 0,534 0,118 1,911 0,170 0,239 1,608 1,251 0,117 0,762 1,881 1,798 0,146 1,832 1,327 1,480 1,251 1,957 0,302 1,923 0,729 0,632 0,91 1,884 0,178 0,132 1,22 0,624 0,133 0,389 0,469 1,375 0,580 1,80 0,970 0,826 1,418 1,232 0,442 0,199 1,218 0,7 0,530 1,483 0,9 0,586 1,457 0,296 0,196 1,868 0,49 1,521 0,351 0,347 0,940 0,954 0,761 1,777 0,36 0,253 1,276 0,192 1,706 1,131 1,955 1,644 0,779 0,656 0,167 0,916 1,596 1,479 1,57 1,975 1,785 0,633 1,349 0,907 0,178 0,136 0,990 1,315 0,694 1,200 0,923 0,506 0,412 0,846 1,641 0,957 1,414 0,715 1,646 1,119 1,843 1,776 1,181 1,417 1,748 0,378 0,293 0,369 0,942 1,873 1,528 1,612 1,591 0,96 1,688 0,968 0,679 0,885 0,666 0,363 0,192 0,210 0,314 1,162 1,788 0,69 0,434 0,335 1,492 0,811 1,275 0,481 1,805 0,122 1,59 1,456 0,858 0,543 1,108 0,274 0,528 1,449 0,613 0,188 1,228 1,19 0,260 0,613 0,654 1,550 0,795 1,805 1,575 1,358 1,919 0,624 0,149 1,176 0,173 1,546 1,48 0,882 0,229 1,415 0,359 1,942 1,561 1,645 1,796 0,825 0,686 1,129 0,422 0,738 0,972 0,696 1,906 1,588 1,334 0,271 0,294 1,185 1,783 0,252 0,243 1,182 1,523 0,36 0,140 0,389 0,754 1,178 0,239 0,342 0,128 1,728 0,989 1,489 1,208 0,570 0,882 0,923 0,207 1,922 1,512 0,244 0,536 1,847 1,625 1,91 1,206 0,455 1,847 0,423 1,774 0,163 0,522 0,363 1,181 1,397 0,816 0,469 1,47 1,275 1,245 0,286 1,739 1,478 1,844 0,753 1,822 1,350 1,590 1,270 1,45 0,260 1,420 0,709 1,508 0,334 1,161 0,533 0,449 0,483 0,152 0,962 0,637 0,896 0,198 1,732 0,685 1,874 1,779 0,478 0,49 0,524 1,1 0,132 0,412 0,671 0,675 0,386 1,320 1,762 0,609 1,305 1,182 1,5 0,248 1,162 1,876 1,983 1,689 1,727 1,817 0,737 0,317 1,205 1,400 1,962 1,47 1,483 0,883 1,293 0,417 1,154 1,626 1,532 1,417 0,255 1,376 0,630 0,152 1,557 1,608 0,807 1,762 0,511 1,115 1,374 0,836 1,355 0,198 0,651 0,754 1,664 0,910 1,529 0,443 1,885 1,152 1,973 0,298 1,749 1,730 0,429 1,915 0,451 1,361 0,7 1,153 0,701 1,355 1,990 0,359 0,926 0,163 0,128 0,789 1,490 1,880 0,304 0,795 0,215 0,156 1,226 0,865 0,885 1,880 1,354 1,835 1,627 0,324 0,171 0,896 0,832 0,947 0,258 0,129 0,768 1,426 0,233 1,534 0,453 1,130 0,762 0,572 1,556 0,13 1,566 1,555 0,149 1,551 1,241 1,392 0,732 0,478 0,212 1,860 1,131 1,386 1,797 0,710 1,799 0,41 1,713 1,602 0,606 1,129 0,873 0,758 1,240 0,646 1,330 0,814 1,668 1,459 1,854 0,411 1,306 0,579 0,162 0,706 0,388 1,619 0,537 0,936 0,860 0,872 0,454 0,645 0,279 1,767 1,871 0,610 1,3 0,665 0,359 0,521 0,70 1,374 0,589 1,564 0,26 0,323 1,203 1,246 1,148 0,713 0,478 0,421 0,536 0,881 0,905 1,814 0,114 0,111 0,501 1,71 1,520 1,738 0,807 1,421 0,532 1,918 0,182 0,171 1,174 1,744 1,542 1,145 0,903 0,136 1,966 0,164 0,851 0,335 0,776 1,217 1,504 1,624 1,25 0,137 0,291 1,979 1,985 0,657 0,406 1,948 0,386 0,377 0,469 1,871 1,486 0,863 1,480 0,918 0,973 0,213 0,386 1,703 1,327 0,817 1,103 1,635 0,533 1,459 1,727 0,511 1,140 1,3 0,352 1,998 0,887 1,125 1,598 0,854 0,406 0,101 0,470 0,668 0,749 1,422 1,311 1,934 1,405 0,943 1,293 0,307 0,650 1,660 0,492 1,342 1,725 0,269 1,462 0,738 1,447 1,651 0,145 0,869 0,6 0,108 1,298 1,791 1,764 1,364 1,577 0,180 1,529 0,453 0,687 0,883 0,815 1,76 1,213 0,22 1,790 0,913 0,393 0,66 1,570 1,774 0,586 0,415 1,561 1,442 1,316 0,402 1,243 0,159 1,45 0,410 1,41 0,885 1,233 0,151 0,366 1,859 0,959 1,249 1,573 1,358 0,294 0,944 1,447 1,611 1,632 1,884 0,797 1,55 1,528 0,116 1,893 1,391 0,357 0,388 0,579 1,973 1,939 0,461 1,735 1,640 1,104 1,341 1,550 0,421 0,918 0,455 0,304 1,729 0,660 0,675 0,810 0,411 1,446 1,735 1,566 0,323 1,969 0,732 1,673 0,283 1,580 1,983 0,880 0,691 0,780 0,652 0,688 0,557 1,815 1,170 1,787 0,441 1,104 0,487 0,70 1,499 0,925 1,963 1,328 1,246 0,744 0,811 1,539 1,598 1,244 0,446 1,802 0,803 1,323 1,450 0,868 0,871 1,397 1,36 0,882 1,227 1,658 0,375 0,24 1,322 0,673 0,391 1,100 0,105 1,903 1,635 1,641 0,657 1,193 0,287 1,52 0,197 0,483 0,304 0,58 0,847 1,555 1,956 1,539 0,120 1,527 1,148 0,855 1,211 0,903 1,323 0,710 0,840 1,604 0,973 1,532 1,527 1,686 0,567 0,869 1,884 1,556 1,987 1,962 1,240 1,815 1,266 1,698 1,195 1,335 0,17 1,817 0,691 1,108 0,238 1,938 0,77 1,898 1,629 1,253 0,937 1,412 1,779 1,343 1,877 1,720 0,302 1,977 0,461 0,706 0,149 1,571 0,51 0,838 0,822 1,29 0,742 0,383 0,368 1,476 0,161 1,29 0,697 0,812 0,4 1,949 0,610 0,257 0,178 1,808 1,970 1,555 0,319 1,874 1,943 1,593 1,448 0,167 1,260 0,940 0,734 1,539 1,26 1,997 0,112 0,630 0,726 0,34 0,175 1,514 1,896 1,474 0,825 0,40 0,944 0,568 1,379 0,606 0,551 0,927 1,222 0,547 0,925 0,38 1,108 1,819 0,130 1,864 1,973 1,176 0,479 1,148 0,776 1,128 0,805 0,150 0,997 1,801 1,536 0,295 1,571 0,211 0,174 0,980 0,536 1,4 0,154 0,943 1,986 0,430 0,56 1,222 0,208 0,934 1,195 1,469 0,387 0,813 0,168 1,306 1,914 0,37 1,738 0,243 0,132 1,586 0,336 0,253 1,405 1,296 0,769 1,100 0,947 1,693 0,577 1,891 1,197 1,801 1,114 0,426 0,557 0,263 0,364 0,861 1,612 1,295 0,678 0,518 0,41 1,9 1,652 1,98 1,362 0,466 1,767 0,136 0,271 1,582 1,962 0,174 0,445 1,623 0,419 0,512 0,849 1,924 1,949 0,387 0,872 1,287 0,453 1,240 1,492 0,281 1,438 1,977 1,665 1,507 0,473 1,573 1,479 1,363 1,502 1,490 1,747 1,771 1,555 1,984 0,35 0,746 0,450 1,823 1,590 0,149 0,961 0,898 0,139 1,980 0,827 1,14 1,762 1,404 1,371 0,877 0,190 1,156 0,361 0,137 0,996 0,371 0,576 0,614 1,792 1,827 0,640 1,566 1,567 1,407 1,730 0,116 1,811 1,842 0,489 0,265 1,934 0,718 1,518 0,190 1,143 0,764 1,276 0,313 0,899 1,480 1,912 0,463 1,966 1,101 1,238 1,879 0,939 1,132 0,17 1,44 0,645 1,478 0,697 1,799 0,561 0,509 0,832 1,588 0,871 1,635 1,686 1,350 0,243 1,30 0,299 1,819 0,112 0,858 1,582 0,533 1,186 0,330 0,305 0,279 0,614 0,347 0,957 1,16 0,757 0,378 0,468 0,7 1,739 0,749 1,999 1,349 0,684 0,867 0,949 1,279 0,219 1,939 1,653 0,601 0,140 1,361 0,846 0,574 1,317 0,146 1,646 1,686 0,757 1,21 1,36 0,142 0,305 0,503 0,433 0,35 0,557 1,703 0,586 1,509 1,749 0,466 1,689 0,917 0,947 1,645 1,619 0,439 0,893 0,304 0,830 1,806 1,568 0,95 0,988 1,636 0,646 0,719 1,969 0,954 1,474 0,997 0,804 0,914 0,144 0,115 1,310 1,102 0,994 1,700 1,863 0,234 1,612 1,526 1,661 1,978 1,110 0,52 1,82 0,270 1,832 0,941 1,458 1,75 0,764 1,219 1,806 0,793 0,713 1,67 0,120 1,35 1,752 0,146 0,900 1,344 1,885 1,400 0,948 0,990 1,54 1,648 1,265 1,687 0,295 1,472 0,87 0,624 1,742 0,842 0,874 1,793 1,197 0,677 1,325 1,230 0,623 1,737 0,38 1,196 1,524 0,204 1,82 0,263 1,909 0,892 1,923 0,485 1,95 0,580 0,750 1,183 1,530 1,945 0,886 1,385 0,928 0,565 1,153 0,48 0,191 0,411 1,751 1,919 1,548 0,931 0,725 1,986 1,687 1,443 1,101 1,503 1,918 1,855 1,513 1,857 1,640 0,327 0,873 1,931 1,380 0,98 0,330 1,158 0,222 0,940 1,680 0,133 1,213 1,882 0,414 1,52 1,649 0,681 1,42 0,0 0,536 0,566 0,613 0,838 1,218 0,910 0,254 0,568 1,958 0,191 1,957 0,448 1,925 0,78 0,263 0,867 1,603 0,448 0,824 1,566 0,496 0,757 0,248 0,181 0,215 0,971 1,554 1,449 1,704 1,559 1,315 1,576 1,734 1,63 0,489 0,177 0,858 1,170 0,512 1,790 0,561 0,195 0,767 0,367 1,234 0,785 0,625 1,905 1,754 0,855 1,529 0,288 0,498 1,506 1,388 1,807 1,541 0,691 0,413 1,664 1,247 1,548 1,865 1,307 0,457 1,161 0,631 0,531 0,636 0,715 1,275 1,279 1,425 0,666 0,588 1,634 1,609 0,37 0,522 0,25 1,252 0,597 1,813 1,442 0,266 0,682 0,13 0,232 1,304 1,703 1,55 1,928 0,13 0,793 0,795 1,806 0,237 0,559 1,187 1,64 0,898 1,959 1,110 1,206 1,256 1,916 1,543 0,746 1,214 0,717 0,150 1,840 1,3 1,996 1,79 1,774 0,105 0,666 1,59 0,555 0,941 1,231 1,405 0,84 1,509 0,79 1,937 1,262 0,413 1,787 0,462 0,321 0,686 1,499 1,925 1,447 1,168 1,916 0,304 0,961 1,329 0,899 1,414 0,504 0,430 1,220 0,149 1,851 1,411 1,353 0,189 0,824 1,434 0,838 0,707 1,214 0,801 0,399 1,73 0,709 1,4 1,265 1,236 1,321 1,756 1,611 1,273 0,937 1,739 0,792 1,453 0,515 0,349 1,847 0,169 0,624 1,42 0,150 0,589 1,572 1,821 1,993 0,936 1,418 1,981 0,690 1,312 0,350 0,640 1,706 0,46 1,160 0,34 1,348 1,331 0,382 0,410 1,390 1,476 1,818 0,155 1,680 0,727 1,227 1,999 1,535 1,512 1,813 1,31 1,165 1,568 0,636 1,877 0,759 0,459 0,646 1,44 1,153 0,128 1,765 0,4 0,958 0,221 1,391 0,368 1,353 0,132 0,192 1,963 1,569 1,510 0,340 0,186 0,380 1,220 1,90 0,672 1,752 1,367 0,923 1,169 0,761 1,606 0,250 1,9 1,983 0,851 0,387 0,418 1,670 0,443 0,539 1,224 1,531 0,76 0,640 0,988 0,256 1,119 0,100 1,333 0,122 1,551 1,810 0,926 0,786 1,482 0,687 1,276 0,265 0,913 1,42 0,613 1,795 1,491 0,871 1,495 1,501 0,631 1,553 0,525 0,5 1,156 1,426 1,119 0,739 0,718 1,249 0,441 1,936 1,697 1,308 1,378 0,310 0,633 0,183 0,838 0,152 1,83 1,174 1,69 1,928 0,509 1,242 0,690 1,735 0,655 1,640 1,71 0,532 1,280 1,433 0,937 0,824 0,659 0,94 0,674 0,895 0,251 0,520 1,484 1,860 0,24 0,588 1,565 0,264 0,331 1,124 0,373 0,74 0,755 1,195 0,644 0,453 0,867 0,179 0,517 1,300 0,813 0,958 0,963 1,919 0,157 0,296 1,12 1,163 1,130 1,192 0,287 0,603 1,339 0,201 1,903 0,189 1,15 0,179 0,799 1,872 1,734 1,193 0,336 0,285 1,968 1,503 0,498 0,385 1,906 1,65 0,257 1,600 1,223 1,784 1,272 1,864 1,675 0,193 1,132 0,604 0,485 1,276 0,400 0,771 1,966 1,706 1,83 1,604 0,778 1,596 1,664 0,111 1,408 0,230 1,214 0,731 1,749 0,23 1,330 1,17 0,19 0,28 1,924 0,486 0,326 0,202 1,189 1,879 0,46 0,762 1,727 0,728 1,518 0,189 1,197 1,753 1,690 1,997 0,328 1,179 0,949 0,164 1,162 0,718 1,271 1,221 1,444 0,271 0,89 0,563 1,341 1,44 0,391 0,306 1,187 0,78 1,109 0,803 0,55 0,939 0,703 1,717 1,464 0,576 1,720 1,519 0,172 0,471 0,987 0,582 0,832 0,431 1,371 0,331 1,137 0,881 1,799 1,989 0,475 1,227 1,730 1,554 0,366 0,895 0,697 0,419 0,800 0,115 1,843 1,492 1,198 1,819 1,882 1,81 0,105 0,676 1,928 1,733 0,293 0,312 0,352 0,689 0,241 0,958 1,211 0,223 1,551 1,788 1,998 1,620 0,579 1,781 1,871 1,883 0,111 0,878 0,707 1,981 0,941 0,766 0,987 0,160 0,387 1,628 1,239 1,441 0,134 0,522 0,973 1,96 0,120 0,889 1,572 1,462 1,68 0,314 0,70 1,992 1,688 0,594 0,525 1,968 1,663 0,734 1,685 1,135 0,922 0,193 1,255 0,131 1,2 0,49 0,709 0,735 0,478 0,538 1,401 0,164 0,814 1,837 0,138 1,57 1,679 1,421 1,984 0,876 0,937 0,123 1,465 0,960 0,37 0,673 1,702 1,187 1,467 0,952 1,482 0,966 0,585 0,433 0,48 1,628 1,65 0,358 0,635 0,454 0,976 0,127 1,276 1,143 0,928 0,271 1,182 0,622 0,662 0,171 1,51 0,657 0,511 0,600 0,329 0,618 0,95 1,970 0,216 1,559 1,502 1,595 1,685 1,1 1,596 1,606 1,910 0,631 0,543 0,548 1,785 1,380 0,837 0,396 1,544 1,315 1,367 0,317 0,952 0,752 0,93 1,910 1,705 1,631 1,928 0,69 0,749 1,415 0,272 1,615 0,530 0,68 1,276 1,974 1,495 1,200 1,970 1,889 1,695 0,483 1,963 1,812 0,732 0,564 1,922 0,164 1,850 1,975 0,586 0,938 1,750 0,141 0,588 0,299 0,265 0,630 1,144 0,460 1,584 0,889 1,954 0,208 1,735 1,216 1,805 0,287 0,108 0,343 0,168 0,897 1,299 1,14 1,707 1,624 1,206 1,159 1,168 0,26 0,715 0,85 1,660 1,39 1,365 0,382 0,851 1,391 1,49 0,862 0,266 0,669 1,809 1,848 0,967 1,320 1,246 1,671 0,120 0,542 1,928 0,980 0,597 0,390 0,482 0,421 1,431 0,835 1,213 0,998 0,999 1,943 1,807 0,856 1,605 1,363 1,495 0,7 1,727 0,382 1,657 0,953 0,423 0,300 0,812 1,734 1,667 0,48 0,973 1,749 1,916 1,599 0,10 1,29 0,931 1,29 1,926 1,449 1,981 0,977 1,518 1,385 0,942 0,381 0,225 0,944 0,807 0,459 0,764 0,209 0,94 1,981 0,482 1,830 0,588 0,272 0,307 0,582 1,835 1,333 1,639 0,888 1,82 0,232 1,950 0,243 1,126 0,772 1,729 0,354 1,489 0,743 1,127 1,163 0,277 1,236 1,819 0,177 0,484 1,359 0,102 0,203 1,117 0,418 0,602 1,960 1,992 0,680 1,950 1,838 0,412 0,272 0,654 1,40 1,270 1,243 1,369 1,944 1,573 0,480 1,533 1,911 0,989 1,690 0,499 1,628 0,265 1,536 1,43 1,261 1,169 1,707 1,818 1,37 1,793 0,416 1,175 0,381 1,855 0,256 1,72 1,831 1,828 0,724 1,874 1,743 0,713 0,95 0,864 0,707 0,490 1,625 1,938 1,755 0,10 1,715 1,82 1,600 1,224 1,95 0,605 1,216 1,211 1,861 0,274 1,383 0,957 1,735 0,994 0,878 1,115 1,13 0,401 0,997 1,742 0,226 0,211 0,223 1,725 0,120 1,719 1,964 1,104 1,494 1,540 0,450 1,225 0,279 1,591 0,422 1,701 1,807 1,90 0,981 1,590 0,657 0,737 1,942 1,562 1,461 1,789 1,45 1,480 1,909 0,180 1,301 1,891 1,829 1,693 1,436 1,181 0,711 0,927 1,188 1,479 1,238 1,64 0,808 1,356 0,628 1,85 0,987 1,316 1,199 0,496 0,572 0,442 1,295 0,847 1,982 0,920 0,450 0,441 1,819 0,723 1,821 0,934 0,571 1,663 0,404 1,447 1,231 1,80 0,253 0,503 0,509 0,819 0,89 1,659 0,470 1,280 1,505 0,662 0,474 1,361 1,211 1,51 0,405 0,228 0,732 0,508 0,285 1,398 1,701 0,766 0,728 1,385 1,999 1,956 1,242 0,352 0,256 0,917 1,51 1,395 1,300 0,210 0,33 1,666 0,329 0,401 0,451 1,486 0,828 1,926 0,317 1,8 1,256 0,114 1,888 0,325 1,584 1,228 1,554 0,666 0,477 0,995 0,675 1,938 0,618 0,89 0,637 0,67 0,825 0,703 1,109 0,498 0,24 0,341 1,817 1,349 0,767 0,306 1,596 1,534 0,321 0,714 1,904 0,661 0,538 0,563 0,562 1,582 0,353 0,863 1,35 1,579 1,949 0,196 0,89 0,46 1,665 1,243 0,467 1,20 1,569 1,60 0,400 1,510 1,625 0,753 1,709 1,884 0,73 0,914 1,46 1,173 0,810 1,191 1,93 0,491 0,577 0,903 1,33 1,343 1,505 1,785 1,89 1,893 1,111 1,905 0,880 0,242 0,982 0,982 1,997 1,779 1,114 1,334 0,879 0,887 0,671 0,549 1,557 0,626 1,6 0,519 0,124 1,478 0,441 0,93 0,214 1,317 1,287 1,103 1,733 1,529 0,185 1,965 0,75 1,314 1,145 0,123 1,855 1,248 0,14 0,717 1,743 0,802 0,548 0,621 1,837 1,121 0,228 1,430 0,18 0,482 1,525 1,887 1,278 0,373 1,279 1,601 0,258 1,531 0,662 1,689 0,592 0,412 0,915 1,349 1,289 1,631 1,964 1,138 0,966 0,676 1,499 1,211 1,376 1,430 0,721 0,18 0,918 0,117 0,332 1,861 1,410 1,950 1,69 1,147 0,458 1,668 0,182 1,607 0,609 1,681 0,117 1,119 1,422 1,630 1,389 0,540 0,536 0,789 0,318 1,153 0,305 0,358 0,85 1,481 0,593 0,563 1,360 1,93 0,597 1,281 0,649 0,924 1,9 1,839 1,407 1,263 0,210 0,658 1,620 1,987 0,673 1,0 0,762 0,661 1,119 1,119 0,273 1,97 0,779 1,649 1,913 0,766 1,853 1,117 1,77 0,314 1,886 0,580 0,781 1,943 1,567 0,840 0,233 0,3 0,556 0,652 0,83 0,233 0,747 1,143 0,629 0,806 1,33 0,944 1,902 0,864 1,37 0,31 1,510 0,842 1,36 1,704 0,243 1,377 0,299 1,588 0,778 0,728 1,949 1,51 1,825 1,581 0,965 0,332 0,753 1,693 0,136 0,151 1,837 0,324 0,16 0,598 1,593 0,936 0,905 0,226 0,86 0,129 0,909 0,58 0,764 1,306 1,333 0,471 1,620 1,621 0,467 0,118 0,75 0,686 0,792 1,785 0,500 1,392 1,918 0,546 1,905 0,136 0,160 0,327 0,139 0,399 1,834 0,529 1,438 0,525 0,791 0,94 1,102 1,856 0,403 0,110 0,113 0,663 0,296 0,125 1,768 0,498 1,289 0,835 1,661 0,644 0,435 0,446 0,351 0,337 0,231 0,222 0,825 0,565 1,936 1,86 0,422 1,879 0,688 0,489 0,328 1,78 0,883 0,113 1,557 0,643 1,180 0,374 1,605 1,514 0,303 1,937 1,738 0,62 0,979 1,870 1,281 0,449 1,440 0,622 0,78 1,34 0,276 1,732 1,535 1,372 1,579 0,585 0,342 1,65 1,651 1,247 1,475 0,457 1,570 1,769 1,214 1,607 0,32 1,907 1,905 0,392 1,232 1,74 1,646 0,404 0,518 1,788 0,417 0,734 1,194 1,222 1,472 0,517 1,989 1,101 1,150 1,90 1,928 0,89 0,343 1,939 1,602 0,70 1,63 1,447 1,977 1,317 0,280 0,728 1,770 1,95 0,529 0,658 1,372 0,327 1,773 0,251 1,642 1,767 1,539 1,221 0,808 0,936 1,702 1,221 1,417 1,737 1,374 0,188 1,129 0,136 0,135 0,124 1,143 1,519 1,621 1,32 1,411 0,760 0,5 0,135 1,664 1,400 1,571 1,153 0,817 1,886 1,418 0,569 1,946 1,985 0,572 1,145 0,790 0,918 0,698 0,17 0,997 0,991 1,361 1,243 0,454 1,738 0,188 1,514 1,272 1,350 1,378 0,875 1,559 1,518 0,913 0,964 0,316 1,534 1,42 0,570 1,697 0,264 1,370 1,195 0,42 1,815 1,132 0,486 0,154 1,802 1,761 0,469 0,920 1,635 0,683 0,762 1,748 0,431 0,370 0,796 0,253 0,85 1,259 0,109 1,314 0,845 1,789 1,741 1,38 1,873 1,150 0,808 0,600 1,36 1,785 1,785 1,206 1,133 1,148 0,836 0,671 0,732 0,5 1,305 1,386 0,210 0,76 0,562 1,133 1,865 0,752 1,203 1,23 1,970 1,893 0,123 0,311 0,895 0,716 1,970 1,283 1,812 1,36 0,364 1,955 1,508 0,227 0,553 0,914 0,756 0,475 0,993 1,294 0,339 0,638 1,265 1,911 0,929 0,923 0,195 1,565 0,558 1,463 0,340 1,149 1,508 1,278 0,698 0,857 0,324 0,924 0,24 0,249 1,64 0,775 1,675 0,896 0,215 0,219 1,257 1,149 1,250 0,271 1,575 1,579 0,160 1,944 1,165 0,550 1,107 0,525 0,178 0,704 0,450 1,462 0,674 1,667 0,620 0,33 1,539 0,289 0,262 0,777 0,737 1,202 1,669 0,82 0,533 1,68 1,851 0,649 0,891 0,454 1,626 0,814 0,647 1,603 1,393 0,874 1,255 0,660 0,170 1,5 0,557 1,752 0,700 0,278 0,134 0,745 1,420 1,280 0,32 1,234 0,628 0,428 1,626 1,674 1,280 1,836 0,756 0,929 1,886 1,653 1,908 1,903 1,186 0,340 0,666 1,599 1,779 0,640 0,202 0,230 1,726 0,193 0,401 0,793 1,0 1,608 0,917 0,881 1,784 1,402 1,868 0,658 0,384 0,965 0,920 1,862 1,579 1,824 0,166 0,147 1,391 1,199 0,376 0,121 0,653 0,484 1,241 0,284 1,203 0,599 0,852 1,86 1,683 1,458 1,555 0,313 0,38 1,102 0,252 0,204 0,221 0,684 0,670 1,137 0,794 1,381 1,196 0,516 1,803 0,430 1,272 0,981 0,343 0,398 0,881 1,650 0,143 1,63 0,359 1,435 1,533 0,770 0,532 0,759 1,885 0,488 0,705 0,943 1,630 0,283 1,505 1,218 1,560 1,312 1,102 1,480 0,374 1,967 1,379 1,186 0,123 0,459 0,160 1,495 0,523 1,566 0,180 0,952 1,732 0,584 0,746 0,349 0,115 1,474 0,901 0,106 1,955 1,545 1,816 0,635 0,763 0,413 0,66 0,80 0,895 0,996 0,774 0,611 1,420 1,733 1,703 0,164 0,859 1,13 1,614 1,617 0,308 1,624 1,67 1,287 1,901 0,300 1,470 0,766 0,562 0,60 1,819 0,939 1,52 0,744 0,421 0,877 0,248 1,299 1,586 0,815 0,842 1,303 0,139 0,478 0,368 1,34 0,904 1,246 1,221 1,313 0,214 0,585 1,401 1,528 1,830 1,818 0,277 0,874 0,584 0,900 0,843 0,25 1,149 1,908 1,483 1,393 0,16 0,48 1,606 0,552 1,4 1,628 1,405 1,906 1,638 0,811 0,908 0,645 0,565 0,775 0,466 1,861 0,523 0,585 1,628 0,974 0,408 1,438 0,134 1,826 0,402 0,487 0,521 1,660 1,372 0,422 1,328 1,897 0,930 0,987 0,318 0,715 1,776 1,294 1,673 0,468 0,325 1,617 0,193 0,275 0,188 1,834 1,299 0,733 1,357 0,500 1,171 0,937 1,205 1,449 1,272 0,801 1,360 1,850 1,93 0,465 1,344 1,708 0,697 1,958 1,986 0,210 1,779 1,186 1,640 1,172 1,201 1,57 1,730 0,508 1,167 1,415 0,275 0,263 0,100 0,324 1,981 1,610 1,562 0,423 1,484 0,304 1,154 1,828 1,912 0,431 1,562 0,749 1,728 1,70 0,634 1,525 1,450 0,203 0,810 1,816 0,768 0,774 1,466 0,501 1,623 1,508 1,287 1,80 1,138 0,749 1,327 1,712 1,167 1,188 1,871 0,373 0,892 0,945 1,166 1,147 0,714 0,515 0,871 0,519 1,492 1,353 1,564 1,366 0,685 1,234 1,612 0,201 0,74 0,646 0,549 1,736 0,688 0,907 1,916 0,472 0,351 1,992 1,310 0,846 1,788 0,539 1,869 1,464 1,397 0,599 1,906 1,526 1,600 1,264 0,300 1,37 0,768 1,678 1,992 1,963 0,312 1,833 0,466 1,546 1,343 1,399 1,104 1,728 1,66 0,744 0,19 0,947 0,261 0,324 0,528 0,284 0,413 1,720 1,620 0,310 1,685 1,173 1,494 1,494 0,248 1,808 0,126 0,875 0,990 1,132 0,57 1,827 0,135 1,316 0,840 1,312 1,922 1,102 1,495 1,434 1,616 0,208 0,118 1,987 0,111 1,0 0,489 0,861 1,421 0,655 1,82 0,606 0,738 1,637 0,539 0,520 0,514 1,119 0,918 1,566 1,851 1,479 0,484 0,467 1,574 1,34 1,629 1,692 0,241 0,342 1,599 0,449 1,481 0,719 0,827 1,699 0,860 1,129 0,115 0,890 1,855 1,220 1,787 0,919 0,813 0,597 1,79 0,350 0,561 0,915 1,283 0,646 0,4 1,90 1,800 0,729 0,889 1,773 0,735 0,477 0,758 0,883 0,462 0,176 0,947 1,666 0,291 0,466 1,215 0,194 0,27 1,86 1,128 0,184 0,681 0,434 1,351 0,633 1,567 1,630 0,863 1,732 0,277 0,651 1,401 0,316 0,379 1,737 1,50 0,623 1,429 0,706 0,124 0,923 0,559 0,598 1,473 1,245 0,411 0,366 1,156 1,193 0,230 1,161 1,333 1,536 0,235 1,303 1,627 0,433 0,514 1,132 0,954 0,37 1,51 0,57 0,870 0,159 0,408 1,581 0,912 0,499 0,545 0,489 0,705 0,560 0,949 0,313 1,362 1,922 1,309 0,213 1,863 0,163 0,87 1,541 0,336 0,718 0,856 1,225 1,222 0,266 0,475 0,228 1,115 0,309 1,759 1,702 0,42 0,641 0,308 1,624 1,94 0,205 1,526 0,189 1,332 1,718 0,886 1,123 0,359 1,578 0,755 1,425 1,63 0,697 0,15 1,830 1,266 0,70 0,709 1,369 1,826 1,584 0,661 1,486 1,714 0,759 1,34 1,982 0,818 1,110 0,769 1,203 1,966 0,544 1,844 1,716 1,151 1,194 1,50 1,85 1,18 1,915 1,614 1,209 1,208 1,89 1,238 1,402 1,727 0,125 1,711 0,328 1,295 0,580 1,621 0,37 0,239 0,541 1,984 1,827 0,658 1,503 1,35 1,179 0,581 0,151 1,258 1,777 0,75 1,663 0,674 0,269 0,542 1,456 1,199 1,688 1,726 1,390 1,421 0,497 1,765 1,353 0,876 1,482 1,451 0,50 1,58 0,262 0,713 1,294 0,949 0,30 1,538 0,597 1,680 1,742 1,706 0,387 1,203 0,830 1,27 1,371 1,213 1,141 0,903 1,508 1,382 1,699 0,907 0,149 0,578 0,877 0,482 1,205 0,911 0,241 1,235 1,771 1,947 1,885 0,579 0,498 0,797 0,402 0,978 0,89 1,511 1,686 1,178 1,398 1,497 1,237 1,891 0,684 1,272 0,29 1,725 1,13 1,548 0,343 1,549 0,553 0,159 0,64 1,225 1,323 0,971 0,146 1,736 0,857 1,154 1,578 0,210 0,789 1,400 1,788 0,91 1,795 0,76 1,640 1,647 1,207 1,893 \ No newline at end of file diff --git a/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt b/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt deleted file mode 100644 index 3c72fcd4772..00000000000 --- a/module/mempool/herocache/backdata/heropool/pool_test_random_numbers.txt +++ /dev/null @@ -1 +0,0 @@ -6440 83916 58118 73171 96210 99187 60941 77067 67540 74483 56385 44103 14249 3621 77468 45805 28846 61704 3778 19999 25605 8390 11903 9676 32848 68796 57758 83283 31574 46562 34396 31813 92079 96868 24560 87814 25060 12651 50776 87012 27963 1745 58558 54954 95637 99434 55304 79323 10470 28553 73657 42508 48983 74191 44279 39591 56340 6440 4778 47539 42540 42284 31888 52594 11824 8082 21597 54920 29116 47633 54038 40946 92453 86127 56273 56891 83591 61077 24009 11729 23255 95003 2789 21457 55551 19552 77911 8325 74174 13760 24497 82273 89291 44652 14389 56653 35818 72010 91380 21357 83166 82859 60805 14278 32750 88190 63779 53622 69542 14122 70437 24773 86451 49744 45499 85293 75373 69486 67918 65242 45166 54186 95268 14784 71578 52834 62186 43042 39846 97586 45617 63213 20449 30401 32361 76282 65566 60707 14565 62902 54598 65662 70891 52140 93979 87216 42018 73799 35034 33819 1520 31488 91662 79229 7081 57597 67181 2473 83114 5646 4259 19753 24663 81828 24840 6313 3492 14951 42097 94157 94149 37423 60870 87639 70811 13968 47064 35648 75660 91983 43519 96851 14094 13547 13834 22641 84026 39133 71339 46668 74769 99837 20088 51680 12703 3606 79247 48141 25866 50896 65782 93643 89466 60922 37064 93651 71066 80510 86518 57426 18394 89021 43484 62969 29900 37400 39432 4403 20027 73257 16824 61671 45451 2966 89596 73449 8666 37 23389 3344 49166 53040 36659 67307 63454 98697 49317 12485 2800 50065 65790 77931 86074 94824 78322 30360 35949 99486 31426 77939 190 25916 44944 46986 54647 88757 55345 86082 24390 72037 99274 96070 82434 5515 19979 88921 68761 65287 87715 45426 83849 4975 24879 77139 52264 56390 70927 35904 84032 13845 70349 77503 8877 45180 13253 86686 70111 96651 69945 59221 64934 48052 3621 33863 54453 50934 14813 89542 29898 43422 2127 81714 58140 19864 50188 85271 24253 20793 77881 21356 99240 32912 98613 52479 12446 44377 61909 78225 25018 8680 26284 1616 35711 84629 95162 54614 77694 36891 79350 10264 38028 53351 24787 21204 13041 81571 68522 71213 85404 8023 78695 52572 61231 58019 21533 303 59429 77751 15474 92049 38369 18915 73823 33249 7855 2394 95536 78452 62965 36083 8405 56170 10168 24944 84620 29188 99653 71016 70233 80211 30540 74749 99237 71708 63088 54280 65813 2495 46887 38657 68164 18689 75583 75606 95986 54812 58662 93270 30086 54922 27677 16421 75866 40602 71611 6548 1070 12037 15461 1831 64545 33752 84404 80581 51225 8082 66991 88093 68584 95676 18148 26331 72491 59526 75357 44590 17122 63198 16251 43218 22122 30409 22754 62929 1454 14020 3599 79898 41691 3827 2778 60130 38760 25662 45536 55642 48636 40774 86100 16011 39499 7 40064 81489 22572 42166 11104 72938 69346 19889 11742 27837 86516 65283 5416 60009 88058 81395 97462 89748 80343 27268 72266 97218 34931 93399 53839 72554 30067 6368 16237 28670 49647 30544 97692 4848 88869 26742 36970 99626 18109 58172 48142 79285 49591 17985 58320 98657 20128 57543 16102 21195 41689 5364 53310 24363 78562 46868 81696 19519 90672 80289 52645 19126 9317 60411 40726 20192 16933 58872 77776 95023 43920 44025 74520 38462 98693 32813 87652 56967 81842 47430 46903 68051 34824 56988 11603 92116 36309 94350 95550 49186 95163 85216 92317 28730 67597 1593 72666 5882 83151 99433 18358 52340 35580 98427 16613 38935 70751 88525 15282 16582 86045 37855 27453 84735 58818 83821 78912 38030 22026 53829 94630 44402 89088 12859 75457 16155 68199 72477 90075 76914 54512 94442 25545 24811 8096 60266 4282 15761 27882 70041 3752 24049 44553 77307 55245 619 14450 65477 80975 6726 84342 5102 61825 59959 73851 74207 50621 6018 38041 63492 75227 65861 52937 11378 27958 35833 81840 42993 44385 97967 23563 55988 29640 45123 32503 94270 44406 73678 81372 92942 37854 67216 96971 77601 69600 49812 27137 7440 4480 95814 4879 82515 39454 41743 87859 19016 77353 8084 67885 11145 84378 71952 87401 50013 58720 91051 72378 24372 57450 58876 38091 23468 14365 2406 20512 69643 97320 7646 83813 90717 10866 370 22861 80644 4494 30504 65184 49310 80984 27286 9913 2494 5024 13521 28214 21615 49029 75797 90573 41210 68920 1739 3760 12852 87607 14965 39593 19911 84655 71803 79122 34 38734 96928 76689 25951 12645 92890 24551 72433 76832 54948 32140 46493 47020 45055 54853 9441 71404 35202 20553 19631 53630 9606 11963 93009 83292 13907 22683 31198 21534 87116 97324 54435 46877 66934 14196 99679 96180 85393 42583 42530 86662 28464 11157 96901 56613 90640 19307 78780 99003 81804 89377 64213 93446 52291 86863 59761 95234 47302 3194 60128 31971 99528 29457 51112 65826 66334 94239 27210 31492 99635 23669 24803 27362 73921 60468 19577 45049 6537 25833 94164 11593 11094 29635 19818 14186 1597 24183 4096 89192 41192 30671 19580 36009 18039 35314 12787 62712 17838 79609 82220 2915 34594 31071 25946 56826 31692 43797 49012 97957 74797 97327 37381 52465 74452 22373 57064 62516 9730 99533 54217 79943 21228 9766 53974 3801 60098 1905 652 25926 54715 20986 6073 11113 44278 46407 20621 86787 68509 27832 30692 53866 34371 47746 54606 65900 28469 99815 31654 71174 55744 89400 73665 44508 7687 19095 94171 55091 82604 62474 80183 69527 51531 18341 61096 19576 190 47606 85449 54235 36320 9161 8732 93492 77451 56029 72637 79722 70600 94940 23155 28780 79295 27639 53139 16354 69189 12973 34104 99383 15312 33871 7595 86186 16341 22627 41381 55803 74012 76717 51484 5946 24152 42790 61906 42005 89574 28 97942 74726 19293 85356 51130 25938 57933 23458 46000 55232 27542 9708 8490 31938 78611 93308 2994 99277 73686 59882 1177 85101 35712 37102 79462 48501 40649 4022 56223 70625 8834 78613 61507 27609 90216 26987 77242 33995 29281 51708 94578 47234 49507 98500 11669 10517 83720 82446 5797 79635 70562 92452 50003 18962 84394 93788 42130 31768 61430 64566 63801 38658 80192 50255 42087 52469 47139 69450 3210 67248 97265 38430 40557 59802 47066 35159 79032 21242 46257 16763 34293 80074 53569 37698 83793 58288 21794 28296 87547 68615 79278 85072 97697 40820 67972 58391 41354 70856 22744 23889 2159 22579 13602 97431 58044 44361 67560 78568 95561 77482 24190 98723 2650 66873 67588 93833 23878 90544 82290 11817 78573 4351 28709 10501 35797 19698 70446 29938 52212 30393 67280 96706 97839 7109 73184 3463 42799 1286 66123 99584 22357 75219 10037 63663 28285 26528 51093 11102 69700 17659 36759 60116 51640 77035 74891 77433 42414 63770 38877 99675 57083 89148 50771 99234 77072 71969 83258 78214 19690 39516 8109 25092 13988 37918 28024 97518 13564 66051 2112 4996 20066 78265 51386 52824 52856 70196 98053 79401 57908 49612 69744 94436 76577 99309 46055 28610 53440 27269 69154 49643 16069 61336 81702 31729 9529 66564 44539 31013 16744 73732 91569 5232 40541 68199 88107 14616 83528 68689 4780 68065 9967 32633 53784 31770 66479 37151 67721 15313 81596 29148 70753 57179 75561 77566 6896 30671 21063 74270 88655 62668 64699 1367 47320 72194 83958 29037 28284 95080 45997 5479 51934 83969 45938 73040 70135 3838 49154 40530 72231 54197 66433 26117 30973 74185 55630 89515 27972 77981 37420 6308 31093 18785 91838 14097 42926 91771 91784 28044 96802 52375 65355 70811 63329 4160 13990 40603 59820 48652 13413 43396 60183 50994 31480 18148 15903 1189 18077 2541 32843 62103 82199 32535 44476 83042 29235 67526 82003 53709 2214 43103 86932 57012 96018 30412 29074 8852 59237 9637 34080 56060 28438 50921 12139 81185 36858 23681 16687 93689 47995 9346 28740 39032 34166 29966 82147 3984 54735 37902 73701 46007 25257 44954 55108 16282 15784 64994 44039 58211 35889 67337 24996 12623 75484 41697 68804 94293 11501 42670 12732 45262 59013 6305 89128 48922 98744 21696 24634 58690 69257 41724 10069 3388 28383 32664 87763 79264 94768 1683 70627 70054 83593 25435 22049 45806 47753 78448 73798 8798 62753 41969 10657 95143 99425 93538 60297 64973 11509 7600 98820 86832 68673 65492 7083 9584 9352 7081 48013 61969 80296 97748 49779 28968 62956 67412 46425 35830 51420 63488 47082 87076 64732 95659 29623 29768 43408 81899 99718 22442 15320 29530 61095 9665 46553 40369 3344 66802 11766 78818 14552 42140 81229 48154 50673 5011 81705 4629 73578 23156 96522 11720 71780 66325 98443 88709 81480 27499 48059 158 89742 74533 11372 57437 79069 20857 76614 59264 9901 73265 60103 53076 67290 93958 77006 89991 21805 1279 93130 32418 92397 55056 42607 27839 19719 11388 19419 58491 8735 92204 20161 41073 99360 40713 4074 28007 84300 82923 94483 84318 16761 49942 34580 16190 60220 96699 30357 7254 37715 72260 28628 12213 71597 26036 31485 39151 40093 20737 51062 10041 28571 98854 91376 67226 15246 22261 87770 6037 91298 70787 46116 20221 77868 78305 65311 10711 88876 51595 60973 24139 17347 75017 48200 88144 70407 85700 84686 20307 13912 90932 79297 47528 17444 70468 13236 89395 6258 73852 75603 12947 19430 40004 34531 81296 78089 21279 86923 48022 65754 1353 71777 70749 28054 57928 55595 76003 88565 33465 62765 99835 62466 5158 98842 97861 87095 56424 73367 65208 59737 63179 8236 74549 59860 41675 42247 44780 89901 69873 36988 27222 399 68171 99635 88500 52136 6998 81037 40092 46346 80089 78711 90381 39610 24831 71945 83190 84764 1108 87866 59678 74702 52529 6502 30852 60645 27990 11901 49171 1579 69981 79395 47213 79772 8546 10115 99663 26680 61703 19547 7579 309 86141 2652 59474 68712 87311 53735 74017 725 82341 26498 60627 16535 98649 96923 83665 7882 44199 25442 34667 70900 23801 44113 63500 35758 52787 26911 77207 58104 40451 3631 8053 74208 57260 39302 32164 34943 10293 93184 6618 61864 4922 21771 43714 49259 28811 50211 25566 84176 80719 32257 4989 17539 30875 61716 2878 40307 33523 98384 22687 66971 13768 63263 99645 87922 2449 96873 85649 64259 21856 89381 41721 76249 88949 2854 65141 43075 46235 82494 22248 19705 76229 6382 80462 5554 95658 73222 80734 16879 60053 31851 76768 27011 25540 10583 81818 3727 31282 51250 43894 55970 7740 96727 49639 56540 10090 11596 59481 5927 44162 92851 3167 12434 20683 81584 10052 38941 5772 29048 56178 18607 60375 69240 95053 18507 58126 4484 59304 44034 57735 72072 61606 99905 10185 21911 34190 10757 79910 36215 54819 32346 68056 26531 73005 26083 1514 92088 33597 44148 76512 99002 18800 62866 81427 69050 5781 46627 89507 71873 57515 16825 84227 86449 92171 48832 31309 21926 50817 76706 4946 12931 59027 72220 43229 41582 9091 19157 87151 75785 92608 41891 70806 59131 67405 78497 17325 39158 34368 57460 32578 22838 45739 44718 85909 77156 2253 96609 59685 50427 57676 21553 57668 95272 78674 1421 49118 32198 44251 55024 53264 34833 26437 88382 62777 34196 43478 92942 33093 61904 41653 49177 5676 52572 94723 94740 9015 98557 30831 98977 72686 95990 67962 1408 72382 74039 43960 82834 5908 13307 28483 58878 38185 94187 37989 98504 47747 52875 68748 51918 66471 88271 51196 63838 5045 9485 66323 79505 90410 87738 88436 16518 49412 88129 11651 64754 14201 27440 42959 78118 8879 94238 80375 6974 45896 53668 84656 29536 87278 46767 57020 47381 96743 29106 71925 69480 75772 8286 47356 39726 36320 11473 53934 15853 24110 63835 60800 73541 106 78486 72003 16239 8622 86769 2130 12038 45295 95162 13003 94524 60200 71600 48292 39252 77649 69916 65810 18277 85149 96149 75207 38190 59661 22416 1061 15635 29040 73545 90800 64493 99321 39828 3864 87961 65716 35464 4249 74687 73531 60526 6943 89037 8994 54521 93497 37337 87370 90632 89833 51548 26669 71670 20895 68055 83810 71029 80181 5613 84659 37628 83879 92229 16304 53591 8501 41741 38520 49493 9404 85003 72400 96774 74686 69830 65323 31174 61092 11609 53188 7838 61375 88450 68884 27927 39276 55350 63854 524 45018 97128 20025 1431 75023 2298 78367 42275 85311 17496 50179 4009 81932 68969 6939 98105 91769 28408 28023 89003 36464 76331 91986 44416 3240 59830 30404 72686 110 96162 38957 29587 80221 16275 58944 65221 80483 46299 10925 97938 75144 80315 74352 63296 50943 94724 93439 43491 96350 81788 25288 84386 89216 61127 70064 11290 51088 60307 65351 92000 50331 64147 61764 14990 11856 3996 61310 45650 67867 75962 98659 58398 38518 75082 77887 60585 23491 28663 55554 70227 10269 68548 31189 92227 26999 70727 51911 64922 49340 50935 30050 61332 20148 86639 86509 8768 91279 47174 55796 72818 79119 42333 670 319 77150 80267 53300 28065 60222 81863 40856 45589 76426 25488 80640 99749 1219 19557 78734 12873 7739 35330 31034 10404 66 30536 59917 74844 24452 28922 77802 11627 86672 39217 65154 72219 10882 41401 39442 17993 28915 22680 30389 71706 65828 9715 3442 97435 70236 13005 36532 84951 55495 887 33714 1235 99155 13841 69739 62084 32487 89814 44730 85140 33862 13444 37101 61952 88843 35105 94069 66674 84498 5384 98325 81100 1374 23807 69193 67685 64235 45323 73177 4958 30622 79001 13629 48715 33116 82220 7102 52028 51125 54186 66470 59743 72716 65753 27119 53679 60971 34692 24780 16002 73146 19549 66443 25185 40348 71043 11383 92605 69669 15699 74608 15336 66508 81920 23861 89648 52125 6743 77340 33006 29238 10606 42772 99364 71598 75839 63836 47998 7167 88482 48472 10335 79552 65095 9850 34830 58281 3983 66078 64511 89739 53019 19140 9863 28048 65304 61725 46574 23701 66665 24305 20917 48070 1332 63015 88293 93039 66015 49733 13254 57098 90187 38662 94450 12104 36348 87607 85196 18063 75893 75196 22904 41739 8547 17034 17407 25599 15432 97111 49824 8283 734 65842 92588 78716 13626 34142 69744 81605 41765 95537 57128 87492 49475 58676 52653 8743 92691 18897 19420 51493 75879 89498 56030 82100 56889 94503 4046 24949 685 7251 72522 67024 98577 62327 53686 99462 20442 94868 74927 40926 99307 72724 45898 17218 11603 53158 84870 4434 71015 62084 71817 70284 17629 49396 33841 80609 44594 61555 10064 93257 12997 79567 37191 20971 81226 17087 33512 70132 20454 90459 81732 2985 23261 23294 6730 13621 17396 77466 19407 87415 21663 95079 20107 52917 67791 62529 74596 52037 76899 27735 6452 63298 83009 52140 10815 44890 44450 68312 49379 94421 7736 47415 31458 76456 22518 48841 37762 85094 50968 76912 40126 29562 3570 6321 52525 33088 95259 4406 61560 90125 3997 94727 99523 46031 43460 64239 85182 79064 14824 16188 48994 68316 41865 76494 61118 18989 27904 8277 15206 2724 20965 33086 94317 72611 68772 65027 12528 50466 11616 40363 4561 94434 83307 75348 95107 27481 94519 4029 88793 77766 93514 3942 29157 80078 74060 36150 53508 85446 52970 35715 33490 87792 52883 8660 42113 7594 87653 92760 45091 66885 15224 7062 51571 11662 55540 70376 94667 33870 66016 60529 7456 11859 63557 23640 54880 41704 89761 59173 23034 57360 223 43139 17568 93455 91740 92510 20588 96622 98022 76763 78348 78894 16517 73726 99793 46746 68342 21297 35761 17478 99292 13121 95828 66413 32225 76569 80136 67035 98744 67819 43230 38053 6197 31505 7495 26103 36671 52074 63856 74965 85349 78180 53660 15442 24622 89284 27660 27545 87040 52283 31896 31532 58599 2688 30221 48993 88117 75116 88669 832 63456 65791 82349 87397 57412 48375 20899 5368 7667 50896 17713 40864 81815 61260 89137 81625 87710 84499 27639 7082 68975 32397 85371 25029 30513 37498 79572 37060 92554 63721 99078 25033 36152 29860 89729 48259 14756 26833 19356 45788 79822 50178 16656 57526 55776 11704 53365 23517 81137 67969 55960 2889 10467 23497 34099 50701 86102 39531 8787 40221 84641 87891 7102 85297 90310 35412 24420 52491 58977 3221 63219 43913 59789 17007 61499 22513 27911 79332 25208 78273 98156 9458 43275 58063 11324 84029 26535 82912 60377 6434 93928 31922 26373 51250 75300 52735 11861 33013 43012 37248 79314 74837 2880 3119 54002 46305 19436 21892 38330 4294 81014 47520 19710 92123 52984 92586 62872 33808 99930 14421 66798 42968 27244 20626 91327 34913 34530 72873 94399 40909 88625 37523 5136 78747 33246 90958 23918 20397 87731 30197 50970 1404 26458 17542 15857 34093 3494 48197 87252 92827 73969 82493 49127 82298 53235 80957 59121 77541 56306 59703 6262 787 19482 46894 95105 99979 68851 75374 80782 22585 80604 70656 18623 55455 80369 54645 60718 90162 37370 89417 64180 65844 43454 68820 35179 63288 25079 72649 83863 78850 17212 90366 44688 26852 93734 7140 93169 27759 81258 36068 89664 362 89444 46488 58600 51615 89468 64596 45146 3548 29409 36039 93893 1527 14986 34807 43522 92976 91666 50782 82713 60106 25602 68162 39977 1657 23941 13238 199 46646 10140 87218 11548 65967 97619 1213 64739 13326 94295 3943 36229 66429 48406 88554 27582 11155 78480 2849 56085 1738 85190 62428 82067 37378 45658 7847 98264 84366 62000 93413 71016 40719 29606 82940 72664 74601 92186 66123 15436 13303 31817 59712 12215 56061 51813 98048 62006 72749 14884 19171 45581 13668 3022 24456 18204 31174 90499 16096 40750 3427 72449 19830 87270 16973 55917 90206 73501 57200 33403 61019 63262 27385 22002 3062 7464 17016 19599 42748 83751 24620 54881 84661 23865 42748 90232 99673 98183 76694 76126 54697 2884 85782 77360 38327 52622 40073 96947 40639 5715 96905 51635 61487 44180 69838 22307 99347 76845 13214 93503 32967 69526 76384 45172 78651 41024 9613 25291 62233 4707 20500 58048 38835 12415 84253 91063 85041 69953 84805 71522 92363 41588 29382 15178 86620 50819 62145 19096 67532 58585 84243 31466 99901 31752 83360 99787 19917 10371 26525 13330 34365 571 92016 20108 17809 42388 10990 49402 52291 65310 8771 51026 11533 67580 51668 63755 14611 82710 79496 5197 68511 22269 44592 21518 33219 15430 15201 72005 62596 86878 77462 41369 68958 63637 84482 71537 63265 8002 97590 53430 43107 71492 10334 22800 91366 76312 68535 86065 91250 92389 95581 46810 24098 22233 97211 52824 96382 90981 1557 63278 51860 43866 7949 67476 41406 55320 84354 49877 88105 18413 8529 66618 59888 38258 18294 50938 10775 45732 91658 9203 28649 95322 36388 11938 28824 94772 22600 16977 14084 1868 9044 73791 41071 16660 74847 95778 20621 62677 7595 19932 31334 57442 22616 22537 73666 60669 25451 4252 35952 84245 37840 65588 76791 15809 11594 26924 37219 24878 44538 12444 7248 4939 95306 90480 21012 7600 16693 43734 7486 5155 93896 9757 84708 56446 89737 51875 1376 40621 61072 15300 31364 27861 98198 9333 89195 55189 43511 4263 60883 14909 49836 12380 33893 80142 65684 90941 95623 43469 80120 2813 72095 89763 86669 55007 21578 35238 35249 13422 52036 57539 95926 66205 6625 11681 66170 26643 17396 1219 96552 10068 7017 89823 40544 66525 40792 41127 39384 34452 55210 3327 85682 23614 65629 53941 43398 94421 20746 87233 50694 55069 42148 74254 91247 67076 7904 36735 87721 17713 75581 94206 51844 8489 748 43982 30545 29680 64400 6963 870 71606 55547 59679 35936 62250 39752 36604 32850 87022 85763 65139 59194 16964 50916 97694 6777 98104 52840 29548 58737 50478 7107 39846 93845 2125 62111 39686 56406 7688 60039 33791 71677 9834 18388 29862 37512 66443 24085 50341 49898 3531 71601 97393 48315 32502 46223 26735 67461 45501 95961 86448 43379 404 85459 62421 53937 31886 1246 85779 98349 13879 76221 92140 15762 59472 53481 67416 92719 8241 48397 2503 6165 64793 95508 3923 46689 97084 96847 52793 78650 59114 31794 79603 64729 99975 85632 64642 88846 79108 86045 72723 51278 72006 64754 27137 14380 93246 83178 97801 70241 16809 28791 58853 67690 48316 75023 73463 81598 34459 32139 71219 93862 4677 33633 14518 79655 46194 22158 45700 52687 31874 85939 96581 71397 1115 45843 10049 54540 32715 34088 70095 89329 12284 531 63563 46204 9261 22690 69733 99749 76475 22114 92831 51466 3 14061 77820 20744 21396 46749 22026 5487 25138 52141 81779 77289 70814 9790 82845 34638 7945 53379 34602 53833 35149 41061 70684 88468 71298 49539 38007 44360 91433 7550 7228 73790 70680 71262 59950 88204 3702 78709 62941 1876 64894 70476 74871 10168 79155 32407 67221 84076 29892 66831 11005 52941 87200 9548 68045 72402 33877 65303 87735 24996 56976 5549 78004 77753 18105 80606 12338 49266 42961 61414 23711 79232 67617 16505 25127 80999 87641 7315 69915 88878 47086 9727 15954 19700 16953 9356 93855 26822 81067 66398 25027 13764 70134 66724 3790 51268 26603 62353 21177 17299 3247 88353 78955 64588 47342 47003 3528 51992 55601 82651 69656 69086 10448 23161 9643 38278 50150 16132 12959 56216 59691 6753 1467 22334 40311 696 21472 36570 83810 44271 46500 51866 2060 77687 69407 68422 8915 99238 41825 33304 57243 78971 23665 12579 25948 43686 66508 48922 99546 85034 38281 30346 9942 59228 81890 12136 76237 94958 77012 89708 61883 99688 75588 63265 14884 29706 67823 88696 69312 10995 88617 33344 7416 16253 87195 97968 52122 69366 38709 76680 59405 98587 62153 38548 24987 34771 87029 63139 53028 82853 16295 89088 56401 65187 66738 19051 54207 52422 18235 41162 5512 41639 25074 97427 96008 82963 73232 97965 57301 16491 57759 4894 26963 4683 6133 39042 44033 91523 70299 80139 52672 58311 65911 15011 6743 51187 12561 29332 56861 67629 90936 77949 51575 23253 59266 4930 97852 82563 15500 87358 13779 15049 76511 44200 14021 46765 14175 65809 70497 74263 13281 45983 23688 82777 70967 77428 66074 16647 85757 33851 20292 11347 96379 68760 14715 47883 67283 7603 32969 91906 53667 81188 14062 86881 7876 35542 89702 85034 47584 9915 68752 49493 66084 35210 20396 58659 20124 5646 42357 84257 38266 20668 61435 27538 52994 17494 28919 9884 46750 7183 40743 98767 25459 86858 25651 30899 95563 43778 45963 75374 63063 29170 89982 83307 95357 57842 86409 33765 50702 34511 11203 91977 58489 20208 81590 98874 45937 79984 17578 45002 87903 21065 55433 58845 14594 51795 18824 34766 55453 8117 69887 5638 85361 59996 26918 25060 55473 64017 54665 64662 32767 90519 24830 31332 63919 97894 96491 49115 37303 20780 80393 5945 10937 37471 37365 75074 78382 94270 88632 50555 79046 81263 24785 24755 82250 88712 65649 3789 36741 55403 53369 96222 43724 1309 451 38310 37712 35328 27473 21691 75995 33575 33451 90953 72942 13847 72579 20453 82732 12413 32194 27279 40744 49314 23962 91823 80708 24414 6353 44153 50840 71599 1692 31134 91250 6222 35557 37630 26209 9503 66319 17118 81587 64702 53715 16117 16124 48415 69280 17365 34343 27817 61270 81562 85371 99203 28894 85177 73105 62244 25794 72897 61689 43687 91845 98022 23853 54272 5872 88858 73439 72775 68613 53611 66784 86994 39297 85321 65316 33061 43892 59991 83723 4253 44635 16080 17476 28989 75156 90812 85272 21610 26925 87004 17186 42039 48343 93703 89740 87689 50805 86849 91580 24590 28353 83161 14881 24610 1154 71 64282 94339 40178 86463 29988 75746 49751 52958 88700 79004 53418 23555 33880 94467 49550 65320 71568 91768 67505 40939 50024 77353 99672 227 58428 76915 29659 76443 17055 46061 83408 72628 88964 27160 38557 32181 98539 46792 47262 33491 9367 10351 98600 39147 17512 77525 80620 18639 10925 80720 20803 85762 70556 29134 29335 93666 51610 7852 21175 70696 90232 26692 12313 57828 17123 54442 99965 63885 89391 13371 88824 40158 54499 55076 49459 20288 11581 17662 92517 2382 91482 81210 39323 28315 92629 46489 20492 40170 10554 41758 65937 91664 28062 11290 31461 2915 54704 12824 20038 96458 88306 99769 68600 81299 97659 87126 37091 61505 45079 71551 2675 87311 44752 80731 59531 64616 26664 30949 82209 96645 55510 93202 54866 8266 27478 26852 12138 8697 2349 42640 73807 37511 99301 57064 60251 59433 85165 99069 38955 67175 37583 8602 86671 34752 50306 18722 61373 60945 65216 49062 81885 48582 55112 99100 59973 4078 26631 47145 75540 40521 30309 32248 87811 36942 96651 89969 68140 27808 7603 41264 44877 11871 10445 9873 98878 21186 32302 29918 19396 50584 98207 837 93020 60884 76546 83705 27080 64026 87408 48326 57272 47832 25152 54833 78915 71608 20012 38337 15950 51511 57331 43162 35212 60175 60292 79006 47452 78743 46234 86259 31491 89422 84985 77298 90170 98800 15213 88619 3908 24202 49766 36800 25600 29323 24890 17291 42691 61502 67109 34301 77433 31867 43488 39941 88907 97925 26179 42828 93748 40764 24007 21979 33758 81716 96562 67534 48524 91136 67946 35888 23028 44564 75587 10540 20848 85103 20308 90781 37362 88360 22635 15689 31375 33666 35772 7479 25976 82986 13508 41176 83839 83044 12208 40520 89298 33542 76171 8223 75434 25340 16223 19936 25063 21241 12251 19545 5463 54520 15017 95336 24374 54701 38907 2147 46719 38381 93259 34186 91854 91813 14991 42042 97185 86789 52119 41992 26365 54852 21269 71292 28331 14000 68894 63575 79126 33188 80310 23025 35517 22047 32418 6933 63683 10388 79 44209 61977 3446 14912 94926 28887 27308 9739 34265 34568 98964 37967 69795 29105 65877 78897 96135 80911 1386 69491 27712 39058 56748 14589 63156 40916 68135 23664 89334 80126 71175 32026 22761 77890 13355 84370 40927 4875 96465 73826 14911 1905 44742 48899 94745 95602 91005 73887 70732 16417 70710 31502 17951 98473 33471 46618 59794 19434 86310 11267 94073 72637 13199 62157 40726 34183 90085 48539 61564 94589 18802 34533 12583 76345 94574 60260 39097 1299 10284 43158 41658 41322 98517 96453 62953 42960 8302 1022 66502 18486 33422 41936 7401 17490 3185 64155 7220 96314 77449 92361 1076 74475 39389 49600 32966 46805 70897 14715 16718 66013 54865 71454 7854 81947 42982 48017 5546 40139 7166 91856 28023 71227 3134 58604 4087 5116 50343 18731 33383 51956 64826 69002 62786 5695 12442 24022 30678 89738 12745 11850 96170 19495 28034 62128 61195 86021 79870 49405 70175 92277 72516 97307 40686 84952 58389 32566 83599 77685 9874 83693 73618 44206 85569 80064 56351 13374 2511 29267 15416 56941 78693 1079 78259 29865 2767 62811 83912 88963 31498 67196 87314 61633 64903 31500 59631 50133 66690 54611 96587 42433 16490 94130 45108 94813 5620 40450 8261 27712 20089 85330 81940 54693 88040 56702 22001 21916 44965 94583 58192 69672 73937 43170 18444 24597 23916 64041 67223 57293 99964 47812 97840 57885 39993 77712 53794 6138 77157 40895 61470 5114 14520 99838 54398 92443 83504 14559 6863 9711 64579 8973 61132 7358 36511 11156 61873 87797 74990 78808 70640 43715 78853 19851 12364 1984 10089 59105 65905 87373 45808 59342 60403 70740 80751 54798 23588 10029 52703 80439 97113 16989 69776 98077 50885 47411 53558 44388 43266 78087 60121 68271 27245 34337 98850 93303 94497 56258 59094 57975 28649 49093 69611 96743 39261 51267 72938 51216 80492 28476 28837 59904 91170 46625 30151 48528 31698 39340 52671 90270 30382 33722 21930 42527 57234 3892 90869 88254 3808 26926 10493 37797 21149 4223 25785 97414 46455 57099 15986 10997 27239 94786 30359 83383 32071 96188 59545 58686 91492 50745 2146 14289 69714 48612 84821 45494 46846 70411 93469 65368 60225 2983 90691 40008 31982 98169 12173 69946 62265 83241 54541 25965 9891 85237 26320 30985 81260 52068 26471 49675 999 63742 6869 35559 58298 92740 2538 67032 81061 35882 1510 83722 51105 13177 70672 54635 56406 5505 79606 30108 1461 67052 1570 95069 10883 31687 32331 83865 18135 68704 48132 81292 941 46380 7466 74380 40220 71052 49526 39294 52512 55728 8439 55386 85657 33682 18797 85098 22379 20628 87539 95291 4713 53702 46213 81119 83979 75439 51611 93464 10292 60896 59254 75289 81930 38553 99319 84670 56389 60429 56358 66009 4808 72289 87250 10941 88891 31553 26516 9088 47304 11720 96567 72762 28425 99186 72078 91129 46730 97170 82436 12961 90360 47171 56992 17328 3502 5993 87712 5677 83883 72287 31472 68415 42603 29773 96198 42873 51969 9961 41929 83302 26885 26055 27003 17099 78382 78619 84889 2923 11493 62318 3528 2575 95656 87263 59222 33643 67882 52594 21352 82129 44804 2025 54880 67672 25438 83721 33685 17148 57250 84717 76820 98881 2759 93000 67198 76453 16416 59798 30329 17818 88909 27756 67722 833 53609 94080 98546 93975 23982 73393 71672 81697 82370 4967 46327 91291 91999 8691 46689 43485 17802 85473 55976 85465 45805 904 83184 61037 70365 26253 7947 1297 52110 26641 62897 39371 98954 28040 48782 83670 49762 70341 81669 5899 38158 60502 79870 92483 73031 35012 43493 24174 630 62223 85533 48651 11600 86432 32600 86857 98477 22196 65066 80324 56064 44024 72364 32862 14620 58661 48196 81786 66129 51502 89470 5713 76540 79644 37410 55363 46514 65639 27204 53645 63418 42459 96268 3755 20663 11291 65657 13424 59529 13375 58352 19087 96415 45811 24731 79616 94474 42454 98232 3178 20919 49721 67660 56238 9004 19013 14183 17697 83234 78027 8902 32007 11111 52435 447 65584 43917 14355 64465 64148 40434 54763 82918 33802 68938 42156 1133 12607 95676 42111 3417 41404 71053 27117 53360 13910 44934 34932 13671 61365 14504 97194 42954 26405 72855 47687 30862 88859 31828 7378 94920 85382 68969 59023 21937 62109 31992 47086 15988 47537 73037 12294 93192 76807 42773 37249 33434 50492 38252 13015 54725 29068 15957 17402 17376 59012 27306 29830 25052 66474 71603 75731 30914 16747 59145 95779 26382 89324 12814 92058 3356 3969 93307 60109 3007 34371 7388 6638 76089 18465 12902 35591 91042 16454 78708 49865 55774 2876 34429 57055 65903 90376 69284 84680 74995 66850 11373 96325 96309 62151 74506 71701 41623 92369 62369 96356 88442 19476 69323 6756 91813 13531 25728 67251 75927 41928 87875 86235 94885 3479 96612 5530 87309 32841 94201 69804 32658 94055 11855 63668 31293 86649 40965 4131 84801 11944 47013 76370 63881 77084 4197 34257 76548 54513 54438 97672 7650 89960 67952 71952 98719 64310 59618 86452 4562 60007 5141 29602 13844 79933 25762 2460 6650 44619 45984 80039 81746 85042 81754 62767 96409 14168 55138 19742 60426 17980 1987 94281 6646 75171 74943 68426 90806 77090 13608 55299 5753 2577 6160 43272 27589 59492 11014 52678 60341 34269 1278 72888 49571 50357 33885 65210 87548 63017 56009 48111 13941 10825 91006 73468 17356 52058 24040 34917 80227 84728 51199 41061 17767 30453 69160 28290 10844 49873 92805 57291 34442 60998 81745 15570 46078 40669 86986 33194 56888 63505 51167 90574 55680 8923 91512 19650 81646 1949 69776 95266 39115 19229 35875 69920 16781 26881 52272 6882 46464 6922 7505 81942 86581 94676 46174 12071 54283 16424 78632 41263 17316 32145 4925 96588 20977 94847 97871 62365 13642 40153 37694 59807 30411 4942 39534 6170 96261 40918 61979 13191 24531 99816 71010 77273 62281 57262 56151 61215 80577 91377 38945 32401 6373 38707 39604 33974 21668 64635 33995 53791 20870 68784 48872 41813 12454 92565 66873 63729 52302 41527 91329 71298 7195 95699 33734 92793 55333 80968 14305 58630 48387 14988 27433 53778 79505 26052 30216 94824 11290 5565 9488 78036 62811 7444 41558 30676 92132 54462 33498 63979 14744 59414 56229 3346 36747 77550 85517 14472 30660 1926 48141 11040 50083 73910 71581 82562 38348 66844 53108 93103 59061 31567 54254 77251 35620 48822 17879 82946 27087 43633 42403 97518 97806 27173 40105 73215 18399 96526 67233 71942 48370 59906 33719 55838 79700 21530 64375 55866 97171 85592 71394 93521 70826 89048 54485 68213 29677 52453 58938 28216 31425 90044 43497 73530 85459 11185 5156 17116 23372 4973 53699 42868 5154 54381 87131 66741 84913 5432 11482 16611 20132 8065 66000 92853 51631 97727 68573 57234 87227 49253 74393 42182 39265 50068 95004 40035 62422 23295 56576 98494 36841 55161 96600 3228 13557 18140 53842 78471 87354 86462 64354 72831 88942 89988 55171 27513 25401 72394 61518 82246 36703 2132 34342 34963 90036 23174 35074 27058 80443 90308 72369 23208 40916 30358 86976 21336 91287 22617 79814 91762 35750 33837 95161 66098 35037 58101 3847 40832 9987 9567 71192 39132 48582 26441 86042 28435 59055 63881 26143 42962 18660 2693 64409 50703 51612 26046 51528 15811 37299 50108 28591 49026 50989 54421 47270 1010 53591 82895 13575 50294 12782 974 83777 3728 90778 59612 29688 6020 52598 54914 94999 80517 6266 28916 34929 53878 83446 19454 33396 79289 42690 86058 529 26411 9022 61383 63603 4082 87957 62391 92421 51450 35256 17424 95812 24772 32637 25147 22902 99707 31213 43996 50188 35213 52508 27037 49999 5261 47398 81198 94362 8699 89230 17285 30948 73603 21430 23281 51371 65105 83260 73222 7600 75342 27536 32551 14712 64269 10491 38567 58995 39560 90638 75851 83775 27881 19363 39775 40407 11033 58973 51725 76680 8759 28190 31749 65952 42692 43284 87809 96574 36915 5383 2150 2883 9590 17256 98034 28835 15540 92189 61517 15473 7447 53797 39290 21598 18795 71421 86528 90575 64381 94838 28031 90591 76192 55712 83964 12260 50219 46835 125 55927 51947 90678 40579 64422 89891 23707 56651 25894 38311 1010 80680 93660 51095 31203 23359 51361 89427 38693 68775 61324 18661 10268 29972 1179 80867 73890 42491 60863 78647 48537 41084 50153 62856 24254 92847 17824 81246 35581 36274 40565 44742 59322 83862 20598 59257 69618 35695 62487 77385 23339 5769 9098 57348 59703 79357 10405 81234 35737 78085 37306 32368 50903 1635 46424 83447 59511 48098 46403 48389 12025 78922 22412 88879 45157 17784 4375 48728 81950 318 8344 68015 22024 83144 76144 4182 21568 37256 52414 9163 68895 77668 22615 19143 63260 37050 99544 98956 4142 84641 40918 48918 94073 95354 44382 94627 27649 75592 94569 78534 56073 95369 36641 99723 26105 40178 4055 34044 37118 52101 28379 77843 104 10919 22833 67788 13478 40809 43000 22553 73483 90150 22436 41188 68684 69951 36680 58175 7335 12242 18535 19397 26117 80949 9720 69375 3587 59751 26168 41164 9786 23796 53653 60470 54349 8409 32500 95612 56377 86483 20125 82548 59887 62275 93379 48870 81055 74452 9275 50752 31348 83082 20976 80757 11139 76574 2445 97723 50888 73469 7033 19617 17675 28062 74715 30255 85771 30734 40717 68981 40377 18199 77190 23429 15071 3675 17612 5680 30537 55262 17200 76008 10649 75359 28903 86416 9185 73658 93201 68152 91987 51884 80105 54065 98616 51394 30846 17645 58945 73017 50638 13279 72337 85622 33466 1092 76378 40641 36364 77736 5881 73197 30443 90323 48912 65884 7909 96363 63986 82818 10331 9187 77834 67363 31693 43767 25759 88267 75307 7468 72803 64693 39084 96051 15177 92743 71691 27972 57503 27377 20502 12244 29016 28834 98174 11461 61329 4585 25777 41067 42784 13709 80206 8230 93475 82007 28230 87970 85590 73793 65797 97579 19279 31668 39311 58400 82436 58637 77137 47218 31832 85261 79952 72614 24226 36109 24360 80343 4663 15449 73168 52996 33110 98388 21415 81250 38623 80483 96193 70923 75148 37451 67099 87578 63020 7464 94903 87034 33168 57551 18258 17188 60582 76448 98873 68159 88915 13958 56608 62377 30056 69968 77383 64393 92353 93307 15505 44231 26972 99617 21313 64803 61268 95611 67216 87135 65138 78876 73623 428 86611 62677 72537 5516 53607 9770 93017 46971 9583 92405 21822 82092 61616 44186 5884 70393 25540 23109 26932 85003 26493 9299 1757 64290 53891 47773 54575 49536 49008 14780 42333 95835 83966 83907 28332 56774 24141 8458 49384 55687 48123 22043 25688 31958 43448 74497 53102 62098 17857 24834 80844 5423 45848 46411 88644 75629 96349 81636 73180 50237 92667 61178 90714 22241 5860 6823 53780 98663 75715 75111 3616 51569 29911 87657 22395 6708 78954 88692 43372 38888 18787 7804 29894 16921 72856 32256 1835 34296 95581 68032 12141 16914 89722 11162 74845 73079 30946 6300 42672 53834 55824 54736 30654 33373 29184 88868 83547 23298 73049 88165 59169 16125 49214 82873 77514 12202 26122 95833 70731 85576 48078 98418 71074 54500 26117 55592 24386 96244 40837 56455 74668 46793 51048 5562 2178 92888 71392 82393 71475 37976 77258 4986 79038 19589 84546 67337 82492 39733 46360 11308 94519 19810 97191 39277 52487 89485 76381 65793 92296 33284 74098 43942 3253 14641 87930 86607 35165 66950 61192 21571 13039 24296 32616 95705 80752 70297 56399 65365 44308 24200 71361 23854 48990 84253 9794 39731 53885 3757 28746 92277 94541 47208 64348 94155 78814 79818 81453 1076 57350 55767 20521 16655 38063 92640 52498 94627 71228 46301 34251 66212 2870 12853 86994 88591 96295 55278 40692 906 44868 52551 28444 70615 74493 16452 57157 46036 35304 43151 83401 41507 6426 13450 85310 20336 52991 75641 49095 78493 14912 48701 26324 71118 76347 6031 3716 67510 40829 93711 12543 79506 6502 12321 46342 58669 20188 24219 80871 48281 61607 6184 1163 91395 5860 45021 37806 3514 50274 84578 84167 58799 46324 4827 23787 37972 68457 11231 90644 31283 28431 19638 93295 27115 89625 95713 24583 22480 22833 70774 27829 75693 2318 51187 94203 60670 6897 53691 16917 10809 8867 13898 49751 69607 90390 11951 31531 62806 77645 51671 62543 33239 65806 9088 35044 58529 3278 77778 10044 80755 23218 20358 80085 80633 24633 56708 69873 8603 94801 17021 70904 35113 70516 34689 68022 97043 26258 89016 22560 14204 74838 92156 98160 51860 79294 41537 5178 82325 96820 7643 84377 1570 24188 6632 75938 18332 23112 34036 1562 30126 81174 95909 46002 39963 62659 39790 92506 84292 6049 47270 84800 67587 22376 85130 94013 59879 5132 84125 85474 73915 83064 83651 50570 12234 55953 88999 73287 92215 21778 67349 2353 64911 37021 39415 55528 1110 72945 78410 66743 61005 74990 50503 77194 50558 42182 94608 22896 77216 46727 28317 33037 41397 59216 25825 46385 26111 599 51767 6072 21023 49885 42379 2009 56092 60179 91927 10615 22818 6382 83835 72567 13710 46551 19787 9858 92896 41666 54141 4462 94964 6225 69193 11364 35907 68179 15162 55030 89686 81038 21130 4849 49942 5146 59379 44228 5325 45887 95880 39515 26995 15740 64535 79116 74001 78694 3726 37218 57550 62308 87154 31702 77910 71493 55966 23513 67720 34301 97009 82010 19489 7907 59918 48276 67718 83656 79249 53494 23358 66328 2177 28475 71135 7582 10132 34788 36353 83330 38480 15622 83841 55224 29491 26867 15095 1626 32185 18070 60617 27805 25795 40334 88180 90897 30089 57796 98016 15442 50202 49760 24219 79754 86198 38897 27069 7320 67832 33258 72120 99737 62972 37928 77429 72864 71434 4544 45745 95717 91451 35167 3604 1094 85905 16247 39830 17199 45112 79817 17277 1543 289 96492 70597 3898 69009 55747 17656 7458 10640 27514 14104 91828 72295 69163 49245 56943 40941 31670 90397 47946 98398 91142 369 98387 71887 21362 44655 56641 23559 69503 66141 79994 54580 44471 61417 17751 71074 39398 91653 37258 49687 47747 87428 61501 15572 30018 95591 41216 51606 56326 91186 73406 72421 28984 38120 22249 35654 29918 57998 13823 12869 68046 95098 36073 44798 18279 91177 15183 5230 50295 27001 64417 74789 51829 6614 88315 49728 46094 19223 19687 65958 59688 19671 94417 35066 60034 577 5852 11667 54571 34840 96480 6368 47530 13133 24361 46517 93589 43397 31919 53256 6169 27515 5874 26512 74501 20025 74333 90163 30186 70484 13990 83978 13761 90997 56893 49722 21052 45060 67700 65612 39588 73809 99599 97202 57326 47326 65574 34250 69719 26513 45138 92991 15974 55432 48106 38171 30870 89758 81352 35738 75923 35967 37944 39094 74636 20197 90388 55760 59959 22756 39733 96104 11350 63765 86789 93849 42440 52531 46730 13842 25230 35117 28337 51761 58062 11263 75558 41228 3327 30552 5812 30253 20356 82363 62144 31269 55212 14959 90902 10317 94299 10590 97847 58210 20127 3502 70246 35883 76533 11886 20695 29709 9069 50233 70798 59237 54712 94988 5697 27911 18348 61048 39078 8805 27255 30768 7451 30017 6909 64366 59520 79872 57807 97314 76973 316 75926 43436 69637 73781 82423 35227 14834 43155 16098 62033 98397 62818 53757 47461 84914 76773 37852 37439 74830 63648 11807 2104 38569 86804 90409 98751 8472 89549 25929 92783 30826 52418 60110 27800 35680 80437 73480 33095 73599 70669 72510 61894 1512 32591 24458 73230 12597 53592 80533 3828 86132 67802 11325 82688 30131 88537 5232 9140 25875 60943 86268 16967 86559 35903 97608 2007 46908 65338 18332 20819 92397 56696 98800 54448 20200 33256 35086 60486 76010 47482 66733 52626 9708 82966 94678 62202 88525 83711 22090 1653 71480 30294 66933 45901 20327 83600 8357 84287 27999 58724 80205 91625 1776 42379 59354 78539 52559 11213 75935 79258 22699 6416 10805 76242 62825 53963 67194 36503 22681 26718 76082 46710 27167 8478 32345 39920 39066 5353 13621 91015 77388 16145 41774 48412 70410 13891 42980 82937 89715 27687 45875 68901 14476 55604 97626 61314 63143 28012 69174 41528 81156 3257 96109 75748 68838 66964 28137 9447 5293 32270 23620 97719 40383 70771 70796 71172 27193 83863 92582 8562 24370 27797 84836 7921 79749 35722 79008 65846 79177 22160 27759 48517 39483 63734 86110 65366 84403 50316 75316 29147 63076 32484 39741 55058 12743 73643 70422 15388 86355 39506 69221 72162 32458 52743 12076 22618 62547 48591 96749 17399 27388 71589 77788 64817 37661 57096 90074 30362 19242 30164 25677 57478 11267 26129 69815 75633 19552 95451 22468 42135 49713 64497 12533 8586 70727 62782 11428 67244 5352 80943 35567 11470 70358 47440 86442 35068 22846 59944 23278 72149 78551 3835 635 13929 20814 52083 70374 35299 29168 45151 29828 93492 89500 25812 2461 67089 56293 86488 19043 64189 81149 65893 30378 38829 63223 79496 66677 40441 89369 19297 38119 38675 31226 26114 15078 21936 78976 56296 66460 35243 58570 79679 13590 16953 77836 99149 8482 36296 94631 64340 32896 58022 73670 26295 26420 55156 47878 89250 54340 52140 20249 5302 48579 60356 34213 41378 311 58759 86592 94607 73082 4792 63746 50760 51873 94981 61937 99977 28248 87913 11318 70702 48431 10202 43451 21347 88608 80938 40488 62531 49180 73556 66662 19264 88143 427 86634 38270 93873 79243 2634 32696 52014 73982 99067 83856 97335 10120 12404 38285 24282 18197 4203 26729 73715 74253 30085 53586 72072 24446 63611 82280 52505 69422 63060 22672 88125 71424 69990 87726 31169 93381 65164 55299 46247 28005 21355 42643 71240 68737 53145 19174 97315 96332 32072 82915 26134 39804 66912 66580 35809 5450 63311 62903 65390 15070 89354 18433 20640 90926 97688 80858 28057 60614 57141 52190 259 70620 84873 32646 10506 49168 96109 41980 81026 13860 46822 24588 74970 81748 59135 69392 36050 1473 29643 70401 66363 28365 22778 99871 75049 65988 97335 53355 82270 23437 43575 9667 93720 92831 95068 47224 33610 63833 93315 57762 71264 1293 70596 34698 77317 39734 3936 31258 39098 44409 21139 75190 92866 6462 97698 62395 75530 79991 23581 8845 53321 17928 50638 33471 83431 93229 23767 91270 1075 18502 11334 31251 16846 21993 99971 25411 57680 47903 343 85768 46154 60500 82148 66090 65936 36458 26114 79826 71847 69426 58308 8131 7842 79128 32857 44940 71104 35166 8672 27684 11119 52016 69318 92566 2810 68416 65140 10878 75620 48741 52100 51684 58193 51726 71038 21444 97470 67773 32587 64136 62361 92849 39201 32604 9244 58776 76646 92500 29658 17167 49030 5664 48379 29760 70635 30951 54415 66457 90437 5641 37378 90637 79517 98656 86849 88625 65496 95278 78395 92087 64575 49401 30461 15737 46614 5282 74930 30744 16232 10150 46150 13018 91040 24412 1137 75036 81850 29319 55536 35730 19593 88925 36367 22361 3023 4941 72008 28512 13597 7353 81528 59324 41350 68847 80696 87769 67785 85477 68017 25404 26871 19378 64783 74598 54720 39263 97759 80366 16711 99113 97649 86541 63246 56743 2083 95139 56017 56839 70136 26844 37631 27226 85961 52256 13277 2813 53388 49106 24099 46485 35865 20894 26671 90195 97738 24105 4884 37091 15375 26105 40091 51923 84566 5989 20367 10801 60957 43767 63326 62049 59127 48731 12159 49033 25331 17723 73626 58378 44667 39542 57276 34693 16234 12694 92059 64520 68588 82718 87141 81643 75 74140 88118 88154 72882 40840 24182 49127 57696 20419 24449 40227 21169 26320 69938 95904 11200 99031 49231 34552 43633 68859 60506 13756 70490 95297 81701 67493 53679 64050 60198 9849 62027 84491 70745 98485 78410 43961 93554 89623 32613 4970 46206 54381 2262 72898 46794 62262 83727 56292 90551 85749 20825 98432 26123 2941 90970 16383 61440 17558 22274 32838 60554 79265 23602 63277 20353 40679 61685 65245 24030 85809 77746 84149 73533 48265 94785 62927 37594 9294 32799 82231 21760 7444 6999 85684 5309 44461 34859 62973 96526 23963 99840 2890 31272 69297 37262 9481 20321 35048 88389 12569 13575 59497 16052 3944 31203 11865 27139 50282 73678 36431 76201 50603 32103 71780 57932 87213 35085 40457 40025 55049 81225 8194 23094 70937 25369 41460 53184 178 7280 18160 36299 11172 61945 51277 55223 88144 7367 89748 9900 73398 5109 26819 70481 2393 86857 77061 61314 90009 12973 32466 79471 64998 33119 49332 74290 6401 84077 87964 53765 44460 56585 93460 57987 17540 78519 74394 72527 96634 28039 68151 21620 32861 60459 93834 59480 40335 34980 21480 1468 50439 55918 36580 19207 73533 74444 73397 40241 1176 73664 43426 846 71028 1489 43812 52288 53341 74257 86828 66019 49166 91297 57249 74559 44359 70446 37750 96057 3480 99664 54830 37789 45228 29008 78325 78679 81060 51436 22478 92082 45058 22876 51221 60532 80150 51406 21759 55462 48020 13446 56995 20859 25887 47190 69444 61703 45616 13355 75576 89232 35625 31407 5023 28631 35132 47368 20679 85913 47550 54726 52760 66841 39225 99491 64033 60366 3621 34053 1875 46291 72337 26597 22059 61247 59686 50393 80667 45459 25428 61601 52673 55200 66737 9756 76262 4934 75868 95462 34408 28371 60331 69468 76464 14209 36589 10177 67273 22377 23605 37316 73514 52749 64189 95588 34222 88009 575 65236 70126 8291 17291 46331 12703 79271 41440 7163 75222 86261 64753 31687 41076 99357 14129 60269 92453 98739 39970 31893 34387 31000 53225 91818 4219 82082 69249 97699 68610 42745 57883 99316 97250 39360 1937 12094 42297 94335 29309 98063 57346 31605 75726 62456 68439 46424 51240 52691 73793 35237 96011 11926 48100 58463 45260 24212 2786 21305 70173 10273 80279 26210 10347 32366 32577 41789 29484 73292 97635 97662 76393 9353 11723 1037 2076 65924 95846 82934 75980 13612 41276 21049 22208 13858 7289 264 85869 24718 19719 73073 28529 21912 12403 96543 43962 86307 36676 52603 60586 62479 34588 64274 50883 78926 99162 14504 59880 20017 31618 69206 5861 21615 7151 82978 50019 64669 6184 45322 98168 49996 20055 99055 77637 31773 70653 96722 79815 50767 95625 9832 72791 88844 66365 40775 96231 24287 44784 62886 32648 25671 2714 53523 21737 41574 99144 3318 46645 60545 71579 45851 28798 32902 54143 89897 66609 61318 47890 85322 87659 41569 32637 41476 11279 38973 87486 11199 49785 51517 80203 53013 7842 85946 99853 61957 42665 58019 37308 90769 23590 66375 18826 67511 83044 34432 70431 37640 28227 67221 82738 85314 74549 79716 54638 27455 57579 56688 17624 43309 41460 29551 98219 37497 2076 53303 4728 4092 53444 52390 9693 82463 52744 86381 30844 50612 87435 81297 3295 57105 64577 40410 87 43040 88936 68843 26735 81273 96791 46578 97372 39709 1717 88820 14749 5996 30429 57814 94614 47463 88119 95416 15177 55266 16532 81044 75713 33039 88263 65851 23758 79156 68180 32612 97546 45900 84389 17170 55956 19661 51841 63816 12491 35882 52006 42771 90705 22620 76848 27026 47805 55117 31033 7123 62974 94231 29527 54383 78059 38808 35715 93363 8082 97564 6629 75554 8997 1195 79036 92531 35748 57537 27251 89003 73576 52111 20979 11603 36486 60114 55710 20294 30286 97656 34805 22403 67904 96320 26501 83581 28521 32790 82454 33816 87297 14538 72140 24853 20152 88859 70873 91467 90213 16465 5176 91915 23127 47132 7695 79813 84197 71645 98164 83929 97115 91929 7911 94192 2398 12979 1987 39333 51671 82126 84759 91231 87764 72036 10943 91700 14890 816 45493 46714 32227 14174 67844 84470 13938 63581 7168 71618 61895 18151 15549 31470 12882 45139 57984 77872 24933 85867 13158 39851 86082 97866 980 91562 40612 64408 38535 62042 14115 32119 42739 70478 32753 70078 36924 59314 13821 4768 83434 21964 56995 5740 20716 22099 82985 49979 93248 20130 50607 98515 41104 58440 26106 75846 77448 31280 29028 57354 76462 92145 58076 6659 84727 33603 85066 49427 76997 99428 53099 38534 94307 579 90835 41356 24983 23418 17696 2939 82900 95853 64664 19927 83198 69091 92157 4017 34831 67946 4168 91375 14959 59119 41169 87998 85677 48765 84271 27059 12230 50410 36856 21219 70720 60106 12098 60441 66965 77045 46693 47736 34785 40233 70358 61039 27884 61896 10454 78437 16246 34921 87539 15760 32368 76471 42183 1363 32131 21596 74010 42554 59274 46578 62213 71180 1945 8916 99118 53735 13023 1157 59617 73620 33836 78686 9084 44264 71289 40217 63776 84753 57551 61881 57461 84443 80253 33610 23833 60133 55113 61720 86328 75888 25713 54283 93453 79193 32704 59446 19993 68986 8164 62857 27644 78606 13689 23219 9252 17366 58889 3063 40701 92641 3641 73766 98233 55078 52419 33844 47886 73693 2308 16829 68827 24943 23938 5104 59967 30036 86568 57090 21995 43214 61284 43660 61468 72899 55922 31797 75725 74973 73381 91284 49370 77197 2516 22854 74247 8823 86711 77769 81542 89972 48050 31824 84215 9685 57922 79174 55133 95259 42546 75069 38750 63166 10001 22900 53225 80484 74679 47337 5677 26802 26642 88802 30644 63084 800 97561 32562 64738 83586 32387 88291 14043 73517 78038 2483 36041 75662 31043 34957 9463 5642 76334 77492 52517 20076 52019 91057 45372 77437 54299 61805 72198 46067 18584 31823 67164 71752 11387 53700 47496 32203 84160 90568 87118 44455 71586 17232 26871 97961 71808 50539 15391 12281 58326 39520 32690 26864 6975 8466 65356 84811 32884 5716 28765 82005 85025 34129 59082 45332 86221 59621 43521 32365 17348 48309 93332 98698 42177 61919 96444 88160 55092 56103 2500 1552 89172 48669 31105 39631 72308 91431 50408 36623 42355 54652 7274 79638 84287 99693 55320 79604 14026 90005 35716 63269 53738 19962 78010 5599 19576 48457 25251 82407 17786 33366 43041 82943 33994 98802 80401 11392 62929 44678 63383 25966 9180 31743 50643 8081 18415 12084 51376 71215 90776 75513 89736 92214 37619 73768 26508 98504 30411 50066 43149 66235 10601 2671 6685 2724 77383 97807 20187 41431 51435 46346 63920 59289 47628 24915 37007 19452 47206 8782 91012 20434 56088 98813 82079 31406 60914 86480 47277 15190 82826 20162 36715 83237 75128 67769 13562 52622 37754 14895 65303 70389 96459 11919 82851 18152 8773 68841 9918 42264 60891 38146 76498 29978 3135 97086 64572 36208 90903 64524 2193 74674 68327 50675 66832 74055 76514 62724 82161 22193 1860 27615 16662 24733 57148 54288 1330 26083 28354 15644 37923 11324 71135 72069 47402 11334 89864 14195 2924 99976 61371 3053 50404 58094 82657 29422 72516 37904 87105 47345 67440 82370 33618 28850 21028 66638 81325 25233 86013 1741 79579 87091 747 90729 33635 12214 59749 77463 82656 54225 23283 95778 23899 85153 37614 14006 30636 83351 73870 6970 55485 24684 82651 5856 91506 36534 9411 91765 31392 87504 35677 21167 52844 88621 32011 90424 53722 69851 87024 2827 13724 73898 84121 44424 29073 69566 86445 37903 13185 11836 89056 77554 19643 87047 52802 78767 8238 71502 25763 67791 62489 81817 41726 9522 28711 67906 43734 26804 42835 4598 32993 65560 99579 72864 28012 57544 63369 12186 79339 56823 91376 65702 27244 37033 5132 48509 58176 38895 45881 74219 57785 27451 29481 9277 72573 87524 1096 55157 80259 52130 56183 53570 57215 93731 89316 61208 59613 39157 54521 89476 36924 13475 85203 56529 38849 8097 93577 64220 23214 26939 57852 48530 21907 83539 92504 15229 94298 55882 30533 21795 77968 81454 84841 32004 34346 50955 39907 14754 37337 42443 41860 95049 3769 33862 58720 79259 85020 9957 21902 39535 95519 73341 44563 12868 96230 28187 91552 3951 83368 62991 65463 45279 7523 1590 92342 86448 96472 37965 48105 51089 90622 28684 15860 74446 47426 13875 44355 13407 18264 36436 88903 2093 3089 7943 27621 13797 40149 5497 65454 63854 69381 59483 96207 2754 3360 22159 75395 79145 2600 97860 56791 45481 46206 40775 27910 84457 97596 31617 18775 35833 81072 76681 79120 98462 25219 52957 42076 79928 64052 41618 82902 54992 99309 42426 45033 45719 94911 45080 84509 54951 95323 17082 26084 17620 76336 9485 66917 74863 2772 22926 16446 70908 91271 854 73538 58667 90366 56318 26804 59952 27219 4082 59594 51344 36890 45442 67179 43273 17064 29086 1680 88211 4897 29322 39831 18061 25455 54638 6824 41459 96698 47733 88069 55468 29156 71382 84182 29181 61270 19195 13808 51643 78742 2492 23422 2505 98842 60833 23258 64355 42241 93285 47671 48127 28325 72769 20442 46584 88953 59608 39651 21438 6165 52506 6347 77787 12560 53467 66744 83759 72772 44115 41174 73291 51535 44134 5892 18324 25489 18243 13628 4511 1570 96693 43277 13193 89563 92008 69367 33413 74565 3532 49334 68813 29867 73551 10089 38397 42181 39261 98290 54446 63497 8062 4310 62745 82866 67265 10769 60205 3218 43029 78852 18178 8697 16046 67678 84569 49310 55737 87810 10620 57806 90172 32095 43525 99147 50611 6360 37138 47172 11302 87087 65647 59212 33550 87576 58186 97637 27984 46567 99893 83266 91223 75280 57401 18948 57433 39920 60741 35973 34602 96992 5349 80733 6067 74107 79622 76036 82236 73115 73756 36109 49776 31993 16585 44286 9025 6827 72598 57971 60664 50909 7216 96212 53006 55930 3837 77616 86397 60439 97336 84867 98092 32963 88085 45234 13578 76497 84884 2649 25546 24911 78259 49791 62615 74046 11195 88001 1996 2987 95562 90720 79282 20116 82620 11070 89781 84880 51630 93046 18903 52482 44224 81698 33468 14300 63337 93419 10239 41812 43464 69454 5309 7940 37810 1513 37499 55525 34788 78441 36595 96662 53544 73226 9136 85453 91294 26775 6776 79712 69419 43914 99487 88765 46849 86030 32348 28470 48165 88495 18024 15386 25762 69848 46072 8844 4535 11955 5513 64103 7547 74027 72707 31242 62623 75404 27771 72184 47189 34153 50401 64194 69806 6712 38478 9637 80902 79102 62807 71484 8883 7710 88134 90562 23223 66535 36069 65605 3895 26511 8545 96855 31594 40833 59003 60569 21487 88450 21914 66558 89676 75963 5472 16575 14340 97732 6400 25881 77033 75511 80758 32634 78381 4204 69645 67016 41967 93532 83522 27861 6443 38410 35107 56010 46184 86672 30186 385 53456 87670 62454 30249 40982 63873 34519 84503 60623 89112 47 25536 14147 11445 78988 58310 60450 73268 12907 47187 72649 76419 24498 64017 2497 42415 88676 44039 10717 11916 23182 20858 90396 16404 55891 45117 35347 86906 9447 76994 10837 29126 92168 67828 74391 31910 46154 78640 43945 74876 23117 52418 64735 74211 50206 42733 52723 25149 64183 57083 50329 90425 57168 92272 13574 18737 5550 77677 67772 81044 51991 55584 38372 43915 894 97506 90464 84827 72937 23800 71697 2610 26710 97551 33013 54149 96451 19412 80689 51307 54944 44480 9074 55861 60563 22456 22755 73140 59575 87885 32186 5496 4748 50143 16850 16913 78944 54375 64987 82145 43865 81889 51192 46494 21341 89203 46601 80392 87589 18709 83175 40871 99101 34906 18615 36231 30749 72311 90748 31889 91068 39208 65422 88761 20098 97055 5170 30389 5975 32537 31261 4927 93669 79557 78952 51072 45242 85111 73393 96096 96273 62662 23730 59727 86623 83124 75696 71164 55902 81999 36207 46876 85923 25469 36110 99347 16339 43457 2260 19265 2706 69504 6223 85744 34700 67702 50352 95988 15086 20537 5575 41508 85495 2388 64733 95728 82168 39022 636 20191 30594 45217 41053 76353 80047 21975 15107 88753 51664 7704 16368 22478 20650 8314 60576 61864 38485 23797 38217 91307 91264 2218 19610 77417 78509 80161 82458 22594 95616 56923 29079 5197 6723 76605 11634 14381 89517 68340 76295 82706 79094 38823 85318 42406 18258 87691 99456 29455 13976 4485 6776 62262 32713 22724 80506 37645 94739 41305 97647 7373 93204 4863 51100 29990 33940 60687 13039 19999 3458 40990 82678 746 38566 45593 39124 51523 39271 6410 51334 79619 29108 64159 93073 62942 70939 37317 49374 80261 80319 21780 75775 61477 79002 58715 15903 70466 38249 55293 1882 64532 49214 80604 430 18091 59951 67772 97434 31584 44096 76887 48936 56391 68099 41243 62665 45035 23228 13702 41816 99433 74976 81267 22503 98572 29661 21350 29098 93587 62070 14149 57382 46438 14651 25623 67612 25569 97470 14562 84974 46008 89172 12310 1761 39451 27219 3753 97577 89534 58389 43340 83843 36631 87392 29779 68144 8191 10659 1005 70278 34579 42353 62580 75336 15831 24855 61387 39392 67608 84129 17315 20982 77382 94585 84490 50396 17434 25228 93977 13373 26288 38826 28388 34105 5737 15586 59498 92324 72463 31555 33391 17872 89401 2808 22963 81108 18457 57263 81547 92421 89631 59244 22119 27406 15574 24901 83670 568 90705 40394 57722 46469 80921 63984 34593 38355 32385 95122 73588 44227 32824 33829 71623 91750 81163 92503 68207 77194 6937 97639 61487 68377 64617 71377 70027 97963 97516 15999 47364 35449 26650 68157 61251 79084 58627 37336 81897 17858 18347 88814 6447 79566 52037 51258 19292 13790 31600 8924 23650 25673 27369 63249 22303 16750 58581 13790 5685 96356 51130 56167 79505 40242 98729 53154 35032 6803 70983 69202 64932 40977 40653 30016 29924 57495 95335 12876 31845 12667 62573 64852 34047 14517 33851 27776 73885 29640 25744 62297 46491 73207 1362 75076 39841 38621 62547 35850 70061 70024 38225 72545 32982 54146 97957 6999 16905 56689 22817 14283 53047 82484 52403 42470 67521 77711 32264 64068 24007 65852 3635 17358 80510 85219 13486 12490 40054 90893 83449 74813 82106 49183 31063 51738 45573 10838 28751 9591 59453 57548 44638 15627 46818 80438 40393 10862 91732 39564 20845 35127 18129 87823 99775 44455 66477 28492 14254 87467 59904 37817 78711 41951 63485 79355 6368 71447 88789 63491 36499 49 80198 10635 5979 28440 75690 34073 24291 16961 89587 46046 932 27945 29256 6018 47288 16009 5668 78999 28903 24453 6694 96862 9857 26222 40452 18354 43380 29807 56619 35507 67487 69782 20265 52972 36433 18371 35357 15335 31947 56189 80676 7776 60032 19784 15723 7214 34714 21135 48015 35243 68192 33728 41159 28802 49267 9242 2804 35848 4595 79371 97504 54158 80243 47978 86627 59730 9658 37773 21654 96406 73578 38293 15625 10381 69265 71406 90526 15295 59904 99083 42775 4976 84493 40291 5947 81653 80378 68042 46754 97839 4225 87228 21998 72111 42628 48716 28320 63621 91589 72657 91583 90448 57737 99329 53968 76459 68372 74951 48108 31990 90346 87970 49846 33855 61474 30330 24294 72802 81039 4964 80553 87170 20257 20840 47004 15637 81181 84029 68418 21335 52908 58348 41425 3226 96013 65458 7264 50744 38660 62864 69522 3746 22607 84843 22016 39712 58511 22764 32598 17790 74007 29842 60117 70145 47015 16572 9851 2695 20691 93642 13318 46557 95458 2388 91135 32089 70992 87392 43903 83273 97872 26543 34624 70045 77427 54061 51539 76665 63470 27873 44138 53125 22689 42130 43804 85119 76112 18963 1169 69981 42266 88406 2380 22570 76182 839 67125 11006 94524 60898 88410 33290 72299 63695 8656 48681 86838 41415 25611 36170 59396 92322 36859 55144 17185 20105 92822 79861 92798 52802 85995 81867 51348 22376 82091 76239 83740 68861 13127 60245 37840 28666 15866 74811 29231 69391 66845 34327 62502 52807 42311 81931 78199 69304 76203 25035 18923 64190 65431 99392 30829 23063 71090 95546 66332 96289 26914 89072 24109 71177 16546 81454 44541 71668 69937 77866 68873 74709 64094 71496 42586 91675 85367 22604 28894 10022 27897 21498 48049 28777 40089 68202 78759 40009 24804 66902 17110 88594 30008 13041 72204 33766 75554 36685 30803 86896 39261 76803 38165 84370 12486 57365 74073 13080 8907 71789 8595 42844 69705 14977 60795 12459 49263 59971 78716 13701 85511 12336 3886 56494 96910 25844 14246 26907 65984 11625 67052 20912 11124 67336 85063 85199 79801 31948 30461 48016 45412 18211 20178 2898 3062 94398 64506 14822 17310 77012 49962 99319 29574 89960 6624 41172 37938 41348 34637 22196 85742 99535 43724 94528 41776 9544 12491 46200 54841 63920 69581 57190 50042 90681 52793 20252 74419 31035 17668 77843 96907 59855 51200 11713 75146 84739 35734 36478 56115 58038 60867 78831 609 75225 15155 43620 96652 12736 12025 81644 76585 30964 99766 98496 21364 45801 32272 53750 8745 58860 44807 89990 78832 49905 89571 67048 96970 91611 19332 98457 93519 94480 71268 8254 56989 21999 36950 13323 27302 94926 27408 44914 25346 15834 78292 42811 37401 28892 71458 95480 2957 98193 72483 28715 50214 41176 63609 48817 81251 67010 65168 51016 14640 84363 10495 51445 35975 41653 45629 90896 19325 67093 99110 37600 3693 82138 57710 12293 2722 99896 54922 23547 36289 10127 95515 72128 39983 47038 86979 70549 81553 43355 37496 93285 37333 94001 3781 95040 22464 68460 80421 40836 45779 36543 43111 31454 85904 60929 12010 57034 18733 49573 48759 63245 9104 28208 13400 72872 51822 3202 44014 10515 26721 23851 80696 92427 10303 53998 94808 10044 98324 55129 89887 69323 78708 99283 43520 21066 52703 96834 74196 29176 69496 44628 64149 63950 26151 95445 17305 30955 41693 8163 66764 1855 45207 62052 94847 75844 48364 24537 36887 98294 92507 96652 63968 31923 6488 88706 48623 19119 5861 28439 84260 97620 90166 59847 25532 12259 41012 75356 11498 29261 36079 86677 28542 83738 95208 57589 66956 33527 70629 69897 3768 71766 39634 10098 42191 52528 52023 70108 11450 96846 1525 85286 56792 70364 84987 27516 13311 90502 81077 44695 69929 46889 268 17492 38784 73853 48691 32653 31997 10659 58651 67744 46652 12221 53323 27354 27711 70446 77220 18243 21309 79219 5338 76863 22321 12292 20171 66842 82154 41332 35410 95599 42109 84176 12882 85156 39352 85462 81708 38680 50054 6199 26515 86476 9052 4503 51939 22718 87225 14697 85619 73291 95991 83276 35280 9860 5875 101 71721 40659 91767 2026 7634 66229 84467 17959 95821 17600 8460 25882 52155 56829 78943 86394 74579 31023 83575 70278 50157 82324 13505 60432 16724 64449 37663 80098 82344 73226 7047 78460 3433 94365 34771 93567 89907 37367 6394 83704 11162 31528 82154 65759 17025 67975 64551 22964 6432 45343 17902 63042 85929 60694 97522 42803 21526 18587 16127 11718 80808 87950 54980 15014 1223 76115 31046 51141 24247 17015 46618 42534 78067 39583 4286 62467 69589 84875 82520 42481 49980 55630 74597 96347 43125 48654 75585 2779 22872 78643 9903 56501 74295 20513 44550 59513 32998 12197 16766 36379 12830 12824 98303 40538 27333 9146 53116 69315 50501 6595 54572 25329 19943 30543 68314 39894 95334 96449 15601 73655 72498 49694 29746 94990 80561 4148 77912 48587 69092 73773 98223 55298 63170 21013 97260 50043 70794 71172 18247 61041 7400 94969 1593 76577 51747 45678 45468 47626 74957 37027 72696 22067 80597 30344 88210 91772 50536 32046 19819 36811 12014 24113 16859 73746 53438 26044 33110 73142 11785 15639 57860 34618 17339 57209 67168 82703 73860 73906 45061 38660 88857 78852 20374 43717 4073 81662 72420 11740 34417 74063 10397 25619 31811 65180 55554 96324 96802 74910 90641 7623 68272 99204 9523 82541 8744 67858 97928 58 59437 9096 15046 46209 71957 25619 32716 39866 24756 69141 8554 19230 61701 7630 24329 17365 49615 3898 77767 90901 33719 17725 56131 67801 38648 55980 58252 46113 46121 22448 81809 68682 25334 8728 16960 57709 94369 82565 98848 97723 77332 67399 69526 51838 42827 72134 51760 75313 94341 35111 45672 95110 35832 12735 35178 36488 91020 64336 86993 81502 95613 39166 66355 19137 25946 2320 83919 57751 13652 91038 87141 95592 1648 97236 2800 88262 57830 82616 21197 40707 4606 94758 50273 86637 64677 31015 13498 30816 53796 66687 63769 67237 86410 5321 14399 44986 19808 38071 16561 20514 93102 49984 31895 64522 9601 40005 94080 53347 46031 2959 41517 14593 13645 59226 81947 8674 94078 19949 38537 10782 2659 72727 46653 91274 96458 32894 24428 27447 89404 62091 71298 48008 26367 39403 76987 59623 71623 6445 37579 80871 93432 85708 99723 3884 88283 98605 25733 83342 71560 87933 38645 70769 69036 12444 62044 23225 64465 9979 18970 38070 53993 20619 20451 90107 74731 66133 26147 25605 35465 71093 54061 78462 85149 45146 49406 91973 70574 7754 26023 71271 54439 67818 41516 82168 31184 36108 98512 27349 62001 62081 71517 92553 49303 95940 25219 34199 96952 25151 28262 16586 33489 69905 8773 21940 43179 81463 23615 4315 92825 6669 85578 98110 57753 86824 25507 12679 69106 67576 894 86116 35128 4384 41683 73505 30812 32148 35505 55676 42206 33859 83232 78890 9686 71248 63445 43765 95547 29349 30595 85901 48628 22619 53504 54166 83221 18609 68077 72196 63271 48087 8530 99141 35591 15113 82842 20700 79618 26619 91577 53878 51454 3041 44233 85701 6906 28144 20271 78579 95727 82941 97034 67424 44687 39938 89399 57550 41655 5614 72926 74409 64381 7873 87348 53762 97635 72901 83474 89046 41902 86503 2193 86626 87554 14039 49648 78972 56507 87271 75099 64114 74553 46734 36421 88327 28529 67219 37339 57972 49417 22219 11827 51279 84400 22226 51138 81087 9428 26136 22013 38742 45211 98110 6877 85307 21561 78024 81615 87201 22948 2471 77534 88519 83141 52405 25883 67695 46654 39258 92822 64997 18798 83581 37928 3762 13085 56784 71089 67844 20916 89828 9628 79848 8452 67610 23331 3597 13991 92388 1461 90718 65411 75106 45907 85459 44100 8731 17613 50923 28180 1962 95755 68324 32465 51564 50512 61130 49679 48954 25468 92141 92028 96086 9159 68033 17422 39050 80081 33887 31392 97655 34646 40509 84074 98651 56004 81933 17486 18685 32996 96150 84459 81172 64117 95284 14237 27187 57363 72808 77224 80083 32327 89734 54052 88727 88454 74303 34908 88211 68884 75890 82018 1417 64717 55370 35219 6700 8894 60741 12804 87865 88440 38756 32436 5117 14186 26711 43826 97477 49838 37310 73486 33971 53205 87438 62789 66053 68111 55839 37316 88477 87148 28305 31966 51962 26974 63043 17108 47311 64178 95672 15299 33333 77095 89092 85020 80350 63434 49467 67567 59922 84037 6330 72521 37775 32373 94714 23167 25699 23945 89948 76298 67422 19369 45055 46764 49691 5955 48380 7675 95361 40430 75189 22981 79679 63897 91034 85702 63930 68527 48336 68501 56862 90750 10565 4389 79154 40563 13947 66713 50120 71031 66891 32796 16943 78866 48839 66967 15175 92264 40576 21891 93018 59430 5443 19642 29856 80052 51935 10826 77842 98270 71099 74892 48714 53085 15028 56874 41505 89185 27672 89900 97321 75870 65134 29455 6507 3916 80708 10929 30703 27682 53523 21106 52591 99645 54237 2160 33806 9712 11841 13159 75114 76602 55631 12670 37016 87182 77785 94611 14368 21890 8790 37676 58073 45789 26634 78586 73591 5879 26196 53177 69791 82289 32253 32942 73173 33966 85555 55151 36177 51756 53825 3922 765 10787 20031 60217 12202 1999 66239 86406 32471 1364 2631 72729 81069 68815 1672 93143 78053 85468 81132 6968 51515 39799 79037 72903 37991 76015 49143 91826 61597 23545 59244 99 2571 58512 73403 21322 86475 76541 56462 63050 40016 21220 8381 24260 42262 77413 59300 59651 45141 76024 6477 50890 31313 49287 27844 28530 35558 17144 68296 80627 60140 87752 3500 19496 63094 34160 15913 62974 82923 34143 25666 4846 71067 96506 67969 21100 83842 37823 75294 98470 68379 55275 94122 2675 17731 39131 59012 70045 96376 84697 6509 88703 75249 29435 9413 85413 1534 91200 49913 23258 11869 48585 34866 38410 15340 73937 95304 66062 27203 32851 8248 76827 65350 25631 23641 76696 75722 62000 59439 93668 83919 87319 99237 90398 4307 96998 81757 31417 19263 97151 97579 48254 86856 62745 45985 52123 63762 54937 60637 28567 39727 77133 66500 51183 56318 28358 89801 5223 44554 35898 55660 29127 10100 34999 42157 80422 22891 29584 28580 4905 2031 67117 90929 32989 1762 20461 74992 77176 69719 34938 21248 1521 58303 34101 34243 28005 22506 54037 59611 14469 94649 98820 42567 81659 58450 73969 42065 59093 32471 58445 41605 74645 75172 4566 33305 33009 8484 97344 23448 93036 36755 36719 37783 67525 7511 22409 29356 64228 36046 28394 11574 12603 3155 81652 2926 1286 81999 79798 32365 25488 29867 1636 68696 71803 15033 20969 78022 67532 36437 59735 13140 21100 22000 55059 34774 58622 60195 7311 56877 43989 48665 30791 35161 5843 12255 15907 13221 65386 58056 74486 33790 38140 84624 43974 10770 65605 32090 44109 5453 44806 21255 41988 45560 80340 57825 88666 85910 17021 37994 73230 47726 96797 11852 54644 78593 23073 4450 14255 25257 94383 23029 11050 97092 1925 86116 48494 29855 57962 82543 35603 43475 86747 79894 42276 4181 3262 60252 91753 73650 38396 75301 50644 81800 45747 2292 89198 62418 87442 87323 65447 49401 60086 54502 4130 12126 71467 60681 30589 22295 26519 96991 3113 25441 45049 79070 82593 50039 50070 96021 32998 42925 10784 2641 98882 32963 17963 42725 48401 72073 10290 44589 86476 73444 44490 98732 75938 79648 36727 46984 67889 830 14552 7645 49728 90426 94055 56956 21493 68569 15796 57487 80572 21965 12689 98331 71450 54239 77023 8745 21483 21544 58653 41072 2587 13997 26324 669 800 34390 4036 83676 94478 61031 26563 88935 33674 74448 75206 27208 53205 28031 24746 19137 92412 95699 11675 99558 88692 14421 20807 11498 69503 44896 95235 99252 27365 63171 60988 24479 45696 72424 95674 77915 52229 61593 64024 31653 67955 58948 33340 62586 33715 13122 52096 77806 99835 5404 30884 68877 97194 80844 16791 90141 77922 49136 8698 14058 73485 55680 81692 86071 30461 47743 9113 89961 97059 66538 39360 42192 49218 97864 57088 80659 48312 88658 76539 34865 45889 53111 8118 27204 88311 12113 46434 78145 3477 36532 1620 59983 87879 14452 87112 57215 48780 52019 71117 60973 96604 23781 9706 27662 1865 95961 87849 96641 25837 54173 40702 50011 76713 2965 74417 49433 19815 77934 52470 91172 60089 84086 27713 89840 84818 67410 24853 77883 13542 78040 30805 38871 51230 9365 20807 28869 13353 15645 92023 96594 22821 20296 92375 5950 88356 57551 83910 9171 67867 93123 27713 44471 97282 27429 20628 53423 78183 63879 54930 56123 63269 24661 15767 97074 6281 84057 25569 11419 82951 45889 27307 92510 35830 71129 66763 99921 67054 951 10765 77795 51250 94301 36363 61879 63252 46533 74179 30781 49770 63382 73124 2908 14490 10265 7457 64461 87142 63803 56915 22350 74276 18221 99435 90977 65109 76410 80122 98328 63293 58158 70199 33609 87316 25528 12239 48751 39540 72643 11584 27338 11323 31888 23212 84037 81577 88149 79103 62541 72818 2805 50081 28750 62757 24970 84275 28641 10829 89234 5916 45855 1363 48668 60717 5543 48656 61033 78883 16372 88777 56697 41941 33855 52346 10749 6560 8164 22982 72399 83563 36234 57576 71550 56811 51888 82148 42806 8019 35195 97535 81990 70365 63896 9051 41412 76652 97802 59328 54130 22525 18939 11764 84565 22114 40845 71404 98088 63589 41891 3785 73039 55840 99428 19411 24540 93867 71320 43764 32477 44171 45731 24919 48563 50591 57494 92449 83148 72619 49849 53209 67052 46324 35652 25775 70631 76680 72472 37950 3760 9499 36793 84315 4890 80653 60319 78290 82608 82379 24344 40054 17532 61005 57284 65277 11895 87372 71982 81375 96523 91298 41965 14562 90239 48240 81343 82186 42266 89938 35440 56451 41981 72577 80211 50454 52639 57418 21469 25314 36080 47729 43585 75725 51731 39741 80717 22320 40003 94566 42050 34204 93699 8099 64602 27474 47788 96452 54417 33816 7089 40516 23748 68654 54964 80626 92053 92168 57136 25856 4001 78014 24721 79966 19588 98453 9430 85849 48174 64833 61995 53482 59792 99131 15063 36344 81259 73114 95588 31872 91197 88480 44175 10295 7195 55513 20833 95098 82697 64367 47449 90023 58707 67128 58864 68295 32280 94129 30291 35914 17972 93224 71219 16986 91650 93906 30794 32173 54528 26430 86862 36455 53963 22994 95555 49045 25312 95437 53469 53129 46561 48639 92542 47171 12402 62843 39405 25686 73684 39901 47137 82971 50266 43922 87223 1735 44536 54034 89464 70461 14768 21338 47484 28821 4058 57511 80120 30476 31694 4198 30495 50819 54608 32118 17785 59134 30526 44379 69273 73971 72700 54205 84346 93899 53573 72899 8016 89714 28231 86300 98598 42495 52333 10267 85968 14657 21078 7344 2182 45773 14883 53899 21565 28616 33319 59299 19640 51136 25564 35398 22971 59929 30839 76200 91876 52944 569 54388 55609 87354 98314 81815 61379 64052 34207 34387 19676 73842 46010 14306 69590 30130 89818 84597 57621 36493 74496 92374 96870 18446 23593 36450 85848 67473 41990 60732 56814 79987 4708 7237 21475 94109 96588 84816 14570 92032 54723 48744 37374 67266 58621 43394 70406 77067 30647 19175 85761 47296 26530 37142 29176 46485 40509 50680 84114 74697 96053 26078 19831 43940 14525 41042 70099 77599 90072 58759 13357 3222 52104 30911 55314 91353 3862 45889 15060 58605 79945 87633 91601 12565 88172 6372 1764 96551 85776 19349 57477 79175 81915 55792 73744 85826 59496 63472 97549 77470 81341 75910 62130 52198 62463 35890 62473 63930 17232 4833 70960 50984 83458 43889 83309 80834 56615 30827 29878 71614 76681 26196 7851 95794 64408 13365 83509 86027 90584 36870 20750 49122 96813 97457 23053 42365 39403 1198 11158 14862 74240 68724 7152 39841 75687 20207 62878 64418 83943 23887 8245 5025 33404 64149 12475 37781 97442 5413 61921 41187 74959 95853 32052 72050 57340 35808 72486 91006 78334 70821 13563 37010 88124 85165 95750 6627 40145 48777 96689 44786 65766 60021 81375 91767 28778 78310 52488 30876 38321 49924 10843 8548 74882 95837 52492 3480 29842 65878 23056 41976 66987 6689 15314 46930 2127 57771 67615 36978 84141 17677 68776 65106 31629 22577 84839 28335 38044 40486 42146 76353 42174 19704 65886 93066 69650 68342 12926 52797 39175 12159 32381 43309 27001 40002 88445 13745 71253 130 91185 54122 72082 33804 27830 64139 69335 52893 18419 12025 65129 36391 52188 97387 13961 66968 51003 11045 81604 86013 37014 21486 95670 50622 70065 13788 2387 19865 88113 84850 23182 77372 66731 39010 2714 80304 43865 58517 18196 8580 5624 25841 51195 14363 7654 58378 48119 80583 30093 76920 11591 66229 81071 72302 16138 49007 78926 30479 22114 65474 48689 50952 7014 14634 72317 84152 91310 52949 46802 21864 92038 1089 63919 93463 81203 73881 98826 82477 46376 83592 44022 99047 4248 36234 41443 47968 65012 91550 97860 51071 11493 17894 45915 16839 79826 52750 84372 70038 96286 25275 47603 78263 582 78823 42322 65944 77150 40006 87722 23554 89853 63021 70260 50256 72493 61936 57999 55498 37968 45937 99376 15918 55880 56259 41874 76417 90940 82832 77907 14590 57231 57584 43414 39440 81255 54380 48103 77910 71609 87004 46110 50146 24047 49710 49005 33043 92255 48374 94481 40804 73283 39764 3446 79728 24824 43733 72520 43503 21570 51392 6397 55981 40330 10696 62722 73896 49790 1704 94463 99724 20656 22011 99929 35503 71269 8125 46081 64436 21470 87582 7580 51213 62146 96819 15186 66562 35939 23643 12390 10170 36319 56061 95352 97362 13990 16940 84782 28615 22504 9980 9579 10308 32672 30766 18551 73500 12735 39856 41258 92820 28947 96501 43344 55091 78991 59466 68120 45978 35593 92918 74763 23257 64692 61762 6792 50943 77202 46199 76464 60897 12001 3331 91735 43410 36342 8060 3007 51254 45384 27910 59973 32142 18164 46137 71159 44920 64238 63045 75273 66098 1450 33632 88710 49695 81186 2702 46351 73081 48579 6835 79793 95623 17165 2977 2182 91088 10437 85866 8297 37077 24226 17897 52396 9158 47346 59654 50791 35832 58045 129 85066 72948 54807 51991 72847 22504 36734 9517 16013 92668 6483 35073 55494 99927 86477 88212 97794 98820 76632 58145 45552 61798 41104 86671 76696 16998 82932 89938 82039 88402 12552 7660 79041 36744 47179 15031 43184 28064 32241 44591 11060 78140 97470 58438 33400 35680 4421 33924 8142 9453 91211 91388 39445 21389 21509 7712 8533 42242 57051 99326 59015 75033 8165 6989 59937 32605 20083 67171 4890 36257 6173 48558 29365 58905 69298 42165 64850 74762 7268 84652 42687 39776 86470 34464 77823 76755 93039 88055 22046 84942 40779 68247 12630 62269 76431 61962 42875 76530 331 96826 30201 50084 54213 75795 9503 60079 51205 46466 14781 32496 47873 34839 64383 39322 52368 58081 23732 83864 53502 47754 62819 98967 21827 78706 68045 56354 40609 12177 62765 19179 43007 25335 70623 4731 93540 94453 43129 96004 52218 94369 95671 37363 77946 18812 2601 60307 46760 8939 34782 68574 69582 57753 95332 20923 13376 10429 88296 69146 32655 63148 4927 62920 30636 92322 29716 20144 97744 86329 66731 42378 52070 13072 16500 12105 73937 63910 9723 963 2263 93417 19171 10657 65962 10064 41887 54848 77186 11540 10762 66124 20878 987 78530 72042 46199 11498 94697 52136 78334 83695 7192 41068 36709 12257 77335 99959 85919 25221 31080 44801 88526 59796 5065 55524 36623 48201 47790 13728 4166 14305 95322 73277 58355 35054 45697 31139 44608 47501 22923 93600 36331 68888 67751 35660 68701 97394 26907 71455 68808 88835 84422 25030 30248 30278 71413 86153 55109 43107 43889 21310 85287 96984 78885 37133 47784 6004 10335 96869 78262 24001 22876 96422 22242 36329 24524 92542 74288 44035 4441 16558 95854 39421 8248 30196 67610 23648 82868 27520 88780 63930 18065 7247 88505 75327 54905 1689 65806 9534 46541 81133 21861 8299 5075 64031 35922 25071 23650 60837 51027 68060 71396 30021 27331 13744 35996 33437 77231 5678 7736 1711 64121 98128 54009 97062 46483 26748 27588 72576 64192 36802 77233 51496 49455 79656 21485 10699 21519 12695 61834 96968 76047 1234 63465 28350 27926 42936 19014 25067 68997 88624 80911 18852 3225 73529 75956 27321 44919 6312 80003 60521 41737 93686 14793 61426 75994 74583 35666 1006 70479 76123 27065 24761 81598 84645 26765 30827 87201 82055 4155 10422 7245 24337 71557 94673 30668 84306 52715 37446 42660 32277 18769 30919 97855 26799 81671 86498 21148 1057 25385 47098 98499 42171 48258 54129 68268 68629 16923 76436 57444 20881 21992 43968 93060 58185 24164 16999 88323 53914 93500 63729 32239 10014 42338 77035 45266 92159 69884 30446 78202 92676 42130 30960 37677 70306 27792 7971 62551 60979 5045 5231 64321 91330 29092 98430 2927 34710 15877 19569 83165 68005 52937 39224 20666 26421 68570 54469 74394 89665 79828 81955 57222 44137 86186 26463 9841 60221 41007 99118 28784 20601 67333 92017 20504 68982 95250 66491 75399 8661 77997 18744 1846 43691 16738 15800 89443 47542 69413 66971 56075 89366 4950 72257 4887 61047 3300 26635 23268 43021 20535 4472 24425 83240 88201 45112 74982 69171 51131 82705 69502 60889 99356 10544 1726 82412 49677 89221 39595 92633 48607 89053 84626 83995 71749 20362 48674 58990 40448 24085 42214 50462 56821 74232 54603 70070 25600 17742 63466 57363 16312 59360 43987 96813 97501 35611 52630 79852 29149 69824 89043 17632 99127 99784 92376 63975 1752 60194 39467 58726 46906 63913 89283 95573 14873 11620 53447 81322 36225 66907 5020 73894 69296 99235 89194 24869 59554 66811 91942 95895 57214 58166 23327 38538 29333 51986 93067 2605 96195 53799 67070 65942 55373 88922 4413 10472 87561 18941 3898 26276 38724 61106 91905 82727 44243 87671 37362 74105 16733 45712 28596 73916 38206 9181 72974 26838 37067 21511 44831 29345 15885 90173 93411 58196 11128 17645 44721 35590 66197 82439 78746 16765 22592 66481 65086 55884 8858 91426 35923 21546 52734 76771 87677 63527 9473 26349 31051 63638 89399 55669 54839 82595 35151 67687 25454 50429 52402 3798 39264 58081 78799 66538 12237 76061 84310 23191 81196 22349 52605 89234 94238 60594 22447 46895 54811 23385 84414 5310 69808 27284 60015 41440 83425 17381 53486 42076 11139 14302 94442 30337 81910 34041 12820 79151 22246 13442 1129 65958 78040 4305 31936 56354 81262 56026 85783 35169 19015 59724 55248 84924 92570 75181 39600 97828 83546 7197 84213 45776 95257 33129 84795 86442 95365 9020 44437 4143 85691 35448 75270 32795 5660 38778 45340 48548 40036 97307 44354 39689 90569 72660 75541 79186 94177 13182 76955 7943 20547 99621 88161 38257 91826 46022 13393 71537 29549 61327 39696 85309 26521 1466 1258 47360 85784 75028 49674 51711 97548 56021 45973 89234 740 34953 80296 33912 31254 29074 52386 37209 24612 96262 32091 98119 91380 99219 81033 97100 96873 68457 41143 18099 18646 56096 21091 38897 69315 94349 1310 74012 13779 84197 69647 22763 34150 454 21662 34008 85773 65366 36194 46478 35081 41996 45758 78864 70859 38684 48950 92188 35659 76175 20921 65859 98433 82731 33944 85046 25006 39809 25129 90278 19991 44532 30602 83529 28467 53426 25654 35392 42277 65121 70952 10933 18313 67069 5033 13904 63088 28469 18470 12237 35700 76045 47908 42268 55561 66586 9994 98142 40822 77490 43987 42795 429 54835 68447 10483 48382 64655 48135 72849 15925 99135 79762 86508 67110 2241 95948 22505 28024 60238 86880 55327 82041 69472 82810 12494 69568 77415 59748 32921 77284 53080 59542 99847 13822 76279 2069 99908 97927 79138 42838 71055 74119 33652 18455 88399 92368 27979 37017 30720 16077 40999 85578 5008 1843 27679 40717 60500 10762 2448 10012 33082 85765 7188 16033 12383 73365 33190 68493 16523 18278 9626 71495 25282 93286 39826 64989 64854 76345 74781 73845 18413 68890 15723 87645 60319 59591 32998 20353 60461 6544 33273 55634 87942 84377 18791 42449 38835 47052 8403 33154 97668 3867 57866 61363 74091 85464 2250 90315 42867 75996 92833 82084 7930 47290 16929 73384 72615 27644 62433 837 36847 69569 19335 22361 26802 96235 87701 17620 91458 70114 66814 27802 31816 70749 64433 46467 87098 52773 4460 82720 73125 75947 3684 41628 66489 49606 81774 60758 59205 98213 38828 43142 43810 64168 27452 5971 50752 28824 42540 43590 97573 96443 32343 63021 18069 41804 39402 41409 88288 95324 68859 78501 46524 55871 72020 5474 27876 60739 39678 3333 2 59541 28533 42924 42848 94891 52651 34306 33060 28540 53705 94692 35019 32830 23609 35140 80551 65106 66163 24033 61810 44521 3568 26917 95485 28563 65496 76305 74884 34501 99966 10661 79489 99337 75157 15925 66799 70961 53106 73735 73051 31813 46684 8487 21157 87525 48856 73780 67148 35880 68750 8327 10365 29753 67846 49702 6371 90764 46308 12066 46368 51326 39643 87519 53871 42902 49003 57898 50080 18632 46831 26585 45934 80104 85797 66127 90887 74794 62445 39014 36548 11001 1729 81619 87503 34902 20521 55640 94620 98489 28923 97852 92661 1786 57230 78921 10662 77297 5204 38887 19091 59271 57877 33230 43228 43872 99775 77953 39935 35671 41878 12202 39511 51532 27542 55648 88205 46479 68695 93551 52125 13249 79036 29627 15361 58574 60728 56544 91352 11834 12801 7111 12637 52377 26696 16911 23475 78645 61664 27215 26212 55590 91984 7810 93872 904 75409 85846 80397 99039 40095 54435 3079 52040 81961 60988 83103 78325 45090 38465 72907 26996 22192 28567 75393 67911 32011 932 56894 63934 36941 62452 11208 8871 1976 61436 32593 50587 97797 42179 93327 87300 59963 71136 16818 17287 16614 8965 52212 97032 58180 42400 94228 33258 86416 56658 40110 90857 24409 85084 55401 98751 84855 612 18007 45828 16376 10053 77414 52495 37118 88783 31889 16838 75320 91357 77200 9397 31387 71740 59426 41888 22517 86964 83418 89897 49548 30971 58704 79511 53435 44003 80268 95475 28552 92918 52218 43435 88405 28670 15570 82590 82627 44139 29303 87446 24183 68893 89396 84486 82311 968 71081 11875 90886 62681 44931 52110 54625 18554 63655 18082 96454 51170 72894 67182 4159 44727 86737 255 62875 91200 38701 76078 31141 29809 35494 34382 50844 50480 34204 40257 12753 15834 6301 84500 17626 10051 46858 89571 9543 86659 9724 97258 19791 36363 48930 50615 90255 14045 32694 88543 95207 56008 1230 68502 74878 54343 10102 96334 64269 61049 94283 72673 36326 1293 16666 83532 17270 36746 19682 44828 91929 54060 34849 11831 31012 31558 51839 81400 56790 14371 25967 18741 13651 28878 88260 88824 39551 82915 5504 95507 87371 392 51198 58548 28548 83917 57507 6458 72180 59546 52240 5006 52145 19829 265 43599 55993 75770 24956 5322 16151 2935 27984 93211 71433 79993 13071 27468 15312 55564 41013 809 95465 20708 84664 85225 89630 78639 65165 94972 973 34066 47861 32038 72453 93402 54081 70936 80424 70093 34909 91934 54730 69315 49618 59894 89176 60610 85012 61130 37488 64545 49389 94110 39681 37630 35225 6897 31 91921 90304 54682 90412 4221 59863 69917 65164 17677 57759 46984 25385 54590 11609 96921 96027 24757 77770 37939 63125 55574 86848 53914 91484 95041 60845 90881 64025 18519 84395 75487 76234 75799 6829 87798 95751 73655 59829 2065 33397 10292 11748 87094 47048 92885 64773 86251 93182 16462 22787 28492 15453 58089 40328 73366 92449 57471 30777 90114 9312 29160 77452 99733 35769 15575 16689 75359 47759 21385 11896 5094 93213 13970 15897 34338 43202 8403 77123 84389 25430 43770 45105 8969 10382 39041 5243 93044 61393 91182 40610 14883 44501 34642 36701 55050 85673 46966 78161 67942 31080 48724 4613 48568 19295 59441 67036 62577 59433 12126 53271 24540 43287 21147 48020 79484 61382 4906 82900 19150 13473 45069 20370 82258 8397 55673 27167 65727 49267 28441 8369 20019 73639 51843 35197 1135 76916 84341 15731 71182 56743 77646 63068 21905 74230 4212 85948 89468 12302 49443 97153 11292 52391 12993 39433 30537 96330 92917 12311 52614 8595 56744 63651 30821 49302 54413 70573 40059 27997 35041 34864 53907 2305 31373 89873 53175 32615 33899 97737 49010 62906 35339 94903 59084 2589 96569 65736 20061 6685 93889 3782 23859 3227 1019 75111 65645 22075 30006 4771 33146 48101 82399 94158 58156 29813 20431 28933 21256 69593 79920 16748 81116 41599 52731 41004 18404 29039 13955 64350 3834 86869 51478 4333 90453 97979 91036 53789 27951 3872 67274 79330 4580 43393 82111 42878 94119 74544 84869 52521 31348 10065 27140 82517 30374 85105 84474 86299 16611 40366 99170 39508 38280 36991 86796 77210 81218 20352 1099 36993 89261 63766 59400 19864 50367 35381 3920 83178 12572 20696 85615 34086 37374 73024 90918 83068 69101 10048 26961 15223 61593 51537 52668 68932 78280 2624 36358 53079 40532 94773 5041 26543 35000 16860 90139 49223 8404 9154 10575 70665 35074 40463 23606 78743 84171 53441 16170 27822 48707 1 51614 25188 87372 57713 37954 56772 96796 96998 15525 6135 43810 15878 8543 91388 99868 1389 6806 86275 69481 80478 5087 26200 57474 7026 49140 7806 55469 43300 78333 71166 25025 99522 41631 38641 3658 7292 73610 29361 55993 5125 46281 60443 75436 61730 8534 38448 6548 63787 99641 82389 27436 37964 53139 75445 72621 15816 1395 98576 89994 33855 96881 72189 80137 45730 63673 15663 2411 7100 28975 63093 27000 50066 76252 45765 86238 49858 95499 34531 22553 21885 55610 54516 69316 75808 53583 52279 44780 83694 80151 70777 12022 82692 83627 90622 24746 30072 67406 4338 88667 6270 25158 17531 91051 78891 50296 87044 94455 9998 33657 62453 24817 76433 12073 55692 63053 58616 27447 92505 9221 90893 76598 4744 57215 61885 60814 180 68409 59491 64872 37004 69553 551 57300 22488 59048 22801 8054 15859 88536 70495 3697 62127 4975 11691 24189 87450 65881 77638 66245 14651 78543 71232 79579 61072 7941 84939 6317 1274 54339 73198 60777 6085 35913 62955 99947 33134 45002 24989 64018 60689 58039 53442 92246 64420 34374 7520 88458 42303 50547 21415 55116 27565 493 56550 35800 18446 94589 4498 47026 79580 48997 311 21455 52129 70561 75276 69848 78445 7147 71521 85180 73894 88848 32642 26732 10673 99788 67120 4787 29302 94580 87023 75216 27354 86872 69839 88830 84548 34001 95590 46319 36193 66997 34491 52026 48224 59416 49634 40886 99427 70550 12965 88247 60516 78987 21292 46883 97377 55248 28647 78640 48319 83720 97774 51803 12620 24925 54050 43933 41681 69231 59820 22758 98252 14328 31346 46283 17608 64535 80321 50167 27736 66131 55783 25127 8832 68374 79163 18607 27494 28795 99126 64214 83652 13993 97126 28147 8891 53548 66889 31908 7460 60866 29477 81849 84790 96917 60478 2753 84288 23764 34027 46219 99198 65882 11456 58603 72390 7837 47390 49956 49367 77930 29247 59385 27901 3806 37688 65051 20594 54747 10583 16734 43203 72823 193 55852 46473 1573 78474 50635 69908 90225 38830 98809 60372 39471 7493 16563 93235 74811 65793 40684 1032 67940 53792 21912 82091 65413 29056 35201 16070 26494 36237 14839 62503 38947 2700 88998 40343 49984 80642 62213 45329 46966 43140 6585 43698 77943 73940 77499 79884 6546 27598 15733 58986 54733 46132 58321 72500 97552 2638 22167 75848 60733 15807 72367 34685 12475 54578 55103 58978 89187 63858 98028 5118 33437 54235 99227 42331 37006 43864 43946 31649 90417 20534 40046 28756 47612 17134 64105 29793 72652 28994 83939 36229 81921 87526 92424 32594 59146 26035 18516 99534 23510 26233 94591 43340 923 10950 98009 64856 39400 34217 98399 61659 26691 52735 93309 26337 91266 6182 8396 78264 99354 36606 78686 87482 89877 39001 88057 2275 34920 78603 39391 81714 93075 170 4673 16102 94694 79893 60849 2006 23895 9762 85213 88414 27042 67686 84502 77847 28882 3410 70884 61514 80126 5603 10140 23755 97102 38521 77747 54707 6509 39099 46731 75742 73579 51480 78376 80224 48244 7501 69112 11630 34040 80761 89973 29042 87776 51372 87053 28317 58268 97003 39263 1524 12677 58168 60145 15395 29241 90982 58250 25856 30109 58907 38040 44352 55329 7980 69651 13396 93710 97675 78476 71989 99489 92756 74736 50738 56012 76619 13610 67061 18477 97955 27028 50863 61002 62999 23199 60639 18633 67352 24657 56476 35249 11866 61680 48981 28951 13270 1777 80826 40371 13164 52986 66428 62234 56696 60452 13126 27812 78317 82822 71111 48556 56317 80343 53120 36022 27684 17206 75144 36246 67850 61895 95978 45414 15108 46811 19058 59416 4891 60822 37330 79438 56474 6620 59684 39571 7457 80015 57267 54329 77865 43088 90852 79685 8064 27698 75391 79062 17153 9495 33884 836 63078 2589 735 46677 7333 93616 5744 1033 19554 95333 25636 39805 3367 2181 48349 29420 42020 58725 75001 94734 47491 10441 88866 41732 73142 66918 48047 42160 68405 69341 80412 67076 43436 98099 32968 23184 1232 15116 40759 58508 12716 84774 61979 96144 87514 48352 78161 80974 7015 92603 47803 12768 37361 9378 61652 22765 5181 62600 41816 59434 2928 46033 88201 74499 54754 58793 33941 65504 3651 92723 20183 90839 27638 49150 82868 53246 1627 8439 10939 86295 58173 33292 78662 98805 54646 76919 41069 37771 95473 80750 80260 19148 70859 79581 59141 48551 19551 4994 10422 60703 10318 43357 18055 56409 58672 10521 97064 84534 24512 54137 86090 66374 40173 26045 82513 12274 84929 57409 64209 74499 5148 72214 67727 91071 38494 72398 86422 9251 16846 14507 42429 66651 87856 84218 52912 53690 10106 56767 40438 78418 53778 28091 85540 62908 5157 54381 66284 25203 26558 1227 51707 3785 97769 38930 44257 57863 14489 38504 47655 82862 29971 58186 97915 30333 94093 68999 41393 1516 35398 19441 91528 64480 12625 91302 21499 40384 9611 68024 16289 79299 38708 23328 88766 51873 43788 66636 36551 34812 48827 25240 78970 28539 51641 11202 85678 71589 3039 56199 62607 83725 39932 50085 13942 26140 30850 41445 46699 32160 3813 41247 29857 5470 61981 22539 77820 32454 33739 58924 12709 84202 31939 40518 39859 52619 51064 41245 94453 25068 90484 14510 49819 5603 16934 20894 56111 19107 93260 27112 87757 88109 40846 44718 40883 17270 92298 12486 2060 47705 54408 53210 44542 51457 47593 85446 47897 30087 99856 65490 65598 17088 93439 76118 57417 38550 25968 25672 92234 55712 29230 44889 91978 6020 25964 88262 87315 79898 81637 51180 38764 21222 65248 19559 9326 22763 67952 28611 72082 31591 61329 65832 35943 20830 76583 48633 7359 49345 44485 73783 7034 82075 88379 83955 51575 8220 25680 15826 22289 96711 73141 50600 93047 86359 34245 40618 4932 99667 77548 8109 3099 5096 97708 66570 65427 47997 68620 83834 27304 40127 89753 75089 52826 80646 58243 63301 85504 12046 91063 47990 49956 16087 22540 4684 60663 90328 12751 9729 14563 28277 4799 34049 34934 63529 1135 37029 72240 97715 87803 94990 43255 38064 16337 40802 44069 23400 25734 30909 57525 49434 26050 99436 27893 54373 76437 69304 98090 80540 14731 47524 24425 16054 6702 361 14871 1898 55093 91211 54872 53444 83730 60259 46597 44420 88880 26618 22004 38315 6650 80825 25756 46638 53198 48496 21277 62963 6698 32965 48183 86398 17795 56251 29419 85784 62357 90008 26365 73067 55333 54103 29879 8672 60071 42444 24737 94293 14729 41109 56206 55208 72224 95698 77904 98224 81262 7983 84877 67002 23642 36782 60266 63928 26264 13662 18796 28135 59519 108 97531 60500 28441 28239 19296 11245 35043 20423 16403 5288 72898 76243 77683 16444 12013 67186 65488 88733 13752 56408 54596 69716 56091 97238 1433 82370 70962 17799 30211 19545 52632 70958 54899 85220 9908 74873 64108 68957 52202 37723 96119 45653 10831 30315 7168 20210 53314 43569 74559 36973 42593 96458 45373 45826 12122 18802 89131 63175 61218 34811 33572 14280 79252 8655 72705 95091 46360 66703 74828 99790 71662 53002 67815 26393 33709 67805 46921 58569 29934 19583 1288 57006 21175 50595 21503 42217 47050 76853 94595 55373 81931 76832 86674 55233 78912 89869 63523 59331 30677 98792 23511 99182 57931 46970 80132 68386 39351 62426 76283 22491 92102 50733 90845 28778 92043 90808 9996 14260 3653 47694 13542 80029 46040 13958 9805 34719 99281 63523 20202 19425 86467 38011 61241 37397 43523 92199 26368 14291 47181 87928 20056 77759 6287 37232 64033 10557 1463 54976 37252 43292 7438 75825 79872 35368 84177 81496 54152 69979 6494 8480 80489 64934 14635 8150 11020 57290 26056 28284 22070 96570 85661 54740 65887 17931 30458 53149 63470 93306 22254 76137 31763 36083 24509 76670 36722 32750 21334 64384 99314 12474 9094 22499 97986 961 47534 75371 22621 45477 69489 17197 44164 27159 81666 93062 43669 11940 64406 1215 81335 38239 10853 2897 20331 92234 79175 61734 99377 51778 23372 30697 86053 24933 84348 11203 71676 80510 5431 3492 30352 41281 72038 43233 40157 75821 35549 17016 25850 47398 94224 89648 76303 84539 6359 50348 52909 84526 85591 1141 80216 36562 75044 94989 60258 43417 34789 47718 60601 97456 19571 8036 68687 95195 10359 58050 9544 35290 74449 23328 98197 59660 82273 45610 82834 37705 2898 27306 56865 4225 68655 15604 11275 37904 77503 40626 83666 16177 5194 98435 76437 82335 19593 72533 23060 42453 53935 25355 50929 11539 55489 27752 96217 83990 63989 38173 33867 60056 55954 92544 76 90667 70185 20261 57861 10240 935 55088 88178 38010 58715 17322 26432 20883 95159 40508 26792 55819 14826 69868 20319 40881 82617 69098 99292 46197 5582 17104 49039 98669 63116 79002 62354 29889 39471 76781 4876 27956 93029 86612 2618 12101 162 8904 64474 52929 68208 6854 22184 40895 14768 9657 60579 34527 21810 95874 19324 33877 13030 25611 38382 83688 16930 60535 20614 1844 21722 52104 95777 30223 43662 52061 62981 8222 63009 31961 3353 58262 86376 2849 15877 481 45123 69825 69649 46828 17275 2152 49418 19418 78183 3390 67985 61049 64144 27350 71633 69891 66235 45583 56257 80777 83854 8075 39385 67650 63206 70537 83671 63890 76048 67061 88416 47044 19411 64856 14376 5841 61041 92764 37209 99734 31221 98473 30852 6453 69549 18303 4401 9009 24822 81172 65859 3670 32481 94988 77503 50191 62894 2540 84740 24800 59138 58197 96968 8346 1069 48220 42367 97560 6533 28369 46459 4046 96429 60669 30719 40018 16788 86175 72083 12114 96077 78889 3556 78410 67777 86599 92730 37569 36402 83857 6312 95396 21162 76281 50895 88951 92421 6479 18213 5482 43219 92551 11344 60649 4586 86205 23250 62739 44852 55387 35931 52892 62317 32702 33561 69260 1903 66465 45294 30649 81448 45510 53178 93791 37920 94951 64415 40713 79516 28847 55558 12905 93717 19113 18390 2345 90678 8667 97153 3004 13245 63816 62409 45124 80852 24802 54942 24275 82815 15227 55735 21252 60022 97144 63860 37928 45268 57771 97102 56947 80837 31604 60024 155 64523 15860 95293 90316 13355 30951 45073 29636 47183 57222 35497 29758 75418 2118 11754 85077 84143 1261 13536 74926 90350 90354 11240 5552 14160 85833 17568 60148 33112 11274 30906 72590 67701 73493 26648 76755 50060 52972 15547 24046 28866 96388 23862 66786 92601 91444 33796 46583 54544 17765 97233 75163 62657 3114 77604 93833 43260 82412 6600 97397 50312 72227 82070 94126 20551 70712 3057 14631 79287 65650 13990 78226 9338 39156 76570 94627 38133 32553 8380 47893 68678 52022 15420 88309 4835 84282 27184 64642 58562 81039 29493 89168 55920 34193 97757 22960 60539 50831 72272 16295 68144 53921 68934 45946 54720 31750 48676 44352 19284 50606 49352 47220 81640 50847 32784 45022 28074 67241 98482 30697 44104 55325 43493 43296 22894 34051 88328 89966 48583 47127 66645 69319 3585 64494 27543 4847 14485 13904 25631 35467 23897 30358 36581 96307 82673 64068 64350 32474 5956 94744 74157 77882 21598 9412 17485 62332 72332 4955 49559 5211 66097 8764 33831 69605 925 53740 47671 93136 15608 85487 88810 16973 72393 14611 28249 88408 27871 32691 79440 62093 83922 32984 96446 17187 94381 94381 23573 15606 4314 22446 9532 67089 82691 4186 64358 15369 91154 435 10767 24128 66724 58414 19574 58333 64776 72293 58200 2391 86979 13723 62531 17839 85481 85076 67492 54238 87242 72225 29030 78340 97529 84387 45624 35575 24000 67790 749 92104 150 44918 38817 75432 86280 86035 47387 88925 17384 32590 61597 93244 77290 93032 31587 27484 41196 5016 25981 61039 78375 35866 9293 48251 78674 25017 68314 36608 33690 42837 27168 20141 22467 57709 35384 74714 40825 65599 50005 76277 18682 80215 99735 99749 30853 66928 40793 79642 17872 85031 27813 66857 49480 77535 98503 7535 72991 15575 77940 63656 2093 9993 48659 24113 64575 83006 91245 33951 55900 83422 86712 3159 77642 29680 47837 5950 67580 27960 16911 77790 24474 10661 50428 35119 32082 97553 7936 50574 41188 2146 26735 24597 86264 69094 77426 95858 20774 61844 57171 96855 21139 126 2990 91607 13002 77940 10131 75925 34753 4217 3242 24603 13067 55725 64338 49935 50760 32398 81609 46857 19983 89063 60445 80221 75645 64895 52364 95761 26482 55055 44339 99490 57186 58638 88059 16030 25781 13781 60785 98692 94401 48718 5194 47339 33618 94577 74804 80445 2807 97864 3921 79854 14220 55955 91488 31970 16319 97284 13397 38367 62126 77113 65911 61598 26051 6459 85214 82965 20078 44122 78359 81357 76579 51201 75581 66807 94576 68059 33776 12103 28053 53021 54799 56751 61476 828 81238 58392 92182 18350 14297 77381 74437 60690 78433 7439 38417 42212 11257 55559 30789 68109 35179 93222 57272 45430 33339 96841 17918 4278 12002 34048 95986 47837 85968 33933 3150 58395 61440 45525 6912 93641 52109 73976 65484 51907 94033 76943 60890 66015 80625 89808 77542 60571 3224 9738 75165 22077 88189 43917 85907 74825 80677 3167 91190 55141 5458 55337 39541 95505 78134 35593 95156 24189 13458 10104 32664 16334 7882 37041 88893 91643 81234 53853 59512 63388 88057 94458 46915 17856 17982 63644 10997 1243 86070 14307 33630 83248 10734 48456 37145 76103 30151 84483 37965 40102 97079 44369 4506 71915 61387 59121 47215 66811 64333 5371 43492 82230 19780 36091 39559 26420 80406 79923 3999 45131 69457 56546 50995 8072 70903 58779 99108 36896 85697 42955 92300 58549 27578 61732 65847 57474 64123 72666 38596 85412 46780 60821 56590 22905 84836 79844 46473 63739 56995 30107 69623 17753 76797 45517 45254 36668 24135 66228 48358 49552 42586 57906 41415 2322 46657 7589 2809 21628 30017 63673 15287 10844 42678 88789 75376 96130 42773 25903 39825 42000 90346 53800 93003 987 16124 18792 54626 18242 57074 83828 23212 89378 94448 91006 91786 30046 10886 16597 55555 74493 1713 11522 7158 2610 78508 73380 48975 48090 74140 44589 81483 89182 39822 42714 2467 54075 60607 74852 1676 74755 35139 37523 27305 56688 86371 28487 72186 21049 41826 71606 311 65518 34975 3670 76833 26669 81003 12875 87650 48016 18891 62385 49735 29905 72448 92406 57834 56083 87298 9171 44011 32651 3857 51879 71544 18500 38249 15263 68422 95988 65833 71221 48810 24919 62638 59466 51260 20976 64001 9285 89305 86209 48722 49917 26766 23954 23012 35485 61663 17974 25333 36995 88479 84263 47733 83529 89753 28546 91069 95718 60405 45896 61726 1484 67114 3204 50225 83075 82150 61014 92189 21605 892 52503 21967 43081 12724 39697 38664 96535 41218 80319 98957 38138 48620 4383 9311 3519 40212 65870 66889 51808 56866 58163 7748 91774 24763 13450 79673 95163 17072 33173 6960 45708 2048 92475 8164 77160 16014 59506 73015 35766 4849 23173 94216 45832 46454 6379 53976 53566 75160 25423 84951 70507 19574 80746 3813 35260 17384 58668 94380 1679 16146 38792 16573 83128 79420 81747 80864 74374 17174 17406 2196 97353 77353 953 5454 55768 53503 70788 82418 68647 27818 53024 49320 2471 40278 20270 41790 71560 96710 31118 69260 36182 31313 86922 82758 52032 31072 57308 55640 89029 56679 71183 92740 13059 46809 33340 11713 51366 44891 83978 63752 44310 61560 18566 74832 25188 63986 47222 72196 57146 91378 24793 9538 63078 31553 92642 41945 57871 98819 26308 42796 36228 50939 73003 46159 87853 28958 28791 82667 24745 15892 24581 59095 21353 30158 43690 49090 92816 85092 83345 78493 12814 11509 44739 75701 56950 743 20984 10871 75756 80395 22947 89127 41653 57935 48931 26040 28759 17981 83368 57702 51194 5410 22013 26498 67852 5308 43268 28098 92586 70934 46494 29647 20749 25720 10064 4379 16508 58274 88388 94266 29133 67204 91494 62638 70303 56447 5449 15192 66300 98620 67092 16989 67058 17576 84770 77244 22191 31373 14386 88966 5155 31480 43513 25764 9574 54251 31358 52938 8438 25650 47001 24259 33211 46171 98027 10778 89354 19480 88016 20592 95252 43298 29501 74854 29751 91865 32870 67833 337 14700 49751 66431 40719 31165 79131 36067 49495 90540 22355 17465 82520 11986 92890 16645 73515 97310 50538 43591 85470 60788 81951 9587 11697 12413 58270 83650 69384 80940 86071 40869 6410 77560 8733 84473 38286 40366 26121 94416 17142 90001 23481 41917 25009 60949 33835 3701 45741 63993 34924 63580 59820 3009 48799 41323 18677 76186 58540 25554 24079 64536 88223 89925 68437 62889 83287 67219 16699 11862 30311 53239 47452 96457 99635 30613 48986 4397 74063 61938 1117 38027 10339 34998 42224 15937 69266 93939 21732 55890 45739 9068 90366 49516 2115 72726 19724 57875 40861 43624 87776 68016 85636 7734 84032 49261 87739 77896 82696 21784 34128 76949 76312 45571 47395 62763 40838 40900 44819 64187 44049 62738 77388 34286 76802 7676 53123 58611 38651 63889 9207 9254 67148 13218 86489 81288 41757 45745 71043 86734 16958 43009 42478 54183 39617 24051 49618 29376 21747 43045 98321 27218 83193 58389 77449 55452 59520 62678 42288 57450 95443 33975 45322 10791 80762 76331 64370 33724 50665 76120 90345 87912 46234 91557 96577 29953 2587 19935 49939 96134 88650 19236 81851 79620 98845 74298 97319 17638 43049 81374 71647 79763 9980 29005 82445 65902 81428 203 1865 10664 8240 86234 82104 72387 96298 52958 52190 99187 99440 7110 6496 39464 90923 56325 27929 92954 15623 38503 18870 55582 49634 69529 98762 70969 58977 78685 26142 65753 4054 88978 19456 29391 18002 4280 28638 47118 32277 77902 76607 89911 68972 34673 90499 56641 45143 63507 21826 97314 35119 10562 70375 50150 23917 43033 82724 47182 47471 22590 40620 89033 2564 35752 30367 4614 92499 86097 45012 41023 98364 48713 86725 22603 33161 48427 5192 94340 47914 46254 87339 38197 38535 19922 51148 13560 35494 73627 63477 30413 60302 30203 34 79026 32129 22872 2614 53558 9899 77215 47389 61941 60484 96054 61943 15299 44871 70778 96451 71119 50561 68191 29167 18528 52251 73226 77300 49005 2280 57661 64807 2066 91597 80594 18595 50188 32002 55809 85194 20021 90133 43596 38825 18007 76815 45055 28538 13861 95582 70999 27963 49556 10338 57005 34875 65797 31255 65427 21791 52982 35444 49601 52130 57592 68044 29664 45449 22410 11755 4472 94015 87102 77405 24937 22467 28814 91219 51380 58058 40073 89971 72228 96194 294 72701 43826 6680 82172 65305 64351 64570 25877 26516 3432 47448 54000 33514 42182 28813 99203 35981 99001 48649 36594 88951 57499 58764 88903 67710 9975 14637 15184 77012 47478 72054 2719 25268 43007 2585 22256 86137 64931 39962 16876 49540 23337 13338 12396 29344 65041 9003 36477 48951 18846 46858 33582 60955 58214 69121 95480 71599 98313 79252 41768 36871 77191 14840 36049 44318 14521 94879 78075 11078 51618 97927 46114 57768 33375 90327 65225 74123 71964 85608 68763 52765 8920 77378 69764 76331 29964 43558 33501 65303 29971 80484 23679 10836 83641 997 71474 22582 52611 9296 31276 73825 41581 14299 49822 17279 80277 16710 12853 60747 18006 81943 31685 19803 54498 28522 1428 84332 40416 64393 98300 3488 58628 11474 56634 86140 85285 21482 62371 16692 93409 35138 23661 58349 63145 3300 53384 79362 56858 53349 97960 20070 29015 83764 83315 41733 44841 42001 50992 25175 96211 86263 43678 50894 44901 53905 74833 43929 86771 11718 98626 63321 67848 78145 75478 22595 92219 43799 12558 62785 20378 75211 54578 27912 92781 92878 97778 81627 94408 75902 8089 80270 64952 40653 84009 88187 40235 84245 92611 97618 51796 92842 86690 51825 53931 54417 85267 52179 93343 94205 30799 95870 61606 73298 487 46672 59258 76096 17959 2599 94075 93955 38742 89132 99961 42517 85739 74655 65634 98272 98019 64590 89624 91372 24301 86020 36371 92753 96799 63319 35987 34803 84929 26224 26038 29241 28876 28561 45334 37430 68683 80722 5821 93073 722 72690 69962 99245 20663 64969 28335 66246 25646 71254 34935 89608 47272 83199 93707 14060 5712 99516 35675 8587 91851 26107 93643 8601 64468 33985 98704 73054 34481 57012 98823 38243 23823 72174 88787 22621 89580 3179 76704 23486 93189 34345 68438 5763 88634 18565 84781 40325 67327 29132 63844 99406 82844 53440 55934 16252 99427 94426 60299 31050 5105 14582 71932 12598 50038 35066 2124 11896 88948 21274 23603 1628 14456 50424 39684 16391 67579 68767 18217 12785 68591 5824 6694 67283 59488 61348 90067 87392 15329 15995 94084 37167 18968 4066 46306 38608 95264 27337 98818 45866 80624 67899 91896 10324 66639 27148 37871 51079 26447 9663 97584 89460 37779 32503 53719 64250 20360 41178 34779 4935 35488 87568 36395 92896 84498 1045 9072 33899 51054 77022 61221 44667 31257 92832 76109 34461 72680 36904 26718 47382 20043 74730 18124 81717 28067 59817 61781 12221 23914 60979 45776 16078 6918 6962 31463 12428 18597 38703 97414 27134 85803 99782 48695 51058 49535 61293 75940 3163 72289 34248 86361 25150 88766 18904 39936 24462 40846 25290 22495 13849 45665 41404 38893 24665 80237 20190 26375 52674 9498 98829 81538 78512 86385 61712 21052 64607 40098 65516 31328 19565 74652 78040 34408 48597 153 70456 9557 87363 91526 46593 15493 36887 90287 53538 86949 11204 43223 71296 23761 17550 13249 75668 87634 83322 72419 89548 80817 40461 26482 25053 72478 51517 58623 35161 37172 32251 21239 81467 40777 7741 45427 49304 59507 50111 66434 28772 73732 29169 62095 36961 21562 78658 93937 76406 32740 40985 1604 82896 24684 93362 33168 33642 29302 48641 62403 93656 33754 56026 85344 14938 68062 44019 47961 20644 23839 83523 51091 10643 44799 58748 26549 55850 71289 30313 38090 70145 85813 47266 40311 99280 63257 13969 85423 87906 4402 25294 53473 51598 46788 69823 39967 19024 86180 33704 6846 13401 49474 37792 41254 63593 67986 95122 15083 73784 52941 34978 54117 60795 20111 21154 46811 27276 67958 98203 92574 44252 98330 69795 11835 70426 22389 26207 41389 62111 65809 54082 60205 31818 73209 55107 47341 38549 73404 97548 69672 60473 95874 62094 28146 72052 62782 5320 90190 93528 28292 51861 74854 27221 19407 30140 77363 87557 12189 94379 9869 79146 27098 44831 3913 59616 91908 41048 72383 409 15347 62249 60190 25174 55961 37074 87061 41801 58313 50530 94763 11631 7619 58975 31961 46687 72167 49963 81947 82967 75974 55467 34516 27505 3630 47680 36389 15985 16985 78098 94207 54595 22523 88506 80158 54073 45907 63196 12686 38889 21589 68059 42672 86282 28939 73614 83820 22228 56583 3896 52950 461 38154 83865 73098 49470 49934 75490 54565 53774 14061 11162 8826 82910 51620 57517 93639 44254 19857 55124 77915 14039 96282 9701 793 70810 85201 55997 95505 91989 21889 78924 51853 63879 37540 14202 91608 78462 24478 12815 68690 27966 76011 70875 98239 36728 98579 44181 96665 9010 59983 66094 68400 54652 15330 10985 88048 48295 27338 13793 40375 48915 65013 43584 97344 44860 9131 2484 50594 23010 10807 11295 58024 51916 92849 50553 36175 99692 94755 86790 71588 87639 35277 20487 90337 66081 23468 8122 32917 41639 51919 19538 17358 27688 99029 64917 73099 48867 65032 20188 58331 78137 54551 8218 79933 93824 43048 37242 56148 53547 8854 232 76554 71537 2886 31533 37653 96105 7192 76151 40642 12913 61298 24200 71710 91326 94889 37410 72719 94697 65044 57979 98491 92021 40914 71081 15797 33655 9837 10408 9839 47030 77556 4890 71967 29083 27695 64748 88774 34042 29332 24922 39188 6807 72271 51566 28800 23241 61865 82357 59713 4708 81509 93594 20999 6841 53548 33263 84474 99150 62079 52887 40780 34964 73494 27207 14709 73521 7856 7425 6930 47210 71536 66532 26772 30168 31063 93873 36429 92028 21147 15403 85155 20960 66433 43146 61645 21854 2465 84847 21189 25866 18468 20144 77968 84850 35822 83875 64125 92419 76963 63023 67744 25044 33741 67274 17005 92700 98589 97840 44227 65439 23895 24889 96595 28629 47289 60948 71991 88194 98478 41993 79663 58607 11038 90464 12415 4248 79292 87946 44390 58598 30369 97432 70050 6449 40104 70021 99098 54866 44233 22285 15724 34163 72576 53332 55283 36194 70464 13487 48315 47255 68026 6532 73883 65718 69917 4779 34962 90240 25972 88265 19892 63386 37629 57123 4283 35757 88079 45198 19179 53383 15993 11038 79061 17075 9586 34611 1018 31510 11686 62656 81140 91852 3499 34062 58556 70865 51413 21913 13527 30284 96924 57378 82845 4601 73127 22038 31600 17032 28714 75532 89838 29911 72634 23621 80060 90701 87542 71260 97438 23384 56393 80026 40825 2877 36369 43398 87154 66182 60516 51253 91125 2503 81346 88284 50546 45159 73640 44080 57539 64361 66497 12962 27430 23072 19327 58737 28198 28467 2856 67045 21039 74302 29141 40396 39391 30273 6895 80667 33413 55412 55489 13937 60943 56251 52354 79267 85107 15826 17893 74530 19018 39870 56650 40937 86283 17337 74783 99498 19111 87477 89465 99859 78273 27314 20135 3040 18341 47210 99821 98718 39096 56135 66152 67938 77739 75012 18883 54464 8707 12832 28599 94645 33648 86125 75344 29685 76982 96615 90468 36319 35462 82317 98548 36179 56403 9139 61335 48934 62567 45904 41784 74760 90578 98696 73271 38706 96690 2532 52410 40313 19319 81275 83367 91798 70000 53739 64179 71504 32511 9158 92513 11600 2882 70082 99958 52249 37215 48463 29548 9553 38715 91344 7582 35850 3528 66964 30043 82155 20868 81228 67129 47696 71253 56444 51273 4523 29644 63811 85473 42777 479 798 56188 3892 24402 82371 10144 89296 35474 10914 78594 16930 82048 53924 75100 40588 97027 82832 46845 56196 61433 28566 60562 38979 15025 65888 79959 1407 21743 92103 9348 61641 85621 63973 73163 19964 51068 27098 90395 91128 74724 83132 84806 71758 59543 35860 60531 16662 1839 72308 9974 91575 77186 5481 31711 58601 13805 67509 14640 98611 1891 11098 68928 58217 16413 18527 17579 61124 32000 54370 36732 3335 75822 48434 74117 63850 52622 31382 80588 91992 21166 27439 10783 88377 11447 10760 79324 69776 59785 56980 84937 98747 4458 55806 42383 98263 62046 66176 29840 99727 68601 2046 96600 95223 56697 58820 60069 7644 65896 35230 81065 65104 7869 6823 24362 83193 94569 2583 79208 67700 25333 43345 2933 85431 35268 78766 70936 95393 46033 32446 39796 60689 89833 47709 6400 50648 88745 5053 45377 63091 93551 7474 56203 21062 83089 41492 34235 44505 41647 79818 85985 96495 27651 57248 45778 7057 54436 52026 56326 7689 51478 34966 27761 67346 5926 82832 35176 20560 59498 22648 46381 25853 52993 43496 52685 41692 68904 78646 88213 89375 36227 17691 48010 86131 8869 38257 93409 4888 57692 33672 74547 87869 63365 66435 69929 6527 33206 59105 34948 32579 15676 34190 15316 95546 17656 60807 97421 33369 81602 69832 63520 42996 52364 46569 69108 86488 23819 8033 45978 68024 31367 86036 54026 91053 16400 58850 18234 6982 66394 95720 67450 41861 5532 703 1464 58051 69936 24552 78310 44112 81680 75559 62752 48661 65147 11737 70315 76512 40139 40101 96349 83554 22011 45908 83847 64576 65115 81179 91164 18106 89834 32109 18638 39848 58746 52649 30775 49764 96545 70558 34893 1270 92784 10317 27559 66028 67274 1958 87261 98463 34732 79522 92015 67369 67325 27732 95198 14289 63748 69299 34398 75646 79438 39651 10924 36627 43687 96534 59482 959 24600 91643 25366 93822 60791 64053 16860 13943 54929 40914 70657 5105 81002 98933 90557 8088 35321 47432 18744 87304 46830 32902 82533 46403 99955 98334 20884 33218 79561 96590 10656 76285 20465 4356 12298 90117 436 34882 38671 11900 62327 84175 54598 858 12107 69137 11457 41861 5247 86364 15008 13946 84693 36108 41631 55011 70169 35343 35650 92271 14757 7075 34280 56928 3294 19288 14152 22046 73222 73084 72992 58607 20327 47894 6345 40990 53822 3418 56252 46471 14093 22352 21603 80163 36322 52103 45460 56438 17357 67145 72267 50843 3895 95797 73184 51249 32687 3294 85299 61172 77464 32303 37226 74759 50929 15806 52326 42644 14035 42727 62176 49660 98849 81630 69024 91735 74446 99082 52530 29648 61581 67586 14430 87003 44358 39338 19905 26919 15261 57935 2441 14883 94906 19244 56879 57641 97630 2850 56563 34373 25162 32626 61038 77981 59237 81707 35381 42523 71604 57890 2268 84625 80076 23769 5303 45448 25867 81615 34636 50586 7766 94061 93423 78054 54051 23882 34040 61054 9031 14553 63741 1847 94091 89317 78271 72856 59907 84154 73770 41203 72793 930 28569 91719 71464 55221 13615 90148 60790 52960 48429 1722 98298 38263 66703 3791 46466 88241 72787 79177 79585 35101 91339 68909 74040 23425 75234 36835 97921 26586 65489 64593 77802 56464 75058 81863 52491 70964 11596 48589 20179 60274 43141 14691 30765 21832 60978 39296 20712 31268 49117 85256 93118 8717 86573 43697 52680 87346 47295 80775 52242 71889 244 28227 14237 99604 62989 79373 48345 65823 38138 60016 23954 12420 28456 54582 13273 25461 2493 2128 29337 53371 24357 47441 94531 48200 79964 46596 17903 43406 88333 45835 71051 88779 63148 74230 19333 53305 75654 19742 84025 54888 93164 7872 69921 10551 8487 29273 15784 59256 83282 22272 54268 76874 31409 3401 27631 67085 46031 8489 6014 47186 1114 56653 40097 5938 63485 12778 7268 56624 55126 79426 94271 88823 89824 6425 28125 90702 86068 93505 12137 80348 26952 28388 96779 47891 92305 3065 83578 47083 74322 50675 58160 94717 24834 56024 65383 12643 80930 99773 86019 46386 9401 24060 85706 10722 55374 81070 24122 54981 75177 32147 98683 74625 39014 23443 52472 46296 60030 22647 61919 65421 25746 86073 18353 18535 10760 91428 78700 1616 77864 1713 96052 11396 9780 85578 45481 87661 35493 28148 7430 88153 22929 14798 97395 79437 73623 90191 19174 1582 3811 80330 40409 57258 37363 74216 60336 29295 54437 40716 82726 57644 72551 64487 81059 24747 18971 25481 76039 94231 98074 29612 93193 85231 40271 26935 29564 40302 63163 43250 65698 95966 41084 96837 3560 75636 7822 46988 5146 28800 67046 69904 29353 95054 5807 39456 10501 10200 923 23874 25115 31783 65202 52487 6900 39468 52409 71822 96682 32462 5216 73211 82695 7029 45436 86568 87889 57449 68068 57619 63749 6569 76370 2866 70101 54625 62304 34534 38758 90580 55587 78713 78362 31114 55661 50838 7909 36773 65631 44580 96684 66958 68279 50746 43240 82367 62191 27638 99555 16853 73448 20465 3198 13588 65162 17186 48364 21665 9701 24344 22083 6993 37795 82323 44362 28067 57648 2250 77888 84349 82893 64952 57934 66016 21610 98199 98059 77758 42601 15354 25943 33806 52311 78309 68049 69933 80444 76176 56970 83652 16515 35245 77330 47571 60354 90752 43162 78333 6414 85497 47299 35720 1458 58269 70388 49362 49244 61181 49790 82085 18319 40801 47831 70693 47752 33303 47974 66270 94105 30498 91533 99632 23832 48398 3452 45433 11333 37595 35511 62622 6933 21130 61139 36790 13303 97659 43563 91342 23561 56269 23096 34828 39004 22401 81384 97884 87287 24557 85947 36492 74423 53394 37590 23688 94637 7052 97687 47530 39889 88095 59068 10854 83711 34361 47210 33994 12029 49434 64019 7102 22818 41722 63827 19232 60329 80683 64975 72715 32180 36377 80003 31032 79422 26851 79580 17427 78281 85845 72697 35005 9052 59986 20982 99155 98639 21659 99015 29010 11173 31226 12136 90998 8086 94653 36595 34724 23296 88459 69754 79 4872 47601 97286 18887 46366 28177 17514 12404 9736 74669 26300 77158 56593 73981 15673 92411 49530 68473 97643 94616 86224 57732 60004 13230 79691 45774 53924 54051 99707 39298 89727 82384 51573 68249 33031 65798 31702 70716 49288 67520 20466 99486 11706 11159 52405 74793 63982 75828 26480 28069 42786 94683 96402 99734 89551 14652 72579 83272 88112 64079 1311 51852 56032 77441 90780 95589 11566 85429 89129 51258 69805 65707 24198 96015 72294 51578 53030 23369 54155 18672 60805 28779 96957 83654 49841 91631 77154 88894 85860 73563 53778 95637 48221 69570 97916 51277 65487 80823 83465 68948 31107 24869 26288 32065 9481 56236 24988 45508 66992 42268 67759 4858 62302 99315 18432 54187 80526 89280 27600 41787 99719 80105 64578 56124 84400 63179 13229 24456 29915 51740 7919 72511 73558 59152 65458 48057 7220 10756 99129 42980 85829 30417 86927 39558 40306 29454 47622 16479 88008 95095 30792 40026 87443 79333 39838 66469 43100 39939 25853 34501 63595 80454 25275 38886 88907 15731 1207 52230 13955 27046 17645 41200 63949 87162 28118 75779 35634 67932 9930 7467 80961 41222 68682 52512 55281 60897 79666 52569 47042 4452 46312 22084 6319 75966 8477 24786 33917 65546 85900 60160 19570 37799 72226 2437 14202 87350 88224 35875 57900 11241 36977 30756 91074 87697 78856 40083 25816 40326 60424 22044 94971 50772 36296 55385 73802 17967 77768 35543 79830 83081 38214 43520 81332 58503 11379 39481 55327 60707 35571 74491 80587 2101 63280 4842 76657 95207 86107 62862 39700 48902 79072 4107 38407 36683 27071 83366 23996 4654 68845 129 2015 51270 53517 75622 3777 31707 67630 8074 14150 17568 77282 75930 6504 49349 54655 99707 28745 58504 8526 44467 57144 22867 38912 34957 80224 56523 21064 89995 43087 24042 63191 22477 66992 67959 70375 57287 13004 82063 8512 63326 94464 1765 66373 61453 79807 92048 39583 24560 97945 30385 61694 88302 90963 89291 62135 71736 33806 30676 40214 6080 78253 44834 62957 69899 86112 75102 17702 67416 32718 68632 94668 14244 66010 46793 55866 91526 21942 98211 83742 34496 227 86525 47048 59781 55899 59725 97597 76106 31908 93439 80628 7870 92889 37494 58074 51395 40723 48919 65628 49705 53382 66919 14663 89101 37470 40612 80153 12343 97241 58681 82406 23736 95884 77023 65171 5804 91668 91532 52203 82424 42345 70553 17905 75582 51847 83801 28116 85479 40444 26231 84335 33407 97596 26573 6657 65502 96351 9515 11315 78431 76944 36816 91208 60329 60686 70241 7967 70675 21268 6268 17785 44565 45901 90071 87867 71348 97178 4055 52376 23992 19345 58109 66072 93096 74560 42553 69913 95426 45183 37815 71053 97867 1692 71505 17228 90906 83466 85591 82689 85839 19328 73548 9413 79837 68839 79856 4016 1566 12597 38833 97872 91445 41006 45677 79239 94735 93209 77365 67890 17462 51245 91404 48872 3961 40687 560 60315 7722 6091 91852 31562 23863 58359 33981 31909 14827 17791 40146 21157 56888 14185 25950 97527 33774 6932 64397 87701 95371 91681 3146 4250 37130 29148 54485 29106 42710 57235 37454 70040 95578 10164 66570 48795 87458 4701 31122 40588 29698 96635 3158 31400 99676 24147 44914 84723 73738 51692 68191 11192 26292 27613 59504 10687 90406 41703 73918 15657 34005 4496 1487 57796 82149 14141 45579 58965 77079 48244 43225 66535 6635 30476 11887 15452 80922 488 8804 85841 29078 53101 66335 98544 19515 67872 54831 86749 78802 23052 55644 83730 21128 24357 6649 74389 91572 71912 47364 75062 9545 99009 93183 1295 68841 44009 59439 74480 14905 14183 76162 23080 19005 51411 93878 64165 39976 74148 63660 24296 71187 31723 54789 46183 38854 27740 77269 90196 74130 17477 98248 66100 25789 62069 85666 19708 17965 2703 35764 63225 60738 55479 35581 33450 46461 62674 17742 15281 73136 50802 98551 36883 70542 66439 63933 85851 46510 76343 6884 48462 60650 25479 17079 62150 61671 11831 23659 37323 14370 28399 43550 22997 48079 80593 30272 40831 14109 94453 61524 44543 1990 21642 19726 19043 20960 57218 22680 46381 44385 3651 10985 78485 24518 1566 29995 40884 56915 33031 2060 90652 7969 17354 52384 3309 18448 74020 4862 22245 96690 27939 30492 35948 32165 83042 93563 74198 70815 84287 57 8743 65025 127 10487 4062 2217 31976 12851 63356 51933 66660 44690 54543 34486 81349 59451 5444 36541 95043 85158 56805 48335 32127 26106 34009 8523 42075 44316 19056 92757 38457 36599 1067 82670 882 31030 93020 94751 80725 68606 87465 95311 91193 57788 33489 68642 96993 64474 32213 95139 72053 48607 89423 33874 83227 71549 39909 38782 34620 57488 93314 34090 45245 4719 96057 61464 86342 78160 58781 1887 28869 37725 4424 91497 74705 31519 85968 63703 77070 71354 5339 92701 39517 82510 76341 58947 11309 11372 35152 79103 14504 22347 88675 20890 16861 24753 95290 67034 83960 50429 68734 65685 31312 36727 37056 66473 87189 7279 12074 57730 94858 4367 12355 21977 26323 12974 26274 91714 64047 41 5739 94887 31230 20470 45662 97089 93986 35052 47763 89705 92163 53817 39560 93268 16776 31958 22770 24361 59842 44648 93228 81721 29574 59348 11030 73885 39096 18477 58544 70571 74653 26478 90885 56394 68048 26740 32665 72974 29548 95932 50283 55068 17007 77463 97357 99738 14213 53871 73093 3865 88878 69573 84120 79056 24222 75147 54507 86532 43508 96586 77399 92212 41753 70995 90223 86229 57496 29699 93740 50211 51885 79986 11695 86082 26440 10134 35288 62196 21318 87552 78561 72425 96004 89714 33508 33460 43418 87504 70280 45291 86904 55886 29362 31807 28610 95854 74366 91506 43734 20343 3337 31787 16136 11125 5003 2804 40128 20176 71185 55129 60847 56147 49827 35506 7633 83777 91462 52850 67988 97433 28761 24635 5571 82960 97236 80949 15972 19269 13764 89954 25547 44052 16305 73014 71114 89573 60262 41604 16659 74115 39359 38825 83215 91340 6625 8199 8242 96991 86021 6106 80607 64915 24279 17984 18871 58202 78234 44785 67489 13956 62556 16739 35205 81323 33792 92014 75099 77855 71795 2685 11575 77449 25246 52689 25358 13651 31910 42029 16408 10630 26741 77754 63310 72715 28533 69266 66472 45351 16509 49816 20983 65061 18366 1414 21884 97851 23290 46193 78158 8309 59257 25509 48308 63262 8936 50110 20467 91194 58576 77530 45050 68757 18405 39216 24161 49464 92419 9587 81871 55070 18504 94479 59996 94941 47173 78984 34050 60971 43298 31151 1518 64476 69221 7032 80558 73097 89769 76159 44539 10351 97211 54480 52651 40270 18536 57514 17098 37229 26320 7888 22220 98185 41875 9740 11363 10552 31463 30265 43838 71431 41093 79671 65486 60082 24681 69394 38889 2991 37049 32365 99918 95610 40257 80450 28869 63139 51650 79806 53587 31495 8293 78300 15655 6043 87591 62845 57666 20829 43682 14452 43859 80445 81779 22377 52559 75032 18411 68265 11340 25488 14056 68709 53493 39801 13937 95586 3869 24989 56439 29300 76133 77301 11961 74430 16334 18082 67553 49050 84282 25405 80163 706 63370 99566 59979 42175 69327 39880 25504 59004 88020 17657 95087 91540 54143 17071 64426 38467 29454 40384 52686 40569 62098 67337 60908 28535 94886 12281 39213 4742 28531 19863 23420 62786 43609 82754 84869 56560 5456 43501 24985 94638 68292 61293 47459 17626 79172 3673 21751 89728 84689 61382 5537 63699 7578 6968 26030 19187 95319 63209 55534 38949 49007 25012 12476 40327 99167 31930 54965 39538 23991 14172 52639 56501 1905 95768 57091 14714 95550 82073 61410 92185 75620 45256 78369 19008 25130 34339 66664 15742 93537 50110 36929 18386 72919 14848 64477 57427 23103 61478 83886 52460 70040 82811 27777 623 24869 67334 35204 3713 78217 29639 75628 90740 56750 72528 12046 78543 35564 14449 25934 68740 49310 68758 91559 36052 30565 86254 15642 69905 61755 35452 47942 312 435 26044 17100 48537 96603 44957 86221 34427 79812 78220 21977 51398 3474 28889 22172 56997 98577 97820 26757 49630 30372 86905 59953 53228 96954 72768 39712 41618 71093 19249 55925 2692 55625 32905 30841 92546 20074 85934 57288 50481 7330 39705 63227 30186 40027 88594 95458 40999 6955 6962 17370 64264 8542 37363 37785 38249 2266 94900 18875 39286 15149 71708 33027 73346 10465 18165 19900 43554 38918 31593 92936 22601 78347 15180 5864 22656 88054 93680 40074 38087 60498 97699 55222 96734 18001 28313 36865 20276 20267 56180 50745 74877 37939 22184 13581 13247 877 65996 9311 10988 72813 90789 7930 32813 570 62090 12326 34739 45971 29828 44148 75201 93731 57899 84086 74280 67128 4033 84436 41718 80081 88646 18905 44793 34286 92545 45879 89652 70406 71424 44336 95921 24860 85068 83768 98866 85777 21339 91878 83704 24337 91796 73648 71190 46542 98034 61857 51828 71978 30646 9371 59135 7934 76235 11958 12008 58937 91469 53826 46672 75622 26214 57043 69424 98705 34446 21902 72341 53052 51302 60576 99614 7434 36838 80827 14443 32261 16486 17386 80433 29468 26837 48217 38465 44105 70057 76419 89800 16468 17355 34356 51437 59632 57862 78389 59389 13002 92677 79300 69747 92820 22100 45230 17706 9901 99639 27339 95540 14685 78499 17057 96148 69439 95657 4332 15197 2062 85773 84655 35258 38237 69361 95467 30254 15346 36826 20421 93104 25877 45178 28658 58247 22290 67833 3626 76833 47142 56611 94938 27304 39965 56673 44454 14682 72862 70584 99141 63815 15920 52584 21145 19362 27435 60088 20667 44534 89669 40568 33606 66063 5268 20496 23888 68758 8552 80392 21760 23276 53683 69570 88302 15931 97745 45548 50680 39024 31497 93840 42259 2194 53696 9929 68174 63876 95450 19779 1903 93287 46570 75192 55777 59768 23746 86084 25731 59520 60920 3580 80968 47955 54962 57115 41707 36533 83960 79905 50890 49713 67100 14661 47071 24777 64437 69887 56444 98432 27861 73819 80609 86910 18854 95102 26000 45297 41714 33789 6405 79496 51901 50529 36929 35284 66268 58243 65815 12690 46062 38999 3784 86372 88905 71780 81410 13920 4014 96713 16594 44101 20508 27696 39873 34585 62749 19756 23209 3530 76349 43290 94776 73684 11471 78843 33711 12147 59987 72696 61400 37743 62711 93296 75584 91183 66058 91318 85268 84279 44107 64096 57427 46377 95711 5622 77331 45666 56030 79304 81283 80996 47378 73249 80193 45327 52070 77292 59479 71557 1415 69175 66411 79330 26882 13078 95664 66556 10458 66736 94809 11483 9418 33593 51605 52068 55136 65431 87820 39135 31272 87576 67429 17099 54819 25379 79364 9290 54746 64544 56685 40827 91499 10588 71275 23766 94520 83915 20866 89050 91819 86297 97291 95920 93171 96734 82106 4436 80059 5566 75567 33387 5828 67024 1103 28727 5921 59876 95546 1711 78266 74394 65644 96641 14789 89260 63007 26842 28738 87945 32293 94724 77472 80556 58079 63582 11504 89335 39159 24953 33624 69881 41814 96666 35420 56541 37503 5077 13806 64527 3550 58476 32268 66488 24240 14109 91995 90924 9930 57565 97592 92606 81714 15307 21737 81443 49920 57189 78111 62062 46972 47236 75940 32011 29524 395 70806 81302 67663 12746 92328 36673 88210 78314 83840 37671 58664 22187 97096 32972 23788 28246 51502 32052 76107 28925 45624 60802 59445 72896 93771 21028 20720 37279 71559 25000 9108 28646 46456 50385 18402 68938 34793 4941 8555 37809 619 31459 2115 43795 9132 73819 55140 27034 27626 29586 90934 49314 26230 60675 20822 15003 74369 25397 61008 4071 9434 38848 25928 28099 35696 11737 96880 92111 17047 2305 18374 25289 70768 56417 76979 29546 87313 39907 68715 32526 1359 61141 63842 75311 14056 49625 86193 36971 65750 83896 20434 98685 70736 14211 58555 82602 38735 79514 87536 388 47682 75127 6207 77574 92140 42344 64463 36421 89342 59032 1045 54803 48758 5858 69386 53581 90436 52654 96518 91730 46452 51183 36814 21157 31396 15913 75389 33378 70648 9905 85843 12010 93469 23347 58151 92599 23984 7334 94282 97455 34972 7522 46622 639 68981 73400 43569 6178 18360 1515 11499 98811 40228 87297 32930 74628 59617 50795 42522 76877 13848 46782 56633 34582 98149 3394 5689 89508 43016 31635 56249 56011 45748 11562 19871 48318 36835 86495 84310 85431 45412 8037 45656 99631 81823 21843 55774 94624 79363 5081 23835 88505 89763 20183 94903 93680 79840 99749 35462 14213 58011 5497 31474 44430 50616 24208 80768 85650 7638 6266 41304 84584 32760 90357 1594 91979 90357 13524 35540 16282 5538 77020 95265 49765 14618 89426 218 73506 79793 77593 79422 60209 84921 19734 89391 63496 33101 7273 57423 88357 11052 13630 8370 25834 52076 39529 40015 27836 34711 44227 94849 83178 21931 45821 53949 1905 131 70987 78383 80419 66106 43640 53785 2973 24843 55039 61677 70758 426 56767 62807 61785 33234 88193 61735 97131 39584 40545 75138 48668 64723 76612 27471 75133 55894 31453 762 99772 2460 29892 91441 89110 21602 41277 46901 68617 11349 55354 55086 46974 36919 15250 20540 40145 7146 63513 62886 87841 80947 39126 20138 10617 23583 85275 66718 40352 13157 4600 4074 70486 80212 48048 22690 33164 94493 54888 35275 43113 99188 94348 18583 8151 77034 20725 66153 65893 84772 80150 42772 69742 62695 32541 66121 34294 77873 8266 92132 4415 5987 7980 793 58384 89446 34965 43557 85463 95807 37944 18384 94745 33716 87511 23525 90469 84607 75391 30143 56300 28151 5768 18539 35386 10580 89942 93674 76256 87504 60315 6221 59914 56201 91245 52319 63968 29995 4535 65626 9485 56017 32519 28527 57438 95311 27421 15490 91777 94624 89456 68798 75702 13283 37890 75970 52944 43023 40541 64476 38940 78838 96780 22118 38496 87627 31933 10784 65476 95741 94982 35049 1635 39967 41118 81011 63401 49341 77158 38933 99199 32872 25433 63486 88584 15876 17632 49507 45539 6362 79559 17315 26056 86221 59610 75736 93109 59 1974 46148 98972 84426 60044 6377 77012 45073 56540 81722 73479 78475 15534 33991 15761 83673 57116 42824 2899 81801 81699 11366 77314 84998 81460 381 95921 94661 31791 68226 43538 50052 44299 86226 53150 64955 5274 87263 5034 57834 29305 35685 70016 85624 66189 85081 36956 85457 71950 56350 38499 62844 10283 72335 10677 56594 90954 89077 5193 93802 52878 71803 60879 45280 20922 46408 88696 75933 78482 30729 79541 51214 31441 65172 11113 92381 7749 1407 86348 10118 9558 85939 48469 85820 20998 60038 70202 83068 79749 39560 63308 16059 23301 75827 58089 76324 75993 6507 68636 28810 34338 33272 67657 57007 51482 42256 69757 23062 30367 73254 58612 80745 18901 74648 45853 4070 34754 40969 68914 69666 96033 4859 58406 74305 66860 80307 88711 33207 78733 11916 72313 96687 61002 84943 93044 68721 84497 13954 52407 93457 64175 15736 58713 31277 39440 78518 18018 79445 78279 79707 44428 30959 60765 28429 13387 5176 62513 41584 94523 56515 6558 21120 53311 47195 52436 92897 35100 6415 50906 27207 66313 71966 52889 30453 89229 53740 46127 52579 52296 6788 65554 80243 5533 46266 71605 27192 28508 32125 70789 41142 95084 15470 93716 4038 92951 23038 62905 74706 69684 40327 19136 44103 94294 7715 89619 4111 31705 50085 27693 34632 79946 93555 37282 70202 8209 86512 76840 63874 19248 99188 72671 47844 24924 30625 20553 33908 4494 3791 26515 30001 93018 55250 61398 65830 51597 95358 55818 26327 25783 78403 56342 80811 12143 42970 59584 86646 49105 51261 42513 78089 63114 47297 86127 92195 26022 13411 91427 24388 2222 68099 19764 34372 97190 37019 28922 73486 9455 68400 50685 96032 81430 52142 1105 56777 11258 4830 70165 29977 96814 29666 41591 48271 28175 24662 15290 53043 81739 48026 75415 90494 29824 12804 67645 10061 21188 661 43149 74059 24365 3255 88943 45598 19550 67412 13026 44939 71560 26550 8524 11452 5623 6550 85685 28117 75914 57314 18554 40818 44698 87751 44331 65179 86550 1633 19478 44866 70484 14072 78635 80463 32093 36828 66404 8890 33904 72442 65152 85593 83243 91543 76596 80265 92494 3931 13222 47424 46995 32956 64507 18726 97123 91429 50306 98063 92054 68653 97076 14679 56230 86304 34883 10314 13924 85157 63886 67732 26443 98649 73445 61644 30497 74903 70114 35274 66032 58014 74954 43462 48966 83281 74955 63002 78792 35633 92363 85019 89266 35280 4289 24612 37114 46508 65441 56243 6133 78140 63696 30357 13409 95984 90630 67090 39321 85943 21409 86836 37019 32574 8736 12334 65018 96634 88306 63706 37667 20886 85227 41345 95002 5358 57828 75051 45813 4420 84744 80091 69625 84778 83205 33271 58363 67643 19322 68344 70706 57856 25283 65919 32481 75645 97906 15926 91931 42926 15790 28023 86347 37034 54502 45900 55040 98541 98230 71017 1845 46810 93167 56575 54893 83862 87212 17763 78062 10334 52362 50214 27536 56464 85924 50747 55654 8810 60466 46105 84038 79569 90786 46324 33620 52658 70512 14532 31197 27994 16246 86798 64695 63098 21788 36305 63063 13597 34123 80822 24878 45650 13131 2798 35032 84645 46564 96257 9049 41911 37723 95693 60055 59863 87490 76585 32315 50116 95549 31079 70258 63461 86527 45048 98805 87050 85997 53610 11806 50625 70822 76597 76803 17765 59906 64080 68891 7990 2990 55150 44953 3484 47045 5179 60990 86122 90973 26951 68585 89028 4462 81426 3125 15794 86809 96002 2347 29333 58193 15382 59935 71948 40153 7067 64020 71196 16825 51117 20129 84066 88775 36942 53085 61729 81490 82090 69250 37498 41726 43792 33225 67836 78196 94855 40680 82895 61600 26511 67772 46741 87562 49087 93810 69424 12600 34106 90042 86484 51238 32916 97408 4183 51141 81369 67581 66396 47739 4691 83037 8056 28917 59513 25302 35878 34942 64124 86371 59639 97921 53608 25338 49035 2005 86701 25900 78882 66947 80269 2400 54239 50606 53205 79865 90105 96372 74390 16029 37691 86685 30804 4976 62067 53384 7424 34136 87095 84876 26595 26599 52123 78518 75149 93108 11690 10052 44971 45438 15155 84714 11911 48460 67094 8336 30862 47864 99550 7615 14966 81206 28069 51937 70634 50045 59012 66296 24576 91989 50683 42393 95678 59376 17803 33025 17303 54402 84813 14098 36201 21067 72768 14394 91007 73825 63578 6826 50048 43518 16001 81748 28031 58120 41915 60333 64405 96150 92927 60605 41294 9596 34356 90502 4311 13506 21396 91128 92940 22927 315 34253 84016 38989 38650 16180 74403 25121 21601 11682 16707 66322 6146 55862 83497 57035 97879 68022 67925 30880 21753 80522 92276 50970 56795 38603 24675 63253 88538 41407 78968 35820 56503 39117 29226 39563 48214 11657 65851 77601 89413 47528 4883 93978 9476 24054 77027 17758 41813 7880 51527 57833 25576 55181 28646 53689 47789 83122 66442 66346 87468 77542 81664 19025 76308 69119 4037 84012 12945 6669 76719 46677 94794 2696 64198 37868 52165 43704 92061 7666 86763 30595 14506 85736 82595 91768 82259 8419 86871 21804 52857 87118 31797 99049 72675 11830 64089 47587 57691 29247 50609 6585 22679 93389 96279 23660 760 74981 40407 85996 95019 82515 79187 93207 77442 30922 32606 36144 81430 54045 84573 21456 21320 54909 46645 30420 98146 8544 30835 43911 33088 77448 41420 80178 67510 38547 5920 48648 6692 37826 16617 69541 27058 77773 10285 41183 50676 12736 96290 77972 38622 63870 97821 10872 69812 17367 17173 38555 64683 34740 12855 39882 73546 89443 50074 20440 10347 28008 26407 9008 46201 85307 56537 5969 11087 99626 83281 13659 30181 11364 57542 48278 48687 28295 77816 61086 80430 82362 63863 167 33912 91404 59178 28103 27402 50412 10272 7665 89287 91188 47163 32034 72788 69498 51475 79928 84142 81700 78794 39807 2318 18637 80345 72325 45765 16015 92791 32274 94887 48848 83695 60768 94452 45390 59841 24884 382 23815 16449 84254 200 97019 57141 36277 41988 87221 52324 98068 41871 21234 67625 32238 77445 69157 85575 7847 11517 82437 24131 61642 15232 35054 56946 47032 37979 30687 93003 52230 90609 49563 60037 77011 3401 39943 99393 12352 37352 95567 83621 56671 73513 67307 97881 60129 83219 67297 13944 1653 11324 10036 33836 6621 949 51988 70611 30758 92977 30005 22476 70474 74156 62418 94577 68463 10283 88260 41601 40238 94276 26971 84663 91727 94779 27894 60909 85190 20488 25861 44222 11921 49325 86738 79180 47111 42530 10241 51806 84527 86220 97799 18855 21904 77505 36306 41038 75200 13544 12641 73012 16134 70181 40651 91629 53388 26376 73661 33284 92234 61050 46963 99802 57320 93017 13754 17532 40694 74088 79091 36098 23182 61901 38872 17186 43338 85489 14720 89300 16711 86182 8946 77134 53437 65692 25674 89394 98896 71055 97838 72296 26546 47978 37260 20033 96934 56558 75337 63901 27163 71209 45107 7580 27206 67044 74469 20656 15492 72623 91039 65045 29781 77375 43432 67176 3724 56489 5965 17825 59255 39470 56410 62619 42881 20319 9304 81291 18006 28845 17627 34097 36999 35259 70154 24354 78018 84699 30460 80123 63895 69341 17653 99911 89239 71128 56388 23776 61901 79764 94155 36710 88676 99860 84519 98138 34387 873 59668 80417 77026 89026 99375 48822 24186 12133 78347 72117 68891 8317 56772 57820 98664 73594 18481 67870 80962 12298 36235 45121 45286 81216 64497 87490 17157 69604 95641 89247 41268 97354 64901 92138 54064 57243 57124 69385 42018 89067 55684 31098 94339 47700 47236 85614 44233 49970 88095 31238 14452 78719 32249 67253 42059 46034 15422 99989 81416 44701 60848 9481 64533 7922 8438 52732 92315 25908 36079 48313 37618 22649 17980 1494 5933 23406 90907 23240 70679 58138 72680 52457 82196 10247 65757 33043 93689 57860 5337 31427 47688 63061 92023 17372 40159 57206 52676 19299 83813 27500 90496 41171 25989 55304 89476 74085 75123 66131 85203 93711 59204 5568 14727 68364 42207 66465 94771 18372 64698 70838 16852 59603 1929 28317 37398 98295 9463 20520 49521 28714 19153 60453 60967 47005 53631 15967 12080 59996 18319 62482 79592 58345 97110 6258 93256 56607 51581 2098 29256 9916 29229 90629 58531 91989 96190 58999 28135 58133 83260 18055 70433 96912 98685 84796 64253 3144 77324 29641 91578 31780 921 92667 26691 7915 17300 47576 99701 79496 90557 98794 30970 41789 34240 7016 96587 34954 35948 96570 76133 6895 85111 74218 7243 95242 81576 67682 42978 63901 1809 16691 70583 67082 84715 34717 27559 53307 1523 82461 96192 48918 85885 90887 45971 39217 68784 21141 97037 69162 959 61316 8858 82349 72183 89678 14551 62573 10044 83471 25165 42907 49971 70070 43958 83508 15842 78280 15778 25529 66657 43245 49616 15973 58011 81036 23640 36675 98295 91219 87424 30210 16415 85684 71462 61056 94629 65984 68289 25774 82171 97658 58000 94545 76634 65372 27914 93205 9385 16858 80301 32107 55829 63635 67835 66254 19448 70416 1657 39388 49583 99168 68893 80696 83488 67759 3856 66332 51614 45938 22552 70978 65653 35007 13224 57692 66441 16547 44213 94918 1988 25701 16792 55905 38687 35069 38774 61432 76001 92612 63163 22124 76078 79845 22037 41577 74508 72113 74338 4771 52642 56970 46260 66068 86213 78322 76541 59474 53418 75779 79560 53977 81025 32509 57557 17083 83027 70328 57997 28879 79432 36675 68668 45088 10867 39277 95538 40301 12582 5560 6688 20918 69773 82044 32033 20958 17496 10492 4415 43195 29826 76051 38075 21856 65339 55445 22240 36478 61127 90571 83831 76694 24939 75002 74663 38883 13960 11839 23132 69404 58228 73175 61277 41363 96030 77841 85913 3404 14129 58222 75609 43398 29611 29899 70885 93972 70408 63672 88159 62283 98677 33278 53321 50127 45267 32524 33946 13596 88994 40853 68292 26500 1868 39553 46459 11830 35791 82620 8185 17455 68124 34019 26230 90898 25148 80636 63927 21916 46321 26877 68354 80366 48742 49175 94444 63796 16665 54980 542 92958 56848 72891 29614 48249 5294 68228 6900 3173 7486 950 77872 1354 90820 594 32630 53944 42739 86228 93980 27346 44283 65954 19047 46608 97404 63882 68795 92058 21878 86189 25635 99568 58178 45133 17707 78662 78213 12610 95626 12749 73795 43736 43035 24465 14596 88975 86420 93082 81748 95378 89300 97916 55382 98758 80171 47114 59855 24842 30716 89751 92470 39038 56483 57651 13088 90622 78051 13698 26236 89503 27917 8013 2931 29972 43047 23289 25342 88118 98568 80686 27116 77387 27318 26959 92112 71640 19222 14800 46545 25701 71637 85001 11956 7469 91144 11438 76590 2309 54766 26810 64260 23575 89155 6848 80085 9441 20 81121 39164 11137 50669 28820 10597 11042 23100 12451 47740 74310 88415 35701 38955 59005 10984 52334 48353 9086 18535 16948 30533 88758 34373 3468 50740 58398 88321 59673 92576 64344 16360 73133 90277 9980 26709 16514 57383 62584 20551 55 26277 4969 68588 54585 74113 86648 73234 51851 37197 58953 18451 54794 55591 47574 12489 68236 70189 65544 30647 91944 34308 42696 50746 51375 34257 78808 1850 92854 73573 89214 27747 80782 48618 85707 79732 21704 94512 4607 66267 12995 49608 70083 71965 49701 40763 10676 79893 55233 91461 77333 44692 79860 33832 26029 82710 37895 33937 82064 60739 33034 44302 85996 59289 17472 33457 21816 75142 5511 91334 84126 25432 16717 82124 62731 41828 17669 89433 45033 46241 68654 38674 31256 73153 52692 70205 7116 86011 61989 76036 97698 97190 87917 52597 23326 58700 90577 49907 9127 62794 17138 86320 8471 35077 90381 27703 50234 83415 19920 65111 30486 12063 11615 84977 54696 60602 8514 53924 17771 84827 94610 91296 7162 12504 26532 64614 46324 46977 21090 30732 6850 78292 82457 8741 38566 69314 20080 84172 1944 35319 13681 41134 75943 62183 12262 55663 81681 80601 28510 83480 63277 8949 87949 52511 82846 33195 79266 15536 86610 5129 36026 41144 57806 87308 9089 35359 67052 343 94617 26564 43852 98891 32002 63 56632 87367 1842 56916 83518 56892 63431 65170 46640 38106 45753 90900 73075 42182 50160 3644 4666 38755 78973 22718 52928 13770 45217 55589 2633 25820 49957 22083 72665 29907 32808 4055 23704 28693 63266 23529 84598 94888 20686 89019 70140 37296 53213 52683 71287 87722 9306 44481 47173 85743 39482 61528 8172 76883 7030 89309 92629 74712 2258 88361 85136 50355 35806 66314 7634 80204 24059 73768 72950 56660 76951 25379 69859 37931 37381 52157 89871 51993 84858 3371 90844 72267 50215 98101 66519 279 71331 2919 42192 96670 90724 70521 13917 63008 25005 7733 61160 92794 53319 2092 97739 66025 63217 20570 42403 12422 71757 65862 11549 69851 11438 20166 83505 57131 38227 1447 50491 14003 2829 71932 2144 25303 86546 38143 52266 34548 67785 92023 42467 86262 27588 48541 68827 52039 47699 67643 33417 75585 31920 64354 62833 27776 54759 82601 84976 21947 81882 80935 24606 49590 99732 31281 88959 87461 75064 30507 13452 20540 36817 29231 66339 27574 33798 5937 7795 26587 27785 78833 53946 66546 11645 95039 75017 38558 95246 9800 68993 15363 27266 17516 12583 64692 56181 23021 44555 52347 65443 15016 44705 59138 8057 27195 64488 63627 87634 96138 72947 46398 48920 15535 18354 45781 68925 17226 48471 93430 29593 12323 3187 52343 93755 67060 23570 91175 21729 99086 54107 94466 9767 69612 82312 46045 42816 54723 4098 32080 21557 94266 92010 48213 74323 60299 11007 41970 99796 94655 59781 45342 64931 43580 28372 69915 32592 75934 60174 43965 79279 78141 53154 29756 54216 36826 86458 96443 38541 36471 180 13111 23350 75580 3956 85988 23099 91709 43545 860 84820 44988 69147 51103 40392 12638 63991 78548 33689 45072 41623 80236 21986 28959 75967 10437 54772 17086 91443 82386 15382 23051 49611 69268 44049 63554 45970 72453 37609 62519 33462 20340 32993 50551 71066 42962 88998 35389 98036 67402 72976 45573 58929 47558 8644 84569 57815 62198 59175 55755 94670 87207 62253 54439 62189 25836 80308 68445 73507 20921 50679 67118 32345 17095 89308 7585 74781 94571 32104 31184 71966 21903 35587 22818 53939 20065 32868 1793 72448 59610 30401 76419 24992 47522 65082 75481 97775 4543 14855 56071 26018 74532 3211 17655 62319 65482 22012 95194 69090 94604 90125 18745 51404 88119 1259 24959 21314 12540 28923 88918 8559 27404 40731 79226 57006 39339 46441 13747 91285 10316 37744 61803 90603 73691 10440 12271 88304 99617 64468 97684 88813 97439 45145 78828 15123 54919 34003 1898 36957 4528 90995 23057 94980 85751 23938 41006 99242 96133 58309 43518 16975 88445 72688 40280 85609 84280 42167 64076 63994 78445 94090 85045 73569 29967 77924 99402 49782 82747 53851 38768 74525 62381 17053 30785 68419 5798 20096 86951 29842 29812 94149 59172 38641 53582 85553 9162 48164 20880 99001 15230 90119 95930 86605 60604 16867 37560 32712 13624 71294 45270 28598 73341 92222 11004 40910 53527 98154 47701 95390 39009 89192 14525 34193 40270 92217 58420 60157 87440 28105 19782 41360 64110 98149 92003 44755 8183 30352 15346 6700 28813 14013 21571 65538 59726 85760 13874 55900 74801 2083 27724 40447 62528 36483 77450 34775 26317 89821 97026 8589 80201 65439 29218 30523 2747 64152 31495 22145 51914 34017 90727 65393 78219 95171 90757 79370 48829 54726 21095 72309 47922 43138 33927 28563 35054 28403 89067 16214 87231 41054 91958 79878 88385 22026 50137 50513 56110 13786 70577 18086 61934 29590 34327 47001 61706 86380 70702 1 50899 41847 28428 81195 76175 48186 69895 4512 34938 2222 84747 84096 64941 13925 10298 23065 54658 26174 3791 31038 71921 93518 62117 31062 42022 88950 73936 19096 48863 75921 98215 83333 54466 55753 98948 56281 34083 83814 26962 43659 49512 6477 6784 94635 5088 34904 90161 78820 35458 96062 33723 1587 79508 56252 34554 66863 19829 34954 75152 51167 69749 30588 76749 43613 84135 86194 87588 46474 22197 43476 12276 3918 11030 41453 18251 98660 13829 47439 31023 28001 85857 19847 42001 17912 15415 26215 78125 96439 28370 83301 66313 83821 9966 15842 47120 88196 45916 38686 13986 63001 3070 86983 90562 87978 65956 64392 40972 63753 69132 59033 55534 45039 23305 58559 30260 29908 77206 60988 48713 37909 13809 9352 84101 99505 49154 32137 83543 86546 39207 56912 73292 3551 712 60746 54718 48966 8008 64406 14927 37701 55437 8549 3101 84839 67943 21146 54094 99303 59456 43208 40177 71660 47997 56802 41280 66331 94006 32655 76869 55029 52396 23166 67345 92812 17497 56885 93017 50426 38871 95152 84652 45935 91531 99886 98883 93812 13777 25381 56274 95000 74328 29112 30074 28408 58223 83137 43951 97127 19170 78569 74099 55708 84763 53383 44189 51064 46672 67924 89565 38863 58208 28724 83522 60930 73039 56387 47812 43860 73333 86313 29897 9059 9846 57700 30080 39116 15464 1086 48082 3602 20121 41680 33440 14542 21173 61245 21621 68591 91430 55095 45948 62863 66850 11053 58426 34274 29882 41847 99158 3248 52317 95227 3646 52982 7015 93704 28416 51153 93387 28485 74349 17743 35754 55926 51161 75371 3448 95912 60108 65576 20973 50577 23553 56542 399 24332 72407 89050 51100 93787 24290 81320 8363 15465 32522 5079 92202 18146 47982 64550 64293 80281 83505 28328 12469 34857 62869 2584 99592 14975 21805 81933 544 18030 47734 15295 33872 54818 27491 10408 19276 61974 15623 73795 87290 40455 95204 7010 16178 34943 68634 71951 12806 16968 63222 19423 74978 83637 68594 35234 36795 32046 34401 62682 93906 6298 11024 51640 58306 2216 50336 14556 779 55369 74637 56655 89584 56589 23803 50108 96288 58889 76470 79180 74984 53992 46803 53640 13048 81231 20416 76596 86930 33415 44843 42922 92079 93966 1743 33880 91109 15578 45554 20752 62362 52155 96151 15565 78056 83190 89365 97456 11698 36518 92643 98148 62760 60528 12848 88509 52072 91036 10972 78353 98979 43370 7779 48405 18708 53169 91390 25133 62901 77684 74434 75934 97280 70049 46992 39615 28916 30092 76476 90948 65870 66194 99111 66264 34408 18965 54531 33673 89105 43557 9808 44413 22506 80484 50069 59365 24214 3212 292 35755 45019 3689 19595 71380 73969 20674 70400 38672 61984 26088 33607 79616 81117 17115 37705 90701 55592 36311 56650 79833 77410 46451 50241 781 47121 18306 32728 79331 89980 76432 69403 87433 44002 54357 57799 3821 30455 23968 51257 30732 41317 85395 11903 24617 31411 25117 62015 90963 28785 88175 23001 99276 27065 55786 10238 86580 34737 52066 3367 60683 77479 31997 77796 17031 68958 61629 11588 7255 42244 73703 27233 42602 79058 42053 60247 53203 58711 3713 58052 22905 79731 20762 60117 72051 5569 75152 86797 88852 11822 63322 52490 73526 51136 71069 89094 67425 56697 7912 79030 30971 9961 94175 16083 38602 19828 39583 37273 24219 80077 12977 95333 31736 86894 32097 47384 1220 3064 89023 75678 84016 67081 38590 71365 23676 7162 36279 74945 10511 1034 58095 31408 9823 34502 25847 59798 43070 68024 28810 61850 24553 62438 32039 40175 16001 65664 24599 20211 5711 21932 10034 57697 64452 44874 24737 29117 43127 66501 66457 79859 3689 16706 18226 33183 50453 60477 66937 79867 73331 73612 3284 98912 90512 92821 64484 24384 89311 97583 8985 49217 28395 937 87833 71722 41889 14855 34364 37785 99979 98294 66183 79753 27761 97755 59731 38089 11745 83917 23346 20649 91426 19123 90722 80806 16765 36441 54196 25028 43765 22831 81398 34674 10770 13026 60578 51841 66970 87671 77004 55823 39801 81271 55013 96529 42156 20977 69545 71515 17937 23414 42234 86208 62782 90767 49055 61310 3640 4657 53262 20032 11219 60087 54406 45204 60535 6523 90381 30014 31128 25809 32856 92918 98673 50777 94499 86464 26493 24070 52659 17625 68361 97464 20128 71559 95665 80613 80859 14748 65484 1933 2340 99652 28663 19056 11333 3863 45914 87222 21089 25223 32668 93710 75970 89089 53030 82951 88032 21445 3662 71445 96564 16417 42134 38866 32945 7842 73547 75693 93733 64566 9935 40319 38694 30693 99565 13137 44455 70214 6953 35959 13998 26866 6816 3996 14610 88946 12615 1715 86130 90435 86375 55369 43713 25876 78438 7825 46518 56977 78182 7726 88832 67722 79175 20244 17657 20010 42146 90185 10239 44313 21947 39911 19113 45084 79630 65769 46404 51218 94096 26441 68080 54139 13276 34409 80735 83049 51649 71937 4452 85317 574 14233 68096 58322 43171 2638 97563 15829 63669 50563 17001 21391 14553 9540 412 49883 17199 8338 64018 90648 83945 8495 69642 68192 53541 76813 86768 93028 20424 73959 50876 32883 65363 86682 96978 64002 81212 81376 37242 75395 90410 27949 49116 78050 51530 67855 67942 40390 97324 4315 5997 97402 53281 96000 8386 94903 23378 43406 24091 58874 66445 63910 37443 29634 10015 76032 64873 94072 52484 53183 94917 33963 73247 48526 7407 52321 85289 6380 11639 42309 83316 69244 33721 68860 19885 57441 16374 21026 41135 29995 69990 98036 16669 38535 25227 69154 80086 57354 47377 39443 46339 34477 62629 30734 60398 24394 97613 4412 66910 62954 46787 42282 20241 6402 57281 89007 81351 34795 527 85844 58900 14935 66674 8159 53951 72745 28649 50725 55483 7425 82875 84015 72002 74745 49906 43316 25688 75477 552 5320 46494 17926 8360 93820 23003 11181 25169 28089 55217 9938 15682 4903 94690 13436 26776 68963 62336 32256 97513 63389 35951 87718 65964 82964 89207 73914 27248 73680 61448 80486 70873 22652 30673 65944 34233 13077 74842 45433 25457 40134 6828 75802 3676 36220 7113 44242 64611 48438 66226 98069 99726 53007 3279 15859 99428 73427 17140 89548 75595 2532 84206 80542 84294 85073 68490 33170 66190 27650 23174 33270 83472 42139 99627 79456 48062 80180 9947 77250 87572 20390 83847 93354 82952 69965 55782 53260 80472 21910 71169 95162 826 68746 24844 5340 9900 93738 14982 21996 17634 90932 95836 75018 74147 64549 91937 82388 20536 53412 65029 45836 26132 81620 18282 42622 26663 74138 7069 22071 15728 26020 48687 27563 64146 75134 9732 65939 91060 60568 76811 14452 22179 21586 48790 35233 63314 63780 64655 19603 88394 84996 59832 34378 8362 20823 51846 38521 77149 47497 7553 18946 20241 88635 22059 25414 86862 76958 13329 58069 99814 86045 36445 92563 84198 53809 83972 17430 6401 74014 41482 29561 75525 70625 44354 68391 55890 72723 20294 41585 11441 10545 19449 39378 80915 59201 72466 56994 70404 47644 90986 44140 3524 96142 64444 57950 67059 26295 41457 89689 28522 38399 45208 39906 42277 62343 90660 97512 27557 38379 62282 79465 38815 66593 62138 39251 22869 74292 65290 49214 52774 64463 24273 73233 77380 87563 17453 5692 14213 25596 52587 35028 28719 71053 46605 82619 9422 89492 48120 34846 55803 7436 38915 92758 39105 6209 10307 65404 68253 98186 51446 29743 91322 32880 51508 99852 51499 88738 46443 80337 46794 93102 95600 80220 52894 42355 50516 38460 54605 53595 76375 60266 8660 51005 65798 29783 97794 81692 67433 26288 10094 32033 20911 36054 50675 21345 2487 3676 79005 55172 86252 50169 3978 39402 78244 70315 41855 82295 97611 55391 70019 71361 87787 21180 34928 98971 6569 70372 2946 25659 32200 81917 26240 58574 76575 77377 831 63793 75403 4864 78917 91733 61464 26804 49910 66696 40678 80284 88035 39229 71051 88506 2430 34103 23510 70103 52058 18583 8488 27077 35730 28973 34770 72229 94581 87113 46797 52890 99489 75633 56620 64354 38806 64459 43265 90580 39721 30432 12127 88639 46179 95498 72687 65669 81626 43672 92816 93856 39318 94613 7555 35110 9907 29652 87897 93047 6118 55984 66482 94277 98392 65552 88821 76363 49699 72046 85194 55724 67845 59332 67848 99392 27513 68758 8972 36720 49009 67808 46580 22648 82357 49546 15786 9757 22295 12079 97279 42524 16152 89601 20451 19701 66649 87959 14486 4288 20945 31667 73134 82197 81740 83966 51494 22163 7893 32857 90351 24541 15850 73238 32327 73395 83945 78252 93871 82964 65893 78733 61535 89156 1504 85577 94258 75491 49571 17671 60810 54593 29056 77837 28986 55760 86670 7424 92666 82205 6948 75011 85642 40547 27486 83681 82054 88074 43542 55174 84432 85249 59620 83986 56880 25906 64364 71693 57390 42772 55735 5947 29258 24014 18903 58463 64272 79047 45701 72366 74932 42508 46251 6815 82787 51285 33759 22668 41276 11042 66262 14157 61523 68329 54927 79577 12278 65918 99030 26284 52430 200 58014 16185 17442 13588 12896 23685 91643 23209 49501 36429 26877 16304 62807 37886 94187 59833 24647 14700 76287 75600 75079 17945 69000 83886 9972 56809 85486 60473 74292 5379 95538 45434 72423 44204 2122 83783 12588 93950 73737 93789 88444 97462 67288 1154 11501 46331 89475 53637 57080 91811 51341 88829 18065 95674 93708 27831 35676 417 13109 41974 9759 88195 16773 60240 2477 30418 95755 85400 41945 18191 78277 53356 29007 23187 32062 5834 70925 23943 93369 93455 63221 76083 78729 42078 13023 49220 80830 6465 77530 5393 90474 39906 80651 76389 30326 20957 31823 28118 9581 24062 75424 14904 52862 33200 20897 58734 40155 59939 319 66150 13403 78831 74146 94180 5421 83396 33317 1142 96716 57131 53970 75672 18150 78091 87956 51738 69412 16287 55701 43075 24159 63690 16508 69992 76910 41788 48188 1101 75085 13354 33182 12947 31787 86510 46850 21858 9794 69785 41153 15925 86468 84920 8732 71125 91543 83629 89172 44654 74658 919 39252 49568 48351 47227 24074 67199 82717 81977 53759 45470 45349 80752 84664 15779 69058 68514 99881 76596 81561 18410 41030 4792 41374 72438 52163 53875 37227 31967 60426 95415 28019 7529 9640 57046 94931 58580 67133 98927 16600 11505 90248 61729 70103 35683 53706 9794 84318 31147 45256 14601 63507 86943 52566 63691 20459 55649 29898 93739 29495 8394 24578 55636 18927 66140 15681 53570 65972 89466 64995 20354 69627 22964 6835 8410 19220 20195 89686 45371 7649 45784 44121 15567 56678 37621 61189 87102 96985 41422 26544 53254 91313 11526 31143 84662 32229 86002 28243 47089 80737 57442 97087 19059 6720 62074 22139 38900 13359 18680 39932 56864 55845 44307 70826 18720 929 73332 86312 49543 81623 3085 50107 85302 92160 7792 91858 35273 94880 80944 23829 31986 75193 96163 71743 39524 75896 36060 10181 51429 62851 14009 98872 35853 61637 37816 24129 79436 58068 46031 38515 74870 89767 28905 76735 66934 36714 90158 75447 76791 11898 42249 18101 97705 65045 45073 92745 35518 17719 7921 53428 88264 35445 41828 13027 72717 87166 42548 66084 50597 65265 51559 9189 97212 10949 17383 75918 39314 88592 16667 4020 22371 53686 1044 26335 14730 92288 30229 47168 59467 29558 27625 99568 73130 1966 43369 82261 31168 31201 84931 90905 24551 88385 34593 91420 57575 44687 67009 14892 4683 1374 66601 8325 21230 2205 64079 16529 92985 14208 84427 54921 35921 67664 17046 96533 48566 45988 76357 60387 1919 23482 39432 87649 43489 80608 75440 79013 39430 56716 79856 67448 96741 83018 1326 71928 26109 90324 48970 50304 22253 26723 5063 61518 2617 59082 52730 67969 65763 67762 79578 22858 60223 33160 93441 35023 79115 19672 16761 65811 48915 10628 18448 27303 88530 17699 56282 84217 47814 68670 68547 60500 72854 57302 36720 82869 84296 11725 81096 2870 64290 58866 99759 43817 33113 61084 25998 51202 2456 50903 34249 53193 91041 50390 42860 41157 78145 71129 76222 91668 36540 20423 82710 96699 34077 7316 29559 75342 37760 19416 4657 31334 14099 9484 57008 93505 33550 92356 67937 33949 62262 92571 64681 39013 80782 46463 8274 35043 4115 67287 35372 9796 10138 65073 90650 50095 58608 36207 77624 85641 34323 69300 95077 25388 94390 91770 15558 44383 37846 9204 64432 59241 60570 20484 64682 35695 7311 2024 72613 57159 27 28057 14864 20610 50543 32355 5417 61979 47988 89666 29986 67522 31650 5304 68293 10509 42773 67508 99109 99156 93852 92172 38719 80977 22235 20060 18735 51337 72527 41125 35516 36399 67062 77335 27215 2055 56262 94978 17373 60115 53388 3450 76972 4495 2787 70269 51820 1272 68020 9442 99317 13326 74587 28076 62010 81269 75729 8748 47659 59231 54268 20200 88999 3851 82059 54182 34475 84464 81810 89269 45180 75085 83527 80538 94514 91597 832 20422 60450 53813 2795 71971 67752 54553 14560 98556 78333 15744 18975 6682 67368 48174 32521 43353 39530 84434 85055 33603 92610 65100 77800 92954 19977 45488 93027 8334 973 44896 81509 67716 33132 64471 98533 5838 12875 29285 97800 74868 75637 72242 67870 81815 32274 96453 48629 63753 3341 17464 25994 90540 72099 64552 93513 78511 25959 86203 83404 28890 22554 93631 57291 9139 85723 3044 99510 2575 70295 25269 81650 19565 69897 25054 29034 80684 39746 54284 42349 9083 36612 74476 74795 5669 48413 55968 53185 81181 81336 21559 60956 19529 11449 31665 35025 82115 47878 93534 83810 64821 34370 22433 97621 42998 42816 79079 65921 56250 6972 37863 95938 41427 82047 56768 37373 58734 63174 51498 29582 49401 16786 69074 91714 82499 83763 20594 30104 32830 14930 43961 16243 7852 4311 5521 39807 23051 67274 20921 22845 3862 48469 61837 78162 39033 44500 92134 22877 97728 98657 18014 84516 50821 47961 17506 98652 25841 16646 48233 38083 79112 16575 53745 96745 1220 51951 51152 78855 95004 81306 83599 48009 77018 77238 98768 27016 6761 21464 48952 78968 26358 52196 10973 88754 61728 35196 37831 70524 19150 3970 79962 68648 18816 8624 50695 43615 52063 98116 41011 4928 81690 93963 36370 41839 51284 42232 93677 87981 3652 51555 78385 56356 67520 92086 71098 18083 59993 7854 8641 46949 11701 1408 55441 37316 95491 47961 60279 55619 3609 83373 88938 26057 18398 39099 49292 16794 7361 64639 80427 17832 44209 96814 21427 8937 79546 56938 83980 61908 60589 56320 9099 78358 33172 22187 66719 65951 32695 42339 44252 17210 739 10467 7692 56662 26117 76447 80252 92366 73211 66850 44058 73220 20270 86882 45753 45973 26940 80181 3026 65786 95504 22385 26671 90938 15138 94777 23244 40423 84933 69887 7311 63637 15416 48828 44816 26738 31969 17084 99413 3556 55411 63201 92195 34664 10414 44452 67 17537 5044 97637 89667 57536 56402 9321 73343 53640 29296 90912 67792 30525 72903 17181 91605 69261 84175 19920 61168 68071 52413 44596 92909 67820 51316 38079 19767 20933 74675 85057 41390 7989 32529 22948 92666 78956 82694 62252 84627 33694 75615 44205 15565 194 69974 25003 26095 92794 34516 87931 95934 3522 54468 72086 5887 26297 95550 10949 85376 92705 2035 13360 52561 94909 66763 73147 71953 40990 78053 59527 35801 93288 66701 27524 42893 21879 29337 54313 30614 19108 13731 35474 65094 91332 69014 37050 76628 49682 83506 61513 23193 77310 9961 278 28023 43332 33921 79780 46773 95845 88778 89708 5879 47644 27909 81733 51087 20919 30892 7003 10263 59896 18808 36939 58881 67526 77957 85845 27077 53530 31765 8004 65561 58861 72883 22767 5105 87796 53028 85438 16117 36174 63754 55504 54160 40901 84729 76240 77886 10494 24464 9632 2107 24171 95079 86527 1662 88410 14753 12931 54005 5086 4774 88696 95011 46704 11508 76326 3122 21824 36189 43731 34657 61434 3991 94741 44393 43687 71398 26626 79993 29445 92638 34986 65655 4222 13364 77378 41989 63454 2373 10294 86469 1213 11631 67067 82190 18257 61943 30535 68428 27566 7343 13406 93512 54011 16707 84846 14059 85500 10770 93003 15315 49728 12910 85950 91205 13197 24496 40281 11136 9536 42692 36183 33242 45936 59393 85219 29498 85095 54917 6206 58718 88661 48427 37702 61010 40047 62079 4449 24436 69918 22352 40733 14853 49362 57865 47131 29656 29613 61434 42448 90386 11550 4505 92833 44620 63084 26026 82828 67305 64369 92291 21333 53302 999 7205 17472 21442 39794 60463 8437 70472 19215 96881 59906 65989 62465 86484 11687 12473 25862 16855 34722 72224 34095 19546 37672 85051 82091 1299 88527 45181 87993 19524 93106 99241 62140 3737 68640 18082 66790 29603 43850 44475 73447 62334 59265 55264 56541 97736 28488 87853 29713 75741 87083 37508 7575 73642 98439 81228 7662 1063 40142 76726 75271 56076 70760 26410 76050 85405 15969 82084 88888 23500 16309 81465 30574 27271 40446 92459 53093 42189 66507 28245 58400 24137 96048 87300 46821 16628 50296 43750 33968 41914 63155 76153 6647 25220 46801 96660 37507 7560 94636 45339 59957 33643 71570 36807 5518 88589 93489 18805 32333 79386 29358 16140 30599 29980 75543 61430 82690 65743 13832 35094 86563 3743 82193 24083 76522 73884 5369 98151 15933 24662 91129 6476 56414 48863 15869 63153 62958 81331 79783 43030 64029 6740 66418 6577 86279 22167 91813 83060 59115 95342 59186 37778 26763 47874 38944 88530 35666 8894 97584 63358 60842 87346 62929 16109 51063 5197 179 20997 36505 88849 25368 35152 27841 61635 71821 8921 10974 42487 41711 83539 74060 45061 24913 17587 48256 92157 33236 4788 35379 73946 1124 74216 79735 2879 47731 61894 92811 27768 36232 25299 12310 34212 59864 5150 77654 34832 15688 43632 69649 95870 74847 8613 4868 51670 306 32855 7430 79091 9539 36371 22077 71192 78215 64918 33915 81746 51822 45388 72865 44733 80523 6708 68745 66258 39356 53867 64667 10072 5612 49821 10781 86778 5790 42631 59053 50363 14802 46288 75721 12151 32382 45067 18505 918 52603 3488 13528 21700 32532 60532 66731 19405 13147 39782 72674 64493 81468 20145 79149 12330 52570 93304 56911 84373 96143 59881 39841 95246 9756 25231 77834 207 77308 94091 26550 75631 7912 36976 8371 82800 835 96630 32814 6768 44998 32383 34575 20631 84371 70831 94228 46685 63776 26669 16333 88959 27781 25303 60552 43237 9856 36217 34039 56650 22745 32485 75534 43423 59841 90814 31145 26247 95119 52869 49364 46312 16666 5729 96367 2440 24307 96917 61660 45159 63388 34398 58179 24879 30222 42707 14053 22324 22913 19865 53229 10127 35819 91462 19226 13897 58609 396 33290 56037 30156 69936 43703 72495 89972 3936 73046 24880 26059 10032 65409 40181 33178 16062 80835 73780 85080 62707 14104 20906 26552 39391 4475 59949 24708 30774 93480 17390 37128 53699 48670 53547 22647 70851 60476 44943 66722 47527 10709 30769 35661 98739 94057 17877 99980 14516 32667 20217 20596 94652 77297 44852 71102 42166 58704 73908 66708 72488 45065 77389 59301 77679 89692 59426 10473 32804 77465 18643 47496 68827 4479 54287 90768 60824 17867 6331 55861 33045 93441 96316 68792 54261 85124 4465 81825 66214 4503 14370 12043 19031 45114 44472 89659 31506 59195 7107 5612 37556 88669 63769 50940 88910 45496 46342 59596 70508 52033 58734 84209 61021 93024 33027 78112 15257 56204 95008 71197 89762 73417 20315 16126 18726 94385 421 56289 19837 96660 71440 11987 11022 88720 76725 22183 41208 33936 22 73329 20033 60835 48061 89651 2387 76350 20442 83408 40232 76494 85796 98739 85609 11482 57441 77622 2412 81728 40993 13195 43788 86011 70865 21435 67225 62974 70146 94756 58490 34039 18699 8496 61828 54057 69844 82925 56825 33204 90907 80041 12339 79257 93378 22226 30713 67766 56093 46683 99725 82088 9685 97111 96488 647 86670 94237 66507 76589 24687 9305 11609 36001 87189 15522 27036 23354 12500 83188 37462 61473 48653 93937 26724 55675 68997 40010 90934 91776 19795 66482 69467 25749 434 21756 52438 75096 70387 66322 67828 3734 65002 67674 79775 74921 74773 87855 88459 6703 22666 89431 89455 52249 73593 16633 23022 73727 42197 7285 79750 31755 79195 69745 88677 89208 82993 14813 64387 41603 75651 62673 70016 19523 22491 9149 58481 33099 29488 76117 14636 94299 30913 42722 84471 33604 84364 44614 64252 89483 97727 73898 88416 21588 61574 86414 30405 29556 74867 39422 11670 36151 65828 91863 51870 92311 57955 65382 16351 18078 63741 15214 81249 28511 38321 21596 66842 45793 35754 7408 46574 60845 75268 42523 74385 64942 67203 7495 10932 54816 64818 44288 92382 99921 48637 2301 25837 18152 93606 26318 18028 85789 15502 82720 36153 31030 27924 5635 34054 1816 2093 63079 73646 65785 27761 62393 97816 10741 83986 46793 97415 2396 81020 2991 93368 40606 88078 58008 28750 87905 73046 57625 3696 54803 97993 49527 60728 14769 90824 60613 36610 61486 34739 84890 88302 19652 18854 98863 53856 9005 44041 70425 36118 44393 73755 56885 16494 2035 505 39503 34526 80473 72073 58162 34156 71094 53200 79698 80155 41818 77156 23351 42568 58356 4826 70852 87455 22717 47907 14503 48947 5434 72881 88553 25348 45280 58420 27772 50493 89733 39279 52904 50823 61782 76673 23146 51596 74459 53203 92594 21816 80400 69901 66267 40521 2628 27722 70756 11848 28638 4240 87733 59365 66999 83627 39840 43875 35774 55182 44060 62661 59704 80935 52802 72853 40108 15490 35030 50889 64925 27631 61243 40690 50909 14129 69669 66761 92359 91847 11635 99106 18178 93247 73895 33087 7052 61893 23560 37599 14580 1570 93770 33455 35560 29647 94042 66803 71458 36198 75705 8755 73544 47484 90015 31150 79539 47713 50256 32538 55568 97239 13018 25375 34543 18537 27642 59747 8278 7303 19840 6361 5729 59306 26154 2960 27508 84782 34126 99717 42428 3227 81980 79959 44243 13223 15693 48895 17359 58130 76297 88077 48760 37747 51884 18867 81614 45631 64074 86983 58688 10407 23579 19019 6620 18564 67890 51094 83624 50792 97347 77428 71020 34481 30111 8473 25399 5959 32406 96994 77090 9685 1537 72107 629 63932 91131 68768 95108 2167 10524 99421 73001 40823 12579 7100 41025 4704 29136 52741 93089 57264 4975 56772 92886 77511 80997 55242 93177 89250 64121 85507 72444 18204 99216 2467 33614 46496 7697 16355 48162 57417 29824 95900 28156 21609 6965 1946 50051 89639 28321 55277 6444 31973 72030 6782 46141 72109 48049 35439 22697 36801 94019 28554 37597 35574 13435 179 83501 25686 67137 72113 49657 30809 12437 62101 79998 95863 28768 59360 78119 51897 44523 25636 6510 485 5590 45367 49597 2726 82733 25925 84625 44666 59133 80834 53996 60725 88271 89553 36712 38984 95158 33153 79379 51575 14240 33218 24417 54155 42041 24764 57545 1120 54836 13044 18347 68830 53983 48318 6099 70213 16734 35334 19587 96202 50057 22773 48031 1141 91465 21214 63162 74878 93541 54380 55289 59960 42888 27650 65967 2855 88587 54060 66288 86412 17760 25463 47629 24592 81631 59425 75617 79579 93150 49582 5707 26595 54136 91874 2610 36335 32556 53437 12040 37816 96193 25358 84213 27658 5252 8447 87557 81051 62709 71981 57464 7199 50318 66330 38201 20698 19221 356 75831 40362 50808 7831 33110 96734 98279 84395 2013 4232 20309 70120 70250 18968 86232 70536 68951 70197 38160 3540 66196 51167 49897 88095 88495 9086 7854 52619 3403 93355 64464 92345 11874 29300 93379 61682 78447 16775 85484 90250 92557 88655 32483 75710 93868 7550 97122 13384 93096 81006 33940 66099 36052 7279 29300 14134 74932 14471 54619 93297 74747 42998 4255 44825 43555 90920 57240 16428 74172 74211 53796 46934 47871 6160 72378 45715 99747 77561 77516 53022 62778 11030 41767 21888 44621 91882 91779 74507 97429 98846 62123 92985 58847 66952 48113 11218 33770 62782 98294 1798 90546 60857 29282 19282 29766 30457 10512 97738 26335 26075 80287 78703 98970 171 57262 12752 50790 79275 94279 53329 84853 87258 98944 57172 94471 96287 32864 27220 55198 57311 15898 79275 25388 20625 52597 17617 49211 9125 90462 78278 39642 51058 12577 61513 4510 20040 76878 69638 13366 25463 67206 39927 3422 93371 47244 49586 66008 75364 77557 75801 30069 31391 47161 30515 25842 95212 77113 73631 97682 16067 35091 6668 77742 13918 63411 82788 20635 92121 93464 47676 93129 8473 71951 13950 62004 14636 53108 88193 38190 55366 64821 29662 43111 22050 85956 16382 48721 23489 6660 89423 52689 79532 51526 73005 15137 81427 42476 19768 92431 84844 68030 43828 19321 66381 9970 52454 3793 51023 94510 48424 9292 21170 3629 81446 27427 12914 80008 14043 41995 15495 63583 3356 61236 43254 39374 95598 60202 32519 12895 17635 78280 74481 33436 18507 9976 76132 79065 14881 65629 59495 69182 34592 53943 51543 88695 76666 97432 42376 78167 92699 78000 67255 61207 20880 49137 80815 49768 8675 87509 9054 8154 41939 64616 6686 5661 56242 97065 21663 22082 43559 59167 29786 71077 26395 25752 23155 69792 64366 63561 92098 21794 28972 73096 75143 72983 30298 74349 54763 49608 26834 96694 90511 5203 9455 96586 99608 88665 60378 45310 18287 77003 37610 45433 36618 67668 8540 56646 63022 96755 53583 45226 28126 31896 98866 10082 13648 64322 80210 44615 44601 97585 63330 85144 2735 58456 38786 38073 33803 24008 80645 78321 14084 28163 84729 64856 57994 39616 7938 91714 10141 60717 65327 5573 54836 36138 15312 69372 50988 39384 87671 22220 64037 47736 34043 90225 88218 8680 82657 46108 77765 22530 6573 25454 26739 83570 35012 94941 72273 49770 54372 82521 76813 25793 22715 81930 97303 96483 17858 42328 66483 28170 11639 11460 61151 75335 64056 94920 46356 15155 14479 72852 19273 60646 74587 6035 48967 55996 59217 36181 33454 60220 74087 45422 36989 44839 83597 95994 50973 44866 2277 91950 62124 38810 60648 49612 4959 81831 47874 29871 73491 44132 72666 77968 56945 47164 36515 87690 38421 18665 34491 78336 43170 68808 28181 63933 82718 87042 56298 60300 17984 47364 86641 36926 57250 10333 44396 48507 61221 33456 3074 51041 62239 71721 29335 87079 89288 17950 14112 54343 55686 41788 56711 79062 97363 34809 57970 21330 87167 88209 4970 8230 40481 36636 7080 96981 29692 11288 63919 88028 17229 67554 78011 74110 76430 9636 69638 12256 56217 91368 60007 9907 87116 74855 58055 55254 22111 85242 45884 12436 82145 94238 62493 20362 61014 16758 34413 79526 69109 73318 49414 75606 53901 65401 22246 73576 65453 1049 68075 44594 28024 75594 2584 63206 36393 92020 35104 38352 20325 10212 68116 93857 41492 73724 30605 12152 97702 42021 25249 68797 63474 60788 70114 44701 50633 51000 50592 1274 19294 77535 41433 40679 22801 36100 4113 48179 61683 45555 55624 33062 92109 96578 13590 51426 78325 49937 80820 89034 6088 65542 10725 3326 15798 26513 79613 73946 77989 6407 79708 46356 52840 88381 80969 97593 50479 52926 35805 96269 89144 85629 21786 87410 94934 27284 52138 84029 4366 19444 50639 79148 5233 57987 73471 77379 76601 98480 30177 5313 1517 44467 26219 26906 77947 97029 29668 35525 60086 58377 18687 66479 35622 39083 27880 96758 30075 16657 67492 27398 71042 80563 20554 28309 55840 39447 17329 39700 10938 16989 92480 40019 27424 64336 43638 61923 95995 6549 18889 20074 32223 63403 48887 25138 71629 96969 19454 42463 82432 70800 66435 23406 15528 7534 75063 21752 46445 35702 51170 65150 59831 12253 3868 65378 8559 79169 76319 51649 22939 90012 7618 10980 37762 72861 5839 10512 83729 36631 36239 75847 28088 97945 40678 31179 94336 84710 94254 51801 92614 58743 37711 13724 16973 74055 64784 64949 5201 40909 11496 70608 44221 77773 2365 56469 33915 30737 98154 65916 31206 26932 95186 31654 63803 34746 44279 99362 83906 89460 43272 2999 19330 68563 43338 51090 6941 73304 91000 65837 69525 75641 90804 9029 33907 48680 9072 25011 85108 26472 50002 48896 64188 69620 14710 57291 51625 83168 33562 69728 68020 68583 39705 1030 75769 3084 17210 71571 3373 60748 88890 74122 90346 28780 62984 76155 75848 81286 31225 54614 30178 8690 9888 59239 51477 83060 35323 64050 45066 58446 19943 77613 54750 11467 44160 6478 30550 44407 40668 42426 69867 25817 61061 30988 89879 93330 25349 65895 52686 77613 66156 15450 96535 57883 77695 7982 78767 15928 64582 37489 55564 99057 76644 88115 21255 17092 4963 70700 20687 36845 94448 19608 28115 81392 6791 98878 72437 4060 73133 62461 64322 12312 44569 71654 38822 24596 75518 3209 82391 30679 92143 55461 59480 12581 76703 47604 51404 45859 85814 39697 75761 59530 47279 3060 57477 10024 51119 2602 53531 19962 14794 77545 8536 62657 84060 57325 1162 77828 55052 11487 15814 20160 57647 76315 76391 27912 70220 65726 13050 58666 60591 38118 73671 89059 47825 93830 55814 82273 29784 98996 62718 56909 13351 58004 32602 80497 77150 20162 33327 16218 1109 51968 16703 48548 62423 45451 7687 35964 20982 23013 58678 65931 51775 63368 51970 47441 73485 62536 5812 68192 41585 80408 40048 45763 55945 69171 81863 54314 61959 8866 62112 43442 30376 65255 93314 44560 93461 61863 21347 16497 87236 86461 28718 88266 11938 78806 47119 69426 58586 56828 49897 87752 58852 16629 39936 62131 4522 28896 39859 93345 47171 88371 90683 99188 549 64450 13647 68338 68996 53409 31072 59574 39782 88309 96402 83288 76056 99669 69304 68125 73066 80989 10439 59197 54744 56337 23342 27860 29667 6987 30869 14737 78664 62545 79997 93917 74598 8658 7087 82510 25837 40806 42409 43612 21064 27794 70220 33857 61027 60046 86229 3738 34774 12127 87706 41189 19233 36022 55474 15852 13039 23406 30212 40687 46551 13003 48194 12284 90775 71983 90236 54217 55436 32060 59283 34879 92713 62055 79550 32146 57938 49210 18330 16820 61969 67138 69572 95873 47919 12897 13858 64493 25840 82183 65686 438 14898 33448 24428 61015 98750 12707 84214 42930 21231 58424 82150 47850 31012 28869 70457 57657 85844 22989 68438 19967 54864 7388 19270 42687 20309 98163 5044 49927 89520 69426 97751 90894 45700 77532 62068 97046 63105 81564 10816 86170 36646 28903 84120 34674 16497 71631 56700 43167 49027 5584 88412 89588 4122 60980 76528 31806 22638 62396 20862 81727 2510 58508 65005 35832 90453 30191 15292 94697 22737 83672 11432 8933 48377 71444 29166 37999 13820 72639 51269 39142 91162 98115 15699 26411 62978 18526 72275 2280 81214 24633 43667 43335 29186 89514 43262 70836 99345 83393 90571 77785 53590 9509 39385 11438 15335 21894 95575 76308 24688 8029 24293 13423 85884 83238 2056 44052 35896 16907 78482 12668 38846 56764 51859 3748 98673 90175 27747 56000 63525 18606 32646 4127 43711 62630 64518 27598 71954 52871 24045 92187 46145 76664 88616 32502 33462 67193 83205 35584 20822 6977 80087 39843 49873 94979 6330 47567 59755 81535 99843 74739 40143 72025 53535 65806 35906 10561 12263 94129 84510 5279 49639 7428 89530 75565 9460 60413 21655 3011 44171 8013 38888 11487 97792 6128 8357 12773 17745 74711 37849 35777 11210 31643 3363 8280 79424 18192 9615 61047 77959 46245 18012 37176 43490 6130 51267 61547 14521 22285 36490 25109 14599 47520 59068 34429 37264 27308 47889 34396 36327 18729 11613 60355 34158 94190 44838 15141 49260 92068 30603 34855 65110 40298 33388 47321 50319 12505 47031 23728 8117 40819 8840 55518 88098 2258 26009 24100 46039 71373 35518 76989 72970 70779 21840 36934 39829 81915 4967 8205 96300 32836 99089 9527 10423 64758 43879 11723 47644 58138 80207 9746 71644 8959 19789 34228 2012 84119 28457 5691 2363 91567 25119 7109 50418 72955 73231 64920 93902 79529 82668 87305 49461 18441 68863 35405 77008 50333 16330 71935 81331 54958 36007 57541 38010 53809 8276 21470 80617 79278 68618 82830 58665 99706 76645 43345 57684 63457 10829 92345 52917 4278 14133 79205 30626 98301 65179 71951 60392 11852 12208 84057 20843 57158 59340 73613 69975 9166 42310 37356 74598 55960 37124 8956 9489 72494 39922 38257 64775 54516 3894 66507 50252 17425 70944 87697 55998 93394 50746 58098 50806 36858 39770 36778 40043 20311 47803 50442 6607 64578 41536 20033 36590 85019 13745 81706 90901 46177 26669 87772 25954 85060 38770 89010 20341 68863 1429 24707 24186 93927 47262 18288 63939 70626 68129 98327 85698 44914 67705 66430 36034 29913 95697 39286 94489 79926 7188 17540 89007 31989 46553 25485 72834 55191 86084 54681 39322 89028 6179 34625 61374 38257 47071 52988 43489 54087 32510 42617 22891 76551 13294 89158 40201 60435 12858 30166 77436 1361 75989 91721 92834 42048 9478 47229 78541 79342 81784 38485 56062 2962 96928 54983 35582 45280 59981 55591 76388 76567 51579 32397 48738 38728 14076 12880 18061 62986 11364 71686 55721 69262 97081 15428 45591 35896 43006 58060 99292 15787 75301 49482 15861 76480 47503 98603 68551 44976 12775 68734 95342 30726 89171 85589 67246 23564 21543 40332 64973 46097 72997 8309 84567 24198 48897 96852 22608 23963 56927 73325 25805 54842 41401 86891 17074 71640 29191 99524 54706 88648 83061 39657 92438 31388 24782 66591 36563 12752 75441 18242 36575 15885 97937 74272 76331 69366 13826 9528 85077 65167 37448 83574 39583 93663 65125 11077 40836 66660 87856 99787 44498 76610 9130 35545 4554 28200 32373 67529 67074 87834 17101 43452 15130 54929 55928 85250 7395 588 46920 72922 76594 12594 86355 71804 4140 10723 78365 66220 14580 913 83164 72823 50107 71392 67614 75207 37048 14101 31767 35929 94577 66750 25512 6731 40843 10855 53488 32464 96025 92596 81792 34752 67825 8312 24749 26067 48550 63035 39342 41379 74051 84874 42615 83091 14723 73349 85020 21339 51887 47073 47271 22073 86107 95788 83386 74351 24164 52449 40415 39383 7665 10863 17134 70016 785 71116 51374 61166 86705 29563 89738 45554 57688 68659 42110 3605 17080 16047 66584 24562 67877 43900 91433 10789 22446 58803 56691 11822 32177 85720 90799 89842 30648 44660 54907 23790 66447 86578 49374 5344 96407 4561 3422 32710 2262 42840 60104 27342 33984 17226 63598 18621 26473 37384 3433 75719 79837 3007 27830 37354 10176 46836 3496 38230 77388 24353 73135 19347 55269 24703 78720 76177 2305 68430 82417 72908 59994 39547 60180 19781 7952 31027 10870 31726 3160 8170 63943 21405 39890 3229 87371 6078 51908 41947 2553 98985 15388 71479 78540 38464 84612 31587 33332 3438 52498 63764 28903 41824 68880 81586 97944 44010 75813 15784 31892 61599 43026 17546 88768 67051 66600 45437 8804 80269 98336 65676 60026 30709 34187 5726 51953 9215 8577 61567 25705 11854 33156 43353 5100 21386 15071 5117 34474 26533 90467 36826 34760 77512 90982 7119 45595 6566 80753 15336 30407 67214 63657 38729 74652 73362 7537 9947 16488 80853 45785 17148 44162 42527 57654 74413 64882 86652 66238 39939 40517 77361 92465 85946 46765 8138 55774 3089 2344 80938 89704 7203 55210 79646 33362 77326 91769 2456 47307 19918 27392 46394 62853 52444 78949 68701 86258 78688 64542 51259 37643 76320 86423 5188 77318 37998 35443 12031 13220 26335 59916 36675 661 51172 34451 2835 7040 23421 56432 71263 75081 92469 95493 13653 46580 72567 91091 4737 23061 16417 3684 81693 88647 45811 19061 49788 65301 95114 18476 92215 15862 28057 20035 72393 84205 28758 89133 27338 50338 10193 35455 60979 83562 43181 95393 87383 29258 77007 54182 14041 11380 59339 77061 77288 70233 23185 39754 6229 74176 92880 62620 94143 20350 33467 59403 49058 32477 22713 3457 44100 15716 65965 6482 22727 99236 2050 98878 25249 24268 44522 3597 65469 90599 49628 51149 9608 16100 62663 64926 21426 4495 95828 16612 15299 35642 78655 86987 86569 62502 55747 78174 89750 41877 60925 96548 34702 99655 87460 42535 89440 7467 82748 91994 84561 30236 83 69514 94435 54779 97473 34534 59494 31625 28134 82451 40294 72557 95039 61482 18763 96489 50108 35225 5376 53838 1531 75932 96770 4800 38049 12733 18124 94256 29829 12295 6250 6466 78661 8592 24065 91294 69690 83007 26739 92912 62650 6120 58832 25442 52742 46266 35383 48353 20203 30195 81290 71737 64578 37224 92433 23936 99354 33325 37252 93588 89580 59384 61603 38595 52684 16471 48126 85650 4516 301 21401 85318 97610 58715 95443 63302 41087 20537 64949 4757 62802 32512 45301 98173 44281 35284 11910 27894 82808 28547 27595 19561 460 70700 40488 85909 78889 53821 86470 86529 60585 10065 10234 34995 83871 81890 9228 57421 13717 65898 9253 97607 71730 87293 99295 65990 77602 27977 12591 46623 89546 17727 92357 44034 15132 77502 17989 13078 81358 73037 71072 56127 97218 92281 76673 47432 76435 88652 39086 77120 68376 54385 46195 8617 24154 35372 29516 13709 10640 84629 87343 29387 42567 34851 58052 52347 70147 99660 85721 91517 42063 54415 51525 90828 52583 39298 27491 76263 58143 32126 4284 45042 71362 55510 44007 33841 98502 51012 56612 13931 86584 36435 11744 35506 95480 20995 3553 73734 69663 11631 75971 82408 49330 56187 29970 93998 38195 9980 79954 53942 92693 95078 99948 15584 77983 44536 21256 1876 28481 19143 92191 48570 26161 24787 63502 6817 37449 65802 55567 6737 6706 21624 63543 59782 27194 5286 34389 45014 98880 38789 56589 10472 66850 77258 52278 55703 72388 96804 14527 32219 16111 72711 91992 36816 69589 95128 77304 30375 2639 46932 84437 39964 76743 4723 35538 49605 18196 97127 68578 74625 844 44016 11272 24404 84675 25152 94423 36513 71090 4821 78101 96701 29974 90787 10031 46885 11547 53208 58551 86710 48759 98151 99589 11410 23932 72557 64540 64679 2580 12015 92914 74036 95949 21488 93012 29170 87737 62612 80610 78377 85690 56970 51094 65054 63158 6835 57122 9786 82093 59532 13655 71400 64172 86440 72567 61762 74817 74438 42673 39944 68128 52913 48809 24130 31841 91920 87560 26012 87535 34328 59798 74637 13374 81652 55574 64610 82777 6390 42999 11315 89147 42747 94042 62106 58454 16957 30336 36499 42022 33617 22842 33379 87174 52063 8858 84849 58353 80414 59588 42937 55117 14535 48059 52118 99856 21866 10591 60070 89633 1320 22081 44769 89082 75923 25453 98065 29887 38688 66503 40816 7105 17155 18396 89369 42863 29907 72525 97233 80286 37965 20143 28375 94868 67497 60754 26256 32803 98536 44102 79778 21882 77980 71374 36748 32506 27927 42821 82400 99385 85650 13582 654 2825 96582 87542 5901 13717 61892 66795 50985 64535 53526 91449 24169 64147 20080 42186 81523 14294 93066 96766 17409 38277 25630 18072 6607 65480 25945 63187 37438 9104 55348 94263 37785 5556 30242 13164 34772 15075 1507 33602 12903 54448 80297 28900 97691 82272 3618 93121 6643 92431 66954 1335 99526 37619 28977 89550 23648 2120 40652 73639 99872 81334 39087 74283 68744 94534 63036 47147 16297 66418 96093 56786 68512 34994 47702 2106 1501 58613 49481 79023 81903 83005 59733 22286 31067 10585 46382 28678 74163 1446 86495 30223 49058 5965 80504 40190 41197 63146 77125 19865 47296 52256 14157 61358 54155 55878 35730 76781 72418 65203 11206 62795 12494 59999 65489 5280 80648 38739 45534 63817 78771 26433 78175 43931 70805 19797 72059 54410 71430 7048 85132 16201 53792 73762 23615 61021 49568 2991 17776 43737 67784 65682 59254 34854 49409 87165 72990 5035 87254 68136 63032 27731 75231 80518 35028 20725 11414 44400 29338 3232 41485 81975 51159 63922 17074 32555 89118 22936 19785 91064 19872 80379 25835 88012 79471 38435 67574 48750 61387 36940 98421 18118 57878 11142 9734 20679 82896 7928 75021 68548 93328 41795 42546 66631 97935 28240 55657 50476 77213 61170 56824 45849 64993 90724 22176 44558 98749 89515 41728 97981 77138 46458 72950 68605 8902 5902 26742 77133 71476 85215 97638 55671 89593 8792 94240 64413 22458 28332 5604 68662 30050 92614 91771 27777 15822 85187 88103 4185 88719 64130 35597 87082 42488 81861 38599 19564 24153 17996 53456 34457 72720 61902 19497 78052 32120 77437 66686 85641 94406 37131 13139 12601 86360 63630 92243 53402 53907 13246 40431 57467 11180 48952 71021 21191 67774 1339 66697 38413 75371 60066 27429 2621 76446 81420 74258 69068 8095 55994 42646 68576 54782 95327 75656 87583 41099 12159 95010 16672 92066 77292 47059 19328 18333 29122 7980 3683 14809 44202 84895 22065 14370 83849 27557 40880 83392 26921 34798 15418 90982 47653 8253 84146 10420 80033 82694 72564 8691 1456 86768 15930 78793 4436 50074 88525 36773 16207 59012 23130 57081 71654 71301 68021 12286 26154 83779 69494 22337 77089 61026 52704 33017 1828 65522 81563 85079 67603 80701 85482 78463 37210 81261 13461 28938 79111 4718 97536 26601 41899 11553 96708 7153 27319 34720 14661 8869 28088 64294 90591 67622 71236 72261 9765 68757 84941 32458 40478 52322 47898 54611 36836 68709 39533 18365 5103 6387 32117 36624 82236 36097 87644 32394 32318 28305 3360 61034 89288 82306 72186 3535 74356 66493 40946 81186 49939 21250 65992 57912 71033 67061 49124 32230 8892 18878 8047 42963 30372 53459 45500 87576 88672 96679 85041 75621 54641 12854 83378 96385 89786 40899 25935 8878 14914 68368 66599 83018 72767 88801 19436 31531 72298 84288 10992 71489 11900 3049 48856 28339 53751 48304 7300 92604 173 62971 46194 29238 63720 61084 94637 58215 46445 76859 24098 89069 26385 20753 68056 55836 36680 60499 26511 29799 14286 78873 41818 49038 84965 77871 82939 1851 48231 70145 647 89304 76602 52633 67971 94482 67563 31860 87513 7842 28792 25784 56189 91060 67656 57784 3574 49359 90895 93104 50482 38947 10035 46107 98784 66341 7865 68374 48284 49838 61729 38370 98732 65790 53825 54203 23109 7163 64690 41215 75368 19908 19530 55601 97676 69129 51343 47444 16013 88052 73741 4156 64032 7327 4639 44034 17506 4866 18760 63624 76572 34772 7843 62210 19383 52890 88794 33479 60213 55851 80865 95519 14852 84960 93328 26017 85869 1242 87285 58477 90131 7180 85747 2184 75501 52579 41488 38431 20531 87065 31154 10943 11604 64136 65522 61023 28120 38142 18648 92524 17239 80456 6789 44601 90199 1916 60674 43938 61791 33963 31048 75103 77546 3822 35724 18459 37427 35385 55594 27024 62983 1724 49546 34816 63271 90689 37305 60943 62858 57589 97459 94890 30893 16122 74940 94682 18841 5881 6332 19907 85070 2690 23559 76649 82728 88215 76418 8152 80739 63808 58147 9872 58278 58746 36227 48264 21772 98889 44592 35844 81666 69861 75879 2886 91753 77373 15765 41814 51848 1869 99941 42738 8710 41653 75369 78743 6259 29804 75711 72871 4842 98530 11783 79466 44602 69779 57128 46074 50818 23532 51237 92814 67994 15911 30667 45314 3153 65349 17646 9671 98128 76062 78612 37571 93041 25846 45811 76948 2518 67818 84433 28863 54229 64540 69098 74506 60151 35635 10542 67496 95055 98165 44608 8047 57139 31803 10577 51914 17648 95787 97020 93931 81289 49732 17024 92070 81820 75289 6514 14034 19109 43450 64663 70936 50753 53242 14371 96273 31229 68400 98182 23818 42780 27089 23198 70122 4890 33738 15091 87887 9908 30029 19142 35673 82479 26307 12071 29048 69454 79809 53232 85308 81560 61399 18643 79835 10471 66298 61993 31485 48935 36420 96961 51729 61922 47254 27284 64387 83620 7939 45183 45683 60679 99232 16687 63328 44529 16903 24219 35360 94461 85316 49087 69657 29744 83632 75470 11973 13102 89740 5099 92930 96873 42197 13474 97052 28778 59877 34123 42067 85973 76924 3153 79608 45884 78365 84466 51098 33336 2463 13769 24663 90366 53706 6249 9411 76200 91407 42484 97586 27719 40979 78310 25549 17475 99867 15011 85942 34140 29998 33416 72472 46581 94317 95900 66230 37781 78935 95068 39701 77031 40096 11218 49151 64399 62414 36856 43777 96873 6172 70262 12437 75563 2880 91215 43921 74092 2801 88276 40656 36182 15193 4222 44294 31887 51365 31355 1029 89481 74971 19512 73286 93721 17748 77095 9612 78598 65893 67136 35078 51554 31926 82037 53453 53889 99469 87256 70268 70338 6424 46980 34843 31269 18095 94757 852 40060 18409 6548 5540 32485 84422 94969 2349 11120 80344 73117 20076 92173 62653 98633 87866 86995 13436 60844 23533 11140 92252 4145 95798 40860 40764 10560 17750 45859 40993 36799 79806 28818 11136 66577 8271 31883 44324 55612 83438 67119 12624 82065 83282 46206 36664 62221 44679 70193 37051 48929 48847 10837 1069 50429 85249 90373 99963 54525 15822 18833 6503 71136 24922 81666 16407 10772 42684 56768 50937 88046 12769 9226 74812 44504 38631 31791 1998 82140 32197 80309 66905 16913 95518 725 58126 64461 61270 64895 18125 34793 72909 10435 86924 81676 54984 50658 17698 79547 68660 19683 99423 81548 17731 57308 20517 67058 70369 27354 33488 93583 64144 51309 54829 54989 97435 45799 97294 97090 84619 67903 91371 45185 36470 72012 99827 33269 98733 89236 86585 58079 20036 55456 65112 79152 45372 27871 38721 77320 39248 60607 93188 15623 10236 47406 72739 13092 80971 77214 49329 99872 44031 34818 72782 19952 89427 63699 64565 33620 56388 62261 46269 62955 21664 41212 76822 67177 99736 67497 59670 19056 37572 86122 55164 80631 18531 14256 39005 53775 46017 9408 60171 57238 42636 16489 18302 96687 96708 42866 54949 40203 23248 98716 52803 60392 26807 8421 11635 72840 82175 49743 46267 42003 71200 93354 18149 4135 53343 38184 53828 3773 46382 21530 47817 25043 93853 6043 1248 24823 59503 15341 26128 17136 51941 25919 50699 76728 93372 19601 40253 60448 66583 80181 9975 74755 85953 9786 3877 78989 36588 48699 5628 55521 95387 8356 94120 19006 53371 78152 28834 81373 70020 44385 86441 44456 2872 4321 40348 65405 42251 9304 51106 94988 18461 55228 63666 48793 49202 52437 18554 76573 84888 8032 42160 20593 98401 58120 73974 22854 15501 89414 42274 7639 1074 565 57695 40029 26753 26008 5643 22313 78083 20341 34282 36853 94345 97714 1534 84703 88704 68065 76907 16097 14345 99980 80895 29596 20357 60009 87478 71348 18595 85162 16665 63792 61689 90324 99864 13960 25079 18620 67652 48294 79822 81321 85778 95751 69270 7482 13057 36223 55948 34865 58744 99504 53864 13138 58085 24559 91511 6492 31268 77162 46324 71516 61806 39428 82629 53872 92735 37843 80158 2059 72541 69792 8666 89243 16490 16292 16310 63139 64836 60863 43504 30914 2157 65668 4287 84788 42722 90570 741 67190 31467 95241 42503 3863 30788 82943 17877 10253 78591 42655 77930 96407 98328 144 74494 79136 79797 6966 11571 60681 62868 28943 54579 62317 17824 76775 52140 82904 90021 31801 12308 928 40906 55971 55655 97360 54766 6217 72375 53766 5216 1236 72762 58392 74335 24654 30741 76759 90586 43260 81162 10115 68194 81204 18125 64717 86566 78599 69082 16190 43277 17206 30061 74907 55583 89447 98406 84489 65680 83489 78189 81014 9321 25286 85521 52270 29347 43244 63684 76046 44902 12196 39718 85296 44912 63703 66142 92870 96099 56610 38116 84648 33711 48990 70863 9457 70415 43120 21775 18595 46705 97997 3926 34806 36991 82536 4107 61282 99720 15863 64452 61966 38217 27498 85924 27109 46614 36609 19285 61171 64429 95786 46880 51926 1389 62673 89831 99465 24701 49536 73673 8780 93656 23379 3533 45830 60262 45945 81315 29864 51853 85236 1406 90105 9175 61571 68695 55835 91921 69849 88092 23726 66986 12205 20656 94279 63157 54286 9477 83230 3967 76655 12464 18328 86591 65961 99286 79201 85854 7379 39506 95558 3932 8390 58375 77337 69541 30028 16237 71950 633 7777 72039 79840 37421 17825 36903 49278 12622 34433 97876 97315 11556 78203 27298 13498 2343 42191 37237 20250 72288 37554 47984 65710 47688 98276 19078 95329 95873 60764 81422 52695 78609 42630 42390 27583 57460 34943 65514 73358 658 21106 18535 95643 68578 72398 82441 92047 54463 47901 67492 74044 29622 59597 55916 72273 36039 79977 57045 92286 1462 33618 88282 43020 60986 98235 31601 45282 52505 80601 41190 93595 79255 12426 97285 919 93389 12289 57152 57074 90052 98938 6723 84497 67714 81963 10838 15424 67717 67346 22453 45066 39154 14905 62361 71479 2281 30423 45502 54542 72474 23270 12848 31333 61689 63528 10982 75563 29566 81399 13692 27887 31559 95042 64128 61082 33453 59099 74794 68547 93676 32705 88569 79598 88974 53676 65198 30467 58216 9749 54361 74374 35906 53765 81545 39668 75305 69854 25379 5220 57572 68209 8155 99287 44909 41104 7992 5050 62745 41550 29258 86780 95738 11937 53242 29485 72808 89667 63108 35275 67899 52401 10843 11678 90949 89817 92948 40351 23611 43134 5396 77330 89489 29521 80100 31089 46834 81383 35715 82322 42491 11185 45846 24701 59079 70100 20798 35976 88610 32139 87344 76848 95916 2306 74956 97115 50430 47227 46295 76846 74375 80691 9429 13782 78191 98601 78669 58706 63414 36042 16661 83850 94791 37169 74400 93150 14882 23054 20238 83005 82994 40790 17033 33344 44189 47580 25854 75050 42814 78794 76553 36672 83640 97870 94644 77929 47343 51812 78060 3349 52522 4982 12716 43041 63930 90234 27273 89812 54672 89965 79060 21113 72422 2494 26399 75084 60332 74144 75667 17435 99688 55549 61916 9687 99331 66666 46272 22544 2380 79746 36071 33838 96773 76488 23119 98168 96977 5714 43744 3954 26006 22347 54034 54482 74115 94492 74737 37273 4605 88296 68329 41114 15592 25228 86253 53612 21232 59428 6633 29551 30687 49245 55269 26608 34069 48630 87015 45184 88392 60332 55616 22215 38764 42909 46763 69642 90632 3391 27627 62460 57193 20018 339 2399 66969 30863 83981 98077 92867 34441 38098 47372 31492 33315 39385 46909 50660 62060 98800 5649 1664 95611 22961 63185 15909 73690 32767 31567 54124 4087 26356 75038 58130 78756 67004 83630 16946 82059 98296 91733 61373 88075 27355 26284 32767 57750 16419 95379 51624 63971 20748 45367 68503 13906 41891 96853 79627 57863 61202 13112 87667 15188 5234 26749 80462 87508 29908 22872 76939 3670 72237 49231 9459 1540 55550 19838 56761 35299 25193 43356 86248 40845 63621 47097 28569 16065 55824 70438 25812 324 59098 58222 37864 90234 31349 89344 76503 4766 72019 10149 87070 67033 26338 71981 93702 12832 79128 84709 24726 31663 93531 10249 34571 85777 62654 5787 70685 85232 2577 7629 56847 98106 22271 85194 84297 17413 90203 59320 76385 1413 60052 69892 75449 10984 10441 35868 56502 87740 71029 61705 8299 44265 29500 58462 99418 84643 56072 96524 98351 39545 5619 20631 1500 21468 73802 16 57042 75705 8717 46863 56263 81539 17111 3227 62450 80225 53360 17677 26709 44943 14313 92173 72453 97753 41631 82714 33185 22379 28594 35175 336 44687 31064 22252 79917 63676 90269 39547 90126 87061 37108 22972 67052 35016 97852 25167 16003 2046 91234 60985 38802 41888 99422 49475 80586 10550 21090 89457 73081 80610 8951 12659 16324 3795 16832 61636 25551 80136 44985 842 66395 4657 6855 82686 86800 32410 37660 3900 10307 60209 57641 45133 82980 32189 21812 95088 4018 47789 19625 68393 60653 24491 50479 28592 3411 96907 55093 38107 55201 38990 49533 32160 91122 99382 5080 47190 96458 22161 72356 70353 57390 9048 70964 18774 92854 48242 42491 67082 62650 19448 21720 30570 44773 56479 15571 2546 26130 22828 42773 87914 22819 67594 28130 7639 77400 47730 5265 56370 54687 22700 58253 52573 39826 54919 44889 35584 97618 30138 71005 60676 26123 81423 33937 28699 33581 11720 15358 55644 21185 64931 64122 45984 6362 80451 83404 39581 3401 14189 10445 95029 54361 94015 7906 78208 96662 50369 87264 58901 17959 98163 53507 29780 1863 70109 23588 59698 91003 64307 77905 75882 82469 32856 75944 24629 29209 66505 93826 73364 69808 61395 94672 86180 57943 34202 2480 32479 44050 69760 26013 35079 82865 23098 17595 68840 86863 95017 63661 37753 88282 83226 5615 56085 8465 97909 98809 10408 55679 16156 57898 34635 15967 3440 3142 95812 40672 80074 56671 35129 45410 83534 16565 64266 87509 84182 90021 68189 82945 14341 61653 21763 54363 56865 78848 9858 45343 50244 95883 84926 61424 67339 76356 15382 89849 91085 4068 68211 70131 75370 8331 50290 96249 79213 97277 6837 43232 43040 31375 84577 65198 91344 69979 87968 64256 2942 79857 44777 8061 67006 65451 29615 79951 74428 87121 81841 51913 33028 9531 35115 44029 50409 35896 41345 9637 1390 54676 65387 85314 41007 96111 85421 86579 34503 83942 20708 2768 17338 56696 52704 61850 43100 50314 47437 59601 94216 4385 97967 84282 87399 33358 8115 64389 77725 59381 76238 62619 80642 7559 58679 34981 93918 39528 99443 45064 79716 86622 35081 3461 32613 48803 41070 84707 53360 72523 84539 3218 15195 4806 35878 21052 97485 63814 32325 86508 99541 9645 23738 79965 49932 38733 35977 66198 16920 96877 83972 85534 2732 73955 52172 25526 81554 52638 60588 84664 15213 25636 25091 71914 85707 31266 69576 5401 10884 98404 4715 20441 90267 15259 58096 88046 32515 58457 70933 69092 6491 46863 87386 13472 97920 18391 49211 94158 37220 55230 58288 83571 58874 87248 57931 51653 972 45121 55168 860 35048 38543 33348 1911 52834 30950 80873 7439 34895 85355 5413 12996 26354 30444 88826 58670 40798 60866 60011 57729 55552 96298 54893 52279 72585 26028 18162 81075 84157 60491 33172 29490 32569 80781 96068 7694 66706 44874 6745 66451 30933 85163 75422 67307 30229 25318 6680 25545 24657 45332 64593 56743 56203 4145 28090 83364 32945 58274 25121 15632 54665 88071 72927 13219 70056 21792 32861 71638 66857 22562 54497 6761 5995 60183 98483 83034 58792 90666 92385 32386 15171 71119 76135 61420 25441 41526 65388 3653 80388 12545 22129 48711 97295 77688 99729 88180 41091 42116 25623 84271 79737 55788 13528 33933 15827 5343 22010 40476 97615 40452 28007 56928 33206 93686 44914 31833 17228 3940 24397 6114 83040 21378 13953 82288 17806 80905 21610 32278 54542 67848 91097 76339 74406 90012 63242 87467 96394 60708 78283 25096 64661 99155 18785 91724 62163 83952 11660 14122 66168 21095 21128 43922 33618 58238 69134 4010 86860 41656 13524 75624 45899 27240 85356 80761 89013 9140 67680 63146 14070 15955 54406 18467 37659 2516 65003 43819 42745 3169 18002 64187 60914 92429 15278 98210 99602 98494 29028 32482 51349 67313 15234 24512 91928 35980 68162 52193 6688 11415 29029 10782 2385 96421 33818 98853 11743 4000 2560 93861 20118 30671 2545 60920 38035 16230 54333 71155 2218 29850 2410 42599 5746 7047 290 37433 24859 69785 25323 66377 50694 13910 71036 68174 62001 54015 67524 24514 37741 66293 41817 7019 11210 83965 79014 13666 49452 6591 19054 91280 66035 58066 38593 32270 86286 72887 51521 10594 12510 84720 67154 86871 98059 47527 15440 32281 45056 58206 19438 96096 46741 4991 9262 74793 85606 12054 63035 83828 46855 72598 44209 52318 61898 90097 24346 71298 45157 33828 29827 10258 22213 53608 99380 48720 28082 12942 21699 25857 71054 65439 90622 77006 64509 46807 96291 71245 76413 77343 53612 11639 26592 9189 67702 65871 70718 70104 52758 3553 91299 7629 89400 11946 81916 95696 84384 39198 25669 96030 75416 89332 18949 46916 4764 96127 76309 80789 69494 23217 41919 3638 33512 48220 43635 4394 3845 18619 52745 12357 56986 98240 22615 78546 21207 19183 43768 69533 65134 84042 55981 38456 8248 52653 62555 14700 27061 75636 94844 15510 47797 23318 64230 48446 6423 3528 60153 78559 88644 89473 58562 45036 88200 15836 13841 67423 18200 22679 43108 91149 27888 47395 80664 77631 90052 66762 57546 65905 44476 47900 51128 4670 75923 85272 70032 59177 51074 96712 32424 82871 4274 78404 97876 52073 93217 52534 59731 80628 2595 38280 3888 2835 64026 84684 65604 11579 20982 37694 8188 21127 42421 59156 88551 69806 55997 75177 13739 263 84105 64204 6972 34356 59384 79916 83616 98996 77379 38098 94823 53042 92365 72440 47887 10956 73301 93373 72519 11204 5030 10844 11840 6440 58079 72069 80210 35927 32511 6579 94491 79896 237 87456 48402 67817 19477 12387 76233 32000 76373 8246 2327 93585 55914 51940 19744 71586 91052 69307 13168 95732 25867 72076 42183 83957 31516 59769 38710 91167 9978 51146 21375 61920 88906 42662 39425 19751 75761 14137 18488 9439 29969 61740 45251 35708 66928 45568 40377 19326 48355 73092 89400 25691 85563 99696 98839 24715 93047 48863 71799 85729 17952 31238 97216 63943 88181 99768 52027 55481 32235 85152 21156 45948 61424 34633 17255 98972 42396 6748 54960 17932 14914 78928 9826 70594 52564 94141 91052 14227 56296 57618 68176 45481 35715 48911 566 74386 79135 97287 1358 5022 13958 36913 67845 96146 53705 92682 8273 34086 68561 7176 69649 81129 14136 43880 26898 84689 13588 218 40680 58003 55548 59250 66769 95038 37454 87321 30338 31128 89854 21605 94429 310 95953 34291 76580 93233 98570 5737 57601 47829 98001 20895 61761 91620 21902 2699 79170 40860 28185 55093 20326 74511 41706 99693 28684 27794 37216 78358 96402 20132 27262 37039 96693 27227 90735 80833 95033 80718 12127 11218 36128 44369 45001 96719 20451 60298 56141 89482 48403 83251 61363 35929 74636 33984 28696 18429 57507 20522 9321 42857 84364 24054 64245 84092 67892 8755 60996 39350 25036 82668 97889 99142 35972 40407 17889 45979 23416 2893 47168 34283 35626 86921 5984 26621 54939 12712 35108 68362 59552 68469 29367 17767 9399 80024 80608 5820 26210 30992 15232 17722 66592 46670 69839 56068 36845 88281 74338 13440 33646 39363 886 80195 36344 20501 68289 91563 85259 5626 39473 74994 78551 22831 9970 25571 66127 9154 59142 6881 13665 13269 17318 88070 32222 25793 5080 75885 52974 1820 68206 3406 90779 85588 72588 74643 77469 27996 55285 32223 89447 70869 73449 57387 23753 21112 1661 18077 35893 31405 85709 57221 6754 34297 56007 59485 46666 11318 4320 18377 1425 52234 83144 55877 78387 23083 59033 94271 51802 7106 99249 89832 30092 89247 44258 67628 80608 26606 95879 9673 48737 74731 99866 25621 81873 4678 40254 1518 14543 27868 73549 60749 16177 87034 10242 9880 67218 81730 32488 11222 27535 81056 66263 50972 81574 54975 61154 89520 54986 36167 9185 78867 37419 73858 95204 69302 95159 80511 58101 71134 23991 9815 56826 78394 81017 84818 82914 60574 35048 25889 57115 46181 11198 72395 73197 75960 7280 51722 37012 91447 75327 66442 57457 79772 7494 65016 11147 63589 66823 45953 92321 79623 74636 79273 6626 41835 32137 18290 21985 49964 72042 97492 34352 4266 85485 26221 32592 57362 8849 12370 5681 61664 10728 83216 22623 83608 32950 41148 91800 68758 91649 39732 93179 90083 61543 61025 45809 3215 33522 74863 38053 59429 48319 12134 68852 55345 49981 34745 69437 61790 62625 85656 57725 76637 74378 32420 15614 37767 90415 74064 71937 82734 15024 32458 65474 48433 6034 43747 53889 36347 26059 63260 48820 91238 38553 68749 63252 89915 96474 43308 98087 96297 86266 29801 95147 74199 96283 21819 63625 76161 65969 73281 67465 90803 55030 39472 21893 58730 38979 11926 10952 74076 46908 80760 4004 89487 3652 90886 80418 52942 44276 50811 83645 77788 32423 93653 22307 64684 71578 995 11599 54920 61887 56421 42892 77392 58754 24989 26326 43203 36599 89811 37814 69666 58770 13103 61863 69431 33073 10367 56549 39807 84513 3718 80031 91217 53975 76699 73694 72560 80555 28898 3445 54590 78935 41854 98653 21364 73867 98095 22752 69513 84297 82664 23404 49532 38016 42701 75744 5550 37265 16272 16089 51313 68800 27736 88343 64196 96965 89088 70474 92871 4535 57423 43764 13235 72492 78844 3640 66005 44979 27539 87022 82865 5180 23823 27346 55434 20670 72974 99047 43767 20630 22038 84456 88737 36825 86903 31875 52094 24310 72622 16289 53760 13028 65040 9965 21085 6129 83808 69186 97243 79039 63365 93443 58849 99325 74300 79573 63481 83106 10900 4460 28930 66805 44824 59517 8373 65035 37379 52587 285 71443 55508 69584 95537 85914 90995 35965 20642 22494 23955 84903 96540 65932 3311 2884 8038 1751 37250 29814 98616 43507 42256 12010 14187 99098 72201 54461 547 5633 68739 62001 91161 20690 84036 44489 78426 43916 66441 65880 35553 30260 76576 13612 86379 59747 17720 27725 70100 3311 70274 63356 39288 76988 69476 89964 93936 17892 47865 20077 1626 55704 60565 33873 584 4311 17839 18643 30337 65048 69237 40127 38619 50167 27454 9421 70211 60367 80292 71658 37283 74288 32683 94690 82350 10724 42940 55948 1616 18896 4552 34296 98836 83775 28612 49028 6155 22788 77935 2997 20658 31386 63513 50581 79159 38718 44760 6706 74033 6577 92428 93951 74668 5883 84837 35538 69101 38380 94285 42715 41738 67781 87871 92332 62988 31664 83215 94565 42220 49593 97037 11619 55989 1871 84124 75470 407 9619 30914 27925 93463 80169 35012 31645 78679 74859 44550 4872 74787 9053 32932 97714 48403 15219 34380 45904 7647 76996 36470 77271 17315 77187 81647 8240 11674 6540 84444 55961 46227 93485 13671 10811 40793 48562 61862 43069 21420 90237 68217 92193 9666 12968 14797 91229 43214 73836 26104 92489 34280 8286 79852 24716 77692 82225 35758 67378 82956 18002 83735 76820 1496 1926 34225 22092 58651 82305 385 45459 54936 27218 66805 61508 7319 81153 78874 87215 93762 52599 73379 59040 56337 86506 13269 19103 88633 4843 61006 15170 22249 78266 46993 14898 81526 52921 96124 28920 98122 2007 70496 91326 64098 94240 29391 64860 69264 71933 81235 84799 745 23026 95069 58452 63007 25239 50677 95588 99242 29718 72774 31687 3327 946 1119 45330 40353 60335 68866 2045 66272 59023 21042 13216 99254 11604 75764 90237 87362 64124 85410 63225 30704 60077 81528 34578 36706 19999 65182 93143 49328 97286 46010 21231 10423 25223 39387 51929 60748 88919 98964 97823 36334 21578 62239 70257 76545 35381 94240 44785 60666 81812 70511 92674 49451 17284 62749 87798 41148 83788 42363 85044 25652 25076 80817 59795 18342 75455 86174 64695 43735 67794 29449 33519 31588 82138 13806 31700 74217 10399 61138 12586 16013 15156 47963 46163 75176 64509 23635 89988 20619 58959 49917 56252 78939 89005 27653 24130 70018 48792 84534 22712 63442 82984 30044 84493 17472 61141 76096 24588 23665 85428 29415 19770 51487 55301 69723 29830 38976 93003 89423 49026 92192 16606 39169 49791 2801 3390 11564 62853 62619 52317 61004 94245 92017 99239 59582 56630 83982 2720 24843 73299 72460 19597 11755 16604 61493 47205 56276 59906 23789 12541 78472 2612 94259 28209 65959 13154 10622 80687 48936 21188 95602 23559 24668 70699 88129 38819 3508 33467 60928 40106 42471 84903 99657 61062 47429 17995 50652 74934 55361 75682 10867 14977 73089 19938 7286 454 63002 74511 29336 67573 1737 58768 88477 47838 92946 90959 73551 16495 41302 16907 63872 90104 46531 54935 80047 89618 9530 9835 14286 80069 22006 73223 80473 92382 82345 87168 66368 9463 82303 94526 94234 48589 49526 31489 49438 8314 9105 33794 65454 50184 11843 67123 34147 81326 12833 72824 72054 78444 59010 30673 90293 35353 56481 16424 47972 91894 25822 52283 29786 18193 98390 61713 61653 40797 67934 1952 45416 14514 91730 52177 63735 58951 21680 16434 48671 61103 47992 20191 79886 98259 53122 22307 69703 46028 93042 6670 98947 56625 92776 20179 87198 10361 47367 1591 10182 4694 98431 68636 94773 61137 24240 98646 35718 25901 80069 63886 79558 80598 23373 64399 14676 73152 24632 17797 58253 83397 24271 99506 88911 82787 89728 87702 34649 23238 53453 14334 94568 49167 51671 64352 57814 42059 91778 91596 15155 33260 2823 59992 66838 4513 77231 1212 84369 99952 4649 44299 59253 29243 79915 15104 31501 88559 27312 54174 98555 68967 51077 30444 3144 46077 96336 73027 14852 7009 67474 84958 68658 15483 49413 20176 72273 40283 78195 57955 74356 32541 35171 26270 83990 12274 60803 98721 38163 35644 37446 46943 6478 2563 23142 83610 45279 7702 46270 24194 63687 34280 21182 5276 60090 15552 22968 75802 25126 26265 44332 9270 69454 89428 66747 67205 39841 48447 58634 71292 28489 56234 35668 77561 28686 44548 36594 41779 77611 90300 54593 42093 81653 30730 3684 94896 26426 67596 38315 65035 79331 74551 16219 3242 3795 43306 75378 31286 48264 38542 79508 42637 5947 9034 73115 2345 17242 5893 89135 48626 24044 77592 80412 68088 65734 30517 41868 50872 30753 55627 21684 47123 26515 16067 10758 37670 80395 30525 19464 70838 97149 99114 90553 34685 75093 61423 8827 4290 64923 47087 1022 5103 20880 77084 17590 81871 28058 14763 55540 38765 99195 27740 36660 92161 83689 17733 57460 60982 69385 38785 69813 40665 56895 82370 22381 57508 65366 99835 12440 19491 44119 70617 58597 30148 50012 53702 3037 70684 37213 74737 91314 2709 26261 61643 2705 82968 846 91648 77614 19810 68303 29870 32991 93953 31272 76566 22096 34944 26014 6191 67792 72312 41072 28524 2171 25684 18769 15644 65638 52431 56661 69791 22849 94539 24948 33173 95070 51428 18837 87583 72172 2303 28809 76987 82554 22102 66141 89999 48353 18003 67597 25449 84941 36407 66745 9179 16053 28358 85595 75682 88323 46882 12415 5247 98296 5798 68779 85035 28060 4202 6061 5145 30916 17552 8574 40311 14028 37288 52637 55402 26859 8875 14623 10225 8064 82183 1583 17516 16454 76474 30931 57144 31640 24329 61597 76725 90926 36621 91792 368 6957 9937 13198 19598 98536 66684 58400 95260 36315 50 37151 16615 70234 14530 61387 66538 66413 2471 56527 53807 49779 27524 55242 36719 53772 45595 51493 23377 64203 37903 3993 53479 33467 73780 84874 68718 72821 99687 18992 55872 68840 6936 6006 50550 26954 86986 38913 39191 19703 80469 59590 67489 11309 86337 50698 50272 65575 85090 42660 42367 13143 18722 39087 57938 89754 67257 59699 58845 76248 27164 68531 55101 19239 70606 73602 45968 76894 78860 75757 50061 57584 75286 40346 29249 47156 95159 8599 31448 83788 45396 80079 32798 77607 42950 25622 52344 93527 6939 79838 9691 30482 12801 50407 53459 13715 31899 23389 35457 76699 80844 43876 20176 21006 30228 80082 82037 82210 29912 41061 8311 9730 98435 39508 74506 93360 82571 33924 34676 34405 92810 23363 89767 36739 73482 24700 32636 10040 79083 29076 687 91022 95682 67659 94019 46704 30919 20370 49648 47240 84435 58022 18321 76325 65416 64225 97916 69422 10511 96327 68875 49654 73368 75950 86591 71872 58255 7563 13794 71698 70041 45618 92575 50420 18533 34038 99351 97934 26344 83423 63848 66160 23302 87384 70040 88639 37821 41791 24219 83877 95647 51439 83668 23099 31665 78769 87603 83459 82218 31655 28299 41831 79925 32501 95020 59367 19674 86922 7293 36693 77791 77243 17492 82109 66406 50892 94688 24829 31993 46769 75247 40146 30159 68188 79441 92573 35441 34085 79578 82004 88108 6803 44605 6355 78616 98587 68896 38023 62620 14044 94602 37319 57846 22396 20804 83211 92570 74701 92861 58615 75796 51802 44368 7580 6946 67826 52655 43012 56735 43286 65928 54059 96340 74734 84053 95803 17696 70358 54640 83395 89874 9599 84568 36899 94155 20298 60565 37541 8719 87730 56831 63896 47440 61188 61392 15261 22305 40432 41457 46776 45972 99421 96252 18207 64455 17029 96227 54516 12026 45345 92117 39235 69467 37043 93721 57534 68116 13371 38328 99404 83741 65207 87015 36577 43006 48996 14137 42147 58462 20246 41736 99183 2893 1334 53174 68701 95679 86290 71700 10923 41181 30685 38928 73824 60997 23634 82752 12113 88310 52577 27129 89289 95744 73242 64868 28129 50444 93968 57428 30927 61904 10695 41522 86589 29857 15570 18542 3557 21513 44052 43771 57718 67778 74023 57234 31520 15770 78595 63068 99287 16358 9502 2981 9653 92113 36855 96677 77653 93754 66180 60924 40734 53141 12748 83197 12331 62485 56493 24415 7659 68017 35692 24160 75516 65923 46883 14494 45799 21849 38806 69592 28589 79444 53710 59958 47899 30702 80135 79294 91081 91693 26166 49113 95583 17448 63278 22326 59055 26036 5471 36590 69242 51035 44093 33400 65959 40942 3959 60085 40553 75370 35897 85794 87572 83248 79262 65085 51569 95916 52200 87975 98234 53784 559 3838 43448 97525 74161 72074 62826 24449 58763 94181 17871 62907 70179 23201 57230 85788 36935 48697 75256 58970 73422 76619 87958 88022 78753 79791 9059 4339 75654 65597 10171 86242 94688 4393 33785 58562 47415 23583 15806 36735 11876 51056 78475 51494 21543 20706 51706 98077 30716 69151 49107 5512 46347 80629 15285 53045 6058 9035 26078 48369 58163 26192 31642 59921 53354 6253 71605 80636 40616 33101 77571 54517 49495 10 40614 51514 22440 24045 86815 89888 68714 90250 95664 49946 13137 35619 41320 84084 85259 61809 7964 72828 11985 56724 36485 52464 3840 9560 49179 8343 23047 4669 52349 72089 81738 92470 79220 66351 2059 70285 95286 30038 37439 25893 95748 99211 47754 41726 94216 13521 55152 15474 24611 3802 40138 5352 59461 66932 33625 59993 14267 8015 70854 65103 97924 63595 89614 44287 72232 24331 40405 95587 24303 89215 98408 92207 23012 73122 68688 28310 23920 17119 36761 17789 46000 15985 69175 34282 96029 72521 17906 21612 91902 65616 61365 30591 92689 41663 24260 51113 10166 64042 91005 25909 81270 81633 67059 64203 71959 8667 44795 77109 43994 59468 83664 65349 62419 75151 58232 97825 5718 25564 7508 54579 95138 35514 69250 28414 53200 84162 1429 98676 2669 28646 47986 22047 19966 12 44304 9487 47910 79647 92333 32452 63666 71922 29974 84155 27104 58358 18289 1267 9251 34529 8291 76379 33216 84886 96140 44912 53202 88289 38289 29959 3474 16431 85804 15726 66253 64683 37662 19413 27973 87692 83861 37214 97613 15754 71637 52628 48101 94194 98835 27882 95149 62390 73741 81566 3036 82527 27714 29425 9086 67410 92940 26096 94239 1976 49376 65915 19875 37976 89241 77258 84683 3677 17701 11520 2302 42908 6487 40973 40445 75284 71219 79909 21636 96739 60294 46647 31268 25532 9998 7828 85781 11321 94142 31844 69626 33760 53328 85695 44681 55599 86929 2062 75 17797 68934 94119 70854 36176 2736 59116 35491 36497 86167 14221 86123 32283 95993 16490 96014 61787 90739 96001 70349 91235 55988 77931 83642 49994 62163 46213 96234 77261 52497 20126 24787 5643 96339 38550 75798 77346 34717 32007 23313 44351 37907 11080 78218 44958 14107 52592 85898 63456 33614 52588 83636 20089 88040 85071 44028 55972 17480 70168 36649 85338 47484 63207 29235 52454 31989 76093 98813 45945 89582 80200 75229 46465 77391 43240 18631 42056 21637 93937 56053 80289 95004 56680 77167 28302 9452 37156 19934 47766 64760 62846 321 58049 35828 71648 38966 44663 2618 24525 22950 36672 84399 72826 36857 63286 17192 99021 87736 92458 49935 22340 99153 90839 42130 28053 85046 83558 83047 90821 14129 37086 16391 64978 51979 82740 27161 17653 11486 1501 66537 98265 41098 29340 18580 61802 59833 97897 20874 71253 89702 42459 59377 98673 86508 28743 50125 91571 39434 43415 55721 18224 29805 79448 71379 29101 26204 67416 34033 46743 24046 32181 45587 26784 96530 66572 38842 833 82418 84228 32013 915 65054 85314 14149 86452 67950 84508 37736 7868 51698 65001 41448 15028 79835 13394 30058 45565 58800 39937 73458 98467 37376 73202 83991 24605 33223 22131 31771 43931 42056 93068 97651 30286 66170 60221 22112 84941 97669 55021 29214 86803 67072 29535 34296 91546 90493 8505 37965 56553 12012 69172 19625 21614 97337 97983 61947 43354 62136 18589 70392 2594 18210 47446 15006 43382 59670 2407 98900 46328 46752 13784 86874 46384 74005 75148 83675 14146 8991 76137 15797 46735 62185 68520 27585 6093 84849 86426 11477 38588 87272 39492 87631 58394 62547 66113 29406 44409 93740 87200 4712 98864 19511 79705 56475 27990 36507 76326 89118 71979 26503 79585 86249 52127 55163 6434 92369 52898 91861 31912 63934 78311 79250 65964 72673 7557 73848 35842 37969 60398 99998 13145 81100 12298 34004 15300 77718 59166 79352 34561 87879 68940 94915 9844 67552 95137 13220 15154 93656 5716 11563 59321 40075 37924 56963 78741 16233 97099 5763 36035 21570 27651 56411 47690 89732 97622 38122 93002 92389 64147 12828 10689 97001 68057 48047 13125 83524 29097 41318 65699 27560 70745 99449 2400 60918 10290 9725 99578 37337 35530 85525 78097 73959 96403 22768 70028 64302 23786 52285 79582 77765 96541 95394 88797 53035 41366 40693 75650 3683 20359 88909 56451 82709 45600 72287 12291 42810 83451 12137 86658 45368 7364 80001 42466 93810 69228 17443 55819 92300 76341 42349 50886 22523 35238 70354 39189 38162 97801 44506 78244 84534 32513 17644 43493 68353 55126 97884 50615 86822 44341 21445 94586 75242 96684 38378 86295 1153 30840 12385 59829 27409 48300 71111 94900 13637 59863 6732 2497 27212 27427 25165 39175 79816 72669 90130 63463 77522 37162 75927 52990 12819 78008 24987 88460 82790 87449 31013 49010 64029 42440 18086 93059 77901 81712 64083 98741 20146 76681 66269 73836 73244 41937 7973 49413 35330 78922 11919 33230 40776 10423 93608 42465 48483 89560 46272 67089 94884 96715 66698 65078 22683 60036 98469 35873 23751 31116 73480 82939 26398 15148 43868 63565 33617 89413 47592 82548 69493 34972 43395 3975 28247 63163 42327 39085 62588 66893 18307 20331 59303 10780 88856 43632 93757 25048 97617 86828 61251 77154 7332 80989 31339 64679 43230 41913 85804 7458 74825 48352 81840 95659 25445 47755 22965 49038 86557 19724 16920 54089 60096 24496 45307 53572 66173 25339 23819 81399 30870 45256 88001 11758 35426 35927 77690 73020 62074 72191 92060 66912 25886 39577 1758 74197 98375 89240 6756 82135 32328 68057 26820 80021 18426 85839 75535 28325 60577 82486 89754 5541 8229 89867 78154 41276 31248 21562 83501 25857 49246 67458 10586 2211 47211 59091 21730 82603 99603 43340 49753 6159 31771 7057 3473 63244 16676 15565 69806 48845 20906 50567 27346 69647 61381 37311 82190 39926 32533 90189 98402 47619 17878 65713 93861 37332 1877 64041 24311 86546 90016 91618 6507 5441 29430 48625 34484 53563 73888 30112 37157 92700 2070 94952 3082 81902 65318 19369 9043 84959 9056 22253 81231 42798 19892 16530 88081 5748 63336 97482 12635 46812 54567 81279 35379 37887 45147 90690 26979 21336 81183 76289 19390 13392 44266 38249 21485 19517 74569 39281 37431 40758 35415 92889 7295 80341 88996 30898 48095 95177 44450 3139 49142 1475 5241 48214 31478 66375 92963 90082 19426 36587 96536 91132 75323 80422 7065 58709 38197 67553 65128 56418 2156 93027 44353 18057 81044 14217 77919 24387 79281 13272 40887 47404 30341 16009 25180 61600 27065 89387 83213 46972 13358 61245 22373 84850 51990 49430 56186 45199 20667 25033 21381 71297 31903 43637 55519 60085 50389 87917 32976 18345 85313 41041 76164 38077 94180 48887 27452 32502 82617 47797 79650 19067 9723 39281 98072 99063 64398 32387 89994 79606 83463 34548 36639 57544 6779 96197 47671 80064 46954 14286 31894 76107 35359 39138 40527 35267 89198 68910 81337 71075 85295 98628 50357 7676 5595 80914 86810 75001 85844 27860 527 72476 78638 48011 72232 63910 49048 20635 20411 32123 22880 344 99976 94393 16043 35801 62078 20491 81893 35832 66924 66848 66870 1690 26093 97373 95633 39826 7962 32466 91137 26622 14359 57244 99311 77780 76605 13628 35568 53508 12271 35554 14442 17178 55169 18454 16338 878 4463 76375 45574 68292 74061 35814 12313 63003 27703 42726 76502 87126 81702 53230 25852 73133 68325 92970 19853 31823 69803 20668 42205 85616 2217 88247 51399 39980 55923 5795 3896 96310 28430 7156 2694 98862 92434 53751 90210 90403 71839 12819 84853 77653 29936 6904 89623 84061 73175 39846 24440 24310 99691 87468 20269 40511 20742 23500 24143 13837 88125 61767 15405 48425 2545 85975 90090 16303 13408 75327 25085 20606 60632 65484 60177 38195 21540 52477 29982 93844 41784 84906 25133 69047 94502 44299 95254 73501 30889 37537 54838 56160 32026 94537 60698 83789 42869 27791 67347 62803 85217 18481 26379 36881 43159 12685 19531 33372 79920 42686 82780 33621 73275 34762 15894 88263 87856 85896 72001 116 62310 87080 86623 14821 41737 49571 48850 32368 56072 10749 79069 27107 24004 64189 2018 74443 5319 3515 17860 9533 875 39640 31751 59283 55739 51050 16633 64136 88181 33832 54294 69414 56626 22088 98563 65 435 60536 82651 96496 27674 40720 77712 38311 68527 6086 26424 46139 94252 42341 20779 71834 82503 24571 15029 23692 43689 93670 85270 6566 25069 71214 97154 70080 59050 93287 78931 32808 41778 25284 26491 5915 71563 69811 52276 89946 98011 14161 11059 82706 65540 76843 58073 88294 2746 31036 79908 82725 4501 20908 77707 23637 61970 59239 52787 65731 13721 70503 709 88217 54750 76407 33290 10170 93626 35858 51398 90558 97594 13009 74333 59009 79452 68894 21684 85504 67433 94861 7460 99094 879 1113 96879 96714 71705 90675 97964 40350 981 52492 12521 39502 3391 41579 38231 55082 9170 205 8568 42706 81755 4852 76791 77896 92594 74859 2447 49101 37509 30825 45587 21547 44511 14572 17776 43191 55120 51665 12969 35998 43993 75394 23757 19732 29039 67506 60688 677 37838 50166 88629 45298 79578 92769 69706 45114 5341 19138 58688 11496 80829 1230 75720 70069 21649 52250 4908 49917 44119 96787 81861 41988 42682 65881 85840 46927 66196 21996 73816 31861 23639 91285 14044 39167 29902 22800 85956 52597 8569 63915 3521 42570 16028 80012 6445 75982 30911 8652 91516 98319 22523 91562 16604 56334 58520 44134 91342 9424 5653 32126 71041 93546 69287 79866 67510 41089 12388 60608 66811 27025 96911 34362 781 71515 71725 18184 43382 81605 34356 70216 13791 45081 16466 67407 58408 75264 43422 57914 60305 53761 15438 90133 80019 84658 14014 13939 37660 5911 79882 94363 91338 17783 65093 96795 56996 76863 61286 95246 42272 50921 27320 59155 9004 60740 46476 49399 49149 27426 13048 2903 46314 54105 42416 5308 45388 95088 73117 84726 97863 71200 45416 23560 71720 99442 76457 99958 43425 96099 76354 36177 90280 43270 51098 65511 60238 68634 93945 5958 46845 78191 55380 17899 39035 26128 2732 46950 19749 75832 43183 92832 95145 6864 71132 72899 37589 6470 60162 94829 46226 91819 23940 33453 5706 49909 64625 9760 53716 33756 46365 16519 7189 55412 764 46584 91060 52344 12681 98379 44309 20796 30129 34843 36917 19239 85238 89035 64978 52353 12704 27080 48101 23082 73067 6741 25837 89476 55370 14293 77243 89132 78208 45849 56646 46205 19095 46193 56812 71013 3849 69099 52255 99977 53523 11412 50000 24332 45569 70674 54825 77614 75770 27057 8125 10594 38566 84142 73967 28226 51040 92634 34753 45208 16603 88535 17402 40815 74312 55900 5498 50694 25209 89221 69964 86114 73166 50062 83483 99114 30618 40416 50293 63326 43674 65940 56309 26920 11086 31236 95367 61216 89506 32829 14529 60862 9209 44979 29757 90118 77238 28221 35835 58664 22241 13225 53383 54150 6912 19894 4435 71832 16765 10989 98478 7955 39361 38316 22059 93802 95746 73685 64971 4528 89106 22950 69066 7001 52795 81822 76308 98227 4503 39363 81093 75043 61025 48719 80891 27260 68710 76485 76944 81930 34133 51188 54566 60659 15412 35022 31260 73044 79006 31505 21612 39366 86516 89140 28041 53666 61757 70945 46224 69029 31625 48311 90695 56704 93663 91973 14495 88165 5114 53403 85430 6225 15184 6677 57811 46600 88084 44628 35567 90241 60665 21413 74195 69546 24504 38454 38249 37352 9732 151 51226 12080 41284 89237 26984 45121 54983 93043 43671 44817 36896 33762 91811 22413 52420 87914 86357 26133 92911 73226 41098 67855 22935 72398 99152 3525 96347 98251 53774 92366 31852 40670 13414 18961 36979 47648 56118 91803 90636 37052 19967 33150 87413 18446 28315 45455 87116 20324 59192 90005 90877 74912 51272 35307 68815 1865 96923 9376 10595 75430 81095 83001 92760 59486 73016 16252 93551 46786 26003 87964 41391 72790 82174 61779 60658 60047 53220 94072 14298 64735 7314 28860 75280 34977 72470 8287 91984 60785 74015 48 71846 63114 98979 41451 53072 39532 74801 21605 74442 11267 27145 48536 83618 72070 19590 69864 30197 33082 68956 45770 95219 4632 91989 50898 4898 56227 34380 55667 95584 35231 9143 95944 93647 55145 34330 91422 11898 42724 40755 13876 87254 71053 59865 49903 52230 81409 90057 61883 14389 19669 98933 49116 59369 44014 87593 82942 17658 48737 44195 8245 81731 5117 71946 74466 23375 50873 73699 45453 58353 99757 6147 64015 50182 44383 54157 27412 3328 44376 41407 5345 13426 57256 39739 28475 90223 83542 3988 9209 75365 98922 30078 3268 69878 29702 12319 70615 55263 3792 32961 92492 78859 14730 67692 15812 76799 68235 94171 25450 51734 33977 28768 74142 21085 97675 82009 67174 28206 45938 59719 68903 89439 6951 88465 34955 5869 6612 70266 41755 41833 86486 19677 32065 33596 63454 87186 14911 36054 39169 71107 76652 40551 39695 48535 75802 89532 91497 34439 23972 62787 47816 16060 67917 4520 90313 76159 87877 74286 69383 78395 52497 56767 26374 3321 29549 19365 97046 48665 494 12481 73925 91555 18115 28590 50834 53275 97834 77183 38549 93974 84503 60152 95688 69100 76147 92347 61158 31807 59365 41486 61428 85378 74583 69821 44765 32081 30686 13976 69024 44248 44877 70268 605 99002 46751 16334 93976 65944 84792 99743 63857 55693 82514 52974 15016 84048 10325 53324 37760 19164 44196 9988 50371 35010 54324 19718 1204 79879 44782 29215 45525 95588 18966 47919 67534 45026 83062 25441 65186 86523 10938 23316 48255 81342 77329 93716 450 77270 38109 13047 50547 40002 87289 99562 15 40064 9494 37287 69663 37140 99696 59824 63447 92162 20362 31763 21595 89082 14886 13596 53725 72225 47133 84775 69710 50667 60748 81363 24792 38974 67611 21563 34219 7367 19851 1066 73899 15340 70688 35376 6788 1717 79253 68080 58417 82824 47370 68798 43934 82356 53465 2618 31685 11064 52327 5129 5145 62010 5463 64322 49488 49194 17270 8706 33480 62873 83805 99709 57937 31318 71035 71644 84457 53426 45298 65635 82911 15779 88358 45879 25070 1082 73228 22977 26040 25913 2076 25661 76644 86465 75290 50840 23332 52110 18851 7447 98283 67466 43576 34154 45517 33853 13129 19638 90669 46389 17947 74325 46870 46099 39436 55403 48529 86287 33927 72367 22133 46031 65843 82027 21616 44476 78531 63167 33685 8696 59815 42869 84924 95181 49089 25944 42054 78555 96160 83128 55838 42883 14964 72061 45608 86753 50383 41155 94938 57645 13674 24176 92185 9078 43432 91818 25317 85600 50295 72481 83890 26781 47213 97204 8716 66090 82194 24240 52270 17264 38041 8631 20414 68712 43648 68951 92544 54287 93298 12491 40355 48978 98400 1102 44022 39885 85308 88744 61202 98206 20086 56891 34195 21353 4379 55162 34541 84080 3653 8153 9913 71714 42381 37137 62602 45550 46769 66326 52813 93916 73604 15511 89793 52553 9184 8084 22378 88689 54088 51069 46765 26695 82567 81875 84894 29683 59661 96252 17880 90002 78009 94609 37946 33343 68812 73933 27785 33875 33802 53459 15550 84046 15673 74989 60372 10816 29180 36170 40836 38536 56476 7728 5148 23782 95064 67811 3378 92125 24 63595 99839 74128 5691 4476 99637 51084 11028 65745 77917 1960 91074 77481 44943 28883 34906 27347 56383 12020 94986 96684 43234 61184 93312 43257 29098 8428 89560 87611 46181 89581 1425 93588 72862 89450 28370 3636 29101 57250 68573 61736 54901 78428 98871 17770 94777 60106 92305 15259 77681 26023 14356 56904 21033 23401 2754 5487 80043 52244 5186 99578 43624 64774 90053 38634 36422 79242 51171 55331 99542 41062 38011 44109 2577 4498 71122 81301 53690 65740 59098 64057 51061 37796 20617 72948 81883 36068 8329 47435 24864 94882 67724 26386 72950 28124 91059 39687 18349 49710 93067 50366 96992 42440 14112 81543 24641 18095 39538 31174 41994 61289 42904 46471 50944 63172 67436 15647 70096 15382 80403 62563 4947 52792 57960 38931 82666 97768 50998 95663 89784 20549 81872 85511 97863 34430 56030 20989 32710 13038 56375 21255 20275 47562 95950 30869 2735 51180 47412 30499 32354 3938 9297 15830 82062 4955 18605 58979 59655 44903 20764 99121 85351 3183 55728 18792 96123 83003 32453 10658 81869 87122 60305 50053 78629 64257 93315 91596 51362 36128 69418 42923 81999 73967 30690 95297 64723 48890 58936 43543 72940 1754 29652 8573 42569 59976 35208 44455 29774 14637 11501 5452 38057 70047 77066 51168 40936 50007 98153 72614 49334 76344 52166 37631 53273 84055 92800 56409 35498 46128 73756 92708 42397 38068 83355 80555 3976 24748 74774 32149 94702 64134 46874 56927 70746 78831 76643 46022 69062 16157 82089 15182 83712 10884 46905 34056 43144 30101 39228 60310 24724 68109 96663 81619 69519 83468 4918 22339 51148 48249 85103 14915 14565 93333 95304 82850 11049 51050 43147 92111 32828 3872 4607 73949 71502 34306 2719 72878 57752 13823 28268 65929 59719 62421 78465 3114 62933 21091 21231 12416 75506 15748 53507 72767 82062 17818 31006 38077 91690 59785 38756 74936 48211 91059 76201 2031 92591 35965 22136 22346 69580 13894 73346 48782 17429 37415 47754 13888 1819 21455 13430 30065 50776 21878 36011 64727 8266 91710 43119 20311 85367 41177 97811 50711 11518 62591 29046 57588 83660 19485 4683 32709 41855 26833 80730 86267 25452 79907 98215 71573 58571 91800 95139 25997 4059 38350 37359 36985 29365 7729 73356 94345 927 40913 54706 47523 34901 93313 76239 12655 45122 33236 63307 46793 22592 44125 43567 83822 32210 1741 99459 80682 70148 9605 82852 77124 53839 70796 64238 57688 47340 40784 67110 87191 64281 84201 20873 16377 21756 30037 82919 94933 78019 43250 23167 13557 96575 66542 39559 61175 99351 38173 93712 22215 86802 37545 72620 45783 10095 86541 67241 21702 78170 19742 91934 93754 85608 43712 70591 17868 91110 64366 90782 45835 16556 74932 26160 81768 61058 14552 8665 9098 13476 57159 37963 99990 18516 13833 71705 91599 75414 94939 86634 31288 93128 95128 9958 37712 58039 82670 23216 5806 89643 12376 73510 94533 94861 76497 48696 73694 6838 95617 13017 33117 66281 16588 49567 27103 59865 751 92877 99429 84148 98424 2289 94221 32539 49434 83536 64990 15644 50463 18189 35717 95814 92781 79224 78294 54553 17651 48924 66960 41227 54487 72304 72191 58879 37394 51312 76885 89155 16275 13362 90805 81273 82258 35125 99175 51321 81185 38733 59642 29022 84725 37061 89208 35906 79170 95960 45274 87669 65901 49352 45477 54801 99873 98429 72859 97409 47488 7941 77342 13464 37207 75387 55500 74382 75156 58812 28984 60945 16485 67313 90870 11480 6489 45099 31278 13383 36537 36602 11024 64970 1043 40184 92237 61456 36151 68887 77772 81125 90610 41660 40003 4619 24225 3664 10433 92033 86952 30575 76435 72042 54744 4179 17484 82658 51232 7226 63305 14286 33844 96445 90155 57234 79956 58048 76847 3264 64743 99438 64620 60445 80431 25341 1190 42836 11543 42539 45237 31717 5704 40314 20672 83793 52884 92578 58648 4579 32878 73740 59511 456 9830 35499 67875 38018 15398 51387 80594 98170 22677 50986 2454 19125 90487 44876 888 53457 36326 86902 65453 18458 73492 53224 8760 20396 7517 22214 14820 78602 31082 19512 94237 40788 62454 41217 76886 3147 84547 44052 1624 81089 50094 46272 63506 55787 85819 131 80850 97504 28161 85023 35694 90527 45543 98582 39062 70972 34586 10652 58489 65674 70473 19475 47403 4308 62252 17954 23681 83580 30712 75842 49124 57771 2852 67177 16799 88636 69684 86451 59893 62344 82170 4053 25484 95545 9623 41650 39505 40561 75170 96095 49658 43546 69366 88334 79958 61279 61421 76657 39001 54771 42851 26938 56730 63022 89884 57692 14906 90504 15999 21602 5882 19927 56994 24392 79646 15083 93085 62008 46559 8124 62869 32958 45727 62508 39008 54196 69842 74666 72168 47973 95613 50609 31561 45275 50995 14511 41837 81616 81951 69945 50601 25794 77845 80620 16578 8254 80927 97829 26634 52795 61400 66316 90033 81498 54174 59314 19185 87448 46294 41143 82471 24224 81076 43286 67043 25181 17125 89495 98363 92835 3456 24885 4310 28268 26353 7314 81911 87743 38529 21602 22878 26708 95479 60114 66303 21678 36487 42907 90827 98656 48878 43702 27969 26174 24721 31844 26548 29140 12085 29392 53745 89247 2811 36999 1589 15549 87497 69346 13091 33444 56931 35772 39745 33192 34887 72723 24683 93078 55986 56603 57769 36632 27074 56160 3061 55760 87377 21318 38938 21009 90071 6536 11270 15055 28903 85593 79321 49204 93098 63125 19205 56184 63945 38404 42337 94872 88966 86533 14920 47594 26839 15670 3592 88895 58169 21415 63795 1845 30393 79544 50788 94935 98066 34496 12062 18814 52298 37421 99396 52345 36673 80602 21688 22246 27357 99975 7877 88165 33450 14764 45528 77107 59029 78591 57882 81775 9440 8299 46010 54710 95845 1419 40594 95839 22805 22654 93743 72652 55996 52235 42709 61322 37683 15693 70571 8872 91994 15117 55511 49275 52319 93334 40560 86758 47991 21036 69951 36530 11650 43442 26551 85749 85036 71531 95210 78252 56789 65895 51702 37206 36104 38396 80242 58399 94398 39203 15800 88250 38366 78384 37292 67698 5207 56854 31127 13549 68104 74064 59695 33560 21352 53207 38584 11177 49176 75072 75204 72769 64243 99982 480 75345 28305 67751 43006 50048 74835 5916 44132 54898 46848 9798 19010 16891 74278 13867 46485 77075 38326 15459 27730 48729 59710 52272 19198 51927 2412 22897 54602 6643 74936 13101 64036 24045 48662 252 56221 77576 57351 23740 63638 34304 58068 82154 17851 69615 39798 70738 6446 51943 67225 42016 56889 84822 16905 58563 87755 78765 24810 55859 40342 8402 13853 75952 91333 15484 94974 11333 13630 19175 22317 76479 29200 74070 99639 92367 29921 98331 34662 28606 99454 52428 50874 2479 48101 10116 34264 55081 80825 76445 98957 31379 70298 96572 33547 7449 89483 15772 87959 69427 92474 17931 74874 65982 36580 27399 10322 39498 50485 37122 61680 77872 12929 18736 4891 69626 71540 53658 27716 80278 64922 7737 4350 90935 1049 23057 74420 5727 45027 96874 68310 9733 24268 34654 42140 59702 85875 93867 53385 85988 72795 54466 88682 71257 45269 87264 60429 13629 39006 84882 1463 72268 59086 80831 43854 29847 4109 4167 66026 5192 1314 4132 61355 74327 39964 97287 18711 11729 96802 19295 76227 5550 54708 79772 55880 30904 1809 19285 46646 75140 82521 47027 65728 73210 33557 32712 3569 28401 40160 16467 65974 85953 3 23300 58233 49640 36603 97885 2747 50017 28316 56444 63095 99150 48347 17030 67881 77550 26834 27579 55310 7228 75814 6014 75609 71528 28490 60726 66106 88302 73165 48711 68891 33414 9125 21533 47203 28423 46377 71141 95719 39546 2333 71788 17467 90740 11846 32235 62046 9674 74825 26703 58137 85335 28488 39753 58856 20580 91055 27 42620 70116 29771 34238 82220 27885 24193 49933 32675 74944 18941 60583 87017 74031 56462 4038 29635 39021 28297 83965 9518 88112 22196 15300 70300 75740 87264 53595 7189 53641 63486 37370 58045 31396 70202 36986 14333 40725 76491 9991 49215 24145 31943 8580 46234 87872 37295 71244 42455 47955 37831 93664 77616 25874 23754 22525 6072 19135 19727 3624 98236 19604 58046 3672 34237 5133 74349 83327 27386 59003 37915 39597 56932 36909 28467 16782 47166 15427 62617 13240 74311 50786 97509 96199 65316 5376 88400 40991 59031 64340 99668 9940 96806 83739 53137 68202 45194 58842 19306 3003 77706 24546 86298 14360 42933 66874 40434 70336 46396 7610 79474 60471 7880 92043 9740 33210 21049 96417 40407 35211 94633 43320 23624 78669 8949 47043 13955 32193 94361 58235 1125 1481 67985 19869 59489 11232 5034 71845 11773 65738 50631 44294 88533 45849 5567 30984 43731 62450 89373 80805 87225 99201 72541 87495 13533 86914 92150 24612 278 66951 37153 20428 11170 6272 43961 3398 80444 65078 12149 64740 58249 65360 92197 99726 76364 13968 2433 66732 72908 99589 64282 48681 26870 54153 47039 52182 72984 88842 57342 46681 54256 37876 67492 19128 51043 66794 9165 25978 20151 20973 72636 64588 7042 70018 32725 78820 23007 6840 84837 43314 61593 91400 89190 90657 95384 55990 96256 32354 2625 80030 54046 5809 5777 15693 94114 18386 36511 38419 39918 95503 8957 55738 13173 50984 72784 68912 85492 11166 27712 92436 15483 24796 99527 78484 35244 164 97361 35964 37203 67148 33670 20899 92557 59515 41391 32727 67684 68255 2028 26264 66732 90460 44840 27124 64124 57534 37681 65162 36707 96896 42827 34765 3 66850 58282 79574 49003 27748 4993 83223 44877 90557 56720 44734 68883 33027 28741 68660 21810 86582 34861 67454 6620 19613 60764 78192 83888 13009 37062 24948 85640 94112 62514 55480 52487 29909 80671 38874 30321 71246 27484 24483 47379 19166 60676 59946 68374 72785 89974 22332 42776 54017 26207 94621 75911 45670 79433 76210 77273 86314 77535 30478 59180 14036 7854 12765 98409 71529 89183 79557 56534 74865 72552 22372 30014 16627 86822 49819 86342 85750 26605 28145 88313 65868 39908 78104 96182 65796 80722 33057 8743 2037 30741 90855 15856 40938 38637 99985 94352 80556 92086 93585 16871 24191 17974 40977 46172 70663 31956 16497 38960 65594 59537 81886 99720 62483 15038 1028 46312 85085 36814 89069 51815 67206 20896 80975 18761 72587 97699 72023 30700 28564 27851 57276 88571 74431 71813 43066 53249 78275 56667 28678 24302 54739 23060 29696 1708 62261 55739 73937 54755 80707 96290 82199 26887 86171 23022 35851 63394 21350 15035 99918 59319 83427 35112 62184 34878 59767 55826 31319 83909 76603 74857 8865 36793 73022 92523 91674 61699 89084 79657 6455 7038 70117 80806 31182 23581 55866 70114 47859 54824 93922 90432 80930 93726 34329 72602 12724 61018 38447 44715 41394 6951 44177 34599 34758 66649 97648 86557 27357 97776 2239 82724 52365 97127 50570 3884 63694 82075 36876 83361 44766 56085 39921 16028 59163 18033 66909 89204 25194 26028 1540 82768 69387 85719 52180 35866 74248 57533 60930 91997 71566 38635 19593 28963 81905 83676 52911 4296 58716 26644 77168 80788 52382 48981 89359 95162 33971 54630 22393 64380 57908 57168 84893 9746 7839 18616 62998 20913 3410 54725 70265 45961 62749 63166 56516 83317 40609 65527 49007 67401 79578 49215 81481 88443 59468 94751 97199 5824 8189 67810 86509 19964 19095 61356 49806 47192 32427 1773 59702 35376 12672 12828 14131 71190 44972 60135 42005 78620 65914 72289 39446 92779 74688 52906 93757 90308 72748 99492 70707 44573 7441 29425 70974 86253 4606 20647 67089 72374 37570 77898 5762 42419 52743 51059 66588 30416 50474 82882 44533 5768 71403 23795 80727 24799 5228 97316 48955 77211 73840 23850 59156 6140 72593 67167 32962 49833 49426 82569 34731 97449 51023 8797 12359 71984 54282 44497 95694 94661 13035 31381 64555 17543 6420 34310 9715 34183 67189 81475 78115 95595 78190 20721 92608 36839 18344 46193 37551 72500 9006 26903 86418 23962 59239 24557 87895 87183 19133 579 76808 92612 31937 77156 5049 11892 71589 16279 26487 30381 28746 96328 98068 95058 3833 60458 86919 67979 13723 80483 15570 58460 21055 79313 59507 38050 55321 62892 42908 96111 19829 19590 32883 5904 60229 75113 94977 2782 29171 86731 37529 74128 98759 14546 24343 63291 21253 16476 16267 49845 404 83717 87791 22294 707 19225 63798 21680 75439 74502 47205 34536 79063 11254 68779 26115 84794 55518 94852 52163 40588 48946 36860 49679 66102 13240 18725 9915 85231 95405 71534 43497 8476 58342 57859 81042 88449 49523 705 49772 76425 51189 95609 23790 75511 69378 56364 23225 53182 95943 45556 89533 33166 44944 6473 66618 11270 34127 60321 33892 28063 73974 50250 74898 10783 54651 11544 35534 9597 30587 10547 7065 67688 10899 15981 82362 38131 15415 3237 43588 79007 53100 5673 89187 73545 239 93672 22219 98734 88780 25658 49369 53639 98582 77733 77106 30799 6063 41111 47917 88859 31266 60912 4997 63657 11932 51840 77864 58841 65277 529 29245 99938 26575 20674 24928 59883 30951 70314 21896 60366 83411 90910 44967 20868 40190 40541 22919 86674 86121 59662 20490 30217 66078 69650 91762 46335 41837 86576 31008 99217 48615 57236 19786 12258 92048 47805 97239 69337 95067 65488 94213 52850 11457 31931 87276 79127 49639 10239 1353 30420 17324 57516 24107 54772 15504 45140 80302 86028 95786 54037 10041 62446 23220 68944 27816 44302 59176 18380 416 72806 69040 43333 29276 81338 80384 62212 53659 57625 64747 23363 51174 47149 42944 28405 3429 64216 77666 78147 34198 97915 2708 58503 38061 63969 67327 10695 31292 95967 88714 88753 48524 64764 54677 6336 78981 17588 61905 18445 18373 62716 49522 24785 89573 91021 3466 82692 959 68426 46455 88832 65212 60395 45373 83150 47888 97752 5936 87064 31417 72644 79591 20391 10027 70995 3203 76388 92565 63146 73710 93633 33763 11967 77120 74580 32869 257 40786 83551 4914 36006 53362 4706 94858 36652 85774 62854 40840 2251 3199 92365 14506 49566 62887 15108 88820 84959 2144 72392 69220 73496 58998 95399 44533 53297 68002 8954 57155 73167 13359 99721 68481 40735 31372 20760 79926 19281 44378 86259 63755 50569 39970 34817 1230 61558 70737 14081 31796 35797 85508 45813 82904 93012 62596 90564 31460 3536 13214 765 72634 98896 52961 47408 7741 40764 92934 770 54145 66879 4225 25337 69186 87038 17437 66497 41924 52949 16220 34309 34352 42572 79715 27745 41729 30646 23554 81013 80192 53539 32053 5848 84167 37315 13337 42054 34750 66558 28344 44954 35067 29171 42911 23379 76768 32370 51287 85139 5091 91234 49390 5139 9829 61396 39430 35151 24606 87072 16885 95294 96606 33949 15114 81306 59692 47279 83199 15780 62220 67309 77776 30071 56053 7616 28898 19277 65158 2501 81798 32188 28460 61554 22619 15085 41568 4167 83128 70374 28366 34759 39855 22625 55310 79989 4929 43778 91207 74416 34651 72780 22065 70759 54231 4813 89680 29182 15849 32371 51693 42002 49392 95601 72988 80872 3288 16993 98126 89635 3779 82905 24719 29517 83862 581 29857 4793 75998 29044 77133 11978 30812 95493 53827 61773 22613 16233 92113 47401 57392 15761 74884 66488 88108 35937 68336 27661 89155 21565 94319 92070 3662 86797 52835 3456 91672 9369 31252 53906 51028 28253 34489 60717 66942 74968 93359 69172 31853 27051 43907 73232 23351 4325 71188 85785 29305 79938 91612 88807 35554 25587 54095 24754 65505 87607 10412 62529 25702 38937 73343 88091 56908 28428 59658 98584 5607 45777 69818 92596 22078 653 43763 17715 75505 45695 64267 78261 7662 71782 87616 80603 64812 74681 89972 55577 45721 3897 88248 76333 31120 81369 57396 61562 11211 56391 10710 60699 3470 35387 24115 15723 87729 42459 31631 93777 61391 46182 30884 61465 46457 60615 57243 50664 78217 25251 71311 84760 83575 61754 65858 76255 17349 55788 90093 24213 37489 24302 48541 98065 77129 45550 51488 27462 95304 23559 17487 62148 35920 58708 10394 67403 36420 19118 60995 47747 93873 55744 76579 94472 78293 83414 15882 85043 91223 67516 62942 85663 3306 67283 66901 41994 40741 54867 51368 53577 22722 1396 19537 32584 94204 58090 76428 7799 38671 74521 90642 56038 48520 81852 82643 83696 3418 78024 27409 64659 47896 69179 24962 72173 3905 11137 93615 63433 49093 30432 93589 59352 62102 5554 27928 92148 99033 40676 87157 82337 90313 38339 50841 71988 92835 72733 65298 7165 90134 60746 41342 90181 84289 79143 63693 55485 52752 73048 59324 99464 89450 58153 24682 88508 14714 90194 38063 55618 50740 22218 60756 70726 99578 48518 39137 60659 96442 40191 62678 22305 64432 92802 3547 78460 54840 81570 2362 50275 19735 42850 42752 95404 21983 67557 5853 45745 50547 25128 62915 53004 81358 61726 75774 95134 5323 21596 45334 3709 32653 72092 71729 84778 48439 60862 92951 75524 38312 2360 36629 15075 7030 27380 38964 54457 87324 9645 51782 60397 61720 11777 33133 11024 6385 39033 8586 79867 11142 70815 56864 42889 24874 80534 60658 88427 51012 42419 79902 71717 5163 10628 83453 37180 12376 47010 26711 8937 46324 59661 80359 85951 86247 45808 73636 6198 32918 35744 1383 96563 57035 1077 72731 18834 33316 22375 18584 55210 52282 67706 98343 24148 79927 29165 19673 29014 17976 75188 18931 46912 9008 84593 38898 83990 33724 79919 1812 90956 34837 69181 61691 24724 88610 42504 52359 99425 65805 51995 16503 78793 22060 66711 60048 93536 28575 79765 8899 80407 86251 52383 67988 60710 33356 95528 10030 34118 73592 48787 38501 27584 75758 81589 53541 12381 11278 85258 13299 44484 18457 87156 32898 71044 20202 4222 3057 91197 72133 69683 87027 3866 41984 85642 19376 18133 31299 15139 91750 29942 99179 89504 36419 18008 47897 48416 86120 66142 78028 34164 33202 46141 57102 29562 39826 53513 42321 91942 74893 78536 27983 55090 27740 44431 33105 95826 47689 4001 55759 17001 22588 14016 20808 83345 13657 71787 50918 28003 19749 81285 6990 5136 72696 80705 93866 80889 66230 79098 5166 61358 98566 90525 82712 30494 60565 371 83103 62948 37236 20155 19397 576 13497 58233 24287 44280 27626 45568 15518 53476 66181 97482 21983 11029 25998 91896 50125 23802 17078 20750 16254 72795 37193 61617 36631 30932 92110 9162 76574 98 85833 68826 2714 31192 23101 91023 67260 15851 21974 62811 18192 47960 13941 53650 51766 41178 34084 92143 543 31813 28954 45284 80896 91506 28552 79800 64650 94317 95675 90889 92793 81367 85228 28533 19489 94873 55754 49505 14850 4492 84830 38202 11154 19823 21214 34779 63616 51842 88935 61583 85894 45245 13812 51263 30772 6439 46537 72988 33650 61696 19022 13516 39997 18701 61584 7868 15670 46940 51294 31242 9592 57045 71601 12103 20440 56856 68982 49197 49133 85364 27882 29999 42283 44450 65649 490 68857 27318 75325 57498 23132 62775 56889 93734 41959 50521 29407 19895 30680 87143 84149 57549 71443 44395 31460 55736 39909 71164 95654 24134 64952 86313 19202 8086 84830 3327 76664 58912 87140 2911 99728 9837 325 66089 39440 12720 69330 23099 19573 35653 90745 59165 37520 48357 22196 82319 56295 99751 97862 16115 81902 54556 60346 77863 41190 3543 86297 3804 36634 75535 21726 84005 11316 80556 66521 49101 69680 26802 57162 70936 194 35258 90262 4627 96104 71328 71697 63966 21937 78563 53245 36401 64296 11041 92001 45952 66245 540 92815 37623 70716 53824 33839 87427 65837 67539 15729 5294 26330 97332 38885 5091 11215 36672 97880 81218 45285 43908 12324 73952 2072 52636 24719 58868 16555 74198 5046 90348 46491 42258 45605 27070 1761 22515 75770 86666 75952 31007 27575 57363 52217 73590 21939 71884 3202 66177 26256 45354 24704 77887 19010 22653 23507 34486 49233 9459 97897 13337 19490 44098 27047 47461 92252 95976 11401 8143 58000 56521 29323 18850 29436 94813 55491 85839 81118 5237 53023 83092 46763 1493 93732 39800 38498 35791 5380 31397 36060 13060 38732 30781 83683 83569 56136 34354 50679 1710 31241 26520 85400 11021 80510 86222 48342 59507 20134 17846 81139 45382 20963 99530 22576 10485 7518 7408 50838 8824 83654 27585 36829 51808 76655 2341 29228 59986 53356 94416 17619 84788 18391 45889 10252 56192 94674 69119 17345 91199 75308 73966 7828 39292 67659 94737 36089 26929 33753 44878 41457 85187 62057 13629 11559 10759 48774 18601 62034 79135 95713 74378 64411 78349 69246 15726 24044 29492 51858 42793 93674 22803 23484 88717 92048 8238 97120 49866 97557 48137 90195 16196 25618 48124 80323 73036 35576 98906 74262 62212 15330 87293 54230 78412 8340 2409 71922 32588 18411 64395 19920 69772 74449 92890 2591 19036 51514 20268 57122 22879 34083 52964 63725 38385 87042 36625 13936 6114 48870 25264 62309 66934 99887 26195 82341 44448 16187 51404 63944 88533 90776 18451 54036 61459 26731 81315 53296 94278 1567 87057 3153 87515 79557 83009 89756 22882 37695 95362 6556 60586 16300 84256 45664 43068 44231 46245 6122 33511 57634 25198 59838 48387 39034 86393 88114 1864 24081 72919 65416 82273 86230 10932 9217 75254 85134 48065 37965 20793 50096 35652 94735 17027 53073 77420 46371 14940 39836 91245 20869 77021 5020 14384 76363 83698 10619 72373 83841 30647 48753 49971 55627 19158 26581 89387 51058 96200 37787 71987 61189 91706 71302 30623 30602 51711 12597 97468 86096 63495 58849 35239 32721 38011 69685 75506 16399 44906 98461 22068 30203 95641 20522 36261 29678 56233 98980 55218 78584 74699 62601 354 25919 10580 97647 10771 74530 93573 67266 31909 60955 33520 20003 14320 67730 10126 12654 2941 71358 36053 31743 13391 14328 49715 2840 80152 57492 71837 42960 47724 3923 11896 67707 89619 15726 68402 26217 99058 82808 31428 14225 84443 40792 43477 39335 53836 26088 14830 96772 5158 5935 12684 68767 49405 15702 82719 74431 97394 68531 59758 65289 82816 80142 89105 11345 31117 59373 42678 6521 37878 18112 26544 22595 99878 88682 13643 1347 50179 31188 17784 18540 53167 47679 66359 34026 95002 63157 15041 60914 20021 36273 41701 18700 64220 47962 14779 21923 87513 85724 52858 31577 46018 8655 70050 90754 23967 92806 75251 29896 42485 16734 14016 98032 26511 93421 87443 24055 70523 44277 61326 67354 31252 6147 67056 59932 559 32179 90071 89638 64094 51914 86772 37081 3242 2597 47426 5948 95663 27670 82559 87231 56801 98204 59369 68124 64248 4227 10850 61176 33579 25966 63938 84162 67893 55132 2306 85636 61471 50415 98011 91389 67482 82646 73536 45578 62378 30555 74477 34668 95714 46865 2430 95995 29450 17056 45437 31966 45206 83500 74966 50200 29373 69212 46040 23362 73190 77991 3975 93589 73638 66141 36803 20655 58135 82748 47633 92782 70494 58481 44375 89381 36088 97053 46720 84938 21785 35371 94047 94934 78150 6978 25105 23705 49824 61841 11413 99469 92513 90140 15702 54392 59410 26898 11614 36186 42540 62741 98560 82370 66915 88476 98056 46763 30656 4528 15973 13374 84124 92930 53708 4 90774 63875 99267 92064 98970 11180 97124 17164 98630 40623 43258 50783 75395 70497 39335 58991 65790 46835 4448 25225 20398 16984 83449 32043 84819 47303 21679 2186 81577 72111 87751 12322 33317 54179 76110 14359 79837 78311 90260 98868 93597 45593 99841 54963 31219 67345 65610 7696 74292 46849 83683 26358 50168 82380 55400 24935 19396 70702 78890 62186 5803 89983 49528 55318 69468 12135 7849 71421 2170 46417 26736 88792 66443 74385 62732 31713 58889 36365 68697 76537 21476 99397 41298 51690 8626 244 64935 92178 72329 27084 88022 20208 92337 30278 72394 85082 22746 39261 33726 99200 52197 56729 63901 21357 78086 85718 69800 35232 54370 33271 94304 35630 28625 9320 86178 2151 55758 26399 99217 28101 15998 50802 19957 25270 91189 14926 14994 16490 53164 30386 71124 63880 28439 48593 56997 45909 27241 58166 73946 36986 50860 58368 55662 40854 5550 50272 60612 12620 7709 14461 81107 13134 44009 33593 51728 27042 29641 94975 49798 91212 8029 9983 62167 59316 48107 72361 36294 38625 81256 85981 35499 21852 54890 51539 34293 86299 18230 9126 62036 12390 23234 63561 37525 91049 32937 2847 33518 81442 97701 52202 14827 92713 60635 99560 23043 19381 97436 21524 40581 13633 74504 784 38913 10668 93116 95669 30966 9926 22161 77755 25471 23091 46876 2410 4789 61298 97625 51755 9674 7620 64172 68403 74482 55564 8929 90723 1160 28380 29054 56619 32930 37266 91972 71476 48568 66989 58413 44664 69454 63363 75012 32888 94021 61731 37641 72291 10712 36677 61778 59447 90028 41897 36733 20210 43086 79370 16482 31073 69870 17011 52759 44726 15208 95723 16411 41286 6096 19383 16528 71639 88839 36718 89761 13656 1571 10981 14972 59557 43832 97732 79181 41698 84854 89504 73258 8907 44483 64534 35181 14177 85869 7193 13339 38615 57417 72720 13811 83444 49885 32611 27755 73184 73597 35817 66640 70463 4275 11763 9095 96865 31384 84033 45239 24890 85430 7438 75832 24592 72365 96346 411 74342 73821 72408 10200 97168 31134 5371 96915 38554 13635 68278 29597 2003 44123 37868 16508 61175 99789 38473 2024 70233 59014 24205 74889 46290 49586 14323 80786 13252 6050 44204 9288 82735 97858 79718 98245 20620 20767 17548 21856 44915 26064 59592 98793 67036 92879 72749 11001 49679 53260 44497 90082 45770 40347 95810 98665 27958 22074 84121 2962 80848 62278 97245 6989 64678 57415 15922 32831 21826 29240 95721 1945 4188 10115 67682 29193 3085 82633 4614 32216 21567 56604 86534 63041 59868 62818 41441 86702 70718 13853 44134 79427 82042 55140 77173 46789 34586 19039 26370 28322 90428 88892 94853 81665 20119 15235 34545 23536 18747 6320 97268 66937 67060 16505 16397 93781 74651 11309 36173 27308 11251 87328 2030 69392 89027 86432 3812 50331 941 59505 57038 75547 54628 24476 73898 27323 90991 37899 97738 45756 12481 74125 2341 94789 29846 47489 69062 28036 83839 12279 64549 6439 49755 47032 97751 43754 23485 93701 57478 3348 9605 1810 95994 83721 8202 86116 52394 67446 81033 28039 10233 39800 70596 58042 54929 7479 95701 44157 20754 47330 76480 62567 28593 81288 74532 75060 21722 81545 17783 12789 50054 67590 85863 94065 43953 87697 17932 56454 94957 91809 64085 61435 67852 8665 51097 11599 26920 78369 90019 93003 48600 63919 43588 95746 27274 42589 18841 37282 51899 52792 78317 3605 50398 20219 81079 80209 30945 80194 61013 99272 17022 75724 83156 35288 74844 71998 26413 29562 2618 63635 82814 23311 72716 65675 74067 27996 93506 69774 76884 84735 71671 84334 21802 68118 51748 42795 8098 94168 54100 89957 20508 96334 40249 74174 60941 79282 61192 50689 3799 77131 14905 75440 98971 56712 57089 77237 8256 43931 61790 26251 12888 51929 97760 77561 83306 2008 34897 1743 32127 69591 15112 49653 99148 3750 57645 64828 34500 13425 24982 1172 20122 72243 71840 7391 30089 7960 34609 96702 81695 69015 5586 64851 43535 63453 7642 31704 86544 95711 42549 43280 87112 64039 33098 39160 96695 44638 50297 41404 14859 99274 69645 67818 84925 4896 52922 44095 7024 91037 7584 14655 4584 63113 64988 66424 97188 82726 15918 74017 95074 58404 52102 33101 56418 92988 75126 32952 89777 65012 29261 32178 65786 18783 72742 80499 48124 58580 92969 76570 88088 2628 44018 26615 93258 98958 77574 21937 86040 40328 23924 64823 12283 3830 29067 52881 17015 82613 68702 73126 57682 12361 5747 38243 13461 46484 41699 51654 63696 24657 15057 29484 52862 19444 15264 99790 91117 82729 40475 41053 63632 16262 29088 12665 60750 45323 43642 49079 65913 93726 75928 34007 85034 29749 55872 39067 80385 86742 14245 56119 99417 64339 15603 24102 80313 38578 25871 26807 64828 37688 46477 2339 19174 8076 74033 5208 5950 61808 82423 64912 85998 71370 92264 9468 83624 31458 60176 14575 33140 12736 29950 73261 15839 20434 47371 59181 29627 98253 33651 7514 41574 8981 5744 97805 25328 61879 59137 36406 23492 60162 88587 26481 1412 9400 90440 70125 4500 57221 70251 28058 97854 45862 46750 47568 83758 88243 42119 28153 62042 20522 27085 49707 1336 38663 96897 85129 85398 90777 15813 43248 62832 81977 38492 9694 81147 11195 52366 29336 98606 27685 36320 81334 34695 43874 59500 16335 70141 1517 43131 84636 36816 86181 48161 26391 669 73538 58112 99697 80554 19191 16016 97755 10128 99522 93614 62331 45310 32335 25823 1816 92445 20332 60343 19429 48700 48624 84809 69399 66401 36376 8121 48554 19666 43109 84564 91022 93485 13233 82931 15695 66034 24005 86684 33890 45656 1008 7425 86018 9613 37282 37652 89576 70472 54415 82827 5383 14737 92499 91208 97178 21653 22361 78704 13012 47556 15228 89487 29963 86897 80728 89878 75187 43187 22298 75107 94238 40183 81272 5308 43617 17917 95785 17589 21104 45789 26307 60029 19895 80060 81819 12323 34274 18141 49294 91264 29629 49242 94891 11364 61712 8318 30335 56254 91904 81765 96462 99570 2347 86550 8681 84220 75705 90781 67806 874 64639 47382 10806 37167 22489 64353 61984 39121 90961 74055 1384 31184 78746 6142 77865 72696 30825 29826 69845 11027 33706 76669 13934 49447 38899 30284 77294 47998 79736 86900 85137 52276 53442 45382 86839 1027 44663 51168 47667 61808 32173 11650 81716 93892 81561 5683 52448 56767 52149 96825 42827 32803 42942 52920 24519 82521 96287 27611 76590 11129 50190 43142 6683 66901 16300 15700 76033 35706 66597 17575 70072 47965 9132 27880 3218 45097 65836 97770 2396 65151 83268 20777 36150 75119 87779 1903 35171 82129 35330 59694 36003 70766 48312 18687 3017 90153 88279 48343 15473 52657 96735 52056 39419 7636 87970 10531 42185 54952 34785 90322 86826 37437 90632 98756 85401 63660 20007 75090 95606 89617 70235 57622 57588 7944 68077 46316 74388 4243 51639 52176 53730 6110 77930 29521 11983 46929 86409 1290 17761 5809 50545 53852 1902 58525 45199 8255 43552 8973 10853 56394 56417 25420 77283 45477 20196 69108 45346 95053 97121 73879 82445 99198 95627 81700 22198 77485 64758 27856 83465 9355 37049 77891 99985 55334 10263 96254 69412 97563 19719 62921 95536 56711 599 1416 79478 73005 16799 72947 89201 28763 50612 23304 45577 6646 26860 5309 66088 50984 40879 12355 29449 79804 64627 64644 90239 25681 82330 2958 11719 17572 63978 40542 59711 6470 2760 93186 29362 65297 73402 81509 86802 23532 1656 5186 9995 26298 27146 54902 67172 54469 20731 72988 95259 87722 55089 56712 20631 329 91358 73022 42197 65035 23932 52559 89270 88176 77820 75732 11981 52635 53038 51038 23392 99221 64355 60896 26864 65463 82315 11516 90754 5329 6748 97635 36044 74591 58390 32425 14309 83665 25390 80861 82569 33655 69320 92010 97055 85942 88348 10041 25666 3024 32957 44664 34649 59138 39240 17026 69391 75463 80123 27602 25887 32981 46315 81752 34372 43796 83207 21390 71375 87125 10836 99792 73212 16556 10371 53263 14568 43741 83439 21684 73234 6930 63753 14818 50310 90901 97162 1752 62897 33514 42439 75005 98590 61726 42014 21370 30646 71220 20581 69513 15569 52819 3512 33298 76511 31611 93275 97970 21746 6926 41499 51248 60248 12963 73299 56100 78465 48449 82824 91875 39185 95630 26525 90745 88580 34264 59954 86825 62178 59987 91104 65488 23629 67757 50303 2808 81271 87361 5170 56526 16290 29349 81145 68436 84739 33293 70526 91265 20866 28511 66251 20833 38823 20381 54628 40821 60397 23101 89336 68077 5386 34574 73226 81690 88257 6656 72188 1196 69071 89328 15006 56070 41637 2359 3555 33543 69232 64569 66 99026 8159 21916 46116 11370 58502 93010 84784 58127 42114 23590 2697 46031 57611 14070 92848 95730 15087 59951 28705 20988 31805 91427 4942 9784 23380 429 36622 1165 67700 43628 14294 79882 79303 25738 62127 96740 17107 26633 67814 64129 60776 57751 3462 48167 1526 67722 31477 61305 34990 26076 72201 16671 40992 84241 3543 28342 7059 73217 66797 84127 75188 11427 34649 74322 45792 17787 78048 28018 17917 39404 53893 61665 91589 19937 65970 19096 79679 30880 24780 74391 34677 72984 88046 44400 22207 99886 762 43196 51808 2715 86838 18487 99468 94782 81336 17420 18375 52967 26153 93175 81819 62529 41731 43362 17611 37894 89337 47219 58486 48358 60757 17945 70705 66501 83785 71729 54382 40097 48394 98011 77551 31112 8366 40638 59643 37053 98368 60760 42038 51592 37670 14966 51334 53337 32834 74704 23099 5906 77150 85526 31694 64380 81728 62321 92220 66229 48625 85556 3924 66471 81694 71663 70789 64537 24441 56906 21935 31997 49105 26824 55659 67161 4715 31940 45582 99520 76585 46618 33323 96388 56064 17714 56178 22529 14546 9735 64061 45201 2265 58875 10071 93233 89768 34343 73123 95053 77648 44362 7920 55679 55440 96286 60294 99727 53694 84504 80681 87318 68778 32116 78676 83 62399 48813 41655 61632 60499 68398 78594 56331 37498 13649 38053 9031 6657 76584 67415 18866 63 98854 17110 39473 24338 70840 68740 25118 36386 3745 48190 68110 4741 66185 24047 63957 7509 29671 80087 66455 27186 52087 13301 89666 1631 34752 17659 98002 53185 97571 93741 84917 5622 1976 95689 81977 45703 39769 41185 11293 73557 4840 16897 61457 25034 81603 89997 28061 66676 12688 61423 72451 9667 65903 96797 10555 3419 29385 48500 94652 68129 72563 76383 77285 22072 38027 22077 9966 84837 57513 30769 18105 50105 33337 69331 63630 44085 18029 61841 62800 8066 40944 28049 9886 98580 49079 70204 82482 79527 52759 11848 18861 6646 4173 55572 72734 63720 73698 22142 59114 92018 99555 7621 9379 30319 23840 85200 11075 6703 97826 74215 19042 36499 69559 1770 9302 33588 85194 28174 64046 92664 39599 36274 61469 64730 81098 18060 64358 61710 59606 80925 99876 99989 70127 69445 31799 63215 70492 43598 17835 81586 39312 20681 16567 30572 86655 44194 52870 89025 81321 89107 44990 65573 11918 7090 1539 25450 53620 74 18210 55662 68816 36577 91637 21305 66310 63060 37669 6157 45714 78376 97594 45406 30834 48996 18430 59819 37815 36795 24270 18049 4959 86442 24368 70903 49389 48890 31665 10232 8898 97526 25860 99092 6513 67176 21449 45889 84883 2206 10229 87767 74973 28206 47422 21373 65219 42485 7873 37525 36052 44628 93377 88710 69737 43506 47018 56535 46912 13328 69515 68431 65855 34795 11143 72121 68046 70349 9216 89919 37938 8956 44768 6098 29130 24338 92387 4749 96875 56025 79871 9040 13692 18953 66311 88104 69749 96304 2037 80589 52616 18159 50002 95680 82185 18059 67024 10124 4418 68190 74612 6297 11500 27536 34187 73358 23241 5465 18177 36633 32544 25917 30083 618 50788 42651 16670 77215 95714 74318 79577 24910 9599 20301 74914 21374 19310 9926 7486 1994 97096 52953 60192 81274 77496 42988 10463 7771 80880 68266 60099 89360 68128 75649 35756 6287 52620 89548 32623 51490 77624 8940 44698 44592 66420 36451 26524 75780 63132 4882 31497 79309 26924 42003 51075 44903 88133 14353 79621 18782 42605 41367 91377 1750 73062 23180 36338 80463 84251 14367 13181 91807 24818 41622 97424 37265 31376 20699 74501 36509 76727 63731 59338 47589 92805 90590 98575 17504 35798 93943 52807 3315 18350 73141 66476 59682 67044 43626 61260 40812 69107 24624 95369 57157 96628 22563 96161 10575 15743 13168 206 83497 64692 16538 94122 52586 5731 34128 1837 8329 41747 23463 90643 91769 51167 48962 37410 19597 6844 4689 37110 26742 35673 73677 90126 95606 31429 30079 98150 52787 95020 78271 64892 43972 4966 39742 73655 34084 14625 30284 74840 32752 68329 23411 51235 41730 82681 38552 91256 82021 2578 27723 19888 82730 57188 52890 68055 52943 56295 92386 35261 70692 63502 29956 98272 42289 52466 20404 32146 2558 51360 49242 71444 14945 63841 22747 80843 52386 88820 5146 19135 93330 1821 45188 63497 61309 78772 37394 91957 95840 80567 57867 90252 54629 9975 91211 28969 6971 89534 67934 45675 26360 53346 30834 42316 10797 23437 39056 86012 56545 38355 9442 71411 29229 86043 20101 24132 58363 46959 32756 47458 43513 60624 20792 9094 32850 76832 69227 89085 90399 82300 44339 64391 36862 68683 50745 17321 43424 46494 37694 33974 33356 48275 22305 67345 41058 18780 88092 11562 68903 58171 68798 49919 53286 98517 77730 92514 19743 93634 89713 87159 6192 23775 97082 66711 86989 38906 82785 8108 59846 49227 62875 79033 27870 70493 25471 13238 21532 54697 32050 65991 41552 58172 19625 67499 69927 61687 39050 61606 69339 17842 74113 58034 10795 62484 79533 53298 80118 30560 24651 44988 28909 28137 77352 78986 45554 41625 70090 21582 74992 54457 63216 67281 96662 20965 4909 18855 19473 39850 60816 68203 9623 56836 51637 48025 67961 85518 91928 23243 35192 13925 40076 38706 374 60547 49936 78009 1170 85456 99487 25441 57038 60556 40684 71240 5362 27676 59261 17776 50662 30805 41817 11926 87560 37327 32705 42678 80400 53424 68313 49249 63237 52853 67436 86680 51490 14694 7921 12472 1002 43824 77201 54555 98162 73494 92754 83214 6811 30362 94534 43795 67740 5126 52303 26882 36380 40270 62171 17092 26326 95980 481 43113 75138 48819 45640 74866 47639 42728 65886 96536 31488 38470 75003 38708 82922 22165 84119 47808 30952 62133 24511 45216 23920 19309 67164 2939 3994 53578 1542 61550 11781 37594 75313 91444 9938 4872 32655 61261 83962 76916 82027 39058 75454 55893 74577 53011 12451 16504 71509 73255 18057 20680 60662 82830 72991 59471 27888 15412 36795 5013 50289 69876 234 27743 82532 6898 23104 42234 95001 53735 78436 65285 28718 21083 77096 14579 44163 72797 84634 27652 22454 14859 32331 86906 6216 19860 39989 16257 68428 4630 97930 79988 56247 51245 25032 45655 20605 75763 9301 11293 38596 54062 30121 6574 14645 43301 54809 11226 90779 30986 36272 55747 17083 4158 2207 73657 9306 85147 38633 29867 70863 8597 99716 98599 79940 32538 92595 38585 60992 3536 18075 36079 79620 31040 51154 55492 25099 81850 67487 64302 57409 27439 52378 48677 31648 50959 2763 47969 32283 87848 94198 19705 96246 19410 25946 57551 56360 10255 95445 72680 82193 20524 50390 28254 83741 9544 21790 95236 22807 61500 90736 10029 10229 78863 71262 66820 42356 88573 66990 15423 13223 12756 31090 41565 30175 74294 31602 80065 7979 65771 3768 9403 37833 89692 71700 64852 11930 27558 30356 47010 74037 50527 91444 15088 91365 4623 96922 10231 15601 8899 90383 94196 46947 17292 34420 74692 56477 74847 3960 37823 76722 20861 61103 66965 84394 42678 68337 58315 87869 56887 11625 10003 41268 49588 92807 72178 13778 38417 10807 56049 5533 90953 18221 52785 85476 18927 64305 60430 59392 68866 78893 53806 86701 52981 17687 3044 21343 72064 26491 65166 81437 55790 48280 98445 1099 87438 85840 77748 47999 63922 6506 65858 32318 80944 43717 5857 78353 37447 99685 5613 40153 3152 27913 68539 29324 11512 15436 46653 97856 31708 56966 21543 77061 42016 97367 34215 88469 70479 37642 93487 36407 77576 28595 55408 98855 32477 60551 34563 16593 89812 57889 6525 73078 92602 54802 4975 72451 58074 17704 28989 10627 40448 95338 52185 22517 85343 40930 96454 50115 36010 82931 85757 58775 86238 93073 21275 64853 9274 58923 13133 48712 6960 85164 24867 79788 97858 32456 55368 79472 88135 94116 24377 17660 56538 29484 78967 61747 55320 4542 63401 67492 26510 3663 52834 71244 38268 70175 52755 65544 3335 49303 78543 79348 91540 26710 38860 58188 85338 6804 1117 25851 33068 83453 73028 4245 19641 55519 67801 45102 24252 48988 61380 27062 39602 97417 21214 17735 89623 50712 37783 18434 22301 82309 71821 44389 75461 27960 9022 62097 79728 91826 58699 9624 64380 16973 36665 94352 74361 20657 33598 90285 59433 11285 33254 5717 69144 74118 51740 28963 85590 12650 15546 13165 56909 67483 99498 75713 27255 86520 42582 4331 72010 38333 76316 15466 16980 77516 15601 47564 95570 73609 34088 49097 56648 75603 68784 91892 63531 22570 70260 47411 65390 84551 8212 20965 6737 64215 56851 31123 65216 26076 24438 39525 10133 44162 97510 2248 97558 28398 10356 76942 18942 47805 73169 2305 6794 66520 13647 53726 699 61692 45784 67414 14607 91911 93154 49309 42299 17203 4314 42167 57797 34926 67027 87961 10607 83782 66920 46565 24573 902 90340 25700 85652 21513 42873 14125 30738 47064 58010 39417 21298 10491 48983 75388 61693 7438 97256 70326 18006 9381 47752 373 911 11992 9097 66648 10722 44274 37477 42125 32313 49440 14786 89102 83863 11222 83793 60916 78727 14347 65973 99975 57675 4961 27400 93980 86362 88421 56121 34246 3218 59384 32579 55672 27723 89173 55536 46141 92381 7806 70347 36166 16808 31001 13260 21009 78444 1683 44340 37596 68084 4507 65811 18997 74080 52779 32003 59530 70613 31569 10458 28555 33790 87162 6225 12333 6610 6821 9585 39689 2847 76280 45102 19201 85101 82219 93555 64389 56343 74446 4563 88000 12704 49688 51051 21404 11276 36328 7459 10429 62394 26653 78235 88538 64964 35024 70348 24184 85667 90982 71706 66341 71487 80089 21557 30737 99378 92215 61841 60551 91834 8208 81959 39805 63310 84808 8737 77918 49994 57583 81381 70481 38302 49573 71314 46283 22303 56674 32533 66984 18093 83723 93229 93144 63857 60967 64059 7442 34321 33718 33244 61033 57890 80888 93662 25557 22825 57098 56679 46457 39194 94308 52342 47849 94258 8385 55779 51525 26683 61268 26874 69811 46627 75690 96066 54236 20000 49132 30365 19337 70347 31245 33256 91310 22466 68202 6080 53560 5834 50181 1166 2236 75492 99589 29113 7303 51738 89080 94599 36112 49771 69228 82916 19236 87730 2867 46466 93733 7960 78482 18862 2798 56358 20327 45544 11749 55393 59169 43836 87892 82278 20709 67527 6289 23365 74436 17923 24949 70979 46235 76399 77668 79706 83264 96441 61017 96503 42851 79118 49410 96024 58250 34405 56040 62859 87287 74104 13678 16052 70554 944 30211 80309 53034 74086 28528 78698 36989 10364 62136 17827 7746 59610 78914 9042 1076 51089 71981 61567 88656 93591 32305 87372 1364 18471 14755 95509 71097 61624 3300 58378 82859 21312 75311 47081 64274 86029 59625 74445 25811 35640 42100 55400 42545 81393 23685 95838 78059 58170 17438 11688 53420 70824 40821 20287 95498 81684 64613 55084 69680 89168 15151 18935 77886 63606 5818 67297 54079 30442 5124 38948 64560 21585 8765 46973 89209 36534 4214 39231 47581 3512 63268 1902 28514 80251 51029 97891 27886 55832 1336 93570 73658 69265 98874 71398 15309 84952 78367 60614 5055 7921 97023 25530 96948 24280 25376 25707 50960 30889 90852 24458 62201 52799 58362 87685 18317 6193 70556 49318 96168 65652 35324 25865 25399 24510 26295 45504 86507 12086 28716 37903 36234 79451 58538 3983 64975 16497 99134 59332 54787 91070 94318 37455 42750 24802 88124 97694 69836 57887 17691 32721 54969 30035 39456 88185 83643 76553 28114 41622 74489 79192 17877 7914 30900 50191 29239 76075 65967 19743 50298 516 55597 98571 58871 19957 71024 29601 622 27074 47693 36887 54537 95266 10914 90132 56829 41966 18636 42397 28670 97361 5651 23016 82194 90706 10029 43182 22576 57899 89187 20377 84986 5865 39454 13141 36592 40614 1010 63903 13972 96623 61047 8369 2857 9041 92962 25440 78534 7108 24767 93326 30593 91631 33925 14331 23520 84577 49110 29929 2421 7503 29174 96218 24838 53511 94142 38761 84198 10410 27215 3558 35649 32204 7652 82807 47256 14467 98416 412 82304 58274 7645 56940 50761 98326 29657 89901 72226 88655 46109 64921 48263 39881 64116 87214 36347 68690 78406 452 31014 81162 15550 62619 28199 33084 39244 73717 46096 66862 71367 52456 27344 56004 49780 55310 55180 68876 59483 50173 7737 47584 46704 80548 73475 71227 57632 48775 83861 15658 3721 8415 68361 67516 15229 30334 86528 17739 87872 3799 12515 20139 71955 44411 49550 86851 65756 19487 336 87843 69007 80364 81317 310 25336 23498 71524 18866 66072 62912 12644 72876 64591 18990 31468 32355 39602 36405 21403 40745 34325 42041 57401 5259 22221 11125 62510 26019 53666 31261 38538 42079 98456 61707 34250 26684 76264 84284 1550 61520 46875 19627 33878 25127 7178 60336 87271 8256 19151 15682 2216 90360 86064 88055 34996 41297 79359 55426 56593 91169 40494 56414 31104 2074 8252 23117 31527 89061 70011 18293 63868 75517 93070 90149 52015 40098 81986 75998 44081 2032 94435 42179 56162 17889 93439 82376 88185 66345 74331 1141 49657 30136 72593 92148 85867 12353 81796 80346 9238 8983 79857 5186 48320 86823 81076 14500 648 87378 14995 28550 10344 31629 64199 3175 85802 24698 17499 86941 69391 21872 37530 85168 21197 48869 94071 36339 71267 33318 40872 59024 58657 66663 94477 69098 64516 7217 19078 21344 52526 83483 36553 7174 7361 63695 67710 46187 25035 89529 62412 76370 31021 19018 79457 36276 36107 77201 67706 6 4976 61153 38925 57167 27214 98781 5360 38965 58364 60440 90422 63443 63742 28108 78423 87402 31981 87268 24164 909 48776 23596 94158 58606 10139 63388 71562 36617 30540 58556 3108 71528 96652 81439 4072 69124 42736 9100 88883 54110 38353 57916 66809 46266 94671 90690 14054 75522 99976 28381 25200 15821 36529 47917 1074 346 1273 13747 70246 99806 73913 83945 18299 86068 39271 15569 3594 85463 84806 78381 34250 41481 65562 45686 12328 95022 20547 16425 66956 85892 21434 24409 51792 78720 37903 26957 10403 40438 4860 51958 90090 60431 5134 20541 77554 15470 63809 43882 27705 19474 12613 23590 85267 10513 43516 86951 38063 96904 31742 12137 63250 43425 15546 83600 636 46377 93220 40520 95781 91099 35874 46127 69623 38464 25772 99932 46981 78270 35042 18374 33299 69813 16715 90789 31183 79206 37091 94391 15623 710 45304 86608 85629 48932 40304 39615 93169 70438 39627 39258 36628 8570 71656 2915 96344 61630 31488 21328 86751 10478 35066 86482 29568 94462 62764 66490 76390 62042 62500 77159 93289 21460 10826 15490 72562 80531 52252 9164 76597 42043 51158 1299 59885 10832 38580 59367 73554 74036 61585 21696 45618 13490 94934 38319 65639 254 25829 10241 64683 14916 37677 52745 4282 99448 64467 55219 83022 93159 47732 33448 15865 61416 98601 54105 5587 30887 3077 76207 42656 98765 24422 90659 56643 13870 99949 25653 16581 42567 80141 7209 32620 14523 28824 82691 62504 79259 10876 88509 96110 84923 77150 49820 86117 15587 63062 76975 72856 65034 34768 60684 72870 54402 63562 94278 7994 62849 69030 93869 92133 12778 442 77622 85333 20951 96706 12348 51867 94943 95609 90895 84623 78377 37462 64078 2307 27049 54861 13402 56668 39404 88039 29686 90475 46231 67980 63738 21014 6538 45377 6470 82812 42041 8823 96029 90139 39750 51034 76106 99484 38212 53486 2489 73480 78331 91485 67677 27392 21047 12551 6782 45987 59772 49376 65619 48048 23639 4584 13307 33087 42725 89236 5627 67749 23747 6527 46558 7452 23650 74598 90460 50477 55232 5672 13368 97087 54891 26682 56633 79472 17566 82086 45853 3103 37107 50522 58524 87478 90862 2130 45314 9751 13901 77524 55360 55092 91665 79649 3365 73239 12824 64293 34035 64528 14928 30294 15629 59303 61752 42233 7376 63793 79707 51678 20265 22749 32523 24981 41933 87363 36720 71531 39387 69078 14011 41751 61556 73502 72862 35033 39330 5441 44045 30980 99991 33341 5971 23507 20530 16736 94347 75784 66945 80993 77851 2897 55960 80859 93632 62420 24810 63199 42968 56714 95085 58271 57162 17808 99788 6140 907 34375 86331 36158 10104 96149 45640 68042 96558 93640 35210 29744 71465 94347 50296 16817 16097 12201 50636 86642 88905 82182 50808 21570 59192 8311 76583 51330 72899 84726 37612 5881 74266 42354 76495 24798 47762 46051 21005 38632 2550 46912 26177 57039 77086 45845 34536 74725 87042 57351 76506 68634 22085 20175 76250 80222 66792 25468 18133 86481 21561 35239 46188 11507 66468 4453 74866 38389 7137 8759 49952 12186 76535 23892 31600 67347 60267 38478 35452 57688 35150 32928 66206 11617 83968 72172 8183 38117 19098 70428 67723 61275 27215 3350 85402 89290 33997 4648 33074 11620 36725 56904 27838 67185 41238 10853 45548 70276 68427 64882 63465 36049 48295 30668 93882 76351 73518 3648 87389 29842 1525 41431 84554 83043 70490 90731 35611 44425 11546 36032 34719 18588 4988 26227 85467 60538 7476 49961 82976 92288 31358 38167 9577 25997 84967 66740 42675 82971 75882 61622 88564 61275 20681 87081 47557 55181 61099 11649 48710 43053 86050 87710 7028 92232 26855 66990 68454 3316 16926 23475 57490 36833 56801 22772 56946 68166 9137 29628 68939 39281 54826 59021 70407 26737 86585 32434 73379 29726 18415 16044 65607 13800 23038 32699 87447 84819 77819 13900 57296 21967 51221 35323 80554 49069 58084 1169 18041 72621 3869 31613 90159 61972 83378 76213 35856 87005 95068 89836 49754 87157 72307 59543 46306 76501 6529 88807 56914 90728 77454 6791 8143 4551 98386 27410 1938 31262 69054 58745 83304 77377 10031 37132 36107 56028 6715 57792 17579 99798 7254 89917 41806 6599 72360 26268 95730 52409 65799 72584 53499 97441 46421 39319 74748 19833 83746 75745 40022 80978 79706 97923 85953 94860 60395 44216 44779 26770 41019 78414 86239 6072 89529 17099 71195 67033 93553 15348 38404 86122 45782 56039 13821 61150 46510 92208 36597 47713 53313 71330 71562 36360 66876 72851 85363 83990 15453 14787 38135 83946 60672 93889 15675 59469 80757 2482 10246 20799 8433 11812 95497 20701 7793 94184 88535 55712 22078 32829 35799 93312 25527 69752 27450 67066 43138 77528 28408 32979 51135 5727 41082 99788 26397 26405 5104 27934 43675 97863 10644 42079 2414 63102 68046 46267 20453 1640 25670 44600 74118 61127 81814 45996 65295 34588 42991 64606 29880 52731 79948 43797 40448 35297 80886 60259 66553 12445 46810 1875 45123 79286 56471 59039 81563 6747 46873 63720 78230 74889 95351 25150 14028 42616 7930 86838 94309 73096 24454 94066 47128 20291 22949 97027 59379 82004 41619 81958 38017 97547 68884 84232 84960 14487 6605 13198 8590 73364 71101 52144 6139 8529 15712 73550 11795 58573 24127 67824 88504 9190 99544 48833 32331 10920 14119 64297 6116 79378 76919 88415 27001 87540 56212 39209 66878 11205 47821 20340 5863 50671 14906 69724 68609 86760 14962 6243 35988 67175 10888 65351 38698 77480 17057 51756 67189 82494 34704 9009 85014 52793 89367 43203 89210 7505 4376 19880 89948 31311 72689 3444 94446 80907 334 60083 97797 16250 21753 29910 13006 75837 75697 90421 40491 13910 63316 28086 94995 39984 86936 83705 89052 29663 51147 16686 56457 43074 54839 48135 25487 11694 76850 61015 20498 91034 26495 23403 83904 72942 60936 76110 46979 13527 8270 72811 5470 36974 3356 96697 49633 45345 5741 29648 34495 11652 75688 6959 64315 4945 26797 81963 21963 45413 1849 53924 58502 51418 53015 77618 90862 63761 17456 57603 91064 75055 42943 17519 52719 5158 76402 49300 9266 17282 95493 34463 54153 82400 78789 46344 2484 4795 95923 73318 35596 9053 56760 1926 78112 28432 62705 74577 35375 71136 20229 98532 31278 77016 43582 21647 99328 17640 35571 20945 12935 75379 92806 73348 39950 63753 75043 82168 18133 46889 96028 95120 66379 20612 28024 964 1227 59279 83669 45668 83706 37467 41885 67538 95937 72038 10800 18014 21575 95237 43960 98461 52779 69063 96271 495 58469 87442 4634 14575 43134 41278 82697 57359 70660 41879 2271 32242 49333 67492 74964 70566 86151 33296 76447 45000 6310 87559 67430 49360 65139 87652 52677 49910 21237 32451 97825 50268 37423 54737 43523 33789 61678 6631 22839 91075 28658 28795 75929 62368 29305 93317 48151 89417 1481 34245 20394 65109 90718 4321 26086 61771 24477 16560 81835 83224 51676 44043 31053 6251 1425 88698 20147 99680 62075 19979 13043 59577 54605 72579 86818 26760 52183 34086 16562 48117 85298 39190 7351 29997 45319 71191 71078 97451 50062 28779 97834 16249 1575 85681 3072 63202 16744 78843 63111 88481 26243 39729 97017 12414 6783 39605 76876 3900 58840 19152 43272 46251 61393 36086 71381 10708 69431 74677 84395 28205 69842 89277 23600 4047 57964 20375 6905 1654 43175 40472 99222 97635 7885 17923 53465 12730 93854 4400 85444 40768 56088 43706 99390 89339 71416 12433 21896 94355 29 41263 62742 88118 71611 82977 64879 33651 83787 93796 75552 35632 53032 69196 28575 15762 35131 17386 56617 11207 21598 95202 66701 21772 29094 31593 75498 28780 62347 38295 12824 69859 20860 21612 43265 94370 33638 18870 94482 59933 71446 92895 24040 73236 9232 81022 42086 17186 50169 43272 78412 54363 23708 18825 8668 65062 8844 71925 93783 2023 39623 11067 89480 56243 69487 10628 9346 94310 17081 37534 56667 25815 60671 77768 22162 72736 28062 87835 43516 43296 72677 48857 93166 67189 27916 41135 24239 7931 47214 25017 44140 74957 45933 9802 77546 37816 37839 9233 66722 79775 70865 6402 17942 7730 98887 87180 54140 44065 89885 28485 80589 82905 45953 98528 94463 16681 35114 9122 62270 37513 30161 91184 55186 42054 56535 72290 64944 51145 23135 79732 88991 52363 28689 42222 78240 69148 5093 51048 67834 6770 85353 40639 33594 53472 55241 38337 63380 48187 68195 75386 8452 61111 61723 16734 53153 5618 44453 25958 10449 81954 66618 13829 99944 64718 60828 72460 12214 19068 56248 57714 42648 99286 95144 42563 50385 63143 7867 12174 32256 69761 75987 51473 74992 601 46326 19723 90310 67859 24244 27095 33298 10617 77160 60242 65975 78339 10518 97596 37186 16698 95843 48483 77883 57011 99766 66185 15801 73905 42442 8482 89419 16202 93089 22886 4170 79397 39348 48449 65753 81303 54443 28448 3994 93492 31097 34174 26568 7679 1862 47589 33671 24004 24192 81789 16180 2651 28255 3013 18044 25751 24608 54667 75262 69356 59045 92799 2475 48542 93764 29066 88872 13045 10184 75240 30374 11017 72561 48364 2100 58321 38273 93399 53234 34127 51688 70208 24355 5507 85069 64687 848 48897 98247 56241 96825 45973 15675 19433 3318 87095 675 98694 79873 44162 22393 67994 75439 80916 99289 46020 99089 11667 88687 55008 63311 50242 20618 63798 40602 60512 81573 51715 81498 2034 93717 41588 41492 49566 25681 6327 38666 5280 40841 71780 69330 21837 89487 47337 10932 71445 93024 42218 33693 18903 68274 3377 63264 3664 5047 12133 55630 99187 48039 90771 44106 45938 15670 40679 75115 34834 22356 10283 98550 77166 33201 87167 17524 6066 62031 88998 63946 90495 56824 97299 38021 14989 4163 31272 54185 73873 53277 52956 54666 78635 2962 37973 16762 41861 42762 80988 7548 5038 25188 72714 60326 76444 75501 68255 81151 45002 24178 7341 2441 88005 74312 88896 16048 63534 21840 27868 58423 87889 71147 25655 25648 9339 40806 35324 34986 83781 55940 19059 69403 40940 61416 85449 60501 72704 13933 67799 41978 3210 77906 21935 55524 64873 30488 98824 76336 62709 65827 3766 96332 37739 52842 70881 6435 9455 69371 35849 24479 44934 28843 78999 8949 66324 1682 5204 73425 13441 78347 39918 19734 26355 53105 53382 26454 46322 35182 35019 30189 70183 64998 19947 49934 27771 69664 42598 36571 65131 71653 28879 58022 76433 51896 59858 17601 36635 36570 12789 29838 2872 543 87160 19050 80387 90307 76213 11185 38464 77816 26224 74194 54558 3854 45767 72797 25408 11848 37173 98717 3177 52392 97967 62237 82024 123 70288 41601 71578 52827 77792 9957 79328 33673 88695 71206 17661 36896 65001 84475 25115 30073 50694 32790 99739 85917 57232 76024 54152 8938 24818 35318 90297 1743 30331 91273 92450 47348 1956 7941 56705 30901 25091 36511 5418 68213 68251 34677 26006 61050 92421 94684 62520 36441 69326 70045 63240 91104 13509 35682 21245 72777 3747 78248 12970 39358 1856 72827 1682 20646 12659 74786 5873 51272 81915 58908 22151 69395 63891 26401 1996 96598 69307 78776 67281 93064 78113 86145 27687 38843 65634 26727 96631 25656 47746 73435 12506 78662 5061 61324 18817 89369 62841 54949 43900 39656 70070 44145 19352 63722 4438 17824 43399 27973 69935 1478 44135 87202 78413 28797 46726 17812 3588 43935 98690 87314 8754 39151 70556 75262 90607 21112 15429 24234 13441 8507 75972 10191 70865 53044 99631 21819 45127 98088 17014 33283 32139 24185 37622 79825 2380 91711 41187 40260 36023 32648 31773 69340 15893 41492 6736 26024 66265 24528 52610 31278 52724 43260 56109 82359 46861 92931 83619 76166 81283 44030 65271 36759 30287 91568 20825 10761 81639 29722 86484 78475 18083 27616 37524 66825 52447 56888 70040 7398 21423 65231 28201 30083 40006 82365 4252 20197 6418 28050 59898 62756 96982 45838 68721 19258 63269 73665 78670 54168 11917 58774 96167 72726 19625 29286 72313 622 48839 335 9620 54841 50047 36288 72407 44851 45997 51700 81655 95658 43621 73812 80096 35308 41202 1004 93106 53773 36598 96376 81157 56166 57239 12221 62642 95210 31162 98289 51284 70273 68378 93931 4404 37378 40254 46038 66427 76801 57549 90764 42389 46297 28658 6879 34520 58495 81353 42792 59523 45488 62841 79921 45208 95854 16420 51359 18781 44039 65041 13212 66206 53395 76321 87982 82703 76577 87282 44497 92342 53642 78681 36807 8778 51212 60909 57833 38702 73875 99249 92677 75915 17392 35042 40949 68433 69044 29676 80295 34811 70653 84439 58142 48707 53902 62449 41614 34443 8543 93203 66326 53479 71002 25860 85528 79486 86127 95274 92907 75031 86046 54883 1402 32691 41418 59704 95349 20276 52208 47214 36690 93001 69814 1295 22271 99453 3940 85575 66542 71009 21362 30995 95058 3384 21949 68326 47469 45190 20486 41319 30528 20491 25697 14529 60791 83619 4098 25389 16079 33886 67182 7821 21163 93222 37300 92890 4197 95833 5912 4429 26297 2894 24438 28822 35185 99726 16729 379 40738 44472 66160 37482 90683 60962 13736 67188 69268 77896 54902 67053 71523 20402 77801 23324 25109 13546 30980 73502 21107 90015 63430 78773 5639 96014 68729 31133 50525 17902 79211 37670 51946 33745 63866 78927 57656 73489 19906 55022 54082 47331 54073 53309 37710 33904 94829 81324 87980 27886 59486 45623 66877 14330 12104 31796 80471 92069 72394 80865 76225 7239 61504 85877 32826 68431 96768 93377 11601 77169 41044 84349 58513 35271 59039 87696 28271 22056 25501 84457 54995 86528 51877 74382 96644 97679 12855 17423 91970 65153 6694 52373 5131 32780 59658 78069 33307 315 68882 49405 12422 4788 40568 81773 10193 40865 16961 73634 38052 26952 77656 77252 9531 89893 25876 54081 47862 44960 5751 77308 66894 58163 44782 5112 36731 68669 16569 41921 98180 89989 64443 72063 20219 28574 35436 12251 87940 71837 26339 92903 5127 92878 81603 69490 36611 56591 62508 42629 66398 76073 21753 64930 90809 28515 11724 69967 24128 25116 24207 4804 82744 53042 23985 17465 78482 7911 22477 73130 2676 57440 36086 3031 49957 79680 43714 183 48865 1806 45077 1147 5033 27498 97652 26798 80732 80682 4794 75415 11475 9197 44643 86645 82664 33218 63953 492 87312 58685 35868 72952 59189 48274 27009 19728 31668 60495 79704 61549 96771 89383 25254 58924 37035 21108 28557 1116 58060 53794 47010 20008 41729 11825 6917 10161 18793 89517 70793 96124 96976 55945 88149 91499 19589 19791 52362 5838 72462 41484 70430 54740 84418 49738 2423 96510 95472 39467 45310 77368 21178 78887 30303 11723 17839 79780 35842 91475 64526 68416 18313 90163 98308 88763 80436 40589 46787 3699 95315 19123 91342 71627 69052 3126 63620 48194 74334 18194 75079 79812 64710 91984 80158 8626 79753 44082 85088 92169 33061 68940 45549 92733 95462 6241 12006 90503 53906 6184 80770 18590 65321 63858 98415 43590 12368 65357 65357 90790 15731 90594 75558 83312 37588 93341 67528 49096 52393 15398 88479 43431 96746 66130 25224 43988 14308 3631 81328 46951 35120 29928 91547 24796 62005 73873 41352 79779 2004 90302 32604 66990 72019 57056 92067 98916 2275 80195 71257 50128 25878 80152 3125 52505 51850 50360 60429 65187 73746 4922 74013 18463 42806 27295 34437 25253 97292 47232 89995 1308 35160 51370 35608 73567 52690 17883 97530 98114 37033 98413 88509 52570 7131 57841 42597 85682 96029 37268 33361 8243 36754 18807 54052 24028 26243 49782 42580 3653 88451 36351 25938 47493 50309 62643 28406 93007 24424 92060 9588 87044 81022 8676 82292 95339 9613 7923 21105 51562 32885 80321 90095 56808 79362 90558 2528 45507 73056 83944 33652 26420 91785 85390 64989 94649 37034 99333 17324 71822 89019 53272 6130 95456 56750 39164 82750 73870 23839 88726 91791 11160 42029 95414 81839 44523 61779 35241 97595 40341 68701 15009 19905 50858 17280 17523 72925 83993 66437 73216 49288 94227 80732 62242 68129 57585 43415 29799 93709 96568 88528 73047 2508 24821 18044 70468 3939 43068 34393 48485 9796 11882 2461 71349 60639 19901 32988 43283 65216 37584 28245 58277 45294 79185 54559 84006 62331 50371 48618 92483 90555 96166 23425 96860 14533 26104 20166 7238 79870 35631 26999 60256 51565 85700 38716 54339 53283 38571 13929 87604 96509 86884 25808 32490 42639 51532 52856 76455 38871 68562 67565 22051 91385 55331 96903 77994 88954 78697 13840 54581 82612 49747 59821 67241 9578 61126 34696 82465 41028 37928 84683 75103 64358 53649 55735 69752 61941 31716 10380 34085 22046 15947 92430 36801 93545 18485 49585 76732 67242 20365 28269 76189 26900 85843 32109 46260 27438 17608 14194 98380 93313 87979 74486 84824 65636 72013 67605 34261 59353 78877 36879 18506 15817 87240 51381 77770 49175 15986 53618 97191 73600 74433 96511 59095 51127 15116 20825 79055 18019 5803 82456 71544 58092 42511 17181 13753 16906 97279 92182 76959 63907 7761 55042 11205 94937 53796 13589 48445 23310 19519 56387 47818 81487 85559 38978 87107 85115 99475 68846 10347 7725 17436 53136 91116 62096 58935 5879 61193 8097 53778 67006 362 81506 92847 70219 3631 49490 82509 55825 23066 58203 31327 870 81028 59942 3795 33370 99488 31074 41526 86384 36026 85582 66984 87745 55887 43947 57684 83035 37884 29979 62959 36019 87881 66850 41972 31191 92359 88237 31735 79282 65026 49098 56699 78793 13268 34522 24648 72718 86694 99649 86206 37580 1441 74412 46699 93582 83296 80029 16063 47504 61891 32031 69447 88413 67913 85307 7715 14957 9641 80201 3347 17649 35595 73172 64688 55970 54012 86193 5846 42267 80969 63206 13732 66290 952 7169 3565 69836 31356 1637 20048 89471 10007 65926 464 94312 71008 43412 66448 2049 17144 24559 31550 76813 9623 16217 72363 6220 73469 68400 5775 58740 27342 11319 69280 96650 49393 8269 1160 39853 51295 66276 42994 15550 89734 92892 58247 26288 85383 25397 24947 53130 32033 87755 49218 12499 35800 35021 74429 30338 41756 96801 41153 89881 8473 65708 6592 52025 40168 51493 74739 41515 26754 70325 34747 91411 28571 38489 68658 40247 10611 60316 24219 25023 13208 4701 38136 62242 9108 52612 4390 57937 26412 86928 71276 63811 90880 75622 49086 27841 7357 90030 26547 23798 12019 72944 45908 40676 38400 18217 37673 91457 12687 25927 29323 98706 43933 85727 89100 94976 35372 77267 39968 99702 8677 80956 1479 63489 32132 2678 94018 69040 35188 23621 96254 44181 14521 81469 44491 35109 61313 85170 42652 77559 60131 23641 25461 33310 95905 58868 99081 14140 78967 96522 37846 86593 18622 22274 49968 66219 54083 86044 75721 30354 67048 18061 65196 59980 94360 83387 4984 83377 71155 7761 93855 67268 20687 94249 847 6421 44405 10012 45313 27130 37017 40213 30842 74333 94359 20977 21196 19909 8583 44351 95815 12831 51547 66934 84611 12437 47372 47862 20209 21552 42223 77160 78298 10449 82326 66478 3093 14935 94803 51295 87858 47180 59002 6015 86177 37836 2512 6742 81829 13319 12800 53068 56948 63247 21556 10822 31043 59031 13719 6632 62742 3931 85141 30928 69683 92371 8327 76613 68692 20441 35110 99556 11445 61464 82092 35866 3513 90256 90102 47574 28061 6716 38126 76156 22062 42875 85362 33670 22797 69481 76582 55601 77975 17391 98260 17857 52855 68490 87025 73976 3983 22144 92231 4348 88192 28594 68481 43047 27602 44324 26928 61570 98811 80785 759 27816 39127 73358 20929 62651 13967 15495 26817 52396 8002 74105 71210 68118 21786 5109 76064 15517 16691 38904 2781 61202 81372 40324 38955 25165 90115 31720 7341 895 34921 53662 24480 29907 87177 60189 50726 72375 72808 64227 48756 86659 68654 8305 19616 42102 79590 30722 39170 2700 46441 85187 36507 14105 94908 4538 51576 39674 75206 7794 93920 69745 72886 55478 60538 70032 11062 72704 34820 25090 23515 9485 39233 29010 85600 47674 39505 80348 31526 45905 9106 70066 46533 49445 90172 57728 38649 35383 30706 24538 96370 42768 71561 64505 38383 61264 71328 48319 52927 89580 66623 45986 40795 43439 64611 90296 7548 43924 96212 95561 6764 50321 6129 16 71887 86826 7912 20102 91187 34657 35567 69852 78372 22485 40416 56363 99010 13585 76524 9879 83463 9055 59241 73383 9916 81547 55168 16895 97557 29301 67304 72271 78010 36072 44513 85800 96425 9807 52400 99347 10491 1997 69877 29197 49325 81717 66971 91520 36030 86396 94168 22883 26359 63459 50212 78365 24206 60046 36795 41348 14699 15666 56322 66372 88582 55457 79262 87460 81230 30673 28648 38787 34668 62381 61613 79711 17833 33752 95152 33008 73672 37393 61576 97442 87304 82149 32954 607 33751 75754 21625 17866 39549 99931 54743 99792 15276 75140 93579 20561 8111 84506 74537 75352 24411 95841 98693 49591 79372 26913 1770 70700 69907 73142 93839 24155 45460 79039 44518 68899 69044 2562 23561 57379 58098 81058 14706 43880 30933 28423 4575 47325 46375 42045 8718 67871 5847 62215 27685 7909 56471 59004 93685 2056 51227 68695 99564 28552 76713 40009 7726 59216 92093 44079 72541 26166 26082 81838 74303 6239 97589 86693 63987 42732 75360 31486 61596 52665 41933 78994 80945 83516 31189 37623 8628 95859 57189 8480 21111 78623 53097 18132 29669 65832 54785 64668 36706 55019 89022 68359 80104 93141 89308 2835 55322 32195 61217 13171 56743 70425 6377 24756 53752 29796 58982 39018 94498 25462 84369 60559 93928 69254 85343 63746 92357 56424 294 42617 22528 87977 45497 85214 90073 10736 64057 43894 15243 43338 72082 28206 13615 82722 1903 33139 42055 27949 35230 75421 32426 61633 39088 58868 73577 94738 83068 95927 57714 7685 97582 6297 1659 73700 40088 86105 35660 29809 7145 23767 86662 96275 17801 84960 93687 15841 58485 76596 94813 55392 36987 65657 40725 53717 1795 15238 32382 29074 52042 12055 38319 82651 77970 17941 21335 70314 61264 23296 77326 9074 12962 25974 90254 743 51254 96671 30109 97377 51977 19024 50511 75911 74833 23970 32582 54933 52435 85243 28046 93599 13641 3782 16190 95300 23146 38863 91294 15786 69874 87725 16286 20997 82977 48718 12323 23492 37941 86645 49187 39312 89693 75931 53666 76763 41051 23376 12974 57801 46213 37066 74488 89633 72853 32770 54295 41254 82969 49189 60202 22456 14821 95498 18923 54157 10568 33803 61035 3390 47105 99561 57369 48388 31099 12719 88629 75824 20812 58172 38557 21063 82364 69360 77537 85652 50916 75159 2567 39733 51685 35124 79229 3677 49320 50527 34674 95626 37969 6946 67392 11038 46929 6218 89051 89832 96030 6062 59127 70755 31713 68809 2035 35902 80142 39517 89126 390 60157 97922 8980 93329 96661 69572 26429 64246 29461 15046 52517 71984 12505 49906 35174 19010 59839 71516 7656 11936 22749 67448 66447 58719 80354 44947 88581 72485 35911 10390 56113 37287 5979 96916 58660 8991 28048 72005 85626 43687 3799 32683 68552 94663 4582 65516 85094 6155 99064 4560 34041 41115 43649 53229 535 56571 61628 95476 15934 8166 99858 43787 95343 77384 91423 62931 63193 95892 56259 82959 96709 31728 63540 57243 56876 63705 93952 87747 50888 14263 91399 93926 68094 96901 85018 85128 85664 50130 32301 72425 46784 5237 91521 64816 88063 54370 16697 9364 90857 27583 14569 45072 1887 50058 2054 6441 43540 70565 83094 15153 74385 90268 49995 2201 73481 39087 64362 36538 47815 8127 21773 63782 3353 70582 99823 31625 70027 90191 72817 84879 73688 82848 96421 23593 79608 79819 63697 72955 24111 36668 84439 57317 34290 95176 14261 4901 42513 33228 3542 29546 73967 96542 94461 71536 93041 66565 80905 73501 12358 1316 89800 86075 21762 53624 23215 91211 64387 36112 39937 30690 4318 64108 839 77893 71661 74580 43044 20165 77863 71191 30684 53852 63401 87665 58554 41436 68582 71021 82261 70657 13389 81603 29424 4564 32631 89983 99104 54304 82579 61214 20687 53616 35290 19739 24475 68695 37083 46375 76460 80766 52873 61834 92264 34888 19041 54927 61293 71589 51862 34285 84095 87092 60034 60850 28797 6346 89297 67285 46287 2620 45967 86187 5125 88117 30444 16568 73488 26877 71512 73962 95460 19668 74506 29410 58082 21562 80673 44906 85394 32644 42047 87057 82022 32137 29829 10104 74498 97141 84583 29609 66230 32825 47411 73826 86422 88036 51533 90618 7712 59404 43330 70357 47416 5157 53062 43648 72063 74626 49283 60615 97999 40966 48055 26189 25730 74082 47348 38830 17511 9146 88476 57760 4043 44374 1667 5997 75002 51449 44740 20210 63978 45842 47304 80576 21793 80516 88621 99424 21185 43094 37948 64999 86953 5570 3383 54811 84158 98422 6023 46907 85194 43583 73460 3085 60029 83563 10609 58633 37630 76334 55709 46868 52942 4433 91112 41719 35781 142 47154 85392 58107 296 7220 69533 7085 57226 59299 24456 48753 9869 32652 27601 70625 15478 69495 76540 40981 73912 11294 28099 31969 6604 95017 60726 91930 71860 23901 63423 85856 94645 78381 49142 22929 95515 44941 49429 4033 10682 18653 729 95398 20558 10128 27482 8401 79664 68034 28266 3756 17421 7909 36837 76486 1878 56924 92662 89770 55145 63821 47596 3362 29560 71657 80065 7604 50173 53390 38590 35190 74548 14770 8481 13580 94720 26572 35985 58922 6619 61325 59159 92197 26070 17523 19464 98108 84534 82272 73451 26811 96148 33191 793 29522 19768 85150 40018 3116 65270 19160 15969 27885 72364 92323 59759 22976 74416 88983 54729 86859 759 27851 91798 77489 48696 43904 19841 90122 94215 87597 75728 94789 85276 66933 47543 82916 39463 20406 7468 82932 10269 26049 35919 43224 77765 23685 37086 51329 56858 97769 5477 74782 14779 79770 30843 96127 13599 5203 80306 94576 38620 97015 64824 76612 90701 4188 11289 57522 58906 3723 41274 2662 96799 78531 37152 10157 54060 59403 99231 18163 1534 20477 1506 94097 28002 14969 14005 68273 49377 37348 66900 32631 18033 69993 41377 35775 10698 71981 67771 96749 23685 36454 74277 21892 19896 25941 4815 13555 15748 62190 97747 27685 82201 65390 16184 19480 1711 46412 52383 92837 48617 97082 80456 56114 33274 11475 74975 76344 3118 93824 10805 54744 91233 57535 19575 86831 92672 20198 22480 64169 13559 88865 27104 5922 41515 31495 98220 55736 40712 20250 40307 50900 16766 46771 87268 31878 21627 17548 57704 61758 51550 25812 52952 53125 74246 49767 69490 25500 19156 54319 58675 27442 56018 28589 35851 2737 42069 21175 51269 55994 61113 9823 43815 54964 889 64543 36681 932 59602 45562 71805 90714 83927 45231 92491 15470 78281 43766 92244 60340 23781 32631 8001 73046 36902 95442 30928 87468 64396 9147 29880 60340 44516 3637 22782 21147 24908 55555 48741 82112 72585 21698 39588 82597 96845 26713 9193 32043 58794 37132 28035 81496 20644 73993 85889 48666 8773 26396 73134 52801 85113 48881 71597 26879 82908 25452 27917 79376 20665 81902 33282 95834 8419 38254 8722 96229 64468 8983 34323 46264 43369 19873 62696 44288 77862 92553 49825 8242 44892 89772 90145 54620 14875 50518 49889 10157 36731 63703 99720 85090 5004 31681 37567 38763 71426 59282 91770 91265 97362 64685 38979 56931 60340 56199 8565 28336 41656 56823 65678 83623 93476 81150 3773 7354 81168 75467 39750 32422 56604 22311 16827 31062 62540 90089 89747 25227 67336 30785 24477 10756 1972 68387 12031 10378 33648 64360 99664 60390 70095 26494 99664 51842 87618 21537 77920 54677 88970 8369 93989 13716 95636 12531 43291 48837 83401 49263 18469 67842 30827 18168 60242 77124 89824 76115 79874 21671 71960 99194 45653 84209 48027 53394 12255 8336 1214 89055 52255 12035 65517 92301 17758 72336 45733 30021 51910 44933 4472 97463 62981 74783 65198 35488 3017 82794 81120 44928 41850 96950 88706 79898 38676 87272 28231 3545 58376 52045 7976 83911 95570 78420 36379 80321 22621 17152 44041 74870 3823 22195 32798 78859 41676 26949 10965 46480 62353 70333 77435 31830 26956 34241 46595 70123 94685 78221 31574 61673 48518 36265 73925 8684 15923 6194 91587 78007 310 50094 54061 20208 15998 18667 89263 9334 57093 46711 78976 46636 36302 11608 46068 28778 68592 56427 25408 21114 98238 18442 20600 61504 78894 93999 48987 85791 87645 62239 54415 39110 62743 1638 3946 52394 47810 52970 62008 29779 23985 59450 17787 57112 75772 76771 34772 44973 30940 10397 65314 43941 41174 65876 59096 6259 42508 70663 85496 43748 5856 54362 88621 43752 11908 69129 5503 78232 97161 11598 92105 79277 27975 78576 4375 71993 79338 93919 43914 43185 1015 71739 74034 68301 99912 28213 86596 90198 88987 6860 71485 32876 87007 34943 28881 8422 79608 49801 15759 36261 60855 36608 55582 5961 24522 72782 3259 21970 98054 62005 78521 78299 36374 60886 88538 85873 2985 11587 30032 54433 35714 64602 73769 88507 54681 12328 1393 41197 89438 34996 41317 88607 59956 25986 52598 78494 49662 67556 56899 2490 87272 58918 87528 45116 78830 85663 4080 53021 46268 83353 804 17483 25280 64296 39997 92170 44365 19198 37328 54309 27309 35002 88056 92808 56558 42087 94266 89560 24602 551 50466 77421 48349 74069 87869 89764 37891 30110 98866 52006 31999 8561 84350 99371 77149 90834 76518 6939 41145 51011 66108 44554 79326 80634 65875 63758 76683 10701 28114 67868 59868 22823 12285 93294 83844 53883 95321 13060 91570 57222 9094 53202 29305 62180 70983 56743 40688 40746 14821 18636 3169 59876 80563 86598 36063 72982 93072 19550 76026 68628 8827 24889 98122 13348 49061 89152 60524 86272 98960 64021 54195 81528 37228 1667 77533 16974 727 69950 5368 95736 86353 10196 80995 80240 7163 78941 25927 71310 49210 76952 54749 44062 31922 98474 94305 66552 6258 31655 60812 26622 78176 75711 74757 40348 2900 44690 42100 35895 44609 10430 39242 72841 25253 93076 90674 34855 76999 62252 48259 86656 19191 77792 30303 46856 31876 1056 33970 28520 5475 40314 78488 7115 56063 65221 93854 53976 75525 23522 39226 40482 89521 840 10300 53897 48316 61243 63035 33640 24618 49345 17061 88005 46999 5621 94842 57941 12648 62716 38137 80487 88212 59553 5506 73759 80174 41574 13230 2506 18896 91345 3732 32670 65917 50781 23072 83993 7166 40227 74731 37760 15095 11535 15532 62129 97915 17850 23265 801 9109 39630 91130 64941 52423 43290 39943 87287 80175 37725 23892 74242 93568 24356 12954 989 85592 79935 79008 77776 84576 50539 9439 62450 71153 8402 54991 97243 80876 11321 23373 27893 83494 95125 52898 24652 86300 13357 71105 51526 39042 10587 89592 64584 72128 69353 86455 80600 99980 54839 43513 74787 93953 30033 55291 72386 24152 75477 71996 61739 11115 48349 48749 66933 81607 34086 29669 33700 18902 48720 4751 8834 44533 71651 574 87031 54876 50753 37096 50772 34640 44091 97802 85847 6675 40612 32042 63225 40939 54110 16273 90974 93813 14938 53449 37004 40243 21917 54487 64927 66468 68288 98204 54859 32048 5087 17838 39667 36833 74441 8447 18340 83877 93792 51492 53345 20325 16058 648 81365 29351 51423 61835 48174 95886 42274 84264 91846 52269 77082 81970 8024 87884 39299 29392 5141 28263 8946 96976 69711 37342 92737 96292 62473 95122 24844 76731 2092 34886 43334 60181 65847 94641 9301 94089 49832 10411 80519 50505 77287 66237 19196 54145 52548 13905 62947 3673 70820 72707 87037 16965 14240 70504 11216 61161 2776 13808 44178 23162 11871 55607 74866 60042 42681 35276 95935 64399 20490 86722 54810 89624 62925 49710 7590 85214 4440 24682 28271 75870 88146 79783 18849 75554 53317 66554 74220 65065 58535 66754 6605 69454 58299 12432 1046 35086 31219 87436 95036 98725 54247 28847 95701 62069 88376 35694 31198 27790 37618 16729 27021 47929 56823 65470 51575 83586 63657 58128 58036 195 42768 19615 61167 15333 28415 89172 20488 65747 13446 51766 96837 8020 67291 22715 51247 10013 9348 42005 23010 13845 18400 46982 43469 51836 37151 82891 39 67963 40940 99595 96153 52644 43866 78658 4355 33629 56469 50732 56420 21881 94358 42749 59271 55715 44191 26637 57730 93679 91314 30768 9577 39386 13147 34889 60349 43663 60564 7424 18446 35151 20306 41185 22051 77197 98821 50505 26002 65789 54746 68401 8509 67719 59727 11630 50950 96068 11447 42817 85642 97303 78561 76229 44307 33416 47889 46781 89644 23953 99205 31924 41520 47217 46031 30040 27380 92915 35346 57131 86947 40946 1155 82928 22262 16 49984 33609 40494 37805 32686 39644 32083 87362 9031 56734 75514 16873 17414 24169 4729 2618 26915 57876 93666 14997 4812 76848 96799 26526 99833 7789 92309 2871 28256 53435 21149 80617 99598 12408 37838 56435 75507 28275 13141 16780 77397 13187 33539 77700 8392 72187 7598 91329 4529 42833 24533 37086 82268 17548 42154 25628 1421 25681 93902 41378 21968 16546 32250 58497 66720 59451 71925 60301 1385 62519 10253 34824 6911 52033 36077 25395 27198 79514 83236 33755 48051 55611 48884 39411 92956 18190 42411 5433 34856 93010 59738 26382 47015 59487 71402 13959 69781 31727 52034 84023 99193 94351 63540 21918 89276 70645 73677 83353 35448 5166 55804 28750 71715 72094 26846 69509 73494 90045 16027 33343 76959 36021 90399 64689 35 23076 27851 4499 72786 74745 32199 91831 68627 72867 58932 42848 73893 83424 81228 19953 34867 47451 89308 3670 34047 95240 41632 92916 35954 55915 22732 12675 62797 74679 41062 43286 14143 64167 97724 7262 55092 52117 7750 22885 49149 40609 12877 18451 48470 41243 30272 44071 21592 76423 66337 63462 43924 89157 42875 53538 27273 11981 81167 3494 70974 19779 97599 12204 55450 11476 54009 94676 45187 15756 31150 3047 3507 18733 29899 43010 873 61275 12629 32876 39105 96784 45572 79888 94297 59580 52956 29599 31241 84065 82215 98213 83200 92155 41068 79826 4055 74391 14277 10341 68282 9909 24486 6451 98503 42279 52379 79993 6197 61538 88643 22842 70354 14299 73908 82268 47894 17653 24861 69809 31317 2793 46550 18312 63221 45695 99302 28301 40583 4468 37864 27404 55401 27093 47456 20001 45132 80233 69658 44533 18376 91328 48262 51888 9721 60797 22871 25902 96543 87709 73854 14737 47725 73394 52599 33072 72954 88967 52095 53291 1450 67954 33609 83532 73772 51669 12867 90910 95873 32485 222 41045 10020 94959 60688 16961 89172 41614 24540 72676 19659 26537 18353 2423 44916 61212 14731 10094 33112 32237 73052 8327 71653 54778 27478 79984 31834 80006 14830 86272 48202 30617 66298 9087 23588 35508 55351 43954 60281 87040 21298 57436 3030 54724 76377 75308 15178 52138 15968 61026 85817 51003 66188 38276 32071 67811 27908 62813 74694 18513 71884 39859 33225 24247 71640 95767 62028 73125 47498 24666 92851 15304 57691 42614 62359 74868 49565 91701 20857 73634 19150 98233 5795 99420 38489 27022 3678 67859 22167 53521 58101 23139 33929 91373 65395 15769 90662 24226 85613 54042 17992 14775 68925 55686 68821 49345 49115 81733 88074 1910 45526 77716 85512 21406 54323 98737 76508 83321 71192 78201 7633 55155 14663 43349 89330 7816 3328 32825 6421 16371 95265 98346 28825 9963 73544 77091 67840 68545 96321 42130 69083 38342 9742 76718 18123 32005 32260 9000 80758 42144 99679 96459 36465 9019 98738 56211 53557 79718 56612 98436 17957 44093 53205 78564 4993 2040 36886 74 3699 42156 90812 64037 91311 84468 85954 77727 151 88434 26117 7478 47081 70928 61661 56503 31871 76792 67325 89333 74657 7204 74799 26942 46135 70592 33907 30750 36297 95039 96556 31076 22320 71100 75811 36209 9848 4495 80931 72892 58827 95147 15960 83717 93224 33411 28367 90842 80047 3706 50387 61084 42026 92152 8876 82565 29158 55898 76669 2584 75892 34054 75284 404 72657 75016 10295 71792 95056 88397 19596 57638 49229 29876 23192 22732 41310 4732 18973 82494 82679 17230 68651 55814 43425 18045 57726 5456 67073 43913 80193 98883 44705 65243 4533 78151 27969 10525 7309 8570 86821 49526 48447 34276 9683 99043 91550 52374 77100 15434 1715 14033 9334 619 659 86962 22605 29800 45240 30558 58516 9644 86323 77419 82006 20941 71460 77649 95083 43035 11908 73354 43576 77213 32665 91934 23533 17996 82571 10535 92806 56506 26883 98896 3408 71282 91046 59877 92529 9064 14953 63613 55852 24999 34112 61332 84229 99954 62446 53153 86781 54824 20689 84713 49863 93048 33059 46377 50773 71474 19909 54338 75997 75754 78101 30353 12274 73641 71374 198 47656 25072 36328 50350 90414 12900 27772 55687 77121 63233 15562 11277 75541 26696 96355 82634 60226 16197 25273 7549 41397 92083 49908 95867 11773 56312 31322 1176 14183 51894 25149 37392 42035 89459 64430 72131 28893 92424 89792 90400 40484 20059 79187 70781 11957 72036 27119 79900 82761 98639 26006 62091 54610 57629 9382 55004 41081 75840 69450 44946 7402 37622 272 48355 26153 68276 49046 73283 14988 23751 19003 5937 92353 32558 63663 71835 25209 24533 81113 41936 42072 43403 69764 72640 31779 94917 80114 59956 59605 21381 41468 64738 4574 76486 16901 23011 72221 76290 73688 44887 20576 90396 88211 7345 43784 81949 34732 50970 76525 82271 72033 25309 82250 10611 79471 13884 55970 8932 45271 91321 74037 50440 59206 94281 54691 77413 23218 99914 75446 58997 61456 10161 91343 7297 52743 46233 27788 14817 9834 94704 54304 40323 9216 9615 14623 69299 4134 90044 86060 132 92030 74268 14789 31343 45531 29293 11432 17845 90772 57798 74840 14916 23482 49495 17001 66454 7503 52174 71961 96767 17869 64545 62382 76976 93162 37960 1766 74667 2110 39941 3669 50274 52420 37614 78710 48760 39634 18810 88492 96251 36786 48147 15021 1676 91805 17178 30621 32232 76934 96546 75217 47454 46085 75623 12654 73581 34625 25520 73123 53923 48945 8142 65222 38090 71692 1978 5721 38941 25410 9325 21406 50485 70129 75717 93356 88709 73798 18675 36468 41910 25676 47185 78776 2528 17304 66106 24798 99952 58992 64900 90967 40255 82458 67552 13038 45158 13966 86207 14623 2031 84260 12407 53037 76905 6493 49909 91975 45737 25534 33281 87422 77146 66433 96949 87652 81521 17369 18994 19381 48285 179 89727 2622 92572 2637 67411 60444 39878 61489 824 80366 65127 18012 59392 7613 7375 6703 27537 85259 24781 63167 7923 32989 81337 43943 5259 39822 88406 59704 52620 58613 33566 6619 1527 12701 73565 57949 80398 33157 14710 59779 72332 45028 88908 4222 7095 33594 3004 6902 15355 36111 32338 22887 97647 74211 16639 47107 73232 79670 50284 7497 43494 3912 69762 28842 66189 56409 63362 58262 68197 71516 37135 23172 57492 49312 96032 418 85831 25356 99662 47673 31395 49661 7468 59172 76778 13218 17392 65311 14458 91084 28531 77067 26642 30436 70041 58219 4975 24922 34100 70359 75552 66293 74899 5485 64720 7018 14976 60151 58717 6126 7541 25620 77715 33126 1931 59789 2245 30255 51235 23630 90449 17637 47581 43883 27047 79178 51012 35895 8653 54608 85189 52067 79580 42167 23603 16297 33501 4729 43819 27628 62652 3723 69395 12887 79126 89334 87588 67735 22092 50489 55617 95048 97024 92661 68028 31417 49231 87801 2245 98235 98435 58618 60794 92643 39985 37930 43118 73932 54585 39454 33622 53270 66093 41338 29708 82662 67820 45164 76565 17962 48246 26886 53928 97377 88718 32653 91618 96189 78307 89911 80656 48812 46634 68994 12305 74568 50632 28678 46582 15950 70159 46309 3584 54648 26133 95682 79394 51017 93003 17672 81999 52840 78104 80393 41891 57027 47584 36033 97357 39345 98068 693 48053 22277 81538 64114 12953 62699 39380 48552 76013 80994 66986 68270 97749 85023 16335 99689 9802 72119 18770 99297 48693 45711 65884 27817 93231 44042 75024 51451 19157 80182 12230 22932 60347 85476 9667 99436 40422 96366 51569 48677 95325 29520 59885 28735 89367 11907 43055 35965 20040 51599 39137 19528 64214 23130 61154 93408 63120 17298 43495 21766 51827 42777 13096 2428 35874 80730 66632 21103 34277 16715 86701 42271 31913 91873 36750 78118 66706 87303 63113 40254 66011 93444 99148 26943 27654 37178 13802 21749 83622 76043 44009 72287 7474 42061 91004 78271 70636 87717 56766 8404 69171 78716 90012 85473 94120 87329 39457 48987 74124 80638 36123 33488 75287 86312 76499 7614 9090 13713 65028 81097 99288 92822 16645 37078 81226 23661 75959 12527 38063 98649 7899 47593 91287 31370 16922 68862 63126 29611 77084 87122 38758 46059 7603 78536 55586 7434 16606 10786 5705 6232 99252 84329 72437 81652 45970 84779 86206 2109 88460 47106 49597 87441 8381 76355 59133 64022 30752 76227 55990 22591 64711 82641 3439 8880 67638 81021 95803 15720 78401 76929 3655 74824 67056 19683 61170 45893 25003 82412 68393 32007 69189 87186 1188 27684 54960 22529 75450 60229 15867 33361 46955 15574 71006 34757 50482 49119 52344 25717 4702 25256 86808 74725 48658 5681 29572 30381 42493 66816 34068 36535 74186 944 46148 48364 29723 55018 64913 94784 40987 90449 73932 73530 72158 61602 71415 55510 91860 2251 67232 72072 29481 96922 24259 21984 30451 22467 65912 74282 81421 9167 60090 1328 56144 98288 25305 66456 4898 89009 81993 78655 21807 88396 38900 78236 72423 32512 88168 95279 97916 43003 67130 32265 7817 5851 13470 12806 3948 62449 79701 5834 72407 28187 52955 63754 86139 78646 47405 9663 9203 47762 73885 93438 1408 4987 65732 16159 80238 30845 39105 40137 7437 54319 85027 24833 59718 24846 16850 27695 7593 73952 26988 154 25611 91871 21090 23692 74821 81342 24561 32988 70591 45561 24723 9412 84826 72357 88353 15596 65100 65098 23019 82437 63529 61153 64183 79933 88909 11591 90438 71693 94902 52952 53184 44963 67948 79099 70649 39279 11774 49165 12101 79417 99247 47263 84411 55756 25754 11558 66569 58960 52068 69830 7019 46040 56548 19881 11450 67910 84765 20546 79692 18855 50290 18804 82490 67158 51906 7421 91333 37330 82708 93118 20 84409 33668 65015 88934 69071 10251 76502 90510 4907 42586 113 73189 81773 71442 40619 31275 99687 35583 65601 83909 32680 10760 99210 80711 98260 12805 84738 69434 81843 46827 7730 21024 57521 85155 73465 36487 90171 68736 2455 55742 30315 60461 44770 26681 34878 89002 9654 35704 7816 54009 64952 39904 57072 15594 73132 54586 47391 23334 29223 18614 14996 6491 46934 29144 53752 9864 61992 72130 41764 35192 71852 62693 4191 42855 35014 11558 37387 82830 54224 57364 68851 34859 32584 93698 78780 81702 52228 73447 49611 98673 77766 6659 15889 28177 77124 79556 38738 288 26030 66531 20908 45085 40563 88327 20824 35867 7213 73834 65397 13038 49256 23422 53689 73985 7439 88144 66844 70925 43233 37057 41520 97161 78959 79239 35348 15746 35260 81430 18245 24320 38134 51531 25800 74052 26383 64634 5868 6931 31275 30248 20262 28956 35148 18364 94701 79344 84024 94696 69581 62743 60251 38885 94518 89637 85800 41525 56197 43049 47147 30804 69375 30500 6253 80822 74264 5450 60286 16731 22819 96119 26993 83993 2929 33377 68584 36337 93466 92326 77023 92926 66803 27752 66006 31873 36013 13537 68956 79851 47075 36326 2096 60583 65683 73646 22705 93841 29155 2264 74578 2631 73570 16104 12122 38181 24674 78341 954 77024 3955 84006 79947 63233 23075 62252 79282 69103 46597 72623 29037 17483 36729 38134 77949 78198 28589 18562 86800 92057 3392 34392 50634 17856 29197 25957 51287 47369 84750 40654 28109 48854 21903 51537 18018 46560 49457 17819 91639 39376 59801 19355 27092 37633 42528 42575 85130 59166 88149 53387 94432 19982 36466 15221 1859 67482 77816 13031 28144 36157 80700 49237 42116 29084 20061 52361 89157 895 69501 8554 71406 86263 53631 22205 2434 38232 25017 40902 84538 59399 67150 96769 2215 86705 34538 49396 13814 57834 12625 6485 49556 62856 52492 69146 26293 47985 87035 67318 656 16761 80494 43702 82538 24205 39364 22710 4470 17058 50507 93291 80007 87926 6341 95534 10775 73356 9075 35788 21959 58605 48706 51072 79144 94563 92738 11986 42614 96775 20784 40064 86363 69607 81220 71829 16573 69301 61982 97014 59501 72093 92784 61049 75142 83104 11779 67500 59111 96529 84985 10150 14976 37442 52926 79223 92110 64889 61769 61949 35780 72013 51494 62525 16894 36816 72236 43492 60689 41244 77117 2476 81837 91993 60041 67507 17851 96362 73019 57482 98718 146 63629 47219 1677 19460 93048 64111 78224 61600 82679 60581 83384 32633 93585 50318 2721 31416 55186 625 57460 46067 42156 68113 31934 13687 98641 52270 98145 38686 19553 38737 50656 6554 86498 29811 78610 93613 2193 26445 60643 20557 8359 5316 11745 89216 51859 94632 17309 97331 45832 96298 35022 90668 59225 89635 46540 79880 74532 3874 79525 16948 66060 64836 73591 88159 50266 73604 18037 55888 20383 97615 1304 24467 88063 49327 3559 74169 90064 62605 14257 61042 40644 51623 84529 48860 404 99089 92546 13451 10677 39315 3647 83763 81923 92965 65999 58603 1393 11471 1153 73871 41527 95462 60792 24316 15400 79732 5265 10214 86016 34441 32033 99606 96006 82193 14094 72821 6616 73736 78913 15041 49860 79749 4412 54125 58057 86018 41774 27808 64320 70796 83552 24151 19615 6999 74812 67229 45070 96848 27602 9964 35237 12996 70237 38480 27576 10419 42209 2014 9445 86040 34121 51836 10979 62043 90151 91456 16027 65622 77084 98569 2074 58211 96907 87002 67716 35082 61577 84871 18113 2522 21438 80823 48309 78741 87208 90260 97959 87490 2580 58777 25782 8076 93487 5187 74630 1657 97600 5711 63585 50650 68081 72764 42516 92052 10272 57851 76274 9195 29729 66862 89958 70389 4742 64270 72692 26887 91578 29631 39999 44232 34631 89118 26863 56128 42406 16474 77272 85857 12007 43375 46795 2151 45351 71786 79858 74075 62340 21480 28240 78447 86556 57627 43536 33403 92324 91181 35039 77381 67762 91791 23642 548 7214 13236 50248 37665 48657 90305 28068 69500 82973 93302 57169 24959 99013 23418 21905 74692 99234 91004 1706 21888 45509 33068 97348 69695 19563 25668 50372 37972 13739 72143 32092 36761 1805 65421 59147 22271 82726 9573 68092 45966 89725 37136 42498 55578 22448 44485 82960 43391 63318 61652 5274 77171 79671 69322 12870 69218 25986 83349 89898 68489 32435 29436 4609 55079 19610 28222 8638 79959 49456 76533 48735 1836 1224 50484 5857 77889 61163 54570 58768 86396 978 55846 42887 82790 63470 11937 9271 23788 27297 49567 7285 89851 52980 40436 59085 93500 10662 59557 69402 99343 76855 53005 40214 23324 47109 38157 14841 44551 30489 56410 51447 67027 32609 6749 55792 31680 29338 12809 58759 62178 74031 14700 63107 82300 4013 51933 21346 77996 66709 12023 63508 43679 53340 33341 67366 62548 10536 90992 57067 8622 31615 22650 21408 56293 15276 53265 68941 33609 57730 98195 64771 18444 16086 14230 46451 51433 95297 24930 71912 30327 2670 19565 54040 4924 86851 38239 19908 47167 57431 9638 99962 57942 6837 99392 40744 18507 29402 32131 34470 37920 40573 75148 30177 28285 55419 94107 75463 95975 45207 159 60822 81356 27314 59856 75978 96753 52566 98306 39358 96062 80721 50028 15738 51461 70799 6952 24653 64946 21699 48130 1086 93421 55445 78237 62193 19092 66863 35855 57248 97955 13563 17538 90584 34155 47781 86425 93558 19718 46828 88609 25790 75089 45907 92892 17657 83476 63222 44397 61204 41859 28066 28799 18501 55007 8638 23106 82064 35740 31293 48420 95736 35952 19399 14853 80340 67616 76690 32323 43686 30675 33031 27926 14882 44426 92803 75826 87951 53607 80439 2300 57540 37456 58382 19839 19722 16157 16873 17502 75804 54362 17485 86360 60620 74177 41721 79507 67120 20795 15249 38599 13714 6176 55298 96017 42711 93708 57622 42256 37554 61147 27681 39167 24284 54051 95367 66864 16493 38291 16834 91817 37912 10706 42741 26862 93553 45213 41537 20021 32667 99533 29045 41652 75019 64776 65617 72456 30372 4365 68674 37766 50850 49009 65018 33773 39587 19585 92522 88889 7445 17100 61200 11226 38482 867 73849 52718 13009 22752 31058 49752 98063 35809 27641 94447 74675 41077 91136 39863 95796 27122 20151 16669 23402 22356 6350 32961 4007 77115 12960 20062 40021 47626 77296 75887 13586 39950 55957 57047 4827 17562 75967 2027 73388 8907 13094 74766 74863 4079 61719 44332 65079 56905 865 9936 69789 30115 5207 90512 81744 99957 71609 68902 54821 24152 64237 78052 55010 79111 68116 78748 85625 90729 22305 77448 35300 72783 63313 90854 42901 31833 29842 32262 2069 44297 51745 98499 79066 36394 39593 45564 98534 89949 86216 39406 17181 55344 29391 74915 87567 39819 37688 63847 88596 30571 35337 47481 67368 41625 67367 29807 47573 27017 51372 51251 30815 50176 12049 53694 3424 57814 54578 62952 27983 78327 55227 85194 71670 53123 21070 67966 78039 44623 22629 83090 821 64123 67420 83158 89146 38747 37768 44667 21866 80000 8463 5305 74339 82957 89569 41273 21874 15375 25763 77884 88335 74136 58306 83748 93425 84722 86948 8251 85457 53393 85053 81145 22240 4246 21957 7150 54816 3414 60225 37512 14839 68975 6844 19617 1501 72215 27196 47321 30394 72821 50874 21473 73064 93600 70971 44406 67406 21616 31899 51144 30448 36320 41602 57464 54568 75159 43867 94751 6142 60121 12585 516 27916 29561 37696 7856 19160 89756 60073 81070 53363 28701 46942 589 87912 77392 94998 7875 64146 59796 89391 45257 82759 47454 57978 46666 69144 29132 7131 67681 83399 58916 59522 22230 39344 58900 71117 10458 94026 49919 54082 89536 29013 38904 33352 21396 10448 11360 21014 57285 36452 61575 38755 25505 22211 39048 95546 20065 31500 34964 57153 88660 34321 63154 49174 59123 42907 72455 7406 89269 80727 37808 94170 47370 94030 28374 46170 69315 40796 97298 26814 45699 16335 36537 74145 88485 25911 15360 69142 86875 70993 37141 94499 38443 69723 5812 42536 72107 58686 39099 73552 45388 21853 19664 77087 14807 12625 27276 62048 58928 61771 36706 70560 34721 68290 70887 73402 74590 29902 7418 40255 7115 78425 18577 59552 9816 45591 17707 44531 96600 65214 4277 17037 29324 79888 56234 45947 73973 56637 63141 45299 97425 80196 52630 44981 22553 73645 22723 43771 39113 72403 45719 10216 59030 88351 48398 76831 15030 39064 25549 49507 55618 55101 10388 24701 85958 14591 51029 77976 52529 8399 19085 86982 86784 30861 99977 48375 39512 77839 76911 56178 26483 29781 21199 81228 91508 35994 23790 9748 92276 40949 94692 6703 18736 28873 41362 86465 4788 46866 4928 35616 79697 91712 32437 86774 88828 55803 20775 8365 23061 78277 95949 94547 73234 40102 90426 97188 52373 92707 17171 56657 14028 24563 7357 10054 28699 88672 80276 22962 23208 71771 59240 79383 1367 55439 45430 19991 79054 25657 571 51898 11047 75433 55413 44124 37021 47903 73273 82675 19012 59648 75601 89332 11696 2830 379 69808 46950 41159 36324 19922 71257 47207 27522 2701 57341 8926 41720 82112 68521 59697 44194 24603 13672 93938 99713 24151 99890 43300 2375 25612 33673 79026 66926 62592 37526 34744 35693 80170 88557 27594 27327 48405 15181 20346 28667 56698 75636 46494 48718 73529 68335 25892 11466 87716 78023 93590 47983 98358 53182 46199 25320 14691 66171 41841 48412 53758 13693 89013 87138 43683 81052 4313 69484 5853 53638 69932 31367 14875 83498 73672 11939 37264 14693 26117 9056 82994 85249 75280 76132 89957 83211 43631 14565 40520 71142 37900 52103 24233 29643 90110 56046 52249 56881 68228 71633 48907 33209 82193 99885 57616 6696 64375 98675 76309 18784 8846 81442 72677 59030 26750 75521 30916 11464 72447 80946 46526 86993 76373 54162 63649 11068 58461 90816 94711 61931 99498 50259 59695 20126 78956 14928 45184 7457 72477 43444 52351 15492 45754 38114 22949 51976 13700 26415 15504 71872 30192 70090 25305 58956 13077 4048 29245 59714 33235 24190 47078 19657 63630 64781 78939 55920 82587 8906 78740 49971 97739 94701 81360 97190 92146 68376 61964 5880 19596 32397 86275 38287 25909 86900 8076 82804 70092 718 37465 60370 12043 60308 91096 39590 43591 55911 37596 37610 83389 30647 31135 51018 21623 41202 14303 7845 8586 28910 79947 61524 59240 9463 19414 41475 83900 14983 83676 37593 86778 30369 26237 38426 61611 35469 11629 51266 54895 24471 44563 96617 33883 91847 88925 76254 88768 48652 79088 71543 3978 27131 63116 43587 84043 34416 55191 26882 63028 9459 39440 67979 31939 90776 92912 87359 37032 9377 46731 99471 54416 43157 23016 91292 3549 76304 26968 2452 76826 30579 38554 17316 6376 74409 1926 41478 73 78581 4290 14313 32233 86188 10612 3522 21465 76346 41231 87610 23621 13818 88861 62822 10024 18753 21370 98491 82848 32870 43649 63839 65400 21141 44811 67279 28015 9403 19441 73596 73279 30894 51931 50215 84926 21751 26164 95291 96234 84804 72221 44698 88102 2469 65330 2556 46999 435 67600 16212 41750 89748 88994 45085 38993 92715 69885 43175 23645 76336 31545 27441 2668 36212 36981 36302 6311 27887 88832 69444 75010 17540 23042 43407 91166 55054 9572 27262 14811 29329 83989 25469 72968 20057 95635 93884 16196 84843 69045 28497 23388 85109 63922 32748 16677 57619 16891 31666 97016 56523 46551 43112 35829 71506 82708 76060 77700 68368 20024 12021 34282 97868 2332 41295 77735 42881 70483 75539 25898 9726 91910 74098 22428 95827 64454 31164 39858 54606 75765 81907 59174 90855 34994 93036 42422 29630 48457 3025 78646 94928 13589 82208 24432 37459 32036 12583 9680 40714 33813 35688 3683 75600 73363 70042 73364 76401 69889 79765 72546 54128 69618 4851 87632 51730 95782 49734 87113 8591 49496 67435 3003 51034 56836 20650 44979 93148 77785 24481 74304 19349 1708 37054 26432 48493 75998 99236 13429 82604 95030 75738 10477 33345 46854 67498 45029 39996 54115 25913 69596 81176 67370 59895 41905 53117 25510 45161 82154 76278 55140 99302 14062 29847 5309 56666 17086 48429 27795 22786 7409 6593 58022 1626 90342 15018 63584 16967 58688 60452 67230 93629 24653 81951 63683 18152 97093 69837 82749 18218 53976 8745 20374 69782 77911 41004 78341 64191 3416 18921 77560 40125 3689 1759 65241 40923 92735 10595 69641 52798 12491 84778 67812 79315 26243 2927 22855 95856 12008 29432 93146 30800 82840 86538 83370 51157 19242 84201 65885 18677 52311 24591 81527 97034 24738 31986 36879 83490 58771 4571 8340 61473 88917 96389 85243 36356 97363 22057 35169 69374 74637 38069 38102 24466 70225 12594 54103 23702 9456 64310 23119 81604 40722 16226 63858 14230 61622 40000 13517 40763 74321 7476 53808 31855 89486 40777 8760 48643 37039 44678 20764 89067 59907 93453 23287 72704 63766 22591 23822 540 63824 9012 85228 14110 27516 73474 2414 99091 3953 32121 68726 19863 43327 38963 45572 27290 27845 14932 53731 91014 60811 78788 38722 20141 64191 3559 90018 7868 76428 81435 89153 53589 76686 70066 48511 9398 8183 25711 39842 8600 87526 81768 46392 13615 60004 32641 67403 64883 66615 92426 9053 12325 23951 96008 23419 97275 61237 82464 76880 68767 17000 45712 8244 62049 13274 74050 74353 8667 21327 79687 60676 90926 12081 77993 80178 5221 58341 30742 20670 75668 27148 66553 28209 71713 4144 27916 5243 79826 26088 41965 33283 38118 51399 21453 87338 68163 4737 61600 58427 60158 48409 79774 27375 58343 65229 92032 26417 83587 96171 92248 20964 66584 5428 37182 6342 78082 7568 6434 75913 50812 90052 8141 60887 10880 25997 70172 53471 25810 82427 6565 47454 26592 69357 38299 96668 13124 14361 64854 21446 55563 87929 14010 64503 85332 85745 39367 5156 18387 77066 46332 8143 61031 46976 70839 42602 38004 66407 69281 70938 62524 25873 37428 10423 64969 4682 6013 79141 72181 86529 75409 75717 18698 50615 56604 63385 62636 56683 2340 40160 64785 64910 40881 79291 89604 434 94785 41349 25185 96834 30331 64558 69382 99797 59979 52149 89436 36869 40546 10773 31442 24767 91955 7308 68738 56615 2255 44366 83622 82493 19270 38146 52664 49047 94901 79370 43139 48856 36367 99605 91775 7962 19687 17541 42224 69672 94195 72643 74011 1361 92803 93601 55500 96506 37753 98008 38498 58883 25417 39476 17131 49514 48896 48641 55058 72381 49986 19937 890 23608 8354 55824 84286 272 13690 15120 57358 8160 32806 43229 85587 95574 91946 61994 27140 63477 61833 51284 39712 51286 68057 53257 56055 86729 90789 28886 6694 6809 68748 61085 26035 12910 92815 94944 95103 38617 64698 37454 44772 76512 87847 95124 19131 28507 5622 62420 66737 29545 99985 77155 79650 38763 28359 31650 90432 75341 2851 65358 44746 94202 80404 86762 44297 11070 52857 676 9744 30402 43957 65206 25505 54631 71961 53863 64398 21392 10189 39183 63416 73612 9134 9906 95901 21332 53982 82014 14555 89936 18458 59846 49363 42560 44369 39446 51842 45980 38005 5255 94989 34551 42330 56211 9990 95726 39136 60437 44405 56556 73189 56414 28631 64935 6362 74714 66312 12089 84642 69472 84354 24687 67640 21065 10608 31665 31117 55835 21428 43064 85254 20406 60699 97808 14855 33543 98700 9466 92263 29073 48592 4160 99479 80533 98341 46640 54506 55657 746 65486 1763 68432 12646 33017 17421 99602 6229 56223 34167 41325 91667 8053 44302 47063 62231 93022 19965 26594 90535 42399 19558 91408 25191 93987 66781 32726 81507 37432 33218 50605 15148 92133 52378 90485 58570 14254 21656 3514 13888 73561 54179 89801 82144 23216 59717 96059 37049 28199 44753 97437 54041 98337 74744 55746 82945 29631 40909 40118 9072 88561 95464 70523 68179 58530 43688 14182 9587 25731 53913 85892 38097 86965 4239 16793 52678 84560 50278 37117 89463 61369 11755 17910 58428 29821 4154 92288 55233 1093 93195 17687 86779 66085 6878 37993 6299 6793 87898 36654 92581 2677 36903 87824 97297 72314 65044 10955 7047 25337 95608 55971 51984 63596 95610 11551 33740 74488 27855 13088 52057 90331 66294 90111 23626 2649 5201 65629 48560 11234 86265 66488 43963 58489 92325 12121 69472 88514 18430 87802 74002 72925 72975 8985 50477 42603 34656 35566 88851 40256 68967 9467 97550 31285 88337 82135 2176 74374 49789 76305 53715 99675 46957 26171 26372 41161 61755 40556 8839 41189 80029 91349 37057 21159 84000 40803 47380 92244 9218 68414 57459 54742 98460 807 6080 60972 58061 94635 48385 84298 40940 9972 74998 33572 84211 92165 54692 66283 63391 36956 90902 9076 42156 99257 99090 56870 13251 81839 73672 34845 45154 54523 62191 39919 75165 32569 3715 22838 44974 43461 43400 83964 31059 57869 64935 33952 80123 85972 409 24923 65809 30150 21249 39546 11630 93331 36957 32191 54954 30583 67494 64100 1243 38232 62511 54421 73199 77039 29248 52672 87860 56070 59714 3196 89457 15222 96763 9692 81563 79739 16510 7478 18520 4588 69377 44600 27579 13107 58122 80805 39873 39626 89010 46458 90083 80164 98058 3049 7370 42441 20929 2268 33251 25093 53406 69660 4583 49524 21756 92973 13693 11986 53742 57089 70608 91271 95552 53108 99231 46607 75166 39825 87068 71336 18357 52954 95823 20542 11574 36523 89033 98167 68276 93663 17288 28874 55030 90529 49633 43305 8583 29297 49164 11338 2865 91223 4679 69178 63315 89380 5544 8074 30170 17214 49448 6344 96521 38352 28354 85851 12962 16153 89538 80505 71602 92749 99985 33715 47507 33885 22358 5841 41398 3474 25872 13440 20007 14148 34978 22541 85707 17917 25588 54226 81015 57619 25039 44883 20753 91933 94084 50422 78880 46362 29453 18224 92507 19663 51455 53578 13836 82456 1055 30659 22939 24940 43333 58246 84587 36080 31001 92641 94351 9291 85974 72251 90546 18412 60351 30386 62463 95244 75792 72000 82638 3942 26990 98113 48990 26371 15639 49872 12569 82310 80234 41714 65601 69084 86969 18278 34411 24474 78403 21631 74866 92451 10655 47829 30991 38543 90331 41565 84268 73402 33568 5534 95844 36920 13805 52119 41917 1642 51143 35530 4208 91163 19167 38850 99687 89149 31721 42701 17929 22708 19735 69870 73189 56066 97262 75303 34595 73091 40122 41578 26597 88688 48693 57922 16677 3850 9513 65330 32531 99201 71071 69297 79086 77275 24506 52964 49478 58081 88102 1210 57604 69333 68806 98211 55731 72463 276 87362 56372 52567 95260 26162 12838 29634 71407 24896 62485 88257 36399 84940 75010 43223 1966 78368 39228 53294 25698 34604 85698 95854 95751 91675 29925 81714 36131 17946 71242 26004 39434 32157 83493 12571 20225 15554 61977 40721 74898 7069 34370 88472 61342 82919 25981 79567 83784 7348 86720 67257 90712 52126 60958 59345 44825 20990 70101 16517 79247 73086 85001 65299 58067 1446 71073 91412 91268 99573 428 64087 65537 40035 24064 92879 17526 31366 64180 68741 25874 44215 11543 97881 14540 69733 99778 2999 22427 96529 61581 79215 57589 99642 29832 15746 65850 55069 71939 56637 10936 26685 57465 23439 62840 64216 20805 95128 20297 53272 73339 62795 86265 3886 54715 1029 89223 66546 88183 14636 23136 57353 18868 46736 15755 95767 53080 5769 34074 91652 58121 91810 97300 84590 15216 61364 46420 16408 55839 39565 5556 76522 32880 52488 50643 23448 8010 14234 24577 56540 53368 73131 82418 52655 64677 16888 49876 43342 8137 63038 9464 33600 76795 73554 78516 38104 58858 67693 22378 97375 56583 75684 91826 13946 22272 51613 49410 95047 60812 2362 5287 28530 5269 22504 9427 1732 97731 52903 6187 65087 85079 69581 43165 84670 50784 79199 35780 69697 19436 93708 3346 61749 64452 38565 16763 49988 39076 68061 26491 13787 30266 54221 92888 96631 64710 88518 26408 26149 24563 49099 24744 27455 40785 6962 28949 58191 60867 16168 63487 87504 18799 95297 17053 65461 17190 62137 52149 71793 76553 39483 46178 20889 94305 39797 38720 5035 52812 12863 71529 92656 16411 9002 15971 95338 70197 50961 87003 61425 58575 52062 86261 74983 95568 487 9999 76954 57818 26572 95152 66357 29497 60420 70951 2452 78088 22513 28873 48975 42189 59796 72158 14132 82410 40593 28813 45238 42482 55040 50925 55376 15879 39581 94098 89431 43201 94916 64269 69562 62770 44737 82522 42212 95270 45655 36991 87820 32808 40786 38896 95223 49127 46188 32886 45942 73353 14114 41781 68597 8348 84490 95176 99227 64219 99972 65448 80736 86972 6113 30220 23369 60910 61619 65438 82963 95925 46328 25113 1467 1762 2133 65633 56204 49878 96904 66411 82298 81310 89873 39381 55127 70913 75811 78424 79183 92758 82849 28464 98386 33833 1021 57584 84938 50950 34781 72607 86837 52315 34044 71817 25564 74618 77748 88211 7699 52729 39029 19530 95703 71695 55677 16069 6027 97873 17271 75902 44387 84692 10940 40655 78234 13005 78266 11153 21195 60402 42591 89177 36685 14388 86332 30862 3832 77665 62704 57687 19056 77563 45261 73351 163 89849 42800 47189 17460 29630 48463 99096 42162 63274 11431 1922 42934 9495 49192 10025 93992 5531 53711 15056 18153 53357 25426 91913 80847 40482 42781 33211 3451 83753 24375 79032 41595 4951 77615 44863 71493 19678 44169 59687 95933 28613 26612 8707 18161 83235 33624 8355 38741 17885 87270 49761 77789 57837 1416 3297 7024 34907 73143 1393 77179 89523 45308 7149 23354 75897 99431 77459 18822 84258 81017 38639 23859 87759 55306 15457 29994 88564 58795 38941 5289 72511 54017 67324 33604 63470 92241 76893 42786 40647 78274 46827 2779 28797 62132 53543 17993 17893 48191 71131 62852 81085 62528 11734 53076 55653 54656 86536 1184 279 44957 1215 54167 12951 59753 7116 81313 21709 85837 17185 15924 60088 38809 10129 68214 44068 82537 35118 44411 1429 52574 64555 70019 75160 56504 8249 18294 57347 68420 80431 29493 38730 75758 81682 84354 51052 96216 9064 87083 63232 93626 90570 55133 62891 35047 78432 10137 78581 74922 37618 98550 8622 98857 30959 55712 14630 92533 20959 71587 60911 89282 82365 65494 263 97820 20462 65915 49793 82866 95797 74312 67798 79419 53788 74263 76503 5078 52937 14927 55169 75074 23675 87825 92702 46068 59830 37522 55427 12553 33690 81485 70387 97023 44647 78471 2190 37637 21800 45249 70556 17710 91166 14392 14703 89232 70531 17653 48830 18456 33805 14162 94241 29585 9048 87509 66653 87082 69079 60201 29194 91750 82554 71663 54488 94843 9654 50188 48420 27628 46098 70253 527 49389 54949 58402 85594 98009 24620 78975 27997 91756 63067 1614 53931 57830 99257 81254 54064 39650 15678 90531 18336 72456 87778 18288 11597 85285 62278 58115 99495 59452 85297 97415 9394 23727 74167 11369 2553 71857 9030 10639 40043 94166 81164 1701 62286 48274 45722 71607 89560 63120 69705 61240 33899 35559 96847 80205 37484 51418 85818 7029 26891 62946 88592 99837 36724 41600 30887 34554 50641 5944 48055 30754 96962 17837 93159 96891 49149 6371 90978 43576 85477 49993 99390 2842 99911 8263 93069 77770 15186 32207 57733 16610 54895 35943 78068 79340 62919 97805 75954 10340 71311 51254 4466 63002 38093 12336 28198 68826 19478 70983 27802 19971 38530 22388 13711 75462 96643 89287 42194 53688 32693 35515 32731 5686 70472 67528 75860 26479 93677 26072 78489 9756 14466 46847 73928 88899 55439 60940 83187 8158 23688 26058 46690 15299 23423 19138 24841 8547 67247 81226 86859 84322 95124 95869 24858 29565 61923 47415 44287 99570 99329 34544 2563 3632 54433 1651 70292 54867 72516 66588 65051 45141 18012 81491 1907 6246 33454 92149 40023 49849 94045 92182 68169 67790 71302 42731 38904 97231 5435 11986 40383 71160 60687 60771 70651 7513 11547 77161 29774 31616 27736 99631 94318 51529 94709 71793 6828 53225 74918 82116 78204 82346 41625 54801 57197 28715 42678 62764 80062 48505 15414 65169 68381 97846 14397 87855 12425 5680 97942 42730 31358 28131 20351 99260 62351 86837 54619 74349 51770 4211 85842 15721 79300 50001 69432 81980 44608 2763 57234 42929 7888 77006 25431 74862 15499 88171 32246 65528 45646 80702 5857 66186 95922 15362 12449 93384 6419 58781 12729 97658 89923 19451 84367 42308 17986 63297 42893 97123 97236 5191 78947 93445 88844 50039 24079 84151 81199 60167 59225 19503 22614 27955 36859 43450 90275 5892 20421 77230 41489 28508 55019 65907 41751 66608 87289 48775 78855 61432 3533 80978 67393 1789 9840 68508 92553 14998 19074 36222 49564 13024 22741 62167 66310 22020 95285 22068 61124 71465 57953 25695 59117 38547 46058 46421 58649 76639 55515 69145 47628 45112 51825 42896 50331 49099 41695 53970 12424 22516 91510 60303 27567 67732 86168 74655 3030 13778 5526 30773 12096 87086 40475 72873 67615 51648 61871 28247 52458 67917 22141 10109 15265 21937 75155 70407 93416 67741 74434 56368 71623 37694 34370 29503 81328 33333 90034 53586 56893 83420 17241 88557 70134 82676 12821 82257 38967 16485 30062 49504 9123 97163 4095 54893 97566 98168 53925 90506 42781 2542 31863 80498 79102 66423 41782 48396 64800 80927 2569 73184 13279 99030 9964 86896 88250 21767 73818 96518 89400 30734 74874 85424 87121 36536 15354 76788 53366 86821 84650 9425 25511 58051 26525 87973 96583 53616 20288 73306 3132 90771 76615 17130 25753 3494 58443 70939 51321 8817 62416 18129 45510 67702 75716 75330 29980 74629 19351 31818 39976 68089 86393 39634 30238 85427 86273 30624 17167 10949 96487 95925 42312 19779 12979 98387 63430 16266 68184 75163 70726 17285 67524 52807 47330 40482 47394 50532 25933 69810 50420 78953 74227 3938 62184 22544 61025 88706 96552 782 45106 1081 62595 36302 58704 19174 16779 11258 19156 58885 59980 76354 37113 23852 37811 52018 69418 2821 51929 79401 73412 24122 87029 63206 21240 60277 89587 43169 54125 73185 4645 11166 75230 6683 72953 33192 63574 39716 18670 96516 32972 2137 59068 13033 71386 73740 10269 48237 24724 60366 41466 47754 71977 73902 6236 19056 57025 94393 56861 86901 41287 60663 2916 65069 55702 43680 66146 93172 11156 28659 670 94808 79520 9911 50665 90417 98631 16224 20245 43646 99022 3146 60935 44475 40084 16217 16459 8172 38721 95505 30122 77840 93731 31964 66309 83960 75883 23890 82528 8508 16674 59177 26029 10464 27548 49574 59474 76446 21871 94681 57207 4762 71936 66355 80347 90767 57909 81632 7383 18199 81538 21366 86234 9656 49062 23693 72371 19105 30989 83463 11101 47169 42015 78205 91645 12660 37831 34171 10146 84186 97399 33460 78259 71950 74421 50261 2320 96444 17463 87240 96454 59740 31178 7145 18979 14088 85114 7618 34095 14992 89286 82210 79431 35006 65694 62199 55116 51335 89447 48207 4112 1981 21260 56096 36732 41375 93742 44603 72794 49112 8325 50277 30921 18311 92379 13190 7294 19385 64667 3437 70326 47903 13128 44296 66678 69295 64765 50041 50694 7661 3480 24678 54428 5468 72428 79956 86740 54724 73151 32413 40951 48091 93277 41662 29512 90326 8945 22397 92781 638 40980 367 60685 95148 40168 15983 35230 67928 1810 23658 701 14193 41574 54442 31289 60322 44885 2029 62720 69461 38782 57208 37008 59529 11224 65736 38917 37820 6934 47029 97492 71395 75837 82203 29507 8735 82373 9253 4250 8033 66455 25262 20706 69335 98937 28660 5590 48829 77412 33541 71646 61604 45924 63118 82572 80855 42549 8510 27675 56803 77460 13202 67420 28149 60497 24768 66584 93067 65602 74854 14808 8665 50562 50452 1246 74032 87071 49139 53790 19361 74785 81861 72817 15315 62696 69940 94828 79782 86417 54845 38184 13227 78863 90950 56939 16265 41748 14064 52856 6684 13094 40139 25536 9847 61952 2726 78110 35013 24736 71320 37123 88219 15033 61639 76275 65267 68258 10322 34381 34268 13735 13350 60733 96322 44809 65304 91891 11626 93180 72218 75505 44485 20652 24454 78009 76434 32646 72331 81745 62105 88874 44578 61691 26045 9555 6181 41700 72728 42741 4965 37901 72451 44312 17394 17838 60368 90114 86793 46697 56711 97989 17171 88127 46052 71153 96016 66859 77899 50935 17135 17487 46349 62506 18397 81168 92862 15549 85835 924 13861 47614 2361 65722 69204 30129 45656 26980 86853 74093 57716 15149 77540 85975 7240 18321 70813 86948 9848 86484 51885 9469 40204 65759 79117 98112 26464 29214 96455 1906 59419 49665 9565 53540 51696 44984 54433 27998 37561 31345 2744 12800 41273 91103 70796 67432 79308 26418 65508 63892 85252 4906 16714 16845 10846 41284 33951 48442 14257 60316 35743 6472 5143 92663 54294 15904 74999 72543 35489 37212 3258 76502 82312 29598 78636 11437 47845 89561 63214 52677 58364 8770 23873 78269 38631 97777 83311 11601 86406 73746 26186 27306 62915 83315 70154 98976 16004 11633 86582 81839 60705 65091 22258 78551 98268 28714 95500 61300 90422 79216 76749 98880 58174 52236 96917 21957 1808 40457 30146 55273 68549 53203 33251 33926 2095 93162 49656 78721 18907 39469 73599 11030 91371 60922 81809 18403 74073 72509 99939 38085 47453 23233 3938 37866 68229 31572 82565 52776 2057 87978 19164 25641 53429 60297 35903 493 18741 53318 22913 59249 12094 77121 41318 66536 61140 99646 86266 82270 41222 58474 73465 88774 84029 21141 77411 12486 6337 26265 75535 82217 53493 15914 15749 53232 17967 29493 63399 66114 44018 49903 25624 83224 186 47166 60488 7023 67821 96010 12373 81679 52337 55304 3273 93864 98148 93277 27937 78774 21382 82700 47206 46624 93085 53443 30515 40960 79890 55208 4789 41659 45357 35479 47343 24236 44924 63659 88934 3856 67774 16909 36027 51889 19338 38735 94814 4099 15817 99230 14200 17347 16930 95763 15695 38157 17827 28520 62983 30262 50811 62505 49182 99827 34436 46803 8259 65408 12851 6418 12620 60122 38836 68154 73523 52544 90865 57094 19368 21651 38808 53525 34817 67257 72978 73893 13760 95533 76879 97863 30799 40464 22853 13117 41110 48133 30133 10116 22573 67238 17343 73960 47883 54323 42920 84455 42280 48377 4480 74622 44587 20656 22945 28419 68216 43380 41901 33455 52420 60205 78010 13611 70379 64378 75246 68670 72275 34071 12975 81829 24640 84070 98355 31863 60591 92371 62816 98346 40580 29045 22823 71669 34677 40266 25730 59437 95196 40600 23652 37757 27005 38521 30910 13694 28858 32271 30686 16484 56097 79175 8837 97657 67821 35164 27098 24977 11091 20318 95732 25871 94231 59403 42779 93551 9078 56627 12420 86885 33762 1498 4614 98215 7417 37941 63886 22905 20207 73593 67999 46475 32827 13833 82561 62870 32300 18124 8974 2778 41383 46029 30282 52086 71378 64810 47652 81740 76849 52637 14946 5533 63489 97559 8201 63204 22500 42894 92147 57765 11617 89471 21884 15103 26937 5408 26866 43762 9974 58662 27370 16994 88875 30656 46054 43720 17491 25843 34586 92178 69430 52893 57825 35461 14003 34414 15770 44429 28704 25598 68347 84799 348 460 40197 12017 89763 50139 50012 2410 83861 46873 67621 63545 63274 35065 93442 35581 71184 69922 76518 64996 43338 34794 41952 42573 79410 34360 65597 25792 67769 84874 73478 9436 16717 98061 96969 97349 77246 52103 71276 48042 64924 89542 41050 853 81625 28061 5526 17948 81118 3670 18421 9444 22628 32939 18508 86477 2063 40411 6191 36130 94819 13951 70770 18226 18155 10060 38961 84829 92526 30528 78885 14031 88400 45868 96395 86871 90592 11830 40927 72673 66705 92562 9125 41761 35796 81800 41575 64852 16011 14943 94854 69343 48307 38820 22074 57915 46264 24889 93633 62185 94218 7587 36684 16800 29801 85 35515 99652 8275 64381 9269 6582 58597 43474 1626 88157 5009 52736 41314 18703 85621 67059 4243 58150 68321 40670 3322 15251 82553 20738 53604 49059 52666 86985 30211 45478 70120 57297 86122 46866 87709 24872 4517 11757 31449 30747 54492 62040 15368 45338 32061 46498 69956 87045 71576 10723 15075 38491 43307 96304 32185 3282 7904 57572 69190 43891 72168 11566 76713 25409 4566 42365 3480 12562 68650 76567 57027 32064 22716 88402 31633 49989 81503 32429 15979 54987 38465 53298 27814 62976 46139 52289 91631 73348 57516 69244 22765 5534 58533 71840 36551 59679 55038 56311 43199 61850 65939 17799 70603 39304 86393 25625 53931 19976 81265 61870 89289 97808 82016 94226 7685 58097 79227 57480 9723 21682 13253 61910 42057 12593 75461 36441 14548 17783 13464 99553 52100 57989 80107 11758 50620 93930 35421 16873 26541 74782 94970 90080 96439 84690 67433 51244 95263 27507 28434 29365 55321 3897 15942 77124 19484 32345 52560 44332 34437 30008 62399 2353 42621 89410 90690 6957 79702 14968 29174 35697 73318 80186 45707 26761 1290 42036 15461 64538 1854 81435 75523 39940 90854 39734 23125 9770 15275 45434 44309 7960 21294 14670 73829 55548 36823 59140 45797 71640 27840 7143 70570 96909 51787 24700 91688 75214 2261 49506 37466 23615 53630 83683 32392 41042 43589 50900 80320 70676 26290 57722 86258 57774 45727 30510 17533 47509 2667 55960 13597 57037 16849 59378 84886 28779 89956 92907 51669 56297 42869 11089 72134 71931 69088 60437 27744 56676 42257 84672 38408 88876 24227 96166 31704 83552 48076 16198 78462 71695 92718 32480 51182 50800 39594 14953 80193 13258 56050 8719 17216 64622 95574 51183 82785 99074 62629 17712 17824 56136 47542 38487 15035 6416 46169 66718 88215 88658 42940 46898 30718 9529 78358 36096 57579 39349 83493 1891 75825 48419 69511 84835 2567 60764 79178 87051 69412 18169 1376 5537 69744 83852 42019 98612 9605 20027 61867 49804 92142 56673 28259 99160 9816 549 70845 53912 6645 33182 65200 23051 60376 27272 79223 7999 97417 6986 95175 66044 99303 18327 37899 96712 10425 15802 17032 79880 45181 63827 36536 78118 60106 4077 16020 49674 28947 2700 7622 70734 63783 88420 17019 12463 64323 2104 6253 62063 50306 43102 15220 78453 9487 7458 32868 37562 91814 36319 82178 45850 7975 58668 59697 83258 1806 22258 62292 3996 46024 58960 86598 86853 72795 19808 86526 48898 9703 64282 2177 84140 72030 70657 77042 54553 71893 56822 38074 83010 88035 25318 44029 97658 10414 91518 96048 51731 72440 79144 41597 39889 26019 94876 87914 90996 69731 39018 88768 14453 13800 25020 33751 48257 19231 86323 45830 39052 44576 69816 40294 6508 28668 27249 80551 47702 61444 78354 65882 72232 43056 78008 61238 10264 44814 56845 10568 48489 49873 63254 4751 54859 21474 99975 78759 30221 67320 7799 5519 45848 77875 12681 98443 28970 33258 73767 61111 49724 22247 13860 18670 95005 84814 1662 50229 29631 66501 26342 70624 99684 27403 49884 66886 78176 43078 49750 23570 84116 93183 16614 92188 61498 74940 46237 89459 87366 40759 30298 73849 57046 87748 74650 95270 39968 11669 51883 19522 10490 90986 51761 23349 78882 96019 98929 26598 18776 66021 81746 23671 82531 38740 27466 17912 30026 58343 46231 56570 82549 41916 84252 48861 52916 28655 57440 65362 43679 73114 82141 70516 36624 68389 80763 43488 68155 35660 15295 76159 82315 64284 37663 55708 64677 53196 6630 45422 65483 83028 25538 23480 49129 70674 92086 65762 38807 3073 68351 95299 48396 98866 90967 6633 75573 90274 60005 30000 58655 94562 52801 92767 67497 56726 61754 1827 19794 14657 24506 71617 23114 84856 74475 39369 9272 11827 52910 74424 58833 1589 54595 51211 32260 24799 43571 23423 47043 65697 23930 82708 63520 19646 85375 59277 18659 84390 66542 32915 94203 96821 62080 87488 20264 24177 13621 43371 10944 2338 46336 9158 70624 61523 16989 89089 36464 23894 20426 16751 17694 25320 97196 48683 37385 49319 23263 83418 53022 24242 23343 20359 8110 29756 88115 9303 50221 14720 61100 97452 46257 99510 4981 39442 94430 6471 78894 8173 74345 12058 65096 96067 81608 96919 91726 69384 23077 75930 45063 73734 66746 86060 58399 41508 93904 512 4771 34131 80062 93188 53834 74204 34054 43286 77714 28719 62636 83697 52918 30555 8637 36449 72134 62354 17220 11715 4127 48019 22586 24307 99271 66554 83297 89679 31727 14682 47148 50454 48698 75816 59607 20843 68851 31295 6147 5970 59597 1288 18004 33121 79884 12568 54220 66663 81869 41934 35336 45402 46327 87023 94902 40729 83878 67697 96533 66597 65140 88643 13640 1437 97605 2704 34014 80876 61765 70202 99059 45401 99076 70133 99180 71652 78915 98448 76131 44800 43972 62974 54587 80204 17562 52000 80149 94324 36814 45267 19376 9096 61067 71188 40978 66729 29312 93919 51336 75028 92841 33196 98873 18823 3225 38663 66075 44898 81297 27181 84333 6844 76387 42137 8094 85575 95166 21724 18720 54189 69756 91395 35784 49825 80442 98492 73402 18893 75375 31647 24335 60240 22019 64446 75881 25550 95602 82547 56479 52992 90575 11737 58318 38804 15276 37141 62955 12992 85696 81498 53430 22518 87523 67817 50649 83938 93029 77610 46968 21046 41736 18409 95120 63857 59362 20442 74097 31683 53171 53826 31175 56374 97306 74273 49883 3347 79086 57057 66432 94809 77574 6955 90552 60072 67774 84215 54618 72092 57639 79667 68428 13234 20374 11106 6209 46117 28326 57995 16884 36008 83541 40214 90664 41427 30559 49613 37483 82862 56113 8978 18207 46145 2535 66069 17302 66646 85832 48227 84984 35395 31034 18176 91657 97229 3072 23829 86164 35864 43048 19085 18428 37590 91339 38228 9376 73049 2462 89500 28853 81136 58015 87275 35471 50770 1918 15012 4097 95792 64710 44385 99819 38054 85694 86894 5591 97684 7234 31140 11722 16288 51170 88439 96162 38879 92962 41295 18916 46925 38949 36518 17966 97981 16533 70798 79430 82763 17163 24569 37988 38826 76658 90178 9394 81093 6302 14981 80861 51739 51670 50588 71842 41031 44583 41690 39833 75257 22837 61473 90881 58318 65728 14985 32840 69406 12747 52694 21841 18567 23794 72057 4090 25955 92496 8496 10992 82287 51651 64024 950 41230 43705 36422 30180 8510 83476 45690 75059 36573 1111 81536 94463 4404 78998 16871 77435 9137 43035 37495 28724 28891 85143 52761 56285 54429 50522 13530 83934 24201 5404 43524 34031 78806 6983 99377 7550 40756 36850 24070 80668 23751 91228 868 23063 97731 74978 33299 30597 35338 27968 55929 55086 24534 1949 41331 83070 78479 39927 58085 76490 66051 23657 11519 94501 92132 28715 38587 36364 20775 370 40489 54982 92487 15301 19481 29994 49644 68495 91799 83307 75065 85475 5007 14500 50677 30865 38055 8202 25765 8789 90130 25626 22582 76562 91044 21273 74932 30992 93127 28433 25545 2452 89415 38610 83729 96368 60922 55565 41651 95560 32997 5632 98852 56921 68579 9396 46881 87206 54597 32813 73026 74634 88299 19847 12922 96397 96190 29365 93702 37611 9112 15401 12664 67732 60123 48872 91156 25286 20690 12870 28410 53806 17670 52781 66921 83751 73351 79496 3324 89606 12834 10008 48531 12168 65857 4498 44679 97135 11589 19162 44114 28284 21597 8359 60098 85978 68016 36743 25741 32408 40991 51301 69687 47046 95783 17043 98977 20215 94998 30455 62521 82217 1703 29999 87582 30358 75188 94955 87482 91228 22257 20509 53360 69105 81129 88483 9946 90139 11191 43191 73359 70216 39896 62326 70766 89266 34307 28046 65514 21107 70630 13189 52005 15348 31042 8085 81537 14508 27974 25685 1811 79841 49505 49692 80046 85470 53149 94975 93051 42486 44459 26907 84324 64337 63634 68053 42735 70182 42767 38876 17639 68490 85717 92260 69038 89714 81969 91010 79449 58676 75860 85716 30576 78677 90191 44742 20955 95062 62968 67415 82941 16020 86403 71567 25424 65397 3278 31319 89887 90460 42348 92435 82399 37297 41666 33342 21907 77894 6554 21620 36474 10308 84209 83949 69215 62264 72903 17384 98559 43269 39449 47036 82558 3096 19197 12400 33999 83148 13158 13579 36852 54756 30393 79974 6166 41160 24203 11092 11678 88775 75789 64729 79939 6657 74525 90548 54791 28765 38479 83754 79295 27507 87575 93124 23938 55829 8096 47301 59019 79768 74284 91207 72726 94971 94230 94534 76370 50419 64052 83990 82691 24151 95273 97571 55212 76537 28186 2431 95135 54639 98488 93237 80912 94122 19490 2235 76265 37231 46276 83456 7279 95898 7969 61783 20893 21030 69045 35845 65245 37906 1932 89683 45995 93898 98954 83739 75424 28996 15744 86370 16632 12840 52891 16079 34944 9123 24609 43608 18590 80078 2431 89772 36039 28042 40710 33198 1144 40103 80731 3034 5753 35839 45705 94687 26920 34389 2804 30665 34981 82552 61070 86868 9664 26903 27209 52916 44508 14920 8086 49943 1515 67771 65487 38385 5610 31079 84999 18850 95074 56417 55454 50393 34439 97607 7592 50941 94055 57949 39409 90534 40619 93558 83057 41281 60682 63650 94015 33181 75131 17386 15335 75207 6170 82964 56314 33387 83171 41408 61533 31563 8614 61755 57464 41956 99596 10417 35538 92291 66058 33254 97043 87616 27318 73927 41186 11988 36317 50434 74046 47542 48420 76621 43669 16748 41516 10000 48585 67051 91627 21751 5172 85754 72597 51424 33676 32367 5806 5377 20030 99295 97511 59120 76141 11106 29092 48904 49321 15381 15439 60912 78191 56349 58301 57820 71006 21581 35309 4476 24494 35912 98701 17677 84404 65261 88178 20077 7531 71327 19110 24583 30533 13738 85685 14305 20117 98548 52630 86161 81568 57685 17014 93133 12404 40032 99669 13539 65372 57954 864 10346 90408 61785 30935 77770 45576 47487 39250 22086 80231 87236 50269 69358 22699 38565 64199 23480 57436 58024 89058 24177 8894 94252 19882 68186 51970 82133 47996 56754 13003 81587 55675 69720 28609 51046 87567 57765 97538 49195 56276 69776 61606 24237 12853 31990 49348 22495 33270 12347 76272 50403 56149 67516 90761 56571 42858 53102 12274 35460 38239 12793 80599 72562 83819 28381 15562 48198 98682 36749 71000 32590 97781 28359 48427 7626 52625 34760 39198 64733 87379 45379 93516 17058 50330 37161 96184 32171 83540 64418 55062 47852 59180 64622 61809 42689 42886 86417 74566 39057 91320 76293 52060 29827 61349 66284 43437 88230 60400 7225 64210 49143 45557 59921 79500 97263 29700 94125 20885 23113 73208 29016 34605 89215 31750 60693 88556 82833 11410 83761 89371 95291 62318 70814 28898 10714 71715 1464 26562 18626 38392 65868 29421 75116 97161 39570 75973 7604 47174 12093 73057 7582 11765 86118 77690 46738 97738 43918 26040 49490 69746 99822 12911 39490 86903 17894 49124 24709 36842 93680 85634 9219 6454 43710 43134 62217 78766 60767 32129 45693 8021 87823 61256 74455 44313 4804 91121 51166 95919 8708 42170 48202 76375 42534 98478 88200 12902 64360 86797 69597 82147 53988 19567 40968 47645 21771 56117 46993 22706 70703 36360 97117 95571 73836 42602 49852 82889 538 70587 77790 77004 27403 13736 96480 1625 65527 95864 1882 43514 86435 16557 35361 62427 27574 77955 77169 76564 53299 88276 33528 20150 37012 34476 28428 66270 90242 28985 71887 72842 26787 56939 60891 40433 16592 13067 40480 20230 96876 73849 9829 68764 92526 48488 52335 63303 67673 21180 94270 61140 70299 43081 17377 89655 31907 34783 88297 92646 58909 19807 63740 27328 88755 71778 76630 89059 19771 64333 32103 97404 8659 50388 41761 22362 69044 8583 51630 80786 79002 23414 85961 66976 33713 65367 98394 87653 53997 60730 8058 26075 79779 43481 28115 27873 46468 30871 98914 19299 92174 15758 48641 38515 19674 267 1696 15800 75780 98835 97000 63383 85207 45538 94673 89110 96258 62487 2506 66809 6725 66354 52036 74859 99243 37050 40216 11206 70684 62096 46987 67247 92074 98148 21757 67830 22928 74131 27516 16558 79841 67093 33037 3280 13933 60744 77581 5365 99735 92067 89840 31465 18675 51147 70858 45715 82197 83194 23203 58593 16180 50038 39085 15737 64327 350 4500 4973 53653 23574 38520 77032 3984 1684 93611 18084 83790 23550 27327 55986 12154 44683 87600 23336 23035 81332 44584 71828 90504 73245 40386 65354 70204 11054 7475 29100 57882 84455 5686 28700 43438 39378 99605 3503 71496 73877 93950 28612 97163 38315 59288 997 99671 33601 88387 43388 85052 83562 4688 84838 18999 4265 82108 53901 89734 9760 20578 18343 31064 3670 61724 58647 81197 50540 68169 73735 48732 37373 78753 30911 41396 7839 13226 3358 41625 19748 69090 98943 79180 73199 33459 87104 73972 51228 12814 96501 25708 6905 80428 55889 55732 71715 73984 14765 4545 25440 58323 44548 80571 9038 37676 22621 54696 67284 21378 91749 949 94697 24143 70782 41034 72414 52835 11793 42924 27717 77240 15363 56443 60756 69439 10907 8252 28507 94446 32844 76465 86752 33999 69837 44156 21805 37599 33643 46659 85096 56535 87661 75971 8883 95478 73734 33626 845 17519 83757 9951 20867 84805 2769 53702 15215 85953 70891 4809 3400 950 43729 38241 98958 42762 10049 97819 44663 92405 15165 61004 37475 77584 98359 25441 35418 78197 91224 73545 36117 95832 90666 88035 84425 24023 12543 58978 35351 81716 43782 37983 71647 676 2870 12148 89909 75784 42088 10479 61393 48012 64544 51756 34148 49981 39850 66185 22265 1443 79299 74985 18222 52898 4210 13721 49818 32201 6486 92140 25239 61559 24565 54482 70564 63352 29204 73569 84863 97289 46158 36788 46731 76301 62302 7790 74273 28717 32697 6299 30640 62348 61589 25266 80953 79541 68046 25453 90445 3328 17575 55250 96067 8741 50687 18368 81322 47742 59561 76426 69722 55066 28623 43254 31393 55823 9332 36552 40418 79244 87427 81438 86823 33621 16848 81245 4056 96076 6036 84145 76907 44543 63066 46711 19251 78142 40121 36586 1069 44854 15981 38898 71234 96367 97685 59181 39446 71027 68745 13782 94182 63679 58587 68299 38968 71834 51065 22217 60496 89709 53118 99687 3668 57417 74721 27 45015 97495 40263 62807 58713 92839 74804 34307 24110 91234 97334 53250 48827 81966 48392 13690 77964 70160 27804 98905 96559 49227 37997 38147 94813 80240 78646 94620 88197 10581 24121 65516 30611 55942 73919 91915 40810 31158 52852 4667 90824 35517 4273 16003 1039 22944 30631 39416 60687 45297 46296 85310 47200 68877 29906 7091 12149 88967 70417 17606 88153 20803 40147 312 1434 65023 65297 17789 66792 64443 23965 83693 96877 59317 70730 77269 83985 64559 70920 998 82014 83562 29501 54042 86822 7273 71566 64974 38510 94774 21822 31093 34963 82470 58756 41117 97826 81016 81596 29831 40526 1611 38012 88564 21827 30099 81880 86906 79548 19367 38794 38689 94671 62861 24077 43361 68155 26106 71653 80090 51711 94530 16599 52652 64024 43905 9296 2104 55146 30832 21340 12095 21922 37043 60076 3440 91779 95984 82268 7223 39749 46853 51598 6850 52500 28268 51367 48411 87863 91798 54041 37346 40577 72531 66027 71073 98719 34236 91152 21483 29000 74582 66573 69270 6665 42495 14672 69500 84815 3339 61777 52698 66056 13121 99875 44019 86410 2732 31859 67646 71860 60220 95259 80340 61867 77509 72551 65966 528 90411 81409 34081 84420 63332 48489 13383 38402 44396 87359 11978 83380 19667 48586 76630 77084 71232 82330 40990 41212 79395 42454 16055 33114 68976 1739 36644 84485 12149 51198 88457 71370 75618 2291 40203 51818 76070 71418 19538 46432 25338 53295 33468 99294 36209 88208 21223 97498 40291 46102 8604 41385 8361 74400 18960 55444 67878 51579 98687 90934 62446 95611 51088 15750 50653 58064 47193 77121 61430 55754 56736 6409 23150 11115 25197 66085 77055 69550 20211 49077 6948 71351 35465 77608 66732 97231 32330 52647 6530 11515 42303 5165 69825 73431 80528 76454 23773 98147 14783 52776 10054 34488 28154 3405 1532 19233 61572 93020 71845 576 80331 4584 22634 94372 64023 92844 86598 68096 34954 79515 64146 54194 50537 65604 79004 42374 93871 56389 82745 9501 41685 63243 89788 19532 30752 7349 75901 28667 85637 6696 32004 60464 44294 27909 33118 34319 78462 75952 2432 81047 15250 6177 26533 49495 23879 4734 81716 94669 74995 59378 37886 54294 97033 37929 358 30978 33172 15767 45469 4822 22106 49495 12246 59858 59196 26198 65875 23867 66595 70695 17536 38993 17696 15274 39470 97144 27563 22024 8932 4072 49461 16073 92401 45075 45700 19118 66912 34945 95792 33697 82920 30651 97094 38414 96259 91453 41990 6681 86645 14953 76581 23483 46896 48133 16037 36238 9056 94430 32491 72628 19925 38908 9985 816 55112 19649 28706 76289 63407 52873 96226 59210 20708 84615 40793 71346 26767 16687 4184 25025 48714 80895 76248 78019 33013 66906 91809 81100 96752 76122 31869 87020 84924 29642 98997 37504 68826 66824 31443 29972 73654 23893 51611 68597 15590 14813 10570 23103 88269 91681 33566 74723 32045 67767 18884 28017 82779 5126 83907 91580 45979 34293 39829 24162 90398 24055 33012 17005 89627 46563 34033 39059 1347 33731 30454 33083 35985 97477 8001 12428 26573 38981 12592 52822 35914 43727 36439 6536 8021 42256 93684 37333 42083 323 87870 2718 3504 49931 38027 69777 3985 48783 87694 70164 65950 79196 61316 82312 1001 57800 36442 95977 53445 99234 58694 43741 78052 58444 48690 75911 9819 84081 2746 35436 67999 48417 8421 4739 34132 18860 76348 23539 66653 78551 83771 60558 28296 1452 94906 96425 12208 82957 81237 13722 50141 13080 21176 36351 85786 76677 54918 94028 40204 91492 37663 78127 36605 99130 23290 57648 15408 72343 77484 41226 67650 69345 29975 42165 49317 39536 69166 79809 11849 3045 41781 46904 63290 38186 98173 64899 25135 12880 3684 5365 21831 58087 58500 18939 74766 14470 5516 91698 59814 48044 64132 18064 18982 49497 95686 92310 32568 81591 56112 24442 97334 60840 73606 15113 2069 51319 59849 56962 24123 51577 48610 83545 65733 15615 27538 82400 53765 99969 15988 18699 28142 23219 48055 86130 50319 31392 70831 22537 44615 25804 79804 65207 68126 60490 87968 79433 26298 41287 21463 88757 46704 31818 22114 7349 65414 68574 98498 81022 79956 6364 59153 86062 25183 13866 79607 79146 46828 3310 16388 41604 5132 81038 14925 96833 76556 9102 91308 8160 43358 97194 39246 74545 71737 72463 1664 28621 22143 33802 39695 19851 30776 85682 23126 51919 9521 74212 51651 3007 83781 6183 95073 73617 75967 62845 7432 9773 35464 85316 14301 61969 85252 71830 9191 18364 92651 23867 77847 94304 54049 33368 92049 77718 56602 12032 66524 5100 56055 57273 55705 77472 371 34150 22914 66068 65710 11227 43401 20951 51657 65771 42425 35813 6430 13974 63804 79866 68604 12830 15415 78654 6778 49758 21595 38604 87124 89684 38055 80532 94807 13999 37700 46828 60326 57398 72728 27373 73110 77291 65274 52532 68358 11584 1491 7561 64495 56203 15988 51148 86004 64356 51546 90426 18375 49949 51056 21536 93924 81342 19668 30304 68528 35763 99762 49105 25150 28485 59216 28374 24400 23202 38835 47143 39148 96329 48149 28089 69677 27114 95945 47580 5870 23996 36116 79518 3013 60703 34567 63882 14517 48852 67442 9739 64921 54951 802 13147 54995 42342 44848 30989 81799 59742 54276 63001 47775 70073 14776 75851 62209 36631 69841 34127 87329 92607 10730 54688 32918 68278 11333 53397 47740 26889 10332 4227 58867 30730 53936 48827 39315 72328 90001 81466 77472 94920 64497 81651 30055 16316 80556 98923 77600 96378 97121 87433 5767 54448 27105 62418 5893 64898 34238 62642 35958 39638 82358 43432 4897 98950 44125 83622 71461 38906 20705 58664 34386 10240 19021 45423 17985 31689 92989 87833 19276 24826 27803 87041 96854 14343 40589 57430 6986 95922 21671 60303 35754 35016 26094 66973 38160 36521 97677 4209 98259 4742 11413 11903 26881 12519 92276 90752 26042 65593 29412 91158 46526 85830 58127 29077 13465 41642 95615 35172 39004 36735 3568 57810 46329 435 13837 89376 2829 34706 56572 77133 28839 85122 10392 18374 59600 55316 65921 1516 41026 52883 35178 51724 28129 68718 411 33879 26875 66729 76879 28962 12610 93664 94472 63255 6917 63028 23693 39585 82393 51065 87904 17945 79791 38351 42985 61111 45602 66194 83417 91321 24722 53697 66799 62784 96554 98045 63954 58703 92426 76952 57633 86163 14202 81747 47466 52457 15382 84148 38400 62350 74722 32767 14790 35269 99515 97311 5472 95175 31527 3005 8968 42303 23220 44270 77382 81174 13572 15344 55376 2767 44075 36575 51769 5742 94279 81272 74743 78378 45006 228 14956 2767 37204 79845 10060 1166 34325 17448 94329 41553 14320 69987 34898 82998 52702 4494 20798 7937 21985 18447 10486 31808 16873 60387 72855 58348 33501 8307 31135 84814 97789 78977 20492 38548 68496 2690 44120 1924 10496 56256 79407 37464 13250 49819 93786 86904 61360 22518 77633 10368 48720 77513 7773 53069 56874 7787 48878 52870 73245 75483 97075 96360 79677 35645 89144 18281 25783 236 22761 90179 82910 75024 62771 93033 33885 94234 66942 12335 11197 535 22866 63777 19813 17562 74471 48341 23623 88005 64592 16173 81900 96571 30356 32496 46421 38477 37407 31041 17784 53948 19918 80127 65985 89538 31359 71753 63871 799 77676 62799 45600 33548 53360 48852 72864 26073 51653 29912 76652 26380 49287 60909 66509 72049 4719 71979 60905 41275 5690 24750 60177 96760 66053 11806 56387 26829 72861 6258 13318 76153 75731 49872 79700 77535 81921 5736 21511 36333 17772 39165 17089 18295 10108 33245 69990 1734 31459 67043 5222 31558 73643 89227 39203 27010 3895 77110 78574 9151 50304 28183 16764 95116 61178 902 35003 19935 31901 32258 40695 62679 8020 97048 45062 93801 85020 92477 33990 3490 54068 36039 1486 57015 71583 79672 74944 92537 71200 67083 81623 14058 5618 21494 54766 8766 58872 78951 23745 94885 58814 66343 80873 3856 39823 50961 66115 18258 62052 24752 46038 44101 63569 63374 97686 83875 56111 91983 93002 92513 77192 27134 62013 25166 34909 79118 83553 45480 94932 24022 61560 23042 92308 98111 59545 30321 7313 2879 24084 1179 42284 27615 28978 71222 39209 39745 44337 62698 12541 86904 55387 81575 18352 39158 8951 55500 26544 44089 84939 18064 91045 10208 63695 59308 62197 16560 90950 62748 14561 2711 41386 28524 11752 64731 74921 22721 64057 91501 11383 19227 25947 76853 81562 94043 95920 27210 82310 56140 26456 38565 69576 76902 45611 82792 50586 40214 14791 43274 52522 32301 82556 29076 14755 25911 34910 2538 87222 51197 88424 97263 76589 15767 19403 88793 40719 86421 40693 68100 10468 45954 51512 38341 46513 38519 44367 65434 24297 81168 59559 53640 90836 58123 67737 8766 54536 50809 81802 97499 59334 1293 32123 77218 93678 47217 30839 7747 99540 35928 83019 99622 93179 82840 32732 88992 55727 20736 43714 69504 28826 10923 82918 7085 74507 13729 17881 42141 20475 2102 69057 50724 32456 98439 64681 92193 28004 29394 43287 18282 3803 73667 39220 23001 88606 21501 57369 31086 37004 20786 51403 52495 58404 75682 17127 67900 61382 46371 47240 69734 4413 83470 81518 22131 90889 24652 29550 39823 76762 90032 98115 40789 36467 91800 85161 57088 27846 23442 16114 3932 28165 50839 99738 77866 99711 2131 46833 51244 85606 52677 14444 26014 47910 22031 71570 56197 93082 67354 57759 97136 56892 17362 79719 36798 47560 84821 62653 79482 35954 65768 52466 35758 19070 53011 48914 72054 67728 10828 61200 7211 20851 19100 79578 68054 97778 22212 71488 12605 17417 92789 85422 10978 13967 49839 78210 96974 2864 94903 24418 54434 55704 77529 30161 5286 53624 38314 34853 5963 45319 27099 75034 2638 94165 51130 37942 90657 85129 57628 76691 73913 15853 87121 3290 41262 40810 91750 12418 95138 32817 32470 7454 52504 4354 34477 19554 1884 82932 94899 72739 80236 8980 95868 14423 57023 71974 55798 15454 61579 22239 85830 84462 55507 54105 79412 81319 97962 73237 3740 15245 60943 67626 78854 80694 10705 57414 724 89636 73707 47277 58338 14043 88779 45198 78182 98363 38853 91251 45517 52746 4698 4163 39944 58655 40658 36665 33394 45712 60993 83293 5339 2565 5865 76042 83665 83922 98424 42739 20227 21087 66438 5543 12543 93500 27996 57885 70324 96941 32975 20489 96096 89188 84768 21027 78589 51696 45792 35965 21257 16898 5726 82014 55605 83936 96764 49837 17248 75642 2063 26214 48865 92362 96123 67487 94232 84291 84826 81713 30828 71506 69177 26195 26379 67737 16244 60009 66035 57541 66234 13652 86863 5551 57855 64834 65670 49471 51415 9775 34980 15654 28377 98397 16234 55918 98660 544 26243 85343 42394 14712 5637 39762 92008 12147 27929 26736 96095 47140 37485 90723 30614 22921 50431 33499 31081 4492 47549 10315 59263 68136 84143 36550 30151 61110 83537 92855 31755 32718 57832 53340 57099 40358 15980 70161 17929 74703 10939 56494 25136 61446 92438 55968 14366 503 74874 99688 62511 32695 37119 49524 64617 5193 21877 32804 96781 15553 79269 10137 35442 73349 70758 30154 19711 47037 10253 82566 52584 62872 15566 31085 39306 5862 1594 43030 18975 7808 23789 41342 26171 29682 73419 35578 21378 20324 3256 47258 52318 11236 86992 44823 1159 5110 9500 42662 65122 88514 95888 71928 16074 33308 20637 34068 57859 89082 82164 27127 16504 7021 85083 21709 56428 53873 18963 32678 30589 46001 98061 28240 32868 99809 35434 2164 93455 85382 93384 88024 38294 77549 72712 40361 69466 74169 40837 29518 68869 46306 89374 43307 53544 5375 53353 47290 90578 37251 96916 75541 5720 49560 43175 39234 97078 75638 27857 7325 55580 80626 29329 74715 21416 9200 48403 70083 96119 64800 7449 46167 63162 28964 19467 25476 35675 17808 27198 38065 11729 89007 1310 86517 91366 47718 84795 89004 31112 46217 39073 35415 42209 86138 31598 73769 23625 84035 91954 3450 62111 36187 83355 86545 8007 99604 85024 47843 66417 54012 61105 52288 54190 90695 70427 73580 52329 30903 38841 35254 98139 40268 63923 22679 52988 63857 27011 31465 96945 81386 44212 72166 74886 59512 64398 36312 17028 46940 54318 29948 83635 18845 31544 85734 12027 20405 7660 24033 53069 63220 38467 53590 66399 72384 86466 20953 27117 43136 59263 86008 72489 98070 95977 8944 18346 68 8684 64057 46761 70641 33307 18772 43140 49857 88968 39859 78284 58161 50467 10782 96834 94782 52840 12381 53556 42050 98653 92178 37237 85486 83641 15866 57796 28601 64052 17541 17504 33738 35874 92909 91086 97312 42913 97388 52189 33048 81193 51403 63370 4317 87550 23728 69702 53857 57722 26913 49673 24329 94178 81431 11695 35102 60569 44378 44866 73434 15614 60556 24032 94699 23074 64018 69631 15235 963 29994 42594 50233 54383 62952 58158 49106 90324 44887 2722 42603 18445 38567 19691 60808 63231 32680 80034 56114 75563 76284 99747 1854 63161 57671 13313 67749 67564 72991 68024 35788 9106 9123 35712 42164 51013 49324 35777 71507 86629 53030 8395 7874 34822 94077 78266 81302 5197 86096 55292 67344 23111 62950 21421 58559 706 70208 34637 83267 60445 40428 76151 54900 87732 91632 95246 60810 59493 6873 44916 72008 73621 9511 87921 70639 36823 20320 81825 50990 15124 25133 78729 22908 70641 25407 15019 17759 98452 4127 12744 41172 45491 11229 53365 35643 63479 56815 41094 48370 66048 40308 88683 87608 3762 45378 89300 91022 5570 61883 1871 40692 59125 41106 79101 90342 52661 33161 83385 55180 64042 20300 80683 42496 85661 91805 74133 5093 22908 12987 51269 29331 5781 89212 14580 15375 12806 3444 36268 94582 59993 52211 90899 58836 35510 86909 47127 77669 43104 41341 77621 50059 5118 43803 45555 24479 62709 16773 73845 83970 41083 19430 93136 48752 61528 41325 21054 685 96632 5156 39792 70093 56559 75104 93726 17752 36576 28026 93578 41974 49241 36676 30933 6562 18769 62865 15284 14939 15966 59517 57754 6344 31040 43476 73970 60512 5201 20257 85653 94181 636 1843 84987 18363 343 7109 6796 66988 91729 16098 40668 72036 59710 16965 97745 64960 71455 99794 31271 92931 83970 37609 49761 48498 10098 44445 28144 74846 18736 89368 14233 13401 28890 60816 67379 34753 9380 54289 35476 56304 89674 55389 64875 46422 33692 66020 33116 94198 75824 56727 66275 12433 37946 90319 3275 32061 81870 60412 95084 14911 57005 18425 25564 10536 39257 83382 86684 59012 26705 67804 78767 85866 71154 15975 30539 85189 51929 96777 94016 80147 5221 98685 57034 30652 69340 30892 85707 73528 67883 10198 71954 30466 5965 6644 97129 47296 28521 22424 42160 13808 20651 97716 50453 51561 28643 40862 41841 9897 95505 8113 6035 51757 41669 20911 44085 35687 64369 59959 55524 70704 37270 19563 2952 46518 83385 85622 95646 74892 8084 65324 91753 56514 32423 73579 55981 55420 65038 23174 81333 82357 7317 79205 53910 71521 70927 26205 88735 3488 1724 76130 42534 83419 94020 50452 49293 18871 64250 96813 71441 4790 69690 36182 24452 16000 88430 97809 38817 40357 80466 80990 61926 66947 7385 47523 28823 924 59536 19837 95039 9937 41442 63429 50966 388 67819 91982 48127 80536 28404 25441 87035 18700 87859 10984 66302 95096 87089 18530 7966 9567 7257 32716 14721 44653 17355 60428 66762 87293 72088 98624 77614 93458 40452 45215 14869 96625 18495 5407 8636 16832 24960 50035 94944 74537 41952 27191 93363 5082 80065 59788 92411 1703 7647 96663 57 22466 23468 82945 50526 85810 87121 92751 86309 47241 32612 19722 20420 29546 59581 88604 45834 20777 96391 77322 25684 48595 93080 26487 10584 16432 3943 47078 5734 13179 41759 89601 45967 5569 34281 28827 20644 77607 97631 79099 54382 84694 4908 24703 23065 91821 31183 74499 70110 1773 24325 30253 24012 84525 38541 23784 30697 86465 44538 98462 94230 13684 87529 79439 24655 71507 6311 15682 70754 58446 1920 3776 36681 48130 71065 26409 1229 89795 2605 73309 90586 12772 32156 45720 2777 57128 18232 45245 19947 4742 66023 4346 62704 6093 53014 25339 19901 70700 17271 45847 16496 21542 35833 73956 99802 95369 23070 73467 38446 12075 4638 30646 82185 35860 25049 30214 71290 44483 90930 2055 93467 26227 59385 39249 89294 84360 82059 5120 12037 55595 62685 80113 35246 61181 64838 46026 72515 94386 23070 11738 11380 93367 48950 80832 73910 23035 61300 58875 28782 93585 91838 27723 91121 18159 78952 30394 640 66054 68488 65318 71018 63522 7249 28251 59056 27612 51801 32116 76376 31180 36885 2055 5167 73297 85415 23984 89759 35716 61661 65475 44663 44235 32761 73649 85161 23533 58408 37767 53290 22118 32452 90407 6566 24856 11508 80350 15019 34603 62462 99355 20283 92017 5343 56455 49949 47924 65833 32531 23155 12939 32623 43162 35382 31805 53954 84284 27712 29861 4015 19708 44731 72515 86484 39464 75679 87591 59839 49825 84001 87741 88909 38849 94251 43100 35525 79592 89783 95699 17551 18856 93527 74215 51527 78624 48789 40383 13156 76362 69263 14438 30130 22278 96205 29994 21758 97224 44667 95630 42240 56332 67794 9748 95017 54696 68363 55206 32663 33431 11783 72242 74871 65094 61386 82457 62775 12151 86905 54288 31189 76421 125 8231 25477 34484 76766 95898 70652 55034 95298 14848 53939 79776 32503 6078 85248 90116 85336 96245 8237 78076 52171 28643 26166 9380 18933 51749 94782 65214 60554 49802 81052 33900 90033 30728 53258 12160 99891 74928 95082 94663 52169 29567 59466 18299 80312 29131 28328 448 64615 93407 3239 18384 98785 35313 49467 80666 28192 2536 12833 43718 32720 67387 79954 41712 90221 9592 39586 81144 60513 54158 17821 96183 31636 39827 49449 79441 52282 21546 19993 49869 60184 52266 64259 40129 27043 10096 49570 26985 85852 41455 90957 2925 40028 33484 39737 20820 92320 146 18830 46218 56807 46527 44525 45306 2683 58247 52825 39146 11527 42745 44278 38553 11339 93790 63837 9146 87747 15745 45158 26555 79966 30773 44964 93270 9867 64136 38349 48569 80779 81895 48676 58547 68043 1055 48756 30178 43607 23955 5408 94416 69426 4606 50313 58113 40156 29068 8479 82228 62456 82510 42819 79924 96581 28024 98299 32814 55140 98314 27346 38485 7481 3081 98985 15653 87921 76321 5625 32307 9174 41689 48845 72030 45623 49884 42921 6148 4301 54501 54425 44115 27648 21174 48668 4406 37226 28884 31500 8637 27646 59893 73380 20974 29516 40935 6085 55797 24369 78592 88786 24026 42428 18660 37404 38658 94920 77051 69068 14391 19971 8433 6172 75891 6765 98354 8431 5789 85982 59064 73213 71837 41069 92085 69418 97014 11715 26675 33031 7858 35013 54821 35225 98885 1213 844 53420 21789 50146 19716 82941 1892 96203 87340 90569 72664 5698 90226 80446 71051 16095 8918 8795 13682 61123 53836 62282 19470 70697 73146 53016 71481 90140 8602 53233 53149 32041 68771 29980 91598 13076 46442 90478 42580 2711 57920 71661 76443 51225 60937 87880 35132 35571 23703 40290 40974 98477 36715 17062 51550 85212 72425 31128 16204 3996 75781 61359 57240 70386 52640 12178 77896 68859 32643 48427 58667 43710 30486 75943 68127 8785 18387 80336 20071 11495 77129 79602 70762 92837 38616 72674 21691 5531 44681 69548 12003 36439 6432 48329 65876 3133 68012 1833 79711 29528 36193 76490 82615 5306 22834 45666 60006 10690 20095 59115 80007 16961 28027 17150 91485 44389 61714 64386 30578 66301 80480 99345 86346 53309 85155 99698 95538 48906 36486 70282 27954 96884 37660 12584 94120 21866 97228 24850 41896 38040 4343 21026 30988 63431 43360 88501 76838 23671 50068 66530 91328 25019 55877 92253 74419 39183 75277 95185 86166 98063 2630 62808 32070 51716 11787 84264 91383 96699 51844 24657 66257 64694 70648 90717 39342 59302 92116 69676 24537 67896 87659 66204 86612 79208 22532 49766 87535 88322 15470 24202 79073 50624 30205 73872 31148 54356 90973 72270 27149 33855 46903 92901 53783 44080 8912 23320 87810 68167 50575 88255 20136 32865 52905 68292 52740 32375 63687 93751 43720 44828 26060 26295 60705 76354 16263 8997 83099 69819 8837 5919 45896 15361 52587 69025 33818 65516 89671 75777 79049 63414 3728 93887 66187 32244 86758 21126 60189 48903 98924 38828 23165 41946 75253 80024 79450 85663 9584 3667 32270 15802 92871 40554 45794 28195 48601 28731 38199 97508 36780 95157 24985 50953 79227 82671 98084 66025 99259 93365 68102 25425 54611 41460 60211 94782 54502 80634 3365 12260 27603 5620 23815 64638 39937 69543 98918 27839 92393 29601 2965 19406 55973 41133 76388 82441 64665 11934 65362 39755 83382 47078 34542 88794 6726 77628 32745 65311 54062 16753 63559 22723 11763 89880 40643 28716 3738 75564 11314 11002 37090 52017 20911 28255 37900 94637 71238 31455 38265 31265 60967 34631 82016 9006 34043 89458 73841 52765 72149 14474 30544 31964 25003 15701 70088 33462 63749 80511 87854 93313 41037 93250 38195 94464 97683 84246 11311 87879 9715 48334 65908 80164 88739 50352 77070 12615 92968 90847 30783 73671 38785 55904 97694 75130 54414 11207 70900 24156 49654 97564 29637 51830 49722 43176 74539 58556 67869 31341 95828 10887 34984 63640 60040 18052 30837 94108 94289 87913 2618 41200 91477 25740 41094 16825 49475 15358 73196 60556 22898 61660 85165 22322 51999 50517 47498 84736 28259 74261 7390 26085 88651 55944 23377 5054 27248 62700 11880 99458 48023 53499 90838 975 36286 49341 85170 36768 82515 83184 22353 95521 25104 71557 38310 46084 4242 37528 26389 38878 59820 62662 76810 97632 54245 58807 10054 20529 57933 49482 30253 64632 94094 28145 80942 43648 52685 93122 66061 311 88606 70571 42356 62410 37345 6074 99782 62882 77144 91459 79632 46530 12565 89186 97851 59466 6339 52615 22614 7831 37670 9533 18865 63587 24520 57407 3051 44144 80389 27486 53421 34873 30624 36202 19385 3005 85174 14497 79666 87145 98909 61224 61283 89257 57382 97504 17636 22028 84766 71529 76605 7474 83963 42398 58808 46937 43149 31356 64387 65228 27180 80606 33004 30873 3133 85439 20684 66674 99799 51257 28294 64321 25298 5407 14019 38956 96203 15079 69108 81386 43451 75260 17860 32249 60113 36241 33267 61 38019 60912 99059 39853 50277 48190 70323 97910 8193 80000 83562 11401 19846 19675 23206 58766 8823 14126 67050 42975 94967 82618 746 47451 73742 59851 19088 38976 12289 83600 30950 49081 14486 753 3632 54341 44034 61300 3383 75732 63481 36785 95851 88435 1228 60055 97763 97035 17572 260 5209 21383 96379 46505 74815 62376 11272 17948 48060 50274 24497 122 10937 17585 59146 23677 80362 14183 46266 26028 84605 6285 53831 80813 19920 94838 78314 73859 9593 51533 40830 19651 31678 50889 78860 23579 41248 99722 43785 49178 27053 78117 51497 28226 22633 2722 14627 97540 53395 38500 74304 7095 78905 43650 92701 53068 97626 87624 92170 37936 86912 79833 58888 69308 31623 49457 17144 59604 34526 59481 13513 69546 43206 66677 40159 10013 70849 23782 11348 90043 72400 92179 70636 56614 10701 30175 74414 54528 75430 7742 6888 6960 92229 48073 92301 46991 58805 30024 46642 96292 60031 21617 95883 62522 145 50381 12672 16590 15947 25 10470 34820 20182 6869 54576 46166 15971 78317 13683 66135 7714 62893 90812 2239 46367 67914 93569 4566 66364 78171 84850 10934 31163 86756 21338 19594 34003 85716 50912 30535 46097 66203 45764 84824 16479 35509 55136 86984 28370 73129 71003 92506 50679 41596 70639 69741 77602 47004 64204 15903 70459 71248 68801 46647 92180 40422 25590 86319 95505 27853 89033 9712 82393 67325 53349 85522 40433 167 55376 44918 85827 33106 46230 63840 38506 6811 97699 31463 13107 49781 35559 70299 81564 11291 74481 80964 41039 41917 54194 7296 53607 87611 71281 19935 475 97552 48966 94391 37783 94059 49059 56275 59908 11520 24033 20509 51615 34124 57599 43212 22412 42104 5980 267 92625 67999 25231 84095 13707 76346 73688 3595 59767 3643 21641 2385 51920 26317 96245 69012 63029 82326 98155 34485 24564 92351 27475 53192 45514 28673 42644 60911 99403 40173 5987 76633 28948 52234 83072 98586 58809 56515 87242 4819 4091 91431 71106 15665 42432 97022 90401 2027 42037 76172 62843 56590 57640 45669 59362 14638 46560 92015 55894 64664 64760 50875 87129 58064 67869 72755 60137 65584 45435 40578 63771 30220 63255 937 6773 60029 13614 9327 98612 75373 8446 38404 44870 11605 88890 95310 32828 23697 71902 38817 84350 8493 73294 42723 94638 42293 8543 90485 90517 95215 51703 16805 38288 80292 84523 35891 35241 69489 88920 68928 5191 92931 58766 5464 2222 89294 90431 81423 91445 54869 85248 13294 16492 3908 90524 75056 33447 52386 83294 26947 86644 68319 40861 38364 18054 84129 2862 41338 87907 11979 73665 85652 77040 85269 54190 5941 20047 85427 68549 74077 13099 50297 73730 87397 90889 78678 14154 61937 51855 76545 94348 11073 81778 22445 77508 11800 89205 48667 29775 67754 97297 38001 96348 40656 55827 96187 45767 57841 20264 25944 44111 80638 2115 10886 96503 73253 63729 52429 69209 98097 33072 93262 69560 61090 26123 62526 13069 33373 45425 10732 97560 77928 4297 98871 85551 25625 16855 55042 16879 6745 20589 70323 83416 91047 23606 64295 81841 20609 99411 27344 72149 86636 21206 79739 24401 253 62829 32186 58903 56424 91914 72637 84369 96440 56293 27952 96096 7201 94279 16502 13947 50602 4414 40542 97627 45997 28840 12902 69707 7304 93685 71064 99169 93971 89011 44950 57274 50348 83245 46911 15742 66248 64136 63000 28341 5637 43658 92816 23151 46933 15864 9032 44411 73015 95485 78652 35321 44332 12549 9425 6600 44942 18140 1307 89531 67182 87542 73880 74622 36764 79831 15792 56513 25683 63881 20433 77019 45829 71071 1452 7848 58904 87882 70375 41861 72480 49192 32429 29459 78295 41955 7926 87846 3890 59040 15753 79986 84083 18781 4421 40984 38240 91251 48304 37977 17444 20930 93575 63240 6673 44609 5513 64725 19131 28139 2541 14270 34498 37298 46812 63494 69980 36931 47334 57973 99453 20613 46331 35147 38789 39805 82014 34700 19765 37374 45616 59057 78984 55161 51917 62673 543 38770 31652 57574 87781 59828 98271 96635 68930 51786 10253 87300 15185 69943 50492 68727 85752 12298 49357 66525 6493 85540 10400 14314 55145 68322 93389 68022 1703 15723 55857 5143 9745 29769 27429 37845 66023 15085 13809 82922 45912 52976 71250 90957 94693 19178 54326 78968 97528 15197 13852 63513 14496 5303 8718 36158 33050 29520 61296 1502 28687 23256 40153 44766 41580 15624 13522 81400 45133 59478 21603 15045 85005 18169 77035 82447 86562 81220 3183 52644 21946 22043 84210 17835 55236 26317 3803 45645 79264 33798 54791 85426 57625 66537 45321 93916 36662 83266 34578 76211 80558 4492 5473 1794 56548 34057 36824 9054 3836 87902 20855 18475 20800 1240 12879 46945 77852 88969 75661 85066 79874 19390 80620 8293 79299 69922 23752 31028 94513 53551 31710 43721 82619 37809 41389 2960 78977 62772 78801 26856 88938 22560 15980 51882 91533 98679 19012 12218 6017 28120 60644 11026 15150 77888 51929 27299 35317 81944 62844 98619 36971 37839 39783 37267 61286 78795 21842 96812 12410 53685 27968 36392 86745 75106 31168 4597 15332 75379 21260 39821 87355 62836 86323 80280 29564 19662 4017 35426 77263 47003 24406 37279 11986 54655 534 84985 59232 69056 27975 74048 29998 81682 82010 65251 2743 89523 2387 43596 91070 77292 84179 67586 59760 45555 85798 81666 16343 69796 90231 90699 5 1944 11914 95538 59128 40178 59515 29658 45305 78474 56565 14832 90044 94516 67512 59825 78363 54305 88898 27699 20840 66237 67021 63970 87058 19399 20765 70540 39501 34946 67358 23159 53535 73575 96698 67330 79614 1839 88886 72649 31392 24154 2835 80693 49208 54250 78291 37968 2884 48529 17027 96942 5318 61454 54525 71826 63561 64414 88777 66858 96060 67516 26948 83120 74542 20005 74034 58275 56186 1417 71083 62731 56926 24872 27578 2925 93510 28088 42315 39059 27385 16342 62284 47873 77390 90762 94530 94657 24974 24087 82954 2917 59411 11497 14764 28181 54728 41265 82433 55264 2093 73910 45164 39532 78478 6221 30101 63475 97380 44656 34762 56606 50683 52385 11511 78971 29116 50074 88919 72598 92391 87427 68022 12089 21915 80220 69873 93841 37088 66855 65744 39074 87886 91436 10963 53138 23026 48168 85084 25620 64219 68558 24229 36214 76871 12494 51785 74968 10408 40570 90143 9634 9658 49296 4138 48772 4791 24868 74918 91541 27108 4318 7116 78546 22734 32780 74905 98531 40338 63122 43159 81603 21466 81654 87940 54698 21393 45949 43653 27501 21918 73192 84058 93648 11929 75516 32158 68814 84738 17774 40281 6953 32413 37606 68687 83735 49180 29535 11380 76021 84398 18557 90239 34838 31291 41095 22000 48919 45766 62555 78447 52449 62348 89401 23401 54232 86692 54548 64708 99284 22935 91289 83107 61342 81817 13830 89018 25448 25246 14204 60930 63238 56677 42398 96068 9420 870 16240 19430 95065 59033 41559 28844 61367 53042 55682 26804 3523 22543 38971 9809 44580 36877 78537 56937 54787 73517 91733 73892 15179 16242 35375 89740 7733 78217 92615 75855 36400 2433 73238 83776 36229 19540 12907 88003 35571 89223 30192 55700 53487 18669 90255 35748 23265 98078 36955 26610 33760 40314 41363 98998 94536 87709 30008 89389 13954 79834 96091 80162 93780 66025 34506 70267 28042 35708 62960 96528 41641 85471 97274 91370 72420 76543 5273 98777 39599 60525 65000 27115 3974 18202 23318 41900 62857 84366 23122 85655 13429 76061 33937 22628 77609 16050 28489 13328 5243 41428 98821 7851 94276 17315 88292 97756 71518 81513 77699 65857 65818 94177 58166 82418 59759 59989 20960 95934 85309 92482 82904 55488 30014 39149 27516 38357 33805 40216 6224 91344 84744 65112 18415 99239 81886 92229 64777 9057 306 79338 60493 40921 66219 850 42997 20065 13747 28120 2600 31411 12054 34093 60769 24000 93052 32427 90663 414 53304 80227 48520 83873 1411 80136 3932 51935 67848 67725 53595 74313 33809 55031 49779 78834 56582 10294 73960 52933 42285 51938 49081 33990 77883 4737 54190 78416 56166 41072 39820 48571 66403 18101 76041 15825 85963 10719 81435 83286 20997 93744 36587 12646 5754 20186 49057 15633 76210 27476 82763 99508 16627 94307 61547 90716 1586 43126 51792 70944 29191 53126 33532 90562 17163 86984 61774 16574 43195 88174 35596 6603 6986 5778 33390 47370 82969 2338 88201 80554 69503 68637 2435 26521 95977 15456 91088 56471 15110 52068 13278 81964 3959 75077 92165 72695 21257 33341 76111 343 86943 49244 19025 32306 26171 26688 38126 77502 55135 68559 48690 43817 92294 28810 97842 24533 15082 64988 99137 88940 23358 14919 1105 99403 25776 41332 63932 58284 88787 7353 32031 5393 45876 93699 89498 43616 81778 30211 35255 47594 95758 52205 38862 3642 91005 30641 54362 16889 19093 34525 82384 27878 24975 89763 29006 83811 16986 32282 56863 87067 64511 93302 26046 89699 29708 25218 71482 62312 14331 86267 25322 69829 93097 93143 82510 92723 26079 29795 86395 69932 63882 75045 76467 16591 8514 96296 86005 82259 66955 41476 4518 79321 28775 37633 58887 46603 46211 93497 20590 66915 32971 56501 84897 7060 45416 58985 61860 31956 55324 63758 22634 19189 29685 23539 53343 94359 83331 50807 76831 17950 59413 49992 54430 92532 55901 76687 61689 34343 60781 14065 65239 65012 33126 51547 75856 63227 42547 65765 76266 98056 77859 68751 23926 29384 44879 74672 78163 85745 95206 78 97467 53778 56802 24460 82173 41186 90130 36822 79141 97317 22950 62276 61500 15895 36729 73509 88618 32596 87562 73819 37556 57521 96474 37173 23527 31640 74767 37768 70523 62207 24473 85618 55160 16327 31240 58682 82609 52704 88697 84670 52610 13019 80318 58627 30574 96955 11380 68646 15619 48847 19743 96606 24026 59090 7983 94253 32865 20574 70554 43581 9504 53051 1732 94907 55260 76119 91124 11975 66640 28885 13013 37218 62239 55721 50192 85365 5818 56870 19833 68012 48397 24391 63336 32970 37500 61973 28829 13736 73210 71967 53662 10050 70270 91023 10841 14774 33094 2881 86749 47489 3774 30173 44773 54414 9218 38649 19745 8472 67242 66057 66880 72032 58564 39265 7975 69608 95568 57290 77486 46125 24612 67652 43803 91261 72713 97572 55041 58952 17946 53010 17841 34609 11719 46646 65283 73949 33102 68893 62279 77384 24993 26639 10858 12902 98977 94753 11333 1293 89842 49099 43023 22913 63986 38984 54158 99834 62387 89585 92797 11314 41592 11277 48344 14315 58796 73700 51286 32100 52641 3282 62656 81649 12973 51186 6578 30728 13164 99591 70503 87887 96991 72500 10003 72152 92761 3740 71517 43539 30589 96930 87559 44888 63550 4913 42858 35495 5564 91930 9946 42319 65577 81460 28966 5441 3890 37835 69634 80549 39452 5881 4803 5927 51286 65451 32550 23664 94571 30027 20121 37945 83742 35769 83158 84699 46242 8982 85277 54383 34840 14613 34389 28562 47534 75215 38729 58945 11543 88914 83583 29875 6507 46670 52972 49669 2093 77696 6962 9208 3297 11801 74658 18496 13064 59775 69074 63135 57289 6163 43061 54990 62279 86398 80434 28717 55370 51968 81444 24192 87553 36156 71216 8833 7800 69888 30321 62075 23238 38125 18063 71482 63938 83116 15519 46269 4177 24357 262 76203 59165 2048 25009 37851 96448 15166 89924 41316 66853 28771 73069 73589 61971 64800 79523 12766 99726 26678 97840 25292 24539 48428 46882 46863 67089 12370 89361 17631 70914 15883 5790 10636 40487 45633 4348 63611 34964 74184 15659 14207 85597 33962 2039 88970 59173 7743 61633 82861 20317 55566 71809 67969 69248 32859 29735 72363 57284 75986 194 69144 16776 17873 7315 96955 92013 41450 56818 70351 33385 7157 42201 77349 13961 90974 52104 57595 78337 54471 58748 64437 89088 66904 57913 36528 26324 76708 40468 18734 7347 51504 6939 68827 58815 61478 87146 37422 66800 31083 80337 47796 18179 61972 75391 69879 78303 26927 78372 41591 90900 93711 7668 7980 87950 40052 89548 83250 50243 87929 74122 64398 73457 66082 63200 23826 68100 42932 69765 62650 199 44521 64232 61217 8207 23358 36084 21149 39488 7181 43510 89755 22790 85725 11716 26187 58684 24562 29967 36546 94870 44652 58689 53721 74974 64971 18739 53745 54473 28083 3254 36209 12693 33476 74668 79021 60582 34600 22194 2466 88847 79705 69336 6895 75845 56761 39971 25685 38742 18686 73472 91549 95664 97859 20741 7788 83205 97012 62800 44456 10733 43601 69106 87091 79953 65228 63565 85088 80563 56425 72804 64582 45410 56800 23726 25971 81562 65521 65981 34765 32293 58352 61188 40492 58026 25846 45254 88751 45452 82980 9741 37465 86824 52871 95029 28942 13687 39845 95305 26187 53089 82956 87350 49360 98908 33006 53371 62492 80809 43747 62395 94396 32965 50386 99307 41924 47714 88358 32545 66829 98650 19253 27014 57912 40784 96068 48805 73574 7445 74756 27716 51192 57748 68365 75759 93303 18086 10149 92966 52461 29536 53501 97210 85159 99615 20933 77385 3222 69807 51772 95276 28910 60505 96539 68663 42764 67948 45303 31959 72707 47069 59139 93023 52271 17824 96792 25115 24923 97559 44222 42787 54600 98996 4041 67431 81646 73863 99386 27750 80615 78019 29175 59287 39585 59943 46829 8635 80203 74715 70731 7821 59101 36640 77120 80027 53983 25508 75731 69681 25229 69680 80235 54615 61694 85040 69857 7734 95933 45903 79576 15269 94374 44225 29859 44330 65729 8990 5081 28742 22628 19803 19163 40089 13909 80764 36680 25586 40523 89233 31373 61379 9431 48105 4017 10132 37514 82134 86547 30124 2766 2622 86543 27689 20716 6887 26162 56225 8257 90454 65210 70508 4228 10994 8984 88619 62928 451 95427 68380 56189 12135 79609 25148 52713 36472 40001 51447 85371 97049 4285 10944 18163 3393 79887 77492 18084 23191 37036 53446 25647 9469 9502 33584 13781 80779 33631 16852 44811 29069 61785 72051 33742 83072 55995 99066 67908 8723 94719 89796 2334 36154 37705 115 78170 75474 20668 3084 70237 36311 38068 97077 46806 70119 32651 87318 42050 4467 30887 32065 74270 95047 56583 53295 85192 54548 77300 71808 70196 93034 45484 74396 87794 71095 21592 72856 84436 81845 7638 82873 53407 34188 39938 79339 36790 19919 99569 72987 2465 15557 74359 93187 23926 62394 80638 72667 19985 30544 25664 57946 14248 8386 24461 80982 22714 5003 9755 99951 8194 28533 543 27733 8649 98688 79444 55109 80112 53101 86777 76546 83961 59422 84720 4987 25945 15662 92176 2234 4871 33854 74012 47030 98123 64642 43968 41047 47257 12099 26770 92969 88257 79455 12249 97035 79003 16643 90299 92030 86682 23331 54560 48095 93061 4583 29046 38200 59805 94142 84893 75317 12225 17835 4197 96687 59163 20478 15587 55485 80206 48031 67883 75179 54818 19378 41 25161 95117 46319 41138 22353 44765 36725 88569 62719 72203 30728 84422 67176 41072 87810 33277 54377 90030 67013 74806 57893 14658 91554 41388 71021 96188 32712 62283 79565 15622 45341 66962 39195 19617 8460 59924 47213 49971 8345 85200 42856 58793 2403 1654 62413 83252 14333 70913 17427 11718 25892 34149 71176 11183 10852 82059 78670 7066 3780 15679 31930 56167 40587 94957 61972 66477 5303 52467 94589 44820 38153 53819 38001 97398 53397 87924 97184 86963 64752 6970 66507 62654 11252 22546 56722 58175 42219 92493 70532 46033 11972 72628 8198 8936 34847 72261 63397 37628 5400 58223 51972 98785 28700 36666 9146 84576 3244 4364 17592 2958 75965 60212 50239 27229 25241 81744 14385 83759 32973 61574 67098 11922 84604 55880 98069 5941 28067 3334 98314 14728 10451 39893 74730 22765 19412 15663 79898 48611 80164 23301 75128 83268 9253 72164 84817 21264 48561 28055 24300 75381 90086 64843 74242 94570 35761 12135 38867 20016 14323 84596 56186 19891 89473 45022 41699 61618 36279 72587 85663 58849 30726 57213 594 89407 45309 62429 68856 91080 93117 86625 61088 35179 60324 63430 5676 95777 88191 27273 546 51872 64840 92362 33536 67538 64637 89234 79385 49273 79220 43477 78207 35455 67150 8258 45964 28996 41670 19554 95916 88315 43249 20883 81911 43956 76170 22438 93673 68744 57074 15746 26758 43730 60983 89135 77 97898 2731 70948 81801 30235 95136 27443 88424 51781 90859 97558 13862 72923 69837 43020 13689 77599 51780 5799 54881 37440 59834 63409 80532 29657 74934 86917 63812 86846 18157 71683 41737 49695 54424 79441 92949 58492 42144 46571 53052 58661 49429 43060 96825 5218 94664 14683 26229 78250 94426 50022 90499 37613 77771 90096 51061 59880 15951 9475 78819 16493 91459 87117 75201 18444 94741 99559 57987 56543 50263 34921 15652 28158 8609 81517 35858 87010 90378 73058 34506 13296 87983 84219 69580 81652 60121 17664 37661 88745 12294 99614 3166 62029 20537 8071 74135 3587 79843 21169 79411 48034 24075 95867 24824 62970 52370 52092 88386 95351 15307 49963 63580 96149 79622 46770 93743 92149 5395 80940 24771 53349 1440 37181 69571 87400 2702 92503 87585 44527 8182 70540 78484 16666 5005 67207 89049 45247 16765 19794 48082 44838 86057 14874 42596 5257 13359 91434 83452 21652 79020 83315 98360 10539 54434 10979 16650 63496 98886 91235 70939 94464 73441 69572 63825 23375 88840 61716 554 3253 47438 41045 86141 78833 58974 53653 43857 3899 46102 88182 49324 39187 43135 45897 69529 78385 35159 26640 42419 77750 69812 36943 9232 8056 63779 4786 36312 14754 56943 76946 69072 85237 19907 40732 91416 39318 31795 82703 57137 84674 99500 30973 97736 78172 1619 4079 57958 99064 79486 58023 75376 3988 69295 22801 80514 8399 28116 46564 28624 70557 44682 93908 93563 2808 45761 5044 72084 45968 13658 53244 42456 32961 23533 80056 57142 88108 60344 69017 45811 30713 73161 83618 2781 37791 66017 44755 89791 48580 42735 40327 84041 68382 92959 79840 80840 98140 99631 89140 90016 21805 314 30631 74327 54625 75199 90891 41144 91188 75720 85885 31589 59520 89725 80067 81994 12060 80562 52711 12780 64230 17538 31119 58941 74801 53361 49173 65875 12441 4016 68122 10843 26207 83836 90825 8020 72282 75360 28722 10042 17687 82953 23126 69286 58056 84768 49381 51255 41711 22114 22397 39684 41269 57695 65887 30135 43600 24390 70670 90410 26836 60952 20939 51773 10833 8669 93693 8341 25408 92457 41771 14035 22630 512 7662 82179 27752 71280 1579 69774 31665 15611 87414 68413 51149 3595 96365 70902 9263 36213 24569 10725 58597 17791 50495 59419 13661 63274 95855 20297 8322 7784 92882 96685 99889 63621 95912 76074 72855 47206 43901 6910 24116 95392 56820 61432 26252 37287 1975 91536 30468 80718 84466 69496 55500 37772 68616 78253 57382 37030 73432 98885 70951 19333 66075 1550 70039 58054 92378 79747 21761 23883 96330 51769 9776 28153 41604 50844 51057 81534 76925 41406 8150 23205 38228 58461 764 12193 16847 10583 47335 34409 32666 19305 96832 44966 44617 97108 91029 21787 83863 85135 20686 18932 79368 97213 43081 73791 37298 36483 65513 72719 40692 49355 87477 93007 81137 43295 22927 66309 48696 13604 7172 84721 52671 15950 20485 46954 20501 79313 94884 23600 27475 22980 97397 74217 28105 66021 26264 27932 11545 36683 62538 42897 67824 29758 42788 14838 27118 88655 61203 3429 97079 77333 67694 80553 35887 66383 22108 6496 58854 30323 61598 48484 28132 22183 78168 58688 43830 27337 152 65655 28324 67973 69260 72854 14615 2822 76555 20123 56595 33981 25976 70367 64642 66886 12034 9955 50834 82372 38352 76635 67036 32060 90479 33225 67201 74411 32953 26628 54850 25508 4339 22754 72853 50522 80152 16000 33251 52509 93776 63775 61384 44734 78668 16733 90320 69475 43655 90842 65770 43792 5898 67207 78626 82651 19265 54583 59038 61303 70419 39715 20915 36346 75684 34922 33725 74715 89009 61516 71098 77995 5614 61165 77512 74934 52788 82666 47085 78378 92925 61265 18031 27065 82934 78505 55958 9843 22657 82390 34416 39414 47484 42582 87711 64670 12756 99968 8747 7944 27424 96357 90611 72007 25523 66425 88937 75796 73039 41662 1494 90854 39612 74686 45653 81365 20194 56948 74829 50158 47089 72672 33097 54226 85660 45514 97956 53693 77576 85344 5012 61903 91073 87764 17086 67726 38830 46461 33025 70925 25504 15916 60384 49119 1575 55152 37974 35071 55859 76274 44138 55940 85659 36917 48132 44347 85447 81328 32943 1462 86064 60851 28584 91876 93627 86863 93673 22619 68165 4960 73707 20537 4708 19630 59741 99693 42147 99186 62659 54900 38827 35276 51114 58578 1527 25815 77219 26973 70755 378 15359 95228 31121 55407 24896 36253 15501 34354 24377 9003 21183 86897 62963 46834 37505 89130 7319 74719 28873 93044 37371 33448 16105 12284 66462 49193 54751 84770 17779 50839 44735 76460 52233 36097 16307 99675 88659 13550 24132 91025 25438 37167 671 78443 29001 89075 39745 17191 38652 89296 68430 57491 70528 16803 66395 53973 42702 5829 25131 42456 13199 37657 59441 88535 36431 60575 76219 58929 51492 30352 39824 8032 70321 82229 19164 39666 65390 31386 69621 19214 61182 39016 86311 70157 25865 32223 30427 57442 38530 70155 62309 38804 89653 34980 69140 68968 12787 42392 53051 48978 19188 58891 46151 59491 29300 98142 89004 67426 18650 68790 10670 73685 18045 10521 73656 3394 79892 57527 66374 11126 13736 11423 70481 97855 21733 60449 40573 36691 8462 10660 38771 82809 71356 90521 88261 68814 12184 65536 43685 29526 48146 61847 7064 75463 28152 83236 66356 28928 77378 66687 14601 85417 35011 75558 38340 90984 23329 24578 48096 36406 1800 84876 44396 54033 67409 66611 69801 39614 78898 24340 2803 2498 63230 48908 55397 52976 32598 9516 81336 43388 90648 79725 99495 18316 53854 98151 64080 11408 14367 64810 65521 53107 40008 13554 87768 8676 3818 26236 98903 67666 15890 65982 3520 96263 38548 45539 74595 53782 56358 40515 37530 84753 9716 77295 53615 36147 14159 34773 67032 78309 32099 7434 55694 74885 83523 2095 71234 23973 94837 79243 9118 21746 22762 87739 80331 56330 27327 74969 21652 22739 33101 43777 10132 96110 85545 53993 686 35398 898 16303 39484 75828 87140 17811 8169 6287 89404 91407 15305 45914 67600 30160 74848 51545 22531 60377 11143 26096 52892 35816 44765 37602 76149 45372 58503 43328 92056 33219 44038 8383 31039 40619 88977 17096 79323 52170 10392 97758 96041 47787 86464 85693 54477 63623 87087 34433 20172 96930 29912 39218 54266 89631 88611 29704 71066 25967 89479 89739 98560 75962 49236 90208 92575 95477 11574 93049 74543 67114 12958 52428 68960 48428 17450 26386 8335 21586 14689 43321 11731 67205 29034 63360 17269 30316 66203 92641 15470 83767 11337 25237 94587 64676 91670 794 5141 77703 20368 19380 24801 2429 66671 65341 16427 4263 12807 39962 22992 91378 4942 87471 6798 42360 79514 43447 82100 28932 17642 17837 82573 19959 43466 5722 64928 56026 22197 83004 55157 40537 21743 16479 90406 16047 59360 38776 56058 60661 38453 85201 40078 42431 16777 34199 57237 72876 42614 13952 86510 99289 80230 37652 14025 76157 91214 91011 49193 93465 97017 47115 15490 73782 30372 71818 51710 42667 36888 50894 19152 44656 65243 94681 13253 62990 55893 29217 88240 27918 95682 94935 24965 65657 94946 12738 19645 13835 11184 11583 52757 5761 59599 53189 71674 69378 57566 86356 93261 96184 75537 10248 94837 53290 32271 70541 10262 70800 95725 32746 87177 47630 61926 25464 72495 93036 81544 72193 38934 83664 20748 53538 52468 9717 93444 93881 50317 51507 20566 78911 57566 8967 1606 11862 90129 48394 75437 71256 59469 98725 55164 24713 14143 19774 95939 60897 53244 1240 6453 2780 69616 92801 79713 5617 51038 77772 74101 65076 47941 45419 86481 31099 12096 91164 33904 9363 3761 45858 43746 18879 20406 12969 66702 7649 23758 26421 99081 44774 51624 76531 59474 9664 13494 45368 82445 60959 27279 4146 3461 66030 1703 69620 49402 60593 96675 54904 81331 59074 91296 53726 30717 63516 5232 83129 37336 77476 92462 86222 3432 22002 88022 21179 68087 70777 63230 13969 2940 40790 37117 26688 56250 31226 90374 5832 50199 94396 99917 56066 87311 37907 47518 73483 86819 18064 97968 8539 62825 42909 29180 60741 16790 1633 29939 79252 86744 72700 52121 27460 47615 96461 81287 63515 73214 65804 18252 29611 93020 22605 90334 4376 30694 97914 87869 46592 39174 52585 61491 10088 99030 32400 9798 42676 64600 83332 66214 78658 91560 90342 56413 61652 13660 66530 73410 55450 20014 71405 36665 77901 43482 97293 94286 43188 5392 69876 70287 15232 35986 48477 19273 5413 47052 47636 20172 55828 35333 67096 5555 46503 49590 98012 28073 24489 18153 8982 18356 22826 12756 75832 40028 82368 31684 72166 67961 45952 4176 33014 14580 6597 51438 42706 37715 89017 17850 67339 45697 51007 29801 95762 19557 37683 61881 98070 89910 21816 31166 91108 43256 60033 20433 94424 15724 50549 66723 85335 46268 10458 34626 24085 71483 72696 62813 46725 86826 98909 81605 88900 51331 77031 36805 92568 71499 3932 86221 9934 37121 68493 73036 28516 44421 76637 98279 25814 60522 74011 45485 12 2265 13068 46079 6358 82854 94058 66474 92105 44060 57081 94884 5628 59417 94003 81034 85065 57110 64630 782 29759 11450 56485 93675 69168 37943 74070 84143 90176 272 68836 27110 19268 85476 17692 53998 8253 4009 14012 46834 47123 88581 37191 33738 53000 5768 67096 64269 49712 80008 67494 44140 82854 35231 1450 5598 50746 98605 19902 47638 36908 5800 87838 54182 13449 9584 33498 45990 15835 39571 1263 54044 86264 87195 42534 71813 35703 32013 21576 65883 4303 22484 87622 25039 54042 99590 28826 38480 56785 57164 75199 19277 67847 12250 59495 64531 51884 45789 60068 12417 67962 72873 34464 54295 97777 20034 62587 48434 79012 35318 87057 78514 93879 69366 21569 95158 20413 81684 57650 69593 88717 88409 15006 43938 63724 75565 85017 52922 20283 35797 38108 1892 76149 80460 83779 89726 31567 26622 72488 15060 95746 42534 45747 72060 62265 58422 51451 41979 6844 43287 46373 6273 31230 2157 21882 34824 46422 73019 98775 59773 60276 35502 76607 76416 87602 38129 42878 47346 84490 46506 76850 6236 56539 84687 82929 17119 14101 3277 99249 89244 76458 95844 76119 21323 50899 24440 68509 95621 55980 59466 48098 48878 9876 34211 18513 15284 34903 62792 61875 50875 42840 30568 33046 66147 40840 43038 85414 62080 48954 2482 34164 38593 19647 16782 42269 48186 46026 7382 29386 89812 22991 45042 66066 70388 54602 98192 86228 3025 94661 18685 19070 61778 54159 98839 64551 47505 43584 53136 95239 10349 65175 14268 63910 44647 17199 60397 14949 94771 44094 2752 9024 81914 36762 45789 89427 93510 43563 24967 30859 42709 42280 86948 75575 53695 13115 70947 77550 27188 71868 15043 19488 87963 48348 15653 98848 67072 76457 82516 74068 85069 15667 1923 41078 20549 10375 95763 6142 82174 32744 27732 93998 15525 36287 74416 76694 67518 23281 32159 93837 19639 88735 34446 12307 80124 52075 84926 96774 30431 73644 22088 15193 25595 99592 69236 48181 90381 5412 22378 79687 53453 57531 62248 71897 94094 38977 36145 83855 71991 32213 37899 10683 52649 34816 84538 84079 29654 26677 22837 7480 5692 1277 99499 60319 71153 22865 12334 96992 36416 38482 87211 52579 487 39802 13270 24091 99485 15168 18789 91380 16435 97949 71863 12328 16265 49773 3973 24541 20392 17775 53124 52242 62848 82033 60764 68938 56434 5334 91901 10487 44016 12728 40784 21799 34509 6962 85043 3270 30932 98919 82623 41783 92882 89999 1687 71463 22655 67696 6498 74646 29791 20899 13167 11624 47772 15386 37870 79257 61090 33357 35714 30083 38519 33116 81718 91937 6340 68735 17170 56105 26877 49627 76922 90357 26242 11505 30712 39493 99359 27287 32644 92804 37130 64147 81456 19942 63737 39647 1571 41069 20 816 11178 2757 24429 96610 78723 34887 77326 17037 70168 90589 37156 36763 49942 99015 94183 19291 59439 87266 27770 41154 33073 76257 49732 95169 89129 20956 7566 83757 62364 26759 58337 48515 22708 3783 15203 751 70451 52410 8020 52415 53330 49528 32079 42815 82591 72308 11207 9678 56114 22765 61786 99611 65668 25995 65750 52954 49613 89752 76570 75791 56314 32346 1952 51541 9609 18854 52308 522 37838 96619 8885 20987 8363 36012 33786 31286 48783 96383 63427 30396 49507 66156 56660 18438 8942 81122 56524 15417 6419 74558 45223 87135 83121 21453 910 72052 52293 56 53382 67217 70589 46728 91654 72277 94301 36182 31863 98005 99406 50306 16775 22785 4685 19773 24769 66246 61383 25564 1904 1731 86780 5965 8421 21787 58103 5582 84155 10775 83314 68945 4613 35364 55637 1170 97643 42744 78885 19552 37211 18266 62693 1253 61919 12422 14854 67193 53433 58064 82444 75536 72255 41304 61463 48470 98184 31216 23823 9181 12371 16412 25203 52530 36497 29523 25179 46376 443 48272 56097 85269 13839 88546 95336 65974 68777 31566 79947 7218 18973 12186 48838 68245 53996 73950 4432 30405 53850 27288 88006 12464 76901 54378 88371 72358 18857 16538 34961 31083 43810 22220 51820 7123 60495 66857 12398 23951 32413 45015 58878 25639 63487 49785 79825 5053 87864 30870 74456 45536 86125 82516 16722 4574 92530 7839 26254 49325 8371 44808 55535 60680 4447 39376 54397 75510 27459 3695 16650 8267 65522 64638 75892 23879 82138 86884 54692 63954 96240 28824 56358 42209 26500 42820 94806 15402 47142 99758 3057 66341 23692 27961 3134 13528 44688 82919 24215 93873 87904 52962 90463 55867 38847 36407 63403 75965 98122 76099 19 57237 28766 63044 54958 81678 27354 18841 10766 35887 87733 98944 50650 3378 67342 3336 26171 85996 12892 85530 33277 91021 13457 4378 67536 68442 58327 1311 37356 37769 15881 57737 46499 42512 14676 82794 20218 11662 56373 91719 5541 88864 19891 13364 47912 7291 69187 51158 14738 41793 86271 1253 45914 42382 93706 14700 35021 20013 33188 48459 44743 15845 75393 36996 99803 54958 10720 28515 20237 13960 12471 45982 7405 70106 47283 87867 15258 61481 22136 16126 82889 74172 90999 7087 50283 21703 9462 8016 49010 33997 64294 31479 21450 18441 40896 12772 52631 32670 12034 10489 6442 49247 72181 64997 56626 47593 96333 40509 68524 94497 59117 86164 48664 31771 96519 16449 87960 6158 627 43458 40857 36234 76093 82994 75327 47171 28881 74101 30603 82339 82697 28263 6654 23533 53103 97608 49568 73816 61700 81023 21446 13120 20308 15575 47492 92306 19099 97223 72163 20524 90751 85132 23143 77518 38649 99670 93963 52392 24398 51540 49012 29495 94725 16097 47121 39522 61726 12707 62615 80405 97993 70700 72645 16848 17594 14736 18328 31012 17342 81729 84300 36309 83044 44619 3982 71876 35746 25412 64568 8010 77446 69656 28845 31437 99510 45088 35346 82251 97119 98935 98403 54346 23543 1650 9549 65611 31052 2218 29472 49630 62584 50112 2767 84903 84513 38622 66371 88713 36788 46781 18522 95255 493 9262 45239 1541 3710 43636 63209 60089 76788 26958 64015 4155 31132 77856 61630 28661 62362 69349 55816 71532 3756 25862 69268 72195 73729 45289 34200 11241 29703 35357 12087 19323 32466 27665 52503 39878 47655 14164 35161 3416 92259 48668 21901 63249 77553 30749 46895 7419 88694 38906 14251 34529 80552 30162 82667 85890 96950 9978 76977 3648 42239 5772 70408 89658 87426 73840 77982 72314 23247 26059 97154 81050 6408 67679 83636 28627 1800 10393 47746 75875 87238 97600 66026 69192 27599 48198 55252 47096 61290 49647 82076 31783 16722 50603 69209 63623 34578 18184 60314 51869 22292 84079 37547 9651 98381 11630 72122 35042 20829 44366 67569 69172 28808 70468 88235 77130 88347 97241 19104 73746 50723 78765 44998 17941 5750 6723 91485 43028 18987 71561 8735 32621 69341 96480 28043 57850 5461 88178 34776 23700 98865 3354 51615 36653 35545 11348 65183 77004 1437 65365 60018 54874 92062 54773 53657 67833 73623 68800 63275 30310 10460 41137 12159 30766 84726 46133 6201 80882 81950 73293 92156 42810 4127 2055 18200 43987 47694 66720 70721 39489 19678 91061 64276 56792 54819 1886 93992 12995 4213 27559 47179 94458 47911 29250 96698 20368 7724 25519 88134 33739 7659 73873 6586 24054 65922 90942 99726 63698 19609 94988 14620 57019 1773 5378 68631 95336 37527 77484 63014 95742 19543 2679 6826 13766 99447 78977 56653 36585 32744 20475 87695 92721 58097 57500 66553 88137 99004 35412 10710 85533 43374 33084 70826 42841 74181 78395 80993 72558 16870 76165 189 76675 66768 54750 85196 79335 23675 29848 77754 18080 7287 40400 98583 17304 32471 24689 64156 78743 21478 61991 99311 71372 24779 42025 55638 45682 49814 76160 28905 77049 48636 72124 9285 32793 35722 27431 1426 80603 40848 27552 53947 9813 45482 24541 72619 23119 99483 78087 57754 27453 81972 18075 7716 47296 52213 83780 58749 12666 62099 99476 37640 12009 86626 4658 88971 54315 24617 52783 72741 10383 73557 84872 60311 2115 81751 71360 75698 85144 84758 13672 14553 85859 11488 68493 71449 78780 34466 59313 50873 98782 28043 77644 1570 24613 84746 39410 75946 65510 9617 77693 34273 29885 79112 41930 61555 62266 14369 83957 90408 81752 89916 72448 81402 45571 31542 19779 33297 92902 7937 54064 48064 5003 34788 62860 94305 41804 62613 5196 83850 10969 71450 584 20332 82043 61624 45180 27705 33354 40174 43305 95271 62811 8939 77842 31005 49083 16885 56401 26453 58828 46364 45158 73910 21628 34503 2619 87502 57852 78581 89350 56621 9455 25011 36221 57476 83849 94391 98396 70212 96212 59798 21656 93379 32743 46594 97604 59990 73549 81197 37888 76091 48814 97821 25199 99283 14783 48372 30641 4867 16260 11934 16971 9658 25344 7622 48871 34253 93594 80983 47498 38030 97794 84356 56268 21373 93100 2775 62225 88345 57628 17214 35823 5392 85668 10585 30800 3422 4050 27455 58008 47222 97597 41755 73022 24222 87232 71885 87373 25735 56975 76471 88592 27658 26654 67508 24567 70977 40813 76685 94154 25077 59348 52604 88067 13528 82295 99931 48112 27101 53173 53034 37056 26633 4674 52532 15307 93734 42647 71132 90683 82584 39592 59635 73978 31775 53523 71104 97188 1928 34096 86519 66439 49982 78241 57699 4566 84222 5504 94910 87520 38507 96854 15304 79450 77277 5686 2437 55615 2207 19157 78413 31794 14383 23710 22912 90143 71520 66716 61780 96936 15682 6984 89756 27604 68882 7278 19846 8367 44280 19020 11692 75529 58565 87642 91542 29783 60851 55664 84090 19817 49964 9852 52866 62892 26085 85163 16952 97411 15217 74611 57755 13798 98079 34760 10945 85030 59004 12515 72318 34167 18394 20145 28953 3029 50854 39248 68580 84519 56263 76564 23767 51130 18738 51341 32640 46363 80101 88295 88042 79705 60855 70935 42419 91252 37734 78358 67924 65114 43566 7078 43366 71248 6354 12987 15587 34928 29815 81663 4481 83637 72505 80490 27042 40469 42 11106 80928 59981 39102 65763 20568 60441 93943 95720 35520 69950 66277 86564 6825 84515 68446 77755 58071 18345 24356 25379 37301 75875 96061 24609 79868 39194 45548 92221 72790 85916 81490 89596 54856 87294 46059 30231 49020 40699 17473 97157 47688 48009 21 10262 25532 62217 34329 74773 19849 72758 12097 44714 81449 60418 67589 97812 44411 15050 85104 71021 98743 45022 45739 76712 40505 50407 1512 27021 83350 58136 77068 21576 15214 85919 40292 3248 27891 84056 52014 30487 69711 91543 34395 79413 80414 85571 40980 26377 11387 19813 30014 88449 71866 64125 80166 94096 80703 25052 78852 24380 57230 19321 79773 72764 530 71851 32211 69035 2127 92884 96722 23088 90587 39853 24027 61281 84675 41655 65597 29096 8371 43878 79442 63040 11939 46695 61987 98701 44500 32231 34730 40341 23433 28442 78879 28179 15658 75588 34291 8583 94319 44689 74470 97862 61054 5685 34671 46631 22664 46708 29819 51454 60936 35381 53980 73100 52335 96864 95786 70547 5801 4878 15377 32134 34755 7961 24240 46232 22373 3438 29541 1288 95911 25375 96021 84482 10100 94492 11844 50345 18698 37533 47163 89646 72094 30412 8624 69562 84469 52771 39698 4777 26310 43024 38486 43727 14350 68346 12114 7662 8117 79237 38856 35301 13165 50643 24307 48969 61617 63576 48426 85812 34599 4130 40252 5751 53007 38748 2708 14950 27277 27487 61706 42124 83618 57514 29854 58422 49994 6708 21109 58142 79648 83515 1824 5087 1253 4276 29873 56568 29685 31952 17735 4880 30320 60033 25388 10670 63448 71325 68301 2723 66835 77209 97908 79289 50862 61145 68203 63554 40540 69535 44741 75297 6205 77101 23108 88340 58577 1148 84274 82014 45533 88645 23630 7668 49544 95669 89459 72395 11548 73160 98311 69256 91222 95796 55182 93064 30538 47164 45081 97913 10251 97613 94342 77813 95970 55567 24338 30307 5894 87344 88127 9331 74442 26305 66074 96877 89545 16246 39481 45516 74802 65610 88974 1120 49673 17830 27728 43947 68819 70475 18995 91209 86645 59356 39036 98310 98122 51171 13155 91373 43470 80145 90593 93633 12844 90154 43310 9450 81086 47767 79491 28209 84436 9520 27723 19258 8445 28093 11378 23465 47424 6396 73586 96019 24593 24472 35278 97506 86035 6658 82625 38954 66291 19223 13209 97052 50497 23508 232 41569 69539 31140 57301 30628 75383 4385 46056 10792 74737 75100 2339 38086 59310 48063 23702 34771 59678 54888 54219 25395 67104 1751 92974 42388 76996 71929 20245 59537 36589 20591 97398 48903 81893 22716 59578 27251 58820 20526 91848 46225 25555 3977 70034 82092 46733 95307 99279 32007 10481 60694 60854 14176 7722 82310 44833 73108 35326 40681 54492 13001 6371 11194 90546 42314 79286 38178 28363 51696 20593 85948 17388 71297 74311 20134 28959 67907 88208 52187 67820 88315 38938 62709 80540 31024 14213 43072 87577 1662 80069 9547 28471 92303 92845 39839 25320 38417 8191 41451 37871 65149 61071 34751 263 91389 66960 23251 9642 11791 56004 44202 19521 24413 76142 66375 74006 27709 19291 71288 89196 89228 81389 77259 22080 35757 63043 67841 63019 41117 62294 88502 3563 52765 53944 56925 89201 20613 60702 19813 23433 24639 4715 44069 55662 57000 95024 18193 26749 3520 23958 95483 78309 74410 46679 35717 84488 34106 4008 11494 80165 79896 39083 15457 57177 36284 21432 67057 49118 23550 8158 24894 27353 37140 61259 87971 90160 56935 5079 81114 86633 47214 86193 9628 24272 9256 70638 66579 24664 27500 43583 65285 67310 74035 17295 61105 36861 62922 78685 52234 32131 48106 81437 40984 55038 20768 55694 54544 14601 70469 92934 50401 3534 81901 53121 28889 60321 56899 66234 9591 41261 87447 15195 75173 83462 34583 42490 16460 57372 43518 81948 70627 31661 56549 48194 59864 7603 87874 53615 68299 55918 80230 86243 75029 43488 31364 14511 67066 96085 34548 3773 76892 5848 85801 58163 71780 31775 10888 71895 37007 49082 15146 36700 31583 15232 55416 41223 47782 8820 84417 699 12908 53740 65605 82269 3364 28297 21341 78638 91757 14647 87940 89362 9067 49874 38541 27196 87759 92778 21504 11541 43619 52700 98630 56798 68560 72773 91717 87411 22736 64994 77102 9945 54454 47612 80748 23392 75314 44292 74793 29522 15465 40126 19739 46634 93922 50816 56098 73636 81129 74721 96046 17578 51461 87072 72241 50342 54089 70760 16699 37234 31845 34778 51548 54405 1575 35574 64129 77987 59059 27294 45246 39899 52804 71336 35867 80533 87386 59574 19547 24113 12236 29820 52496 46322 4535 91805 88912 91254 10008 22464 99517 48358 58408 24600 72972 24534 43927 96533 48754 83212 3423 69630 53530 40737 95362 59686 94582 52430 41431 21113 87049 17476 90676 7185 42186 71122 22314 34181 51295 79442 89624 60741 57884 86092 77175 64158 27882 40636 78420 99975 14127 49079 86534 51086 42288 72556 83486 58214 1464 62340 73076 4296 49885 19132 16559 94415 90713 10885 58540 7839 15196 50284 48935 65045 62778 27182 86378 40495 10798 77131 58847 71574 94516 64594 67550 23120 81647 31873 76641 18710 62283 90546 48817 90215 88971 17069 26037 73783 24760 77418 45332 3217 55993 2315 88902 40930 51549 53096 32344 8703 8283 1367 73438 40059 33656 80743 54756 19247 16510 46438 96861 57322 49736 68734 27663 97553 1076 52720 89781 75777 61273 30503 34675 77423 66982 53226 21422 31612 27156 50460 80494 5232 92123 48592 99751 55080 24002 20470 87231 8109 63578 36972 21408 75320 6591 57698 32469 58231 53137 76519 93598 75967 21814 92252 75703 47424 55036 4524 12823 36322 3660 30345 58956 86309 52739 32966 3244 69694 42026 8501 66238 31173 45053 70713 61039 78243 21126 59112 29351 73465 83263 10723 83125 9786 59638 81851 38103 65233 19451 86952 83965 92877 80672 5770 33443 53014 18693 9016 46192 95321 95775 97213 50577 41929 67175 49802 69953 59934 32595 71862 94357 16370 94190 67256 69607 80256 28698 28516 19235 3523 45081 84313 73598 34724 48572 47832 58013 33739 98241 62494 63575 93500 15667 20518 69208 65320 89459 95252 32968 9395 53403 52274 66795 45249 1627 75057 54638 59497 21465 95269 35288 6040 19030 49631 85955 79949 46074 10968 82873 25472 18885 83963 47815 63359 5850 23736 542 29930 58909 90663 84229 52396 99461 71446 84955 17946 77542 9915 21932 92831 78426 87822 42790 47026 44803 90408 48938 57940 85242 98560 22465 39876 84199 82357 33108 22683 17666 2881 48328 52843 58256 49958 24275 62732 12419 98566 40585 6529 86881 55428 47535 20734 79261 68657 93794 54583 88032 44589 89944 71136 48878 37950 91300 57336 92504 2073 3304 4875 33353 53423 91425 89564 38405 74620 54214 50598 61466 88677 72006 95033 49692 66989 2451 97264 79766 52319 2466 32700 543 32750 32136 68030 85671 1887 16540 60609 46220 49525 36590 50451 49925 7488 51424 89393 38860 43634 7863 78130 38671 45161 75176 68101 71746 76124 80739 14555 2354 38511 659 7522 73192 89787 51991 17474 34376 69550 92168 68655 2268 45145 24735 46781 70802 79467 73604 54547 17734 62465 47887 12184 94644 79225 51300 30953 9241 19970 45110 79647 94164 55812 94000 23515 45894 46988 2442 44959 99830 47838 1404 85961 19491 57460 66680 50533 81047 19280 95237 39270 49970 81017 45149 22080 6961 70239 55010 2808 29892 34376 276 93631 3341 3132 45362 45513 9858 97287 19197 25498 59681 78463 30502 66471 29160 54159 41676 53595 19072 26391 1737 20004 18354 48341 47273 57216 75464 6616 47032 37242 84561 10219 92479 97127 37811 28587 54586 10448 40229 37802 97816 26503 24481 92459 95818 82231 86337 62490 39526 66190 66500 20080 88356 50600 18035 12147 80398 8146 54850 41817 70033 60167 91902 70021 94270 74463 65396 52674 69600 84355 2020 21043 10485 88384 66970 1186 77446 51213 45949 30888 45149 20243 58118 41182 39918 56630 84679 10787 80631 73895 85664 55141 60843 37767 52669 6644 93093 85671 74900 49470 92789 15524 3081 43822 79788 92298 56159 38549 52793 78409 44244 31996 57528 19033 16223 53740 48467 17584 76549 76621 46887 85030 4574 95516 49377 94915 29864 81680 73430 29154 40262 85479 6305 20570 3185 12345 37070 57326 16318 44264 49696 8485 77287 4809 7354 11664 98722 54672 95952 39041 94482 40837 15925 25812 51669 36264 97525 27199 81449 21092 48246 55801 67549 37888 61581 80337 80819 48413 97085 53731 98956 19875 13278 61330 35919 93620 63522 67557 96829 99541 38302 72349 89790 95905 92437 43071 36813 43454 39353 3590 78086 53248 56710 95128 37925 72240 41669 19072 68835 28485 4375 65748 80575 32199 9319 19411 25090 66519 92451 94866 79986 5241 89597 42948 80079 56679 64354 69807 96167 7453 90198 42053 4139 18827 55912 15449 57667 45694 3659 88800 21184 66669 43863 10665 55718 81022 36072 62099 8084 68088 91545 55978 44242 61715 78932 75647 71741 22535 65698 81078 51857 48648 54406 90375 3031 45528 47595 7793 44303 42732 38850 51231 57937 55224 21947 96699 91528 55658 78942 79645 20527 19682 74436 33932 74375 58098 38732 78224 8303 6720 70961 68934 94232 3696 76016 31578 35544 35293 29936 19302 450 53747 3815 85072 89582 56129 96305 90781 7677 72310 11848 55508 2186 665 65300 32833 17598 25043 36697 18164 63773 77990 29474 20759 554 56950 50691 65307 75103 79936 54810 77693 74794 58555 83857 59745 71477 30858 48470 84058 54844 30158 44478 22772 93133 11499 56068 36296 99317 47986 83848 14653 71421 79412 99286 47963 63848 96321 74652 60710 78288 29074 57026 4788 18447 96221 6415 59370 59934 84996 20016 61304 96018 19108 24507 95889 46783 15213 1966 5220 82885 41830 7752 59965 65327 73034 3757 12384 80513 50516 54919 46984 62018 30204 28605 2579 63901 51811 57264 93549 1897 66499 27697 95460 32862 78940 45608 63970 57410 93440 75181 89097 49673 25822 50799 95765 32175 36154 38093 90474 9601 83114 89514 67676 99895 31925 64306 45172 23285 34011 60713 14982 5025 96789 37139 61999 36781 50895 78089 16410 11162 45617 804 54097 95890 68091 84171 36842 87353 81400 17162 12898 41530 83911 54174 90820 71245 74204 2803 41949 71732 39620 46568 83105 62758 38728 70197 94031 41631 75630 99682 23552 85947 27546 75932 74377 98009 86254 25083 16278 69030 93110 91217 16795 80666 37512 38728 12553 74839 72625 87254 88064 63523 73781 59824 77630 3889 86376 27815 13232 7758 87467 81142 82703 60479 7444 40055 5618 85224 65444 35258 42064 63421 75398 17267 5330 99343 27724 92407 85765 45441 26888 81705 71311 8297 63061 10767 95827 8397 20230 6918 67722 82965 68398 9257 16978 39900 55974 12331 97521 15281 95728 32125 77711 94865 65801 49707 60619 41860 66705 40112 67041 72121 1266 50590 93143 72443 91386 81973 28276 84364 76646 40547 5338 38484 2752 72351 94636 93076 60279 82075 738 98769 74054 8295 21549 47537 37469 68314 90949 70006 58978 3263 21089 75478 47297 80313 75817 88646 27950 64911 82592 65152 66215 39301 29305 84950 1958 42911 86534 22768 41784 46822 52210 13326 70032 14178 33827 57114 16626 69290 55431 70649 26206 49318 90723 57149 9761 5131 59410 55780 61031 9851 10058 11941 6970 4910 75319 26426 10702 79145 20164 14869 36316 99022 76099 31179 20185 32577 41122 24063 7207 42212 95481 67920 59402 56350 25421 22460 55614 28692 56130 33808 53823 68630 47982 14119 79495 15890 28238 21196 76331 49100 57609 25826 1545 96394 11457 22659 68610 2411 75299 5697 98648 90998 82548 53629 19248 39107 37511 50344 51680 84255 56929 58124 19273 10178 69172 33160 42660 40890 5063 8012 87048 34327 51815 98565 45044 52169 38776 86354 63204 6993 61744 42121 85657 70685 22803 33492 13977 88662 30758 39407 20608 31289 75704 3567 88680 83616 87252 80665 67142 73698 89673 51447 94323 78673 98418 39169 45125 93237 35379 12042 20200 73556 39469 59727 9646 23759 74772 89547 7360 70546 29547 21584 75729 56701 32546 59857 20667 23176 28269 44976 6100 50437 7536 61060 46715 45998 32367 17363 98546 641 2328 59089 59475 29175 54306 34819 19814 28541 95836 65849 5283 14666 62592 20148 25078 63246 23956 59858 46805 45782 32429 6830 76858 29130 32576 41558 28973 5407 28327 95737 25315 94446 94886 87205 46599 35410 16910 91733 834 63302 43732 11177 22318 71988 3696 70227 96611 51103 66609 86113 73303 14800 17312 6196 99240 47902 30283 92819 67532 86116 65202 33560 64572 96132 30576 98385 78393 50686 78405 1308 48719 46454 36714 29144 28683 44806 76491 13464 65341 92080 56094 46432 2428 62855 29479 4571 7752 64487 24033 88324 6079 37429 11348 5530 67023 3275 40907 53884 83930 19950 36653 21479 47203 47293 91159 77953 39356 8685 62294 48415 70363 49577 91125 11294 52176 21264 72115 89052 79062 13325 60819 27441 65944 22204 69846 10405 47938 43694 95858 33861 10865 39256 18154 33131 99255 75904 15495 95368 2443 42281 14689 71809 44806 55618 99472 46199 51346 5257 22159 13604 36721 97989 66241 84143 79769 14910 74889 26086 87761 4800 62643 81174 86858 71988 48755 42324 43594 16607 16444 1540 88239 41639 24204 37545 96989 95590 87541 70012 6516 28355 73888 22509 70519 99059 62194 12706 98107 42020 48592 65362 6412 4328 83573 21267 58325 33927 97951 64937 48799 6975 4738 17737 82665 64110 28967 80890 55591 49832 86969 34998 9122 38926 66928 46740 62138 63313 83055 58264 74352 26218 51797 62324 57725 25829 30526 33886 27671 64997 65296 76639 84422 95878 54212 40190 22719 57660 40691 44974 67775 64057 73491 87190 69306 13749 75454 5968 3777 21458 72974 70064 35156 71331 27956 39339 13117 64827 6374 58369 27009 32784 55351 80775 83069 96575 76390 7002 66927 30743 45893 64293 98022 43719 23926 52692 19802 90810 34201 51096 88310 98166 81675 46731 43939 21694 42181 8209 20952 79175 75488 8400 60381 83648 33135 13829 23992 6109 70660 32840 95732 19794 93004 2531 14893 67265 66178 34507 33773 46057 14521 71991 78282 11218 21286 19363 27212 62222 74287 31243 46160 87340 65465 52863 27549 89581 183 10423 13765 32388 28568 67796 47900 80086 77308 54179 37167 68989 59656 20710 58209 72375 54575 64781 29123 78303 54213 40533 10286 27738 21638 83936 69872 70959 77304 21538 98043 60845 42629 4877 38191 29435 63831 68110 80732 8196 99222 54588 3587 54687 27924 3969 81004 79758 28348 73235 28398 14926 46610 59557 3563 56335 64937 18269 60746 84237 81210 3206 59771 46239 32481 37917 38760 56034 71538 43876 67553 32986 94326 71112 77348 41081 32489 3597 15835 39142 39973 11152 90030 20946 30741 18248 84072 13432 51709 74968 70237 61896 70740 89879 54494 89248 69147 75981 98270 44023 84347 41572 87407 17210 6741 38538 22751 79930 9798 90594 46086 85802 53493 14971 81626 72899 1557 34754 60804 74774 64407 82340 719 49199 8916 57574 84190 56696 85470 90515 8152 42303 69709 88401 78377 88830 15522 58353 86845 82175 76461 44604 31118 68383 62221 4922 86200 40471 33402 31793 96882 19181 95331 65284 96264 48043 97364 7154 67575 66597 79342 90488 5305 73971 47586 42457 30966 99774 48679 7412 91016 13856 75990 84350 18869 92005 79117 61143 92529 29013 17074 46895 5643 80328 83486 58588 41369 79232 3068 42102 5954 60957 35799 90455 96966 86325 91439 8574 25523 89019 38027 95988 39947 86399 39304 42000 84899 12534 82297 14363 21665 88133 4588 79790 57682 18743 29308 90938 32814 36798 46425 87130 59686 97898 49284 19858 38133 11114 31658 12004 35394 74698 61101 62790 39499 13008 99095 64168 8369 47634 64314 277 3880 13666 57556 81336 11513 10503 75316 54482 12121 50272 79307 2731 8064 89594 22137 40519 12102 69407 766 28646 89909 73112 159 50017 4296 78706 63549 2484 92391 72170 34557 6283 77212 37654 9800 91636 13794 80268 13801 92207 54820 39495 28078 35484 66237 12516 7195 74390 93323 41312 44153 59496 55200 89231 64238 46716 63534 10181 47169 24110 83672 30473 67675 35840 50359 70767 70993 92804 73517 25411 3934 24538 65386 98474 41892 74524 55732 97914 87843 45535 36537 72130 42455 68929 47898 7259 42914 61548 85724 27356 86702 54514 34359 77084 68787 48074 85819 85395 65205 55422 46344 32755 82899 58898 67245 24277 39466 10883 54283 94240 40450 5044 57607 26744 66442 75776 79407 60564 43249 17050 83401 26331 24681 89520 52735 326 93819 76900 40469 22303 48700 90427 79001 15108 39093 90117 41814 80011 33815 93729 10544 99091 7282 696 78617 72785 67205 57265 43439 23099 23189 51123 31918 8197 76845 77445 95190 72658 62101 80884 20193 85323 69745 35654 10606 48224 15588 63460 2047 99056 32389 87326 213 65625 17867 61476 80827 1696 35839 52198 74075 11572 90991 56489 20903 12089 94242 48711 12412 6087 96553 16064 88477 87899 74467 43707 73720 5856 42212 52792 28565 70012 95030 62848 72690 67625 34330 39063 77750 8974 46905 5314 88535 29847 34225 80475 54179 45263 65597 25937 86809 79843 83467 6319 89514 59161 15384 89547 66947 74116 70734 60791 16669 23520 24018 97629 75928 82851 77471 31097 37691 42756 5870 3605 43900 86649 15985 1252 57123 19783 71797 71899 42409 42966 60991 11423 99524 21189 69378 6011 38222 3543 53374 18827 88619 86377 63247 26766 29165 73665 24273 65089 96155 74444 70209 96628 38996 52205 55478 43091 18539 85612 53815 64965 16481 99870 95563 25914 61977 85076 39471 52274 16330 17865 12451 13277 31197 27022 53427 6911 35269 64095 52946 17627 19211 69955 44657 76728 22379 76040 60913 11823 52278 78747 8539 47880 42647 438 79965 95505 72556 74998 88044 88093 81191 13734 97440 1543 42243 39022 18581 7551 24111 16808 60655 8020 41949 37372 19960 83077 28951 66522 27943 35179 91804 31565 32982 92018 20582 70253 86257 66126 56753 57823 91493 69668 47503 22185 54854 5426 65241 51189 96571 14252 44095 34542 3860 82553 80462 30740 53192 29457 51861 40005 3287 36418 27887 43254 44987 97676 83259 8859 75374 85925 83348 11820 83630 76729 19866 17397 60852 13121 4362 60706 64222 39077 50365 11716 21555 32010 22687 20652 6765 45784 98624 78224 69696 42681 61156 86090 332 31171 89839 55244 72327 35680 31975 25228 48694 70768 29952 8377 57720 288 35360 29481 27755 36616 23684 27672 10575 861 36918 8083 29199 37298 84992 4932 19337 92937 20894 34537 2653 48224 42820 40187 76778 7897 98775 27920 36296 19911 65630 42136 2893 61228 34738 7995 1968 65089 37959 97912 82068 94823 60088 72825 29471 61875 60918 96813 44395 81316 32723 38364 60606 57469 65002 26183 98949 15956 11963 95660 60080 7709 96440 69294 12188 34945 46026 79448 19189 66140 98986 72280 86711 7972 48622 59223 63915 93761 94730 82469 32469 57579 42967 93130 84477 71489 82365 54501 87773 80024 87920 87446 74550 2753 39138 55584 51934 28602 49295 54488 26938 55724 38496 21752 33454 66297 1746 47110 79999 345 22137 3254 27308 60743 32683 64609 70727 82116 18772 24634 51161 33155 10454 29582 21340 75852 76956 38835 10794 13199 38675 37960 44066 59769 25923 68632 90325 8882 12239 69769 93650 64995 41293 54357 30205 68751 16028 70502 69023 38327 19445 33919 94811 90964 5760 23799 49746 2339 11823 68040 83331 93728 64974 15055 82229 84578 45779 91931 36648 18098 22972 20567 87002 39790 79143 58644 76524 53291 14551 41339 19543 26951 71537 88425 33437 64535 38039 15858 18708 31473 93581 4267 21004 56416 1804 87463 50648 57398 81612 59071 10772 98360 51885 73871 2822 81403 42780 47663 72203 75502 485 45142 53673 5315 2774 95005 57178 81073 36559 90263 38728 83339 9726 65663 48564 58107 92266 72318 95091 27576 26545 6807 99602 68697 27237 43854 71317 55182 65960 84105 80466 40739 23302 51532 1168 47895 69550 34393 47556 88267 19130 93681 13457 80282 71195 18910 19115 35083 41155 19401 60615 56064 45438 94035 17131 25217 24163 90783 10509 96592 94380 72867 86277 43444 43996 22063 83995 62114 13701 92472 92098 12747 86257 18476 63506 91896 86406 84244 80183 2935 17214 60360 61773 56936 58816 99623 61583 50737 50994 65217 3696 15704 96827 8880 59187 86294 3911 59420 97516 71055 65415 48131 91164 22538 33478 25140 79604 24825 93335 16363 3282 2561 61328 82890 48445 87502 72912 72285 43232 77566 5094 79169 52925 3520 30808 35981 36769 60458 59494 22872 73245 14380 38798 61087 69516 6904 93295 34719 11230 90873 41986 26670 62281 34517 56636 91708 96809 84691 37115 233 32330 84953 81720 23759 55997 64977 14325 46481 12074 89189 40691 59890 3199 55703 61812 35049 37816 38283 54197 76214 90817 70071 22553 64350 56046 55874 30755 44066 43810 43418 49742 53103 63698 45264 58481 57359 51248 26227 22052 11182 35662 81325 58462 4273 63898 30876 55965 25743 91303 96192 84396 43975 75668 60598 39111 12027 15149 46875 92272 40047 68132 61303 21179 13268 65016 32503 33720 11630 43756 9998 5341 27809 69292 40454 29864 36772 86955 24639 27544 23123 19706 36172 55640 8702 26579 41577 43334 48869 93823 81495 39669 94151 68106 65061 617 12163 69866 45945 38980 8249 12486 74056 31 49059 63711 55896 58381 55619 68781 67130 73621 93528 84985 89102 83436 91357 26145 19152 15620 71584 77292 3401 2525 53913 52053 5295 69410 72073 12633 76389 67380 81216 83344 61107 59379 86992 93838 842 53206 806 20198 37738 28015 6510 18665 72158 60048 31633 31947 68860 68916 79753 85648 22710 79761 41884 75435 25279 81055 46557 42414 5858 25640 55442 76758 58760 63156 15491 70442 27271 83001 90490 11419 97549 26318 31696 76513 90237 83041 15657 60451 58013 18778 65586 78294 51518 94633 40567 52667 44443 13009 17561 91377 72561 59839 55801 47934 15367 31902 89693 20662 24423 46189 78553 74389 61384 82148 34191 85548 61283 62557 34624 58299 39915 49454 57680 38353 63549 63352 84485 98497 47882 82772 3323 80142 13852 59542 40243 70924 94580 19401 79384 59044 64439 88652 61660 14062 38284 42747 45240 24317 91559 19147 70140 49938 82878 11897 4880 36269 72130 89940 87250 75841 96930 58404 65726 78301 22787 14988 22495 72018 12574 12158 77675 19607 83175 85407 59289 39932 39630 41230 30245 24482 31105 29225 34369 60023 37979 7114 33570 7709 63164 97185 42660 24979 57511 53615 60232 46225 14257 27460 21 54442 68087 83237 66949 45666 5121 94238 7713 1535 8813 85953 65043 85411 92014 40924 18561 65069 64041 11874 34240 62479 81689 60774 77652 86765 79236 19509 42676 33715 56278 55416 64663 37737 70398 59419 71581 85959 74468 56357 76750 11536 66850 32157 25304 93293 69288 19950 24622 30736 82894 81935 73627 72763 34940 94943 73154 8848 41804 46391 60999 89421 37805 83283 51991 25400 65068 44831 70643 9612 42859 62457 26749 76268 3230 89696 68131 45318 80320 75528 76673 453 89442 12892 27177 78610 2386 93857 2448 31741 11057 52114 80299 18762 13577 58871 93439 72705 83791 73510 62178 56687 93320 90255 71549 92240 87851 39214 64222 63037 34745 188 17126 99908 95167 29147 42711 97072 58813 15898 51788 85384 82330 46021 81016 40961 41637 9078 63403 71424 27442 43856 61792 49397 16552 64049 15657 85188 46973 30056 14493 11093 80453 21716 44348 3268 86692 79380 513 71907 72247 14734 33649 64240 9384 56621 53442 30056 28399 64108 85863 13412 19583 5676 7334 41089 78779 13013 64659 45882 71794 39448 70187 14986 27937 79177 98567 39141 74012 97382 80467 64153 29281 96741 57632 6130 934 68191 81750 19241 29138 5138 50227 89806 40611 24020 29920 98419 78007 31433 64521 48733 84399 82275 46680 91591 15624 7533 89351 36878 42120 13491 38516 30820 16552 83840 65619 17725 59501 32319 59879 86809 48481 82342 46008 66733 60670 72098 98175 48325 80265 10546 73935 65717 89542 5754 50503 78473 64559 41263 94847 14392 58425 96819 8693 80610 53174 59888 63532 32578 15101 39253 2813 97334 75476 12675 32921 66426 73202 46859 66301 19865 27973 22585 11054 13715 6747 5642 61132 99122 79703 39397 7065 24846 64130 84450 86519 69510 23346 88012 42683 95215 74448 97450 29186 42610 6857 71183 10202 22082 12229 22 48643 94699 21973 20914 34019 8384 25564 40324 89026 65932 27011 90997 82998 55537 96076 97069 38053 22451 42595 66177 53167 77793 65003 83435 64488 10257 91366 9400 75947 89600 61413 29376 94009 62817 13455 94830 14494 45210 87012 93206 560 15635 59970 30372 90452 22063 47954 45771 878 26517 56719 86925 22619 70167 42362 72686 69746 13395 3799 37680 43128 19253 22505 16976 48554 8503 37073 23012 74107 49677 4058 36800 43418 76491 3158 57553 34540 70389 28169 12171 5011 9053 48960 33367 58388 49205 29771 17471 20583 13078 13110 62728 43945 43477 5249 39328 14792 99518 26936 11937 19206 92780 29281 5454 65727 33541 96601 66692 52010 4981 31110 76312 37680 59640 5419 27055 48507 97070 2157 59136 99923 76790 66425 5055 50554 34757 3299 52103 43091 56410 30288 44218 99868 17132 54214 2912 57958 42544 29845 36109 6606 21368 45385 7519 99320 27087 43615 49045 65067 69955 31645 43868 52081 46576 82367 25506 62521 59675 98520 61004 91221 97661 33712 46439 33292 95370 50497 23796 86856 70261 95641 60771 53437 35439 75074 20365 26020 52631 94533 36244 6869 6152 29788 30957 24690 52538 33583 87419 644 48112 16387 73963 286 65788 13821 23394 11611 63169 60396 7549 8833 66468 69850 80491 42793 4022 1864 26256 50392 66046 59310 37184 79565 18378 78726 23456 13483 39416 54665 36713 89197 10215 62647 64470 33834 33116 43151 31727 50630 24917 31078 49292 41154 64097 59892 10101 70975 89188 15880 47235 1565 46203 83217 12459 37434 58936 12840 10070 2136 74985 58665 19947 10322 1646 44248 43515 59846 50959 58162 85007 18426 80968 37963 36041 38120 20745 78109 76998 37473 93081 50331 17110 52643 37126 991 25576 96755 47301 66789 7643 18625 11813 97273 65859 79368 95573 84047 73751 33029 21229 24210 18237 22462 9150 19024 11601 7917 11946 39483 42087 69423 13927 13066 94984 53437 57174 76355 56116 19810 72005 15177 47245 39431 14818 43096 76343 84814 58539 5300 31255 7789 6000 37107 64433 4813 47784 56121 7406 74449 61761 70672 22361 19102 7169 31127 73098 95808 46596 85629 31077 26129 53344 85538 63086 86868 99696 48260 84173 11643 21291 46161 16103 98130 79480 81763 42062 61256 41590 81363 55076 10765 89871 33028 54085 93464 43191 25412 78876 12092 66084 94461 91 20786 77362 39172 6632 11227 3533 16824 48593 92092 17078 66164 18105 2277 80539 38450 32168 3323 51738 3118 70288 4454 96596 68670 59451 5508 56349 47649 30477 30714 61830 66619 49089 92311 10925 85598 34513 21674 71536 17768 39637 8815 6749 11231 7563 64207 92640 17343 31516 57637 69270 25521 12220 23257 3179 63117 15845 41766 74040 5175 57584 58076 66387 29912 85501 48176 31974 41392 31513 64757 44846 79340 59045 48548 97232 79295 36867 69762 58924 16680 26542 21432 53843 36209 60633 58402 21496 90607 94284 90687 3688 27492 16900 45871 72533 96875 63823 87635 20995 36143 13838 37606 37233 46928 72066 93903 78729 26050 25551 67345 45524 79059 23970 99419 51360 74179 65265 38443 57773 18594 53233 75854 63210 23949 59466 28624 63115 46209 784 54209 67916 93109 12828 84886 70292 85588 26471 16259 11586 13208 45801 45654 95925 77637 64090 61192 38186 57564 59521 18014 17761 82518 48017 73178 68794 31490 99117 74335 76399 51151 40134 19085 94279 29640 14194 22850 2953 3088 24118 60084 98143 65179 64859 81787 32909 64024 91278 91293 2650 69408 33644 2469 99537 69429 92120 96604 54145 92392 92740 70713 86203 97470 87742 67167 37563 64756 38190 8935 83394 24622 88757 8514 42626 77139 95719 31201 81411 96111 87409 86184 42909 77648 76310 46368 19135 54215 66513 1189 48944 42989 8172 765 31136 10364 79791 59649 95614 45234 1659 66369 81307 49297 80874 60795 31332 7192 96123 84149 17782 83863 19649 39462 68046 10020 74564 45371 30232 90694 31258 39711 7719 34712 84214 54906 57078 62009 51283 36207 26260 25025 62203 81686 18483 99272 25327 43685 54878 72568 35774 58046 25548 60914 25307 61653 7799 70568 56085 22268 87549 33905 23628 87008 56398 62849 67043 60974 58997 83816 61069 18688 62038 24424 72865 83684 24606 34276 11799 78704 44366 89678 12556 55332 84139 84842 57079 15118 96370 64078 29016 27357 41184 29089 68660 10026 36984 18920 16242 32969 29519 42360 92083 97073 87409 86742 46105 25298 52720 65907 23414 59322 63367 13369 36319 57102 1365 78325 49118 78281 77457 53780 23471 65054 61359 11538 31350 80378 96010 73545 33327 12859 91533 85979 81540 23613 23589 29331 52485 37424 41180 26551 57513 29042 93987 55792 86936 89663 92512 16752 96743 97307 41849 40260 10454 72735 67451 73342 53534 93435 39288 2600 25371 68990 27313 94569 60479 10277 31064 11580 42613 11841 37162 83279 97467 29260 90727 24214 15171 73480 62336 25907 79502 12507 4707 91713 96317 99442 80528 47416 55377 78262 7686 48304 55581 98432 56539 48521 859 70243 7333 85495 98606 28412 51597 5180 99515 96129 89565 42758 31777 64905 4508 99793 62763 19656 79481 1829 54826 97014 9053 83444 62234 44492 78301 40752 31924 30905 41098 86446 43590 9656 86371 26106 82892 73060 57134 36250 58627 18687 75603 18616 42579 25857 30217 34283 48100 12659 98246 86856 5910 18639 85179 9848 95597 77945 24394 6213 95056 41923 14419 10222 49898 62697 99024 52980 79785 51295 5034 55434 80889 84512 61790 8812 2836 59131 75280 16144 28790 77111 13964 48007 99707 22405 60830 74658 86770 73956 11127 64794 59836 33249 85140 59710 96970 83303 22603 75689 28646 2730 42644 96092 86414 26832 40110 97822 67742 5443 79116 91668 84471 60433 55231 54213 32943 74150 37521 60571 85047 55607 73103 49863 42357 9301 50753 98016 97076 13665 76831 71397 26352 70903 81716 63893 35071 82167 8661 87653 70639 89395 98082 39782 4982 8854 91162 85797 98874 34627 70708 63319 53379 18969 9831 33817 85486 75505 40962 15216 10972 26775 657 75809 67299 28713 75617 25358 40732 6303 56188 69440 33403 12074 14732 87933 19968 33673 78436 58838 13860 52328 58326 6805 59371 66606 1087 58596 41814 2404 67915 1457 15464 42833 33805 84090 81360 27004 98230 26304 19982 64781 46543 30467 75822 30219 51309 23702 5655 33278 26732 10230 50138 49081 6095 631 48412 30215 93043 40816 86506 48766 72634 57953 9064 24020 82906 92714 78286 81990 6855 7089 98510 76636 21018 63203 66807 34364 86334 58829 52207 29750 35799 59636 34838 45792 91310 53007 41398 71508 34436 48311 98643 81559 3392 64189 74083 27829 53899 52572 72823 41164 95134 39573 12316 52610 73910 44554 18066 14505 31464 99434 69953 71054 22805 84318 60250 20102 35371 62234 12219 3204 39973 6466 4981 20662 78132 68332 3780 111 55799 9672 39435 8411 43942 97472 93700 57315 86895 20204 8098 77449 26317 65068 85865 94950 188 51637 80902 57017 8006 10367 12104 38189 42637 35069 63592 22046 88419 31002 88493 16017 5768 27427 88094 92554 4228 28003 42459 23536 7506 70196 51867 90981 35913 94862 87766 4437 36613 74897 87346 13898 50166 53051 28613 49684 32354 77259 83147 95587 26635 15414 35230 39422 23104 44510 87210 2660 41845 1301 51095 1266 55488 75986 46582 9097 13626 76419 67355 99621 69063 56247 78158 28183 72160 51117 9048 40560 35577 32851 84888 70298 19265 66859 58835 22035 25189 9774 63148 16338 15124 42631 85437 44026 32746 68823 81065 23769 21764 54514 552 39986 91313 53142 17766 61839 41471 48230 81434 368 41509 16833 4771 18128 16111 94225 99431 596 70441 92821 68518 96534 276 51583 40467 1063 42513 5955 17966 19620 75720 76964 71030 95370 36796 91574 21180 97531 35759 23228 81421 64803 41016 40402 24346 6834 90436 66015 24141 46228 58699 56992 40524 32343 51601 6 66842 14250 55526 36861 32994 83714 63319 93222 63111 39615 13401 89891 22710 68909 77261 21859 29664 76381 43983 29938 91442 51171 30388 12379 42391 67694 74054 15950 10266 74744 99710 34246 79336 92778 19893 21952 96149 86997 49961 29714 73072 21786 88099 11923 20071 35823 11079 75515 33468 51846 11908 53613 96172 12678 76724 94769 72254 55968 16493 98256 84935 3910 11699 14725 28682 4765 1229 87108 21765 9 76237 68863 58823 51540 81609 77370 33979 50534 69054 26484 36303 24678 26155 7238 57413 25952 16837 13005 39399 9895 385 91642 38095 1430 63005 92885 91276 22559 58996 26909 12481 13845 37415 74875 65639 45629 56723 66253 82408 23239 98105 97477 7259 49340 59916 12267 89194 35411 88371 3148 91698 33282 22883 42801 79830 34416 56649 5474 55980 19183 62404 74161 8549 42281 47953 84273 43509 9552 24082 2086 26869 68622 56322 75958 6265 50146 71840 79440 84980 24759 39502 36772 65105 95099 6285 35219 3309 55296 35430 49682 65532 18178 8794 64132 42164 87656 95381 5750 51423 31064 41251 77930 41524 96263 2457 4079 45794 29396 42019 87412 23243 87681 95543 57216 2428 70079 42080 51955 45659 35293 64434 47140 86157 84652 59177 78156 75061 15106 4582 67365 95871 73434 25243 47451 28413 48328 42538 10722 36928 9832 50239 44154 10746 11836 50343 71663 33564 99576 9318 87450 4437 22800 33626 38713 32742 59576 9180 60698 92317 80052 34273 61254 87689 29747 48604 86500 31578 29348 87544 87046 25539 67946 65857 18701 55556 19459 79330 72691 60267 93633 71059 65443 94305 80177 9975 22174 66412 21532 78978 25110 62197 3463 98502 74907 42801 95725 16794 91642 31142 94398 9267 36649 55613 39946 80465 97884 46162 83747 98082 76748 6888 56295 11012 56106 77146 4426 49154 28685 20379 38325 64034 14623 35686 5876 90953 35370 37586 33553 69843 89281 66929 80974 54415 69441 53216 5520 42948 5379 13810 53464 2214 77285 56489 73729 97595 55398 56988 12041 61182 68164 98170 6008 21286 45312 65687 10905 612 96693 28443 50691 14807 30353 50585 43261 10716 87423 81331 29769 63361 43044 89512 89680 17818 91701 11696 8981 51983 79321 20727 48562 76839 89037 7618 82847 55146 18330 53809 25075 49655 22493 62635 66131 20125 50524 57075 60542 18414 11417 39452 84320 63375 61854 75676 85115 71799 62831 15248 64898 54696 35009 64596 5615 46680 96615 90465 28277 12544 26724 46631 75529 10593 56930 32899 27497 92918 57722 88593 71141 3775 98047 76983 90416 83846 77400 72072 28294 6470 22037 12102 13760 78477 96905 59602 22004 24548 84990 28568 80176 94700 6259 10298 16445 40545 25808 24178 4263 95770 66535 55322 55032 17530 47650 69238 5506 40996 30128 1146 36649 55988 99161 5861 23574 40737 97038 86341 85467 93735 96157 61395 32690 49600 66469 81214 65913 69102 97760 68519 2616 21716 39334 25325 53666 62012 92890 85330 28794 19721 39122 202 63480 62460 9427 99734 17991 861 13338 56358 48994 92684 84998 34258 60787 96380 18459 51488 63011 6699 38110 92452 70177 90825 13017 17701 54604 92203 62239 4749 65097 36836 8680 56286 18952 11819 85855 677 10793 90609 35814 89161 73424 30650 38639 4665 97379 60284 28792 27799 69182 12231 63038 88664 29331 49881 49218 9978 82818 31295 19024 79087 1989 70736 80219 95409 97814 86674 73690 13294 67991 5499 79943 24332 46267 37029 94705 97027 4711 8769 77882 38005 67927 54613 98635 33086 14553 9437 28779 17945 34970 52657 46941 45406 61888 55159 68806 32751 51798 16454 6736 35967 24380 42727 38361 2167 67205 32557 94882 86856 12081 27827 60915 24999 89530 30428 73245 72656 9638 65303 35128 69282 9100 35269 58956 49320 39186 62762 61985 52146 72476 93273 24753 59274 87142 7683 71683 79048 2186 59620 93911 1673 53209 82983 96355 26758 69183 99255 97282 47668 41450 98872 74693 48231 14341 86773 6503 65418 51601 29137 62941 69501 84197 37455 8398 6539 36690 98434 30392 41620 73138 94789 14102 20548 59345 22624 75877 48852 25637 14830 44871 969 3347 93173 74767 70833 92028 97483 80148 64814 2374 81891 25069 39403 39708 86650 82848 12899 65744 25753 94205 61633 16645 85867 32457 2812 78213 77524 5612 77409 48309 97157 81646 73578 10064 7483 28808 47440 28657 64840 60852 9669 54724 3767 68466 32211 27135 29450 79944 52056 24564 12257 33866 84317 18328 64269 25159 70210 44587 32416 53478 43973 54914 11524 37980 61143 78936 89828 39497 36630 84447 99296 995 61850 37664 61309 55147 18233 85937 85413 60800 47394 91403 61769 17899 73750 81974 59141 17993 28426 64087 19308 44623 81403 48156 47754 5969 35332 23817 36430 27731 98313 12681 24952 98950 35144 22451 40228 81361 59570 7342 16136 88157 49562 98738 5397 10487 68314 53131 20638 57736 96770 13544 45581 95352 96336 68019 24702 63242 33272 58280 88723 48486 48430 91618 93855 92796 84387 63036 1144 83528 16473 39694 42750 14659 33118 47096 97639 95541 863 90765 46310 33804 3676 38264 96497 45797 95325 16343 65180 98897 47124 54890 31795 33913 44624 66871 73342 73958 20889 47266 48866 35868 89719 88349 55943 88178 14563 42405 18356 30226 67931 67923 90035 1751 6518 54586 38939 47476 65210 83376 13052 4143 36484 923 10881 74305 84062 89711 54252 74449 79181 52714 30783 99735 96726 20681 78573 7048 83231 43716 91433 75961 15305 79258 47627 1682 96840 44650 64297 41312 65449 67001 47383 36074 20478 76068 95137 55178 66082 4668 55187 15233 33857 98490 51093 29146 96454 41193 45931 22496 53577 58681 97556 92371 95472 13738 59378 7152 44829 29453 82576 64824 68817 60455 19568 91394 41952 76383 42323 65363 27719 12388 42579 64358 19622 63497 64724 36434 45693 67418 2922 87671 34952 14917 75098 50161 86809 48367 76544 95654 2508 86101 64039 63930 52327 40015 52754 15094 49891 27706 12449 76338 37467 88701 4068 83550 78579 82526 21257 83389 11645 95228 56363 25205 90627 41913 89386 44562 77370 24279 17694 72459 60051 81168 89407 73928 93542 81866 65981 92086 62428 88534 91440 69333 94409 19083 46097 3721 9479 49937 22794 24444 38594 15772 17745 26773 19230 32191 30297 70776 45113 83311 97415 6114 45625 90746 64388 24293 30676 45178 42868 18312 88298 56665 65477 50363 89041 77525 80021 36124 24762 12273 41487 91380 72917 4226 86545 76209 36082 30161 20723 96033 66087 22861 97671 53520 95478 46197 28710 50526 97806 115 49503 40314 8772 45333 81625 77071 65520 2197 17134 6229 63242 37767 43673 46728 32001 68676 67180 46915 86817 74009 55319 91417 50305 58518 4128 71603 41838 50340 29506 98710 65793 85576 53807 11270 33627 54835 2782 22964 49208 79228 8 31641 78950 39750 93738 13052 25629 74137 42123 64915 16889 64338 68229 88153 82907 37308 51869 23952 34746 17270 65725 16706 98880 12770 26459 64748 74707 5389 71775 36057 1183 60134 30163 94785 66849 59706 63266 95225 82781 93381 85584 30082 14936 43446 53090 16394 47730 31064 28351 75270 71828 39466 97859 58582 85270 8957 83720 83590 52731 33112 9286 1412 75131 62952 14634 26714 3671 67576 24869 76059 39126 68475 40692 77030 20418 25977 72363 19470 17798 19463 15725 52214 19945 58978 76373 89099 13426 51068 39828 79018 53401 63014 3812 31853 43693 64481 77031 4785 85393 16340 14611 31680 32974 85272 67192 71887 32126 84679 31097 44491 32568 60277 23623 29436 76440 63801 40764 6075 25965 76122 57015 91930 13662 16608 50045 60732 75614 38430 23675 25864 34841 65271 48087 16186 36406 45600 81121 12028 18588 73927 15742 7850 94877 13439 82613 5742 36759 77915 95234 64367 52572 27252 8862 4031 66655 5851 32958 53970 58904 80496 47135 83112 24060 5541 44109 58908 58126 47349 90265 72521 45128 60862 18926 49413 42471 89198 33185 66646 63067 32104 42167 87999 50081 69776 76996 35067 46473 12403 23311 17483 62805 58948 79811 62230 8489 27516 15816 20144 80354 17327 28026 55768 409 30403 50871 43463 67008 91260 26335 99186 14337 18639 34238 35248 56042 86426 85872 76476 86273 43565 75321 68164 24690 30928 65 23693 24597 15597 14663 60921 94013 43609 80059 1559 81231 21029 79199 65942 11751 29789 27356 3696 50817 12010 7597 3998 1642 59878 23009 44676 25852 92998 29858 91949 92216 65954 59514 82330 9267 19500 5488 47739 84648 51207 62728 17554 19025 95397 72735 76533 21291 24701 13574 28434 95728 98258 725 75141 77561 89736 80107 93807 88733 53320 92069 82894 17186 7859 52610 87014 46403 91326 14983 25295 24049 15016 927 76022 63486 96457 32228 56441 5541 38174 34222 43285 24682 28783 71411 75955 63245 7483 56624 18856 88362 20012 91932 17873 8013 57938 23861 29091 38306 72973 22995 14333 14629 28021 39969 16838 47038 50370 45005 26501 92177 21441 96756 34663 64978 16521 29726 36547 92403 20659 67373 4169 31921 52213 59890 16988 2693 7492 9774 52966 15833 85163 94847 66092 77448 85721 55598 54391 37775 71758 9593 88096 16129 33728 66233 99901 59371 24333 80169 79822 31409 62379 5767 20028 89163 43007 75337 15159 34202 3173 34726 2804 98502 7223 63537 37490 26757 20891 99522 40246 35646 5095 92569 81703 56342 23241 40113 20516 86867 15491 6583 29888 99539 142 98027 1599 4449 30078 45458 98176 12518 66416 28673 32822 20584 4205 7061 91689 5560 14856 49748 62439 79065 80563 14575 28430 84060 18283 4001 8316 28917 22988 70365 29279 35716 94046 55095 48569 65256 87452 26226 46645 78153 1403 53556 69160 30143 44143 84891 87861 55417 69356 73800 28300 49984 37923 62565 26472 82202 39349 54603 82320 64683 72528 7799 68143 76511 33108 48773 94269 99827 12858 23094 4691 32165 87429 74811 21267 81713 51946 18380 62235 46828 12405 27984 76499 18389 92012 4480 52692 8795 58736 3387 60117 4862 14004 89526 24557 42788 76347 35695 42488 90093 72671 6715 73608 1378 57849 49863 53188 69986 89963 36455 62543 19771 35740 3004 42239 93219 72502 80127 74971 91613 89631 72069 21491 73077 90008 60146 52516 92917 27516 85872 51026 74219 84503 62113 78129 70411 1313 60826 54688 57060 53567 89639 89995 79368 64106 65945 32541 23968 25808 49353 58156 5756 31210 77731 48089 33211 75114 58204 12790 41762 73552 55331 26051 37707 45111 73529 97748 58639 39028 44404 13817 97405 84707 75512 3413 44632 46778 70751 59793 9218 60034 11429 42238 76392 28493 45893 82051 79432 84165 88035 5140 16261 68436 55643 89949 71674 49821 89687 10565 5730 8537 53082 75707 74413 75068 29573 71708 10685 34526 85575 84263 68507 16502 56719 59149 70648 54562 47797 44520 70236 97910 1870 93727 95215 20259 50470 46658 76133 9551 24168 10776 64582 62644 44865 49434 59608 38990 35911 79302 73204 16993 92461 20704 79827 58606 59402 23067 46224 78906 78443 22907 42271 96361 82158 48807 62800 10478 20833 52253 89711 44933 6508 67700 44655 70976 49955 60136 4445 28089 91550 62062 14716 40231 77173 18445 43929 76813 49548 63256 17640 44595 18630 64971 91801 96958 93304 36840 59109 61740 93874 19502 41805 82878 28769 58268 9263 61091 23068 89395 41009 43529 54668 19691 30903 19261 82286 87222 49600 12706 48295 58795 23275 1777 30839 46348 32961 87259 50219 27208 80335 12433 61287 3814 44909 16382 98655 38180 64903 5877 81566 5797 63944 12771 5066 44206 30096 57633 61489 67362 68197 15569 20708 17971 34483 31130 9523 27922 99182 76947 32666 70953 89487 55691 32789 94640 82351 51347 45982 63384 33189 55930 58201 43799 65658 65323 89265 64005 88259 49406 48868 97175 3350 25372 38436 49186 79159 34564 24496 12930 1644 28506 77963 2889 32319 13119 87159 49477 54643 16530 89985 42932 50272 87776 68213 80558 49007 88893 10152 40500 86196 37296 99637 2633 97782 41443 27966 45474 69390 13098 500 91515 89680 7650 5168 45682 76422 81592 40582 68571 92459 99290 93972 90395 5436 5822 54904 2329 63686 64227 52388 52218 56399 21822 75112 52914 47081 16906 27406 85281 43890 2348 42724 11573 25828 5002 88705 93507 79644 83993 7806 12955 69143 39076 68336 19998 8193 52258 10824 55161 1552 84920 48256 72684 72700 78990 85990 74668 1698 60761 42706 10979 35266 35656 36972 65162 73966 75139 50210 7134 61444 7144 44686 90660 53935 76928 99396 73906 83361 39610 57928 10553 35847 1572 23206 42271 25095 96142 12633 30308 43115 8362 65992 29709 48951 98805 5930 69547 78218 31020 14946 99156 86786 46180 52202 97175 40716 71759 76480 83767 97357 86438 7997 33102 12232 81618 27047 96787 76829 2769 27317 67872 18976 51068 87787 15037 5494 57764 5640 31285 93010 50523 80988 83953 82722 82089 60752 25446 87011 20172 10153 68593 87100 27415 97562 50815 66486 86649 79402 87104 38914 31060 32484 48002 7613 51398 70974 44899 8349 75497 60993 98792 30080 37530 39704 86077 82071 36670 55642 71449 46581 24938 27866 82498 61968 58001 20934 45693 53572 96499 41102 76976 48100 79857 52680 13286 8910 37478 98085 54997 77773 21368 42746 84785 21288 86879 59011 4605 44348 28123 75584 65938 13792 30007 30098 49579 84387 63157 63953 30108 32128 22596 68636 54578 85810 55588 34482 28207 91556 13278 96130 30042 98019 53542 80599 87119 83140 52375 94603 41333 58052 59707 14327 30477 84628 45928 88370 16590 50589 49068 3724 70992 73426 86536 32719 33380 88598 22472 41420 37144 3753 33560 43002 72187 98633 4876 86449 32897 53086 56807 57037 3015 73609 34969 47918 15816 15492 20876 62487 76483 32903 44201 34366 82213 59814 8060 69885 51997 74440 4406 74444 75324 60584 96939 68335 2768 65484 25175 82291 76118 32685 53213 5137 11528 28565 8446 80662 81558 25585 53796 6867 28784 59482 93754 55536 29063 1537 78635 83898 24829 45599 82648 26233 1025 85457 49270 30400 59949 25981 80263 73750 83901 61521 61698 37437 57526 16553 74925 46369 76562 59172 63812 38890 13117 28644 12444 90345 10384 35082 79958 81487 45930 87593 43742 56038 85006 16468 19641 51437 5153 21468 61093 87699 7492 2019 10403 14362 81320 15824 96778 22194 50341 91772 16645 22462 40984 85089 93355 12623 3335 54255 70082 50785 75930 62286 73889 22326 61420 57210 68498 30350 52872 28613 43250 68489 87429 17267 268 3111 43308 81575 32472 9070 15343 17694 8664 61633 93517 23575 58577 49824 96406 6354 22583 22231 8025 34832 68297 82705 29260 41948 43730 79322 97153 23156 34897 88287 84151 48726 84708 96728 74617 23716 89384 77719 8617 57553 31781 62175 35557 7544 53215 41284 36954 54135 82343 16833 29260 19423 74532 57375 35469 46408 22336 64357 90442 77756 56763 97260 63205 58407 52082 61658 3903 3807 85286 32111 42391 74572 31770 21531 59093 25974 27729 81505 47191 12875 12839 48941 90080 61189 37874 62921 19514 59103 79693 93263 47739 94175 19370 44480 55755 27066 41784 75788 44098 16318 96036 75091 76288 27437 36677 71547 5394 91134 37439 59290 21609 47000 37809 72067 25506 60594 56690 46929 80280 43183 32923 4724 11645 18367 97781 95416 53038 74604 23645 13745 13055 62166 66237 26286 86329 69879 2408 88092 7905 78065 80200 10673 50393 62021 74504 17365 3485 60648 35098 18684 32467 47737 52553 38848 96970 7613 42427 88476 49900 75512 8832 79014 59373 7688 23582 15398 73148 43024 22918 76675 17876 75051 23685 68975 47949 41442 21950 33571 9748 60934 93786 21974 84013 6930 80374 19335 2378 79700 331 23037 73533 57569 79500 97876 16218 37949 32759 48478 97228 17555 75443 8554 80672 64078 94195 77173 39110 89147 88025 2198 23633 11174 32542 77558 44151 34357 3490 25638 24632 42568 29145 20312 80025 18035 40250 41992 57909 43875 28176 28665 48498 649 26793 26388 56874 27327 92725 79774 67738 88071 54407 62622 87113 3143 7288 49693 85628 83599 35531 18278 82705 43332 27814 21280 54803 24690 59313 79941 30429 59924 70092 86845 91470 497 25546 4848 54896 5131 55790 14764 86393 88702 65524 72952 75811 38650 83918 23493 10769 76104 60401 35995 13252 45526 76899 40091 40142 87898 56564 46070 27 31522 79518 75309 52993 6189 41345 33988 79035 36764 97437 45450 40487 85723 88284 26980 21824 87112 36350 36045 20835 33331 39897 77843 27478 50715 29826 22763 35854 84326 85433 97852 19971 75441 71626 71708 14444 23581 22888 6103 78066 76880 85754 89866 9493 61175 85454 65222 54839 23025 31124 53159 67440 93459 62359 63160 27026 16858 47967 59015 74515 97714 93863 13145 31663 72170 21942 63320 49933 56038 69256 48287 80364 91506 8089 86538 27318 42404 6468 45241 14702 75159 48803 44038 82705 44038 14764 97928 79896 97243 68480 52584 89370 59140 3057 23887 54139 62177 32589 44492 9863 5218 87401 31773 83044 85998 39302 59826 65341 65889 15707 16974 78197 9265 14846 95876 51549 40160 67964 73758 82218 29274 20811 64986 48313 37273 37384 79110 78066 31609 52585 17388 32477 62474 77836 18484 39587 76479 71567 88887 20604 88849 60501 63410 64824 31314 58522 51789 23306 86426 39154 69313 74695 71419 72002 27156 52115 16677 52397 79636 91623 69456 91614 40615 6602 29942 11652 96965 86277 51446 99777 87675 96463 76421 90289 29233 86474 55042 95721 69904 52231 47141 60558 47630 7188 85549 50527 43445 46612 95917 62537 91041 92159 74955 59092 48358 9402 65028 96844 79973 74894 85185 93286 27710 80126 88086 87921 92247 16433 78667 18319 19852 5601 45437 68984 45913 91873 82050 9618 68844 12459 37304 9560 65936 7539 11912 83806 88846 91631 55777 6819 64316 94113 76459 15221 73230 39170 49790 8589 84142 25541 29430 2509 87470 72303 57092 30897 1847 30830 51511 95233 39691 92637 64444 42491 23050 5857 67088 85019 60480 82665 90933 31347 89871 80992 42048 35871 3636 91526 20100 69385 18847 33664 60121 87560 80877 8130 72634 57626 62850 55457 40128 40815 4930 47470 50890 16438 69605 59156 18602 9306 86691 27818 37480 72146 80882 62933 37185 79690 49514 93370 5937 51463 61363 60138 93861 97768 33047 45270 99294 53931 75760 51069 37669 61722 89536 7133 9761 64287 45115 63150 87312 32096 46467 87205 76105 71445 72229 3087 1955 83034 44373 81721 13450 36298 18240 64329 16313 71227 67555 5269 10016 35361 82762 87796 15262 79794 92241 35265 12027 55484 81086 68958 20439 52878 73772 17943 14486 22566 50321 21824 33111 38527 5434 6719 88066 92325 32318 59662 45023 35904 68058 90901 32199 85927 77844 9530 39808 83259 18859 34586 33776 27547 35962 20781 63483 57729 73590 70449 83320 78044 27058 16085 9947 27131 84354 94213 77505 86215 27464 56652 15940 94531 72100 76492 12171 55738 41927 12660 75310 37311 27322 15953 78741 64637 4599 79729 60861 6406 34810 79227 76336 57973 60901 60448 28487 30709 9964 90509 6277 88745 77904 40231 64666 16386 71894 151 55877 51018 39649 31685 410 46863 52815 62280 36461 6518 19697 91399 69064 97681 98403 56135 70992 51586 84816 42048 3741 12574 921 5466 21116 29634 66023 68042 43610 93640 91560 96096 78308 20228 10167 87587 33673 73661 43187 3223 88958 67100 63647 16287 40016 10418 91467 62877 28520 22113 3213 20964 15559 67377 73920 26638 50762 70875 19309 43222 17678 33886 84171 12128 72005 75113 9198 60368 94444 15849 88754 30800 18670 47666 41159 38443 58300 77908 99737 5026 47746 65647 27922 65925 41377 28685 34343 45191 80662 82994 82476 50577 38400 44647 29171 94838 86156 32924 55979 48792 10137 62361 32248 21672 31776 78445 43646 62722 78192 88787 36058 81526 34923 99022 30853 6530 48183 19902 8554 9353 1877 29865 3552 89101 66445 84648 11287 86643 63349 72110 54851 24214 7100 58466 34493 90557 52683 2231 81893 37639 46583 80706 5590 49896 70817 4142 8828 97243 6859 52433 64468 78238 89009 82714 56451 117 47068 27158 59217 38924 63959 28452 1049 17755 79987 58399 23176 48623 54638 59521 36325 49762 60199 91622 40572 90096 62966 44005 30748 19526 85233 77065 20281 77965 23549 39008 93660 68198 5308 56485 73713 23455 57841 8682 50044 17388 51651 72226 74382 6113 8504 85918 12297 7759 31289 33084 66700 91639 13789 54655 63994 71077 11637 29674 92752 96387 36393 98999 82387 58988 68258 32994 25169 83402 49160 46480 21422 60268 64052 85314 17998 82069 4428 90650 34231 15987 78594 79261 60816 28139 62100 60458 52454 33433 20750 61849 64630 86078 38774 23983 79340 79758 44884 21251 5463 95201 93991 74265 54222 58144 45280 97423 36432 85456 93566 46516 59236 56433 89172 9159 42602 93465 87552 4003 34063 25648 87339 60237 28557 35056 94339 69787 84716 45314 22598 64941 49230 38564 24100 44750 33804 30442 62859 85079 60888 54546 59203 16579 7700 97633 20627 60998 42469 54034 73932 96793 76168 86646 59154 22560 10618 32036 63725 52369 48277 94961 91162 8157 50076 94246 2031 97834 13160 90985 9319 42716 35845 90598 79036 30399 78851 88766 34837 74646 56840 30466 23781 68971 61390 60644 60145 98472 28664 65176 37442 64256 34141 12554 34165 44557 46491 63231 81936 11729 89188 13471 16453 84823 91649 30260 7729 71703 75154 57432 9670 86758 70402 10551 8034 82481 7818 41165 42079 54200 42487 20335 25945 40375 94879 22314 97394 98045 10741 81496 66863 80836 90896 12903 16149 33379 48714 62210 18914 67813 53757 31817 72907 71644 76279 67064 93462 44017 66223 47663 55787 80330 97636 84283 99146 53177 27772 27675 40149 34054 69361 79035 97145 89016 34973 60339 92477 62903 45070 28912 20729 28412 4923 23540 85137 7549 9823 66503 86624 53830 9445 43098 69979 83623 49925 59629 18009 73485 9537 96447 37323 41076 99752 13206 47723 42169 15814 87229 76694 6515 24535 87524 94404 9507 98164 28615 93857 8351 84303 27546 95602 38723 71615 3582 97078 17518 81949 96671 57576 69868 46057 6158 21879 80688 78355 65995 45571 5901 5725 1664 49152 1173 53160 18866 77027 97860 62149 86052 7436 31078 20200 17626 71199 21390 49568 39729 59654 24778 67036 31449 34719 22669 71331 50544 3566 62501 882 70833 79852 44863 48254 84068 29579 29343 97114 76658 57914 36213 92941 99779 32636 35009 39195 36301 17767 60665 12442 3685 96672 67067 20495 40851 77781 42237 6178 20611 8690 57231 16412 74802 79393 81749 29886 90223 25260 6199 11176 62333 62604 30302 6921 64552 3043 96188 35390 48903 32960 67510 60836 60104 57574 47214 79293 54745 40761 86668 73231 23641 8516 9080 37491 70992 26125 15860 25805 51479 40310 2829 54866 89359 96674 84676 48450 25490 17720 25973 3737 26413 68572 62932 74634 41648 21695 19283 33226 42122 95695 73292 86983 43517 21504 6731 74797 29070 97837 63430 656 78153 23860 29313 60752 81477 71695 73491 55655 19558 10235 32568 692 17483 14817 83105 53777 92929 42397 33726 6642 12766 59894 73752 56097 18764 67424 1231 99207 48710 47132 34554 70069 41492 31179 85266 73776 47798 3788 89593 73015 51247 49084 5214 60922 55613 3609 70077 18152 6455 80281 90069 60017 99477 33086 38224 27388 71435 65703 73431 83773 48285 58529 19012 20738 77020 63512 44542 27483 32659 31950 56657 91621 2425 5583 56239 39080 17108 61034 25068 71294 76351 5377 26820 8095 42085 11907 19558 78990 43323 84539 30660 18739 12331 99428 42604 2622 35934 21811 2228 21648 98053 54427 61655 52560 4081 1587 73382 22603 51027 28581 78576 48700 80350 23484 85248 15869 42765 17389 14308 98608 80962 13263 21497 94994 64005 16008 66805 79290 78715 78401 47924 8075 34632 98903 39470 23860 8993 3216 43821 46936 65524 70138 70751 3145 28936 70450 13620 95763 42170 17932 33478 97978 9218 13210 83006 70002 84159 62431 88600 55498 45907 98128 99183 73402 38026 7322 71045 72432 31531 58518 44139 52598 80201 14838 26953 80741 49840 61134 66212 7309 37016 32018 51575 20764 14601 72616 22702 74816 97036 44983 67812 94405 80445 92264 87407 73180 28999 26769 90078 49996 53820 70048 91743 65222 30201 29457 59739 52826 41523 19898 34364 18939 47569 61332 61344 5642 75179 13898 44565 95942 84551 95383 54382 44130 41241 45419 91924 45636 83721 45395 91344 2655 68178 19254 69333 53571 99592 31451 94932 65645 46555 29680 4940 5259 93641 29694 92366 64435 9687 46742 23183 21174 97729 17030 22794 21077 36266 55423 74479 4739 43145 88638 71283 46078 76155 69029 64106 197 78702 33074 48252 5430 87996 54743 52417 64369 71443 51265 72763 18805 78254 84499 75818 87484 41144 78586 64974 13547 32988 78009 19638 88273 65845 45697 83947 87737 76725 96115 26245 71618 89492 45503 23300 37552 45576 64053 41797 33655 53853 16519 68417 906 64177 75757 99161 99460 79306 53447 16770 61063 27472 65523 16126 97019 55405 29877 11937 43342 62225 62433 38896 20249 23906 17256 36272 79657 80884 75493 92177 26070 64925 96915 82444 69211 42139 46982 52075 19470 37368 45189 50223 54891 4099 77526 97026 73432 32176 30141 3414 76991 88661 30082 54942 57564 43126 11165 7247 121 61053 6487 80650 22563 47593 54781 49307 322 49954 2127 12328 71230 89467 31864 19477 7831 81201 13729 72199 47459 34784 26930 71146 46125 87868 52606 89543 4913 4809 24375 82232 14469 51430 59046 76038 32207 23574 4162 26676 67145 64009 43047 72578 22856 60434 70893 21463 31091 95622 91840 31873 42533 16493 70583 22856 73370 83115 33119 39386 49509 72978 54341 71877 48050 25477 45883 64405 75165 91411 11512 33211 83845 26107 72608 58271 57345 321 38723 47004 14182 57435 56356 2021 53737 18065 62876 46535 80190 30974 22953 73272 66600 70418 52112 16599 87747 91484 97964 74213 84079 30757 15220 89179 96237 88228 34508 64755 57738 27146 52455 51534 61404 1978 42697 73058 53231 95966 45430 39834 63748 13078 22609 34099 62192 67141 22293 61303 94112 20136 82371 28807 23373 80355 22743 33079 41889 6222 30679 36489 40166 49775 60807 96057 95352 95225 92251 26795 97950 63174 46206 22635 8498 13424 61048 2289 59485 55294 74746 29472 42832 34102 3333 75918 15330 82521 85382 11169 91895 71120 33386 85441 55260 95942 60451 62960 38475 47042 10547 65919 14751 43325 1745 74538 53667 21848 90794 36241 24721 20445 25006 55774 25079 87473 96555 73654 74383 45386 52514 18929 7589 12606 2157 33607 97238 14095 21704 71309 91131 77862 64757 18920 47509 46682 38529 81322 93908 34978 6502 79495 25574 68033 42239 44433 74771 99110 63107 15101 74601 92715 45050 16860 71807 64846 68957 37231 67967 18086 3688 34265 14425 19869 26549 52597 90007 14117 38416 68923 18354 95799 74708 64027 85910 50640 20473 15318 59505 13641 37826 84168 26145 49679 22280 86238 38531 60767 6244 45642 3653 4696 77680 81234 48739 47653 62672 80263 28449 42703 53539 27796 16906 49406 55169 79402 67941 74840 75896 50017 42285 88112 87622 90099 71677 96897 89360 54318 58934 28181 61299 36379 54180 63443 23821 45113 75605 77982 95972 2881 25049 80510 80232 95784 29102 89080 20249 82127 6039 83891 85503 42187 14166 52821 38795 16287 73958 70793 36052 65925 66180 57910 30128 68210 68069 48938 15582 73316 24164 69861 27706 67531 78811 29593 69419 80424 37293 1784 15842 11563 9299 85927 95249 3775 27355 37667 59036 88578 44786 71126 32373 61485 43849 93454 52471 34299 37277 82609 37224 47754 11666 66148 39408 6040 25198 99334 86579 39081 98172 99453 32049 81445 30279 44228 97898 55601 80471 48206 15599 38461 31580 71163 29444 43408 65404 76958 12436 8964 34611 24623 72094 77791 23562 8593 19320 55773 66833 47047 67753 11527 64610 408 50361 83277 33043 71357 76728 41548 45898 56487 61437 15912 80164 46321 14749 66970 37237 81114 52478 35865 99326 20821 39458 69151 9918 35096 21509 76514 4918 58452 88952 51730 64989 61943 62864 14961 31995 1070 55682 29630 20836 58374 81407 29487 13489 76083 21021 7388 66070 67566 82766 3089 20792 36457 91180 69137 97016 86850 79398 58543 73728 36131 36731 71521 3459 43195 81940 55728 49551 28689 9027 87350 34216 20812 82803 70016 28913 77204 55400 19316 91009 57002 58162 44029 93576 42604 7179 79668 80700 30195 89942 67847 58246 63240 80350 91128 9368 57188 45305 85506 15933 17393 66339 25471 38614 69169 72287 7270 24041 41272 3233 73574 63530 2853 71618 78545 5171 64497 31967 50373 64659 37514 33279 26099 50014 71842 33627 10018 79316 34647 87013 94214 35135 65509 44349 15233 43162 29145 89450 96327 50468 44968 11121 70797 8326 9016 48567 70981 58708 37741 7963 53311 93841 24161 20591 994 61856 17906 25187 32702 16544 21367 67077 97901 76637 87789 16067 19831 42350 51098 86512 38395 84158 38317 79306 65796 54275 92437 57508 56071 35848 51228 50467 56746 46287 91693 49847 12227 48463 20451 74207 29395 54586 12615 29204 87693 11336 79235 56185 32056 34369 30651 50512 66621 31477 33592 95252 69741 46731 39421 14112 87073 17383 57515 21756 19433 84976 57436 9866 6495 39714 88069 58624 94851 65705 96455 99349 73736 55461 61652 81194 68046 99184 87065 58538 87968 45966 29360 42362 70076 3091 41220 72032 71258 41879 1172 82044 45342 67576 88663 80079 74485 71306 18515 94020 19166 42876 98523 32696 61946 81943 88952 12852 20144 3269 69446 72999 94485 63536 94698 80259 51598 37635 53570 75031 24058 99951 71766 34714 90911 98984 18607 74860 31526 33977 59392 92856 11292 14297 76542 54387 19079 6284 67790 53772 27206 76988 7882 9310 21940 7271 36491 47631 85189 39958 99498 26698 20602 42107 84451 52605 33438 47961 63533 47784 50991 41805 75272 32269 84770 36818 88680 18018 44958 86583 59437 97760 85292 89273 79387 15657 19123 55251 96471 69210 78621 94095 34138 96037 38459 5504 10358 41099 4380 98927 26467 63960 80754 49542 75120 16192 63898 49093 52058 59291 33999 99323 84674 88429 66363 30123 386 22466 21625 66720 43628 90762 72246 78601 42755 58982 566 81448 92737 96706 4814 72514 49162 51441 87038 66948 42139 15811 56901 11775 54324 39095 12691 15342 30178 20272 57046 37847 61215 27520 7662 87838 18880 7123 57568 38977 81474 84606 84252 8309 56840 53170 51833 93095 7920 64417 53312 59361 5350 42643 21381 5776 14829 88511 90494 55366 17299 98527 99086 52422 56949 39262 95192 93709 6907 51119 96994 13900 1853 74950 51397 15988 80326 22055 30493 39700 68899 20048 76519 52512 22575 7241 97661 77947 45336 56464 43418 6711 56322 82899 9972 70388 84113 92213 75992 61907 70744 6690 29090 90163 77183 58257 49209 8742 89017 76822 21276 79709 97042 27266 95313 9325 1368 77741 28511 19147 18252 35175 98406 78265 93279 7532 80394 7545 88951 47535 97896 19745 29074 42388 17558 23032 43356 49187 53530 37180 95634 6766 56800 46029 11509 89946 83588 84830 26198 41496 87816 59157 39692 39699 94423 55150 43734 59712 96210 98163 6371 4951 70389 45278 23711 84441 76552 93428 73005 66733 766 10884 13878 78621 53368 90006 30975 53329 68162 40430 32955 91474 17552 69734 53478 81486 682 26337 11552 91894 62069 44930 58020 7185 35999 2653 82702 20877 36706 4747 83120 10842 8016 1689 59861 15545 65439 93354 32748 30275 34508 21275 10202 75572 62156 995 88360 97295 73330 81183 40005 56405 49429 81645 79912 18346 79365 22155 71249 18681 97673 78081 49255 97645 52629 85933 58677 37229 21323 10238 24946 51512 98843 82523 23005 7999 50172 95381 60808 33768 29080 26168 98585 51907 3349 77219 60365 42380 60476 27075 64292 58443 53429 61365 1572 87923 74969 71172 38782 70178 98348 1460 67984 76900 43737 80078 55673 95359 55752 50541 41465 74068 90978 1636 53482 22367 17542 79541 70274 95001 81283 79474 87970 65879 81882 24468 94541 73766 76240 49148 6522 15667 17288 43896 33597 36111 1248 60146 24570 6753 10583 764 20917 53555 34157 12941 33689 44190 3739 72645 92251 77936 9682 30516 31114 24667 33653 94735 80291 75210 98439 49451 78898 13320 79227 45634 58716 41148 97509 74784 74060 14387 60487 70535 75558 3711 7236 85142 98679 22150 60669 16014 28340 92522 60441 34143 55746 12501 70771 16866 65917 23881 77988 7916 38678 23048 47559 63838 3667 26565 42227 64187 11887 97115 2359 80090 77177 71397 93392 14718 53091 20602 19985 66773 85512 9721 94087 56634 36798 72166 86067 37527 84067 89234 93883 52499 86229 46591 17725 4555 44489 89946 9392 32154 44933 78128 75284 55278 6462 41257 25824 41319 53247 15465 49116 24250 48893 49904 74105 38258 64137 8346 95772 80253 33017 95763 75548 45652 5617 16833 5999 87903 64362 34951 53415 90841 35838 69459 39028 89167 22633 60698 94422 96354 83421 45935 29343 372 56871 13948 84806 1755 35682 2697 38206 73325 83847 40551 98869 58197 64446 30985 76871 19505 54797 80868 46900 24068 77630 77554 2934 49514 20449 56857 66303 69668 99236 27907 31082 68856 51892 20716 76241 80220 63638 80279 95015 3376 89380 59619 55527 90855 63227 85348 23981 54445 78076 54398 89653 97881 32527 6768 61558 12549 10777 27553 95941 57934 15138 78822 56850 7121 99589 35783 15049 9893 62782 90955 61987 3743 70412 84867 50071 17199 60882 89496 67243 21957 68432 8244 92709 19868 36284 75584 72740 29972 32104 37003 10560 59112 24944 65988 47166 22582 27122 89925 523 80228 27710 57214 51670 71602 93592 78420 16176 91782 70593 86921 70328 97428 43677 21265 36806 56555 28096 72740 19049 86914 42486 39499 60350 35677 63261 8167 21293 13027 75906 69072 91362 97720 29356 19543 849 61074 96802 75728 75411 44516 70392 38069 43117 46559 52957 73005 27479 1154 46708 95013 90112 2063 70379 14290 74534 83114 54232 45262 43444 79614 68100 80342 24221 57490 65547 62658 92989 19594 29518 76124 1512 99973 81849 19846 94664 26183 67455 31730 59471 2580 66307 29000 24276 41448 55961 78609 39224 24519 44540 12077 60895 8774 91359 27682 30755 77460 71311 69455 36092 185 64696 61429 79707 66584 84241 4924 13698 27905 31191 38825 85435 99437 99277 58179 77943 17249 26039 74561 44601 27535 51027 1668 15366 74674 43818 39681 8 32651 83273 58280 8381 77137 30229 57859 73597 22503 79727 92 71518 34368 60650 41765 18769 13032 45499 61798 86294 41108 53070 76436 18827 48223 28552 15590 87221 6465 96469 40355 96859 40512 13883 64929 59622 16211 63777 88195 69441 83439 73176 4698 41997 42737 43991 52185 23273 86175 79497 64193 38196 56458 8500 98605 18817 8501 82081 44841 54883 82175 83392 69045 16966 92326 82808 95189 16938 46291 15053 73979 94037 61938 92786 56036 90289 42591 75155 51833 5547 18792 21717 51455 55789 34708 59428 83803 31122 15614 50567 59438 99863 46705 59396 64818 16029 13365 82323 9079 197 50915 79282 27896 38438 9819 36555 68507 68829 4177 60451 67752 76214 64357 1368 96686 60690 31817 85230 71094 27557 61624 9093 39303 34427 41616 45494 4431 27249 75412 57098 89342 96684 55860 57166 40121 55045 86773 91009 6736 75695 79915 69210 77982 27553 10726 55801 6218 74562 40031 68898 83274 59603 56182 64539 36375 84918 50241 15775 39395 43421 24407 91588 25769 67173 84656 912 11467 41785 19028 89259 936 18406 62591 64540 91101 51595 30720 22072 79529 14924 61658 11194 25882 18249 73996 12775 73742 31640 19835 5291 13434 16432 8123 75950 16490 72731 91684 6339 45349 85462 13317 53292 52580 63074 11429 72866 11837 60420 96246 52850 21299 29552 50806 93663 36849 94112 54994 22341 15744 99387 64939 21184 48738 27556 63794 84811 64541 69820 18817 15215 48553 44110 62099 56538 78181 72325 10845 61237 92608 87828 55325 84213 98246 70220 73361 53741 98853 90722 13396 10775 87980 93922 5949 22193 57225 85693 16550 24104 49283 52915 58234 92460 40831 40364 23499 57208 46606 47302 74082 32175 57680 14466 90421 63391 43761 28816 83939 97201 37466 43002 72276 44653 40396 32459 99858 26890 21092 75908 90867 25534 32623 78665 52212 64990 47410 705 73858 71272 92463 79428 7241 49382 55049 4543 8746 37627 32595 45019 53151 87609 73522 2914 38884 4041 93161 71472 27509 68811 38078 8819 60071 58341 81916 18073 22264 62045 18736 67457 21398 55092 99807 49396 99373 72121 9137 1200 61899 86807 58093 66819 23401 87549 40878 31651 63637 93756 58255 96639 55535 8847 49260 90091 69685 27138 17490 48061 88806 46824 51329 45808 53091 32447 72919 63694 64226 81656 29163 55659 51867 99176 37703 14599 39182 27966 90057 24250 21860 55754 65898 85232 42052 18126 61609 69508 28248 67055 39574 70012 60889 88721 451 45644 92004 8962 2317 75160 5577 22484 8746 44852 4879 69318 60033 55209 17133 29070 23470 26142 54281 31033 22092 75592 27588 631 61803 74555 7543 7357 67169 25947 63942 16002 40777 74130 22831 57882 88521 90820 62995 20992 5428 53629 92300 31176 69499 38319 94303 12210 96853 69402 73276 97850 46109 8058 17996 98375 95942 994 14377 21675 412 99624 1688 62877 61266 59434 79131 79845 80019 12051 73 28079 98000 49655 69069 60 48690 62814 4836 45040 33251 20813 26759 95658 16811 91515 6548 17706 73390 8417 4482 53461 20872 92901 39496 29226 9839 44983 81058 41916 9021 29524 59417 2557 57990 21646 55652 52886 3861 51455 28659 74669 75565 583 23723 70267 77832 91209 48231 79918 52600 6681 33536 91220 1415 59320 83374 49090 29152 59722 77467 67817 70245 34539 63232 95306 66024 58380 48754 27916 30568 67698 23294 88876 66994 23945 94597 85114 652 42572 31754 54425 56396 15170 80013 69431 72732 79707 74686 76415 98713 53791 41203 92123 55670 66579 99915 47941 2714 23672 42216 36220 49810 90423 50979 47173 20566 71929 45245 85935 48519 1237 87140 51275 80138 32509 92206 94554 73719 32780 26067 30932 83669 40107 3507 60160 34091 37418 19016 82687 54247 37515 24449 34603 52637 99166 60464 13177 22998 62624 18485 23067 86811 90834 9567 55406 88482 58141 826 36531 95188 51583 97049 81260 77499 79734 11378 80081 65274 92623 57289 97300 43969 78645 51942 47836 29785 74951 1131 65243 65651 15905 90712 68682 36021 79385 60163 20933 94868 86028 85895 33149 58799 70267 78376 27377 97407 73497 13779 42911 14104 24988 29223 94237 41399 47785 65357 92442 5251 12385 78513 75847 7538 8952 27726 36845 63846 84492 74905 90975 39451 17425 44013 16421 86257 22677 70263 7465 39460 1760 18928 26669 37674 81036 50748 79421 22531 96947 96273 58998 63147 1877 85375 64098 47124 56391 41049 98621 74097 51578 66568 27813 37021 35146 52305 1380 47725 20415 33954 22200 50648 6673 5957 41783 83032 50287 84861 2100 74589 60300 2362 27416 98718 32229 84854 72783 90435 39242 68528 34787 14272 21636 57546 20857 55328 89764 6753 58412 13096 20903 19981 61309 85 36377 11823 33495 71353 96726 77077 883 45259 469 80571 10701 28023 77255 78151 22771 5182 82374 9523 17433 26834 75615 45299 98812 45978 98750 13235 81422 20060 88518 52368 40127 16454 10945 6195 24660 5025 40423 72101 55685 47865 83320 45105 29418 22496 79425 86867 25609 83691 3149 72497 51210 15810 6432 39831 76666 28091 74148 34330 65244 647 38604 74038 84995 90691 96664 93590 63529 48188 28434 15439 59944 31198 52642 68021 17664 98983 23538 73371 11536 70528 53708 92484 73299 7862 36961 4116 65006 75498 82860 8484 6757 6040 84980 21990 15885 7635 82666 44820 23427 74613 75717 78827 79748 55455 51815 33075 90453 64774 47081 86521 24921 38007 13417 67234 91488 70434 56392 59612 92593 68380 87149 77972 73732 3870 27223 91626 1295 69894 62850 19833 81444 86721 77245 23009 67247 69873 78903 21532 60939 65347 95480 48784 50867 40615 49776 40739 51775 12541 81485 35866 78381 73209 51189 20531 917 42853 98349 56293 48542 65948 55862 3529 83175 64462 43262 45119 15213 25476 19966 23655 18490 94520 28302 74229 68250 46643 52644 93890 23292 14383 41613 93799 67301 19409 17823 22479 47274 26868 43868 74430 13602 43305 45289 80397 43885 2594 79571 72966 84043 8527 17702 4232 2107 83241 26010 10226 27493 55469 3097 11868 77183 23308 93209 89883 16360 85864 8568 24086 30116 23411 51954 48701 53057 9906 16568 88839 13969 78667 38324 49587 12084 99825 5240 34322 90008 32331 3260 48381 24011 62803 88860 78780 88208 43367 25084 68413 46471 71609 2838 43707 58289 97428 75960 20951 1658 34779 82439 48205 70845 9948 36201 60473 82645 82061 76560 75435 83139 29973 46582 49973 59803 87451 54366 67410 33915 25440 15293 13709 38881 75439 52802 7827 47740 36648 8317 93258 18929 13253 44365 40257 14396 24605 6182 96607 67080 52442 38278 46279 82734 36093 14364 728 49221 9529 89730 25148 52304 49070 39373 55150 68044 56679 62243 21348 45517 29043 24185 42303 48133 85043 62823 82994 68869 63261 32235 50863 54183 42673 97160 90560 99373 28059 21836 83644 23642 22161 17451 8674 10401 71397 71089 46140 5182 68362 35680 22456 5245 10566 38480 52765 99563 72872 51587 11977 1506 2824 67279 39518 62106 86652 31150 35747 28697 74565 34699 59649 13907 16469 31358 69285 22652 14636 39197 71987 31427 86430 88992 59388 50843 66126 64501 12524 32026 995 15475 81235 53831 42380 33667 68051 25637 50974 17135 15065 8541 41431 43516 16100 74973 25241 64491 47964 20486 24016 34828 48023 95757 82418 66239 48335 94264 40647 56386 71895 47498 21011 81819 58251 12762 34707 28808 45779 54932 18406 31777 87409 86694 91341 10184 61874 76860 36824 27546 58149 68401 765 51603 94537 25257 64075 34035 14521 97375 51271 20588 59235 57899 8357 50151 29156 44334 60711 49752 24301 64398 4817 60210 68779 75116 62792 87333 57046 97429 76227 78143 84708 67208 34720 12162 34978 61448 42184 9354 54233 15772 70042 96642 23623 4077 7228 66848 99531 93495 86159 96950 90510 50205 11358 37807 72756 70223 5067 24542 33549 30705 68962 619 85250 24861 9706 55183 75609 44455 98112 85721 38390 9473 76628 27059 46990 62141 13860 76582 31771 92884 29549 72590 35674 26164 10608 53318 11633 65311 87026 92294 51808 44841 10611 62506 35637 12051 55337 84854 43528 16276 70 26075 92981 61692 5816 237 54047 89434 77915 22732 97388 5085 14951 92540 11066 36241 35828 71713 28153 87326 49617 38376 72416 56735 75321 54303 42986 30706 65975 7526 50347 49980 18578 4747 56010 94004 3149 70047 52916 67728 72246 31839 47866 24532 18944 33931 41474 46092 44697 75394 89660 36418 94099 30250 13321 32738 87376 53571 13962 13619 90505 37160 53092 22544 43948 65779 71925 3657 36628 64392 83146 83730 60019 33984 67299 96379 7127 48474 71972 91400 22003 48933 62734 50479 82500 88718 77603 74299 81937 82395 85372 38811 77122 48123 82444 1849 77209 99888 28925 55807 17215 43285 91178 92456 24853 22664 56018 74310 79428 3090 94882 19853 31099 31535 18411 81885 30268 52993 90110 96216 51527 73493 83037 13398 60263 82211 26685 96142 16137 1657 82380 32065 47146 31821 28512 6792 87989 35183 44373 12505 65272 55808 94772 79221 48400 66791 255 47319 17803 14018 67922 42424 17558 13496 55119 82465 49723 38823 93814 21277 37792 43243 54886 93450 71675 29683 26070 69510 75878 81645 50973 32763 44118 79592 47704 66345 93425 13502 24489 95647 60808 54145 45510 13595 71317 75591 57991 99591 727 36675 42345 9033 51548 99141 50295 72649 18692 54419 88629 26940 60280 96060 59587 87781 60806 74081 79409 4851 40352 80069 70293 35932 27604 35178 47510 64239 53854 64319 34975 12072 74981 22025 91684 80071 1394 31890 38725 31962 68572 77712 89437 15850 86567 72491 147 66065 84475 54826 70396 17438 80774 21767 20563 32910 37776 50858 95309 93634 14785 24103 16227 62507 34498 23348 39892 71435 60340 33087 593 38501 9179 61457 5896 11466 6482 91009 6392 38232 91623 28304 85249 52056 33978 20654 44534 76222 4054 32395 27731 9516 8883 89783 99916 20697 14647 21187 8901 96931 61162 76267 18181 51564 39280 14671 91434 82323 73304 80512 68272 38006 85383 31914 19610 50172 55221 69252 11108 88342 13616 51531 10411 15720 56589 51026 36399 91234 49617 87357 96755 29435 65282 88404 50035 3441 12378 15050 31908 48101 31530 51934 24417 40262 66173 50978 19805 51506 99105 19383 48919 1899 50240 2954 24195 86949 54449 27429 67900 92739 11673 37189 3707 47412 60555 29830 13025 99383 58797 75759 23999 90857 18665 38546 96792 9978 61345 90081 31556 34886 56479 23488 98974 65019 86524 34296 8858 708 82186 66353 91321 21441 30036 50931 87486 51561 98449 52448 93405 63806 6071 72572 17479 75154 75391 50874 58714 46299 45634 27479 52747 69359 9655 6698 17382 26791 70253 30791 55463 50673 40595 96664 45584 15770 83594 11413 60990 79487 14902 70856 72036 10822 2151 86921 29933 57146 10363 29535 15002 74295 66096 5594 17281 96819 68780 73540 71940 29452 44501 47677 43591 58891 868 61515 20649 81961 71960 16105 27372 50486 55338 52814 64346 6967 60149 73332 86171 62858 29070 68904 45775 372 49611 85130 62313 9672 26169 82966 7682 91726 16166 34792 45744 30222 7783 24597 32899 83603 22610 8830 69839 78180 87443 5039 74334 10941 92319 99500 99029 84873 62410 28457 77081 69682 40880 20760 93309 26443 11848 88803 18585 46418 42147 46799 54572 42613 43397 61069 40892 17265 34021 95365 80084 50823 51474 11647 35495 71835 69851 13364 25109 3667 60137 60294 68437 76938 12259 75151 87747 61061 31646 29119 24556 63385 42186 66225 75238 82123 89609 91190 74570 57464 50394 42953 89752 43786 61410 4564 52779 14576 72865 27678 36473 51311 92717 8414 83279 2427 34000 5157 71393 36612 34738 33918 62842 39600 92459 89181 61728 60396 99294 74907 21725 77978 93189 60992 92722 34075 52246 9963 95078 87737 15401 60607 13673 24271 8654 4713 43910 65949 80177 1643 29873 7363 99106 76915 53716 37061 95735 80538 76244 58872 26959 67168 63985 96107 25116 9702 67254 36227 77582 8439 97428 71088 2136 20640 8740 18463 9682 51837 48866 99204 1632 22739 89782 55888 35477 88708 17156 62643 37759 47565 48846 99320 45160 61652 69253 79119 17504 11693 56414 50134 39634 12490 33845 2124 51419 39441 2025 81105 48899 46405 90886 10265 71725 87862 97023 32183 43308 4461 5218 87505 10408 19392 21663 21208 45078 89353 64494 49219 99811 15079 66376 13047 35138 97933 10356 54011 7536 53399 30873 15482 1010 7894 93043 69064 80596 55567 19166 99345 44772 93988 37688 70889 15892 79755 61221 11871 39542 95735 84699 8476 22419 48540 7443 94491 13059 97705 45700 5508 72737 35680 40133 84797 97908 73239 52178 16430 85051 66842 35958 58472 82022 90101 34699 94615 76313 66612 85901 37699 52703 12107 82303 6039 98935 76296 81273 94802 11329 24901 31383 54829 39899 61030 57936 33734 65406 52156 37102 98171 39412 17382 88051 18240 61323 87698 52358 8489 41311 76593 58276 16588 98369 33881 27275 43157 24857 51047 58389 31102 69686 66735 98605 6612 76102 84569 16309 46803 40610 94370 93832 1362 96857 98077 61711 89861 64326 92315 7295 12655 25980 15361 26959 12778 95610 18129 76238 85517 13614 70957 84185 55648 60203 81941 1768 43248 34157 30816 9518 51871 94536 49196 78972 35377 31546 46156 64157 46457 23855 48056 40497 80384 49755 36727 91917 6679 31983 27692 36838 74612 24453 25684 1580 89139 68502 73463 65881 47767 17313 98248 5032 64647 49239 15794 57324 22553 32804 63585 78431 73497 72195 72809 59226 49206 38595 27063 54241 44811 48374 24882 67220 94760 5649 36421 73079 90214 56803 99699 31768 79248 14870 17984 92004 82726 66233 33351 5401 71961 24411 26717 61758 17584 56882 22116 84662 1482 3483 46219 46509 17623 8247 9599 88887 79866 36744 26886 19671 50944 48507 70011 56625 72788 93667 29193 29889 38502 79833 29785 16010 54636 11034 53467 61866 27988 19387 67094 17184 47462 70008 61830 71513 74966 48483 1651 53991 56855 13974 68480 17298 62305 36940 93343 7903 17597 2556 42316 83845 69546 73142 85847 29616 47019 16550 35968 66585 65481 48783 410 99832 76948 56966 48084 40349 1674 59264 85262 16986 21068 22605 656 19331 96073 45528 57798 93371 12838 63001 39891 70612 96238 50417 52687 22066 46045 14910 53975 99779 63923 17979 12034 79076 48576 39805 97948 52740 38672 51150 5582 94681 82214 67362 88890 34756 34407 27272 91780 70259 40770 83539 93792 41766 61292 88574 11418 74758 44289 49870 51683 62007 7281 1501 45064 68045 1637 1972 95363 28055 98532 46750 27342 84279 20697 9584 16755 96869 19306 5449 33360 14873 70861 11763 71245 33740 84992 62476 29724 8176 9723 48752 42139 47866 27170 4643 57979 12539 43107 54289 36045 59813 17178 8318 61260 92684 65792 33820 24230 48564 33401 42675 28672 99282 83797 85988 28504 94572 72382 81683 41386 34853 25849 70648 92279 80629 26706 38614 84238 90019 34286 94392 96254 61038 17302 76468 27435 32800 1582 44207 1385 24336 51600 51496 14541 75349 49168 79884 65136 21601 70679 51489 25417 13726 77719 11725 17763 52127 13429 94295 3439 36233 24380 94566 88237 57214 64781 25717 6484 33059 7698 17918 29240 30760 29906 85144 24042 44645 29078 62570 63705 30730 30730 21458 33164 18173 95832 86263 93397 97483 21077 35686 97711 79437 90307 45474 6461 16459 81614 73917 40804 90815 72148 14914 30516 70504 39521 22148 9661 36693 73879 45513 39957 97530 39226 42390 7878 12027 22887 1375 8137 63498 70762 1215 27702 9611 49085 78322 55395 5942 45536 13409 17911 7763 9199 15629 3757 3894 14112 37951 14466 54831 48938 8069 33 86102 96519 79679 92394 83952 34168 58498 43848 92195 16405 55573 7009 46757 19991 14488 95152 17541 10804 36714 53480 81954 71084 56721 156 72196 44167 90273 46211 71498 18320 97588 82058 66547 50032 69864 24483 84467 18619 43808 12388 72897 54809 84782 62684 36794 47483 60404 11847 55564 32125 74242 36023 48158 39570 74091 71324 20462 55765 92766 68176 14159 30496 88017 58182 79054 17992 85077 58388 15847 66406 70077 80737 37426 78064 38869 32490 79285 80668 76514 5202 31353 3196 66998 69420 60638 90922 59368 16247 64730 13498 5214 28209 99075 75581 71389 32845 27197 29065 22779 56146 96406 81227 92616 45102 16100 41642 13294 59490 4250 47117 66969 89336 99468 89635 80728 47176 38200 30596 58264 72547 36630 60926 41227 66695 63488 1629 95140 94423 37195 74073 90418 76848 94043 94088 48987 69778 99054 46938 23503 93727 39639 75596 50409 88241 82556 47275 84490 80339 63777 33789 18498 68273 87082 8311 51607 69998 39067 29919 95317 68681 46340 42796 85811 29923 41958 88564 35317 48611 35978 85660 55651 20206 39383 16660 42113 54513 81725 36323 4561 78764 68574 38584 28766 70115 65217 38166 96516 92901 26404 12336 25302 10876 35040 97825 59152 19549 7846 29917 5775 95981 19102 95087 22221 8792 36698 86978 60792 5882 76484 42693 65211 97696 18682 31787 66789 70448 49045 74039 3019 53806 57311 29946 21123 71344 58332 10995 47162 69242 92534 22361 18304 95423 76973 10711 87577 78998 90046 2801 26959 53782 62989 29513 83270 79228 82491 52832 64924 2955 13629 32000 60577 83449 75469 26924 63686 83355 36395 55181 61330 87086 40464 56920 89137 10413 93606 76090 86922 13530 80025 93427 76001 78662 61196 50828 37754 63252 3507 89602 70589 47930 50415 65351 32457 3693 86257 77979 37975 6492 77879 83775 60507 58468 41794 22718 14154 34832 17741 29078 66284 74935 64046 74982 80892 44097 99682 91147 75983 58751 4349 21212 45505 79832 79685 37756 41172 57760 51825 30461 93029 6645 50350 51749 61910 90253 7012 59193 9390 52309 99312 39850 22860 26303 15638 519 8678 75882 49501 27538 19643 55420 15244 29822 51238 3728 43043 74367 87759 63125 66439 98193 39336 76711 74748 4496 89043 12966 16439 77188 26890 25954 98080 13514 13909 21399 5157 72393 23665 28090 50871 2814 14328 56117 77215 35364 98236 28300 41176 61629 15793 23018 72328 67251 3361 37582 85575 49886 42322 2794 9573 53111 923 74395 36470 70962 89312 20304 15271 19645 99578 47881 33095 88345 79655 1451 59063 77757 82979 83743 20538 26326 38719 10115 50849 23178 85740 8527 65886 87482 58694 75600 40980 30000 85472 96858 75555 88583 53375 19652 87988 57391 41634 23372 83566 93091 70139 70090 50621 67895 5319 32187 67917 57779 35373 70524 91068 51146 4258 75854 39506 44201 66047 29089 88488 33678 80723 92985 13637 86213 96145 25408 52361 80693 64733 85115 44407 59711 75437 68189 1693 41666 70499 54340 45735 78130 40111 21504 92589 39039 31834 55364 78264 74520 431 65 39898 5682 40419 47419 97155 4012 58277 9390 88093 51704 42552 98514 88646 51153 54782 79962 70825 53093 40099 58993 47024 44822 25252 62926 28166 86282 67938 64912 24462 14478 54930 96997 73300 94216 86947 17052 32736 7925 51792 96811 7829 86388 31514 20900 48136 62112 61665 94390 94784 74735 2513 19898 40319 95103 132 47919 58960 42595 73485 23084 9392 82654 83993 55927 59084 89771 82999 84425 69007 2453 63062 28246 94001 48083 84441 54434 4459 68149 24579 51323 30616 73060 53234 84658 74353 56242 55092 19329 65166 25911 31291 94774 99676 58424 99025 4363 97991 5724 45701 9003 50594 80901 65458 50784 23197 30147 2209 98971 52181 80112 69660 86026 99453 22641 79302 13285 66422 53616 70669 77692 75241 40059 552 94780 72402 45532 36284 3595 89589 90095 90023 43434 36485 98616 97728 34516 83116 80927 93346 56878 87516 27815 57977 70998 403 89513 77345 99087 86473 88353 76077 85407 39720 19098 54452 90213 94475 20736 27297 15148 74736 37376 47752 71133 99462 42311 40665 52587 70501 49084 95451 58926 69707 59818 2262 90480 58717 83665 40788 21386 30161 45589 55491 3471 50800 19640 94180 94984 82225 34524 63086 95503 67858 45492 58495 20405 78159 75760 58817 18005 90783 27364 28381 17094 11723 32884 56902 19332 66523 349 46247 50203 10357 97122 33144 52450 95234 59377 90237 85184 52438 6345 49238 82898 67670 59590 12998 37772 90182 97564 17270 55976 33994 58037 40483 3727 65489 36586 7729 4248 76038 63104 6226 33706 70263 54864 52908 75985 27038 47501 91468 1928 75541 51177 52912 69480 62277 50680 27797 69554 1279 69238 71857 79750 13452 42998 27281 14390 2640 79106 71211 80560 33854 74749 90308 27502 55731 48763 74011 13697 39309 99763 36737 88893 57663 74457 81077 96741 84879 22494 1705 76078 44654 19027 65029 80987 95939 70280 57148 66015 1236 45375 93243 39857 35581 18418 80683 52165 55218 29253 826 78196 25499 49483 13617 82907 18725 23641 81661 69512 40660 560 70717 37967 14985 78893 3895 36831 51249 19752 64830 49969 82129 74604 96703 19027 4067 25874 60220 48397 74816 88379 72612 20374 74545 31476 19427 59126 65430 67973 37391 33465 90037 65273 68484 46560 11721 78282 35240 61490 38482 8478 79694 94280 24288 82946 37653 81517 73547 62915 8680 59331 57356 11171 64350 57280 94060 86883 29854 38604 70505 13512 67198 26477 42947 88590 49406 89260 5727 69697 67147 50694 87471 67595 18722 61749 45609 10156 8785 39792 93197 76531 38025 56790 8786 68943 18662 337 12496 21091 3772 71225 40653 48024 86283 16123 93716 14557 24345 11438 44518 69110 79641 56476 60389 36964 79548 70487 81360 86811 95840 47998 52712 36593 17208 27437 25242 40635 71 8842 83049 79882 55730 62153 27028 47970 50071 9274 66026 84459 35975 40101 77589 69373 71892 71130 20343 59479 95523 43681 38874 1868 89945 78330 41678 19096 65572 1539 11124 82271 38885 41608 16198 30120 20428 19481 21591 767 62203 89694 12873 93413 42656 60680 80069 24266 18521 38302 41196 35252 3820 57461 87998 83119 62462 42806 18262 65906 87890 54175 99401 5975 20582 5950 39580 37356 11972 26058 69965 24163 55732 17303 73711 76962 37500 59049 21021 59576 75169 68179 95631 42969 11734 40916 6626 55197 52629 11975 47238 45256 50776 48336 12232 39998 95705 22706 2929 99193 51526 93167 1873 77686 71059 12192 10561 35679 87959 82215 32437 25458 84768 78351 71089 97296 23135 20303 81985 44154 60899 48050 64636 90198 54682 85975 50210 96379 5904 72449 32134 16549 62486 96654 26568 67283 74465 98083 89888 32657 11404 94321 38378 67716 54662 69539 75242 90233 50951 58024 42618 94163 91830 99707 27531 11159 54901 16452 16097 55096 16749 12836 92222 83084 91469 59446 40841 82561 91022 52954 41062 44486 89645 73727 52410 49446 97769 60706 88430 17098 29991 24426 10279 36565 47935 24568 86559 46489 24266 15508 91382 53326 41209 79758 64125 22773 73390 4156 8759 95571 96296 27305 92086 76846 96319 41416 83608 25500 24335 88531 43669 25575 7784 94343 717 32171 24254 78335 1212 61812 8488 89726 84363 5860 33968 42460 9642 60957 90680 44466 60756 45955 66083 84221 45915 92549 77308 23385 59243 88200 13542 12664 48421 13289 75526 24583 39374 81585 15699 33252 89184 63303 15568 15050 67681 99611 88439 61159 82387 8033 31739 79224 28912 96738 91174 93831 70075 14766 45597 84154 24690 7052 91419 23269 55342 85258 78854 7911 58617 96688 33997 49524 48267 61680 97744 18749 10069 64610 47065 47133 27542 20896 70611 67925 89214 75159 21815 79534 84778 48053 78883 2058 16508 8853 16788 38353 22536 9544 86998 7944 47297 95920 88117 78958 79207 6570 8852 45481 49034 56147 26026 74593 57401 12000 41817 98295 78197 31647 98267 53551 80182 82367 53392 3705 12838 56419 58563 7436 55035 35031 96282 98032 85876 72227 14644 30801 17320 30852 14700 84014 4638 76512 79400 42322 4172 6163 37954 93960 75440 69713 67053 36840 73819 22085 65662 45287 40449 87401 58003 86488 66422 47662 16287 79601 42404 75128 13370 37582 28893 73160 2234 98316 79337 65582 77909 96354 54081 77678 82480 91926 43001 47576 43405 44921 7640 77635 44323 61216 83539 67480 52355 39898 46839 88969 20276 44327 49592 34297 87884 50503 61187 12466 82716 81668 49186 7638 43702 98053 17041 83063 8514 26493 25554 95530 79996 54108 66740 55329 97065 72532 2043 4306 51199 30202 55835 19503 13827 10461 54904 12053 63122 41228 44055 31370 71988 45376 38842 42773 79496 53431 76821 25168 5992 87654 98884 91357 33306 49166 47063 4617 38919 68920 50888 72764 34313 53500 66390 21274 88788 74627 49532 18354 51780 48229 45724 72770 29012 51538 87022 84434 20049 49835 38015 73638 8482 42862 34336 98731 18035 64914 78896 88282 99780 39825 68619 4611 59043 85218 62213 74472 10926 72809 35423 73812 64982 86502 10095 22487 40946 52125 28116 31855 8575 88088 44141 84201 72416 57308 92557 93118 15051 22088 98525 57668 26215 27290 93332 19273 77454 43274 69637 62082 2314 72379 78523 82917 78255 81073 73098 78755 90449 19306 11678 89040 64333 85480 10153 85196 40215 49359 37776 42168 39518 76944 38115 97674 50941 3471 4798 93569 15304 34537 31081 46264 82646 32572 94434 14351 31304 15013 72417 39155 4843 4021 71001 51522 9191 45637 59445 26309 97122 44004 72320 49933 12043 98186 93516 19904 34360 53371 14592 45027 12623 2767 11557 53301 82487 39529 85106 74037 79646 67332 90145 13742 9804 78634 44340 12910 65261 57965 87318 27117 13537 39662 46002 35947 97853 80670 15261 53296 87305 92290 53387 92882 63994 99743 18153 99695 49845 76829 12180 65385 64396 25083 72289 39392 39976 93139 27559 95687 29010 90554 16608 86850 83496 54230 80369 20959 81174 42595 47348 87901 33840 82304 38935 97858 3884 35457 31606 57901 35139 70479 91398 5463 90382 78019 10828 11883 48162 60077 83544 4122 89782 91935 77254 75164 15027 6594 68491 5325 67952 77948 27465 15040 71515 3737 41244 40707 92170 55685 41022 45127 23979 15141 81690 78184 23858 43586 15717 17516 20018 9298 12531 97749 43365 54128 29395 23713 29022 32052 88147 25964 24245 90035 76908 32015 13585 20778 87187 23610 21125 50585 50728 99957 45472 4330 48160 76136 39408 14294 39365 18097 90592 89129 65300 56792 61565 66447 70050 28876 34280 31708 58263 40350 16437 41668 80887 57128 67091 83742 19254 58036 70326 95291 56420 88967 31662 67453 37025 26167 98439 45567 92426 50149 21086 32230 11274 1720 17448 51265 84655 70034 50270 51768 55371 26445 26767 6044 30989 99741 96215 93357 1571 25886 8914 13357 21538 90484 8628 82695 10225 40493 17686 17963 68577 22792 89529 17598 80616 50893 99032 41579 66047 43028 82591 27425 67950 74819 10223 70342 57920 85770 36524 74058 44806 52365 77572 24354 27407 34289 95892 40933 97882 78383 20504 40731 55109 62955 87400 35305 25683 38767 36029 41298 11846 21761 7083 59754 78527 60517 98572 77219 23914 49725 62540 80439 92222 896 64187 73964 75279 51323 67245 36496 25589 88020 83185 1227 61489 25436 34955 18393 69334 19383 6186 95011 10801 43296 48878 27751 36267 92632 84220 49722 82098 52094 14249 34495 85687 35760 52531 36955 59576 82524 51381 72155 52703 80000 1824 46121 58703 29357 88398 97375 56900 16805 56368 91536 20640 5117 83973 55411 68340 33996 61272 49174 73136 67524 41451 56485 1992 60700 34041 67899 99 19959 11147 30794 50151 84838 99917 60382 49166 72553 33383 82874 54803 81313 72452 77637 47457 73423 72549 29689 2127 6018 75366 86916 60387 80573 15589 77352 16496 58459 11558 61170 43319 89889 73196 17255 95174 99487 36127 85273 8800 58518 99054 92506 96601 36997 83030 96342 61849 95328 55439 99419 1206 65096 31376 14029 65672 81379 62804 76819 13051 86886 590 9788 18143 62035 3315 71427 7180 1518 79898 69491 63195 79142 46684 9275 86360 81448 36317 75206 44616 33208 8235 88473 68258 91565 21058 63401 8282 41 95666 74132 72962 34008 96691 8405 55417 6679 89556 83873 82358 46147 90853 77625 54135 63603 55148 32629 91381 79308 56090 87367 94706 64935 15460 73569 79335 33373 96627 65179 17249 5844 22682 23130 52889 62473 31923 73283 70768 98482 80495 5175 80173 34845 67674 87174 53767 19330 61470 51410 52483 81068 79317 85735 38526 43883 94490 23864 90405 43531 6012 81102 38932 7497 8065 40418 6714 14930 4394 81774 79206 71188 34008 32123 45780 36083 23048 55947 43112 3285 53661 39898 74844 64856 2201 31940 97992 35889 77151 30228 56016 15918 59649 15752 29013 71241 2781 72371 39916 1078 40338 98242 32122 22773 94571 42506 33567 59862 47020 25020 99707 64860 18203 90657 12295 17149 5336 24680 58166 47695 24983 59802 45966 56992 10108 98062 75248 87705 52726 44640 37667 46788 62658 75824 73446 13115 15676 27401 10669 14523 90058 73671 44829 72548 71400 25927 39888 66372 37592 62684 22152 99791 31884 89157 14323 92221 73296 39939 43795 9644 49181 93197 90944 88557 67694 87450 54153 19638 37690 78407 86114 15996 65361 50041 88329 94332 71546 28805 24359 27536 22689 9838 94208 13573 63963 83467 97079 62195 83025 93433 96639 99878 25924 64328 27901 67427 84081 362 47291 46297 29305 74007 3727 98125 73350 81864 659 86543 75559 76282 90263 98110 16425 15229 88287 56856 29551 35551 69555 93028 96510 51388 54921 96105 50808 68794 1740 60596 36741 1184 96038 11694 72157 67620 81157 65603 90179 3665 53422 5649 90870 90364 72506 42062 77441 33551 12990 1640 16266 36 92386 4707 60466 27246 57393 97035 77798 1971 7134 6597 16905 67923 49460 96108 3687 15986 10289 74992 69585 48238 91637 72796 60118 79210 94606 62745 25943 90298 83569 86796 29625 58047 5551 66663 40448 34671 92666 42003 83692 12745 73337 83551 34994 13464 48847 18686 96429 13503 51754 19546 76960 12345 99048 53619 31494 51565 37231 96254 32954 77670 13349 16143 30535 84995 31139 20771 41232 60934 19189 83704 40108 48657 91373 73608 48005 30045 2044 350 48213 42651 0 46165 74178 45377 99200 29524 12978 73365 97211 68280 97730 74444 27003 13487 2897 92271 72474 62218 77751 20288 29428 26556 87027 89267 14287 45171 76499 74974 20907 46007 19694 30529 72748 70807 77837 52076 65902 50100 3471 14055 74092 73716 52344 24994 84628 82393 90894 66294 85304 60334 82259 94361 41114 12274 74570 36126 32895 37561 30507 26874 31516 66794 46743 35208 16647 63653 74955 95038 20855 64778 21656 49695 95534 53051 4720 45058 18853 56037 56893 99978 18572 37244 1218 47978 59442 97705 46606 32559 78293 33813 90727 81351 13326 34396 57077 46118 67541 34827 32953 28406 2895 47548 61301 74864 48316 26974 29855 79541 9211 77588 79393 32570 38925 84687 9568 89275 99767 67222 74771 80120 46053 88546 17853 97968 61161 77805 50070 5904 71487 96616 79775 60866 18870 47946 18272 11610 64 34088 78790 94883 43658 96170 74048 65573 82992 4299 64199 71801 33959 14908 37391 77599 12042 42794 35065 92353 97754 39669 29011 82255 31903 71379 55501 55213 97098 59671 93481 24061 1951 58109 68485 57381 6301 80012 83457 56850 17968 58781 31129 79668 71190 11226 78486 92863 67416 6547 9961 93892 53510 24138 48786 66418 84875 92303 65089 25984 16151 7129 96262 32602 83285 95938 587 80715 57699 22009 25797 22166 22004 2008 61909 29972 28811 23578 50149 92966 37670 22516 40267 69263 96539 73118 4052 24799 73887 41679 68288 49248 93190 8072 34509 80294 49742 56368 96246 96844 8793 4287 34457 97636 94951 99482 33576 42633 10076 78205 30438 44546 38452 54174 22394 51098 39706 16664 87570 54067 2866 11038 90262 4428 56429 82225 92979 26468 30106 27690 65167 8974 79606 26632 5176 32823 34216 37410 9566 82460 61771 11218 20009 85941 7373 10254 76686 69499 97475 16560 13713 1607 54770 79679 39843 48239 83891 84577 75537 94761 68576 86729 48890 71206 56261 52720 19577 99030 63902 69376 52339 11438 74272 4936 89582 72712 76529 22367 46999 39465 39828 81476 87568 87420 17491 69331 47509 36026 13692 2584 63684 27053 33546 28565 39648 85385 70758 26006 7011 37148 95092 22659 61108 13888 18887 28920 21548 44723 64626 99928 34826 83926 16246 69213 4513 2673 36062 57973 9544 60682 51263 13103 37604 21451 13768 96846 65753 5072 17241 90477 64625 29445 78388 27190 50580 78318 99648 5676 36272 17570 75943 88655 41568 78330 18667 20135 20094 29312 8784 2298 26059 25627 70488 17970 74231 3419 97270 51389 29578 76019 45663 2496 37608 12389 62897 97948 94679 55023 48572 87949 92586 55940 74750 62722 26265 52236 24350 59335 23571 84717 96449 73050 94323 9029 44350 21711 57188 77280 43972 65987 21831 73570 92145 29638 45178 43335 90829 5200 11319 15649 64858 46279 52868 777 12618 85998 45559 38990 93371 60344 74772 69572 72847 99682 25175 35811 74000 59088 56383 2846 36751 90105 16385 20522 7466 33826 7606 62218 44965 84252 2089 78080 25863 42076 87809 24164 16381 67367 96300 47878 27012 3357 96017 45876 36644 11707 83872 78920 16516 4371 99860 40412 91076 70447 37745 28554 26119 42443 8669 73607 34180 70397 9419 23210 45354 60350 93219 36249 57242 86484 73904 8637 24890 97302 88259 31967 79591 85131 3148 3350 48283 947 84921 19297 2960 32245 87725 35385 69090 72324 33053 96309 58774 63333 54637 64007 17816 34228 84197 16465 49418 63364 11966 30979 5082 99646 42618 15465 7816 3802 85649 17556 55257 21889 90429 14615 65038 49669 16905 76608 56296 27101 90960 19264 68271 95219 14351 48862 90242 83936 13674 48875 94665 3571 194 97737 6244 52690 66223 87683 18576 57215 98073 51387 65058 22119 72853 43166 68967 25835 38 76701 55609 86716 66472 35060 8191 58724 30266 83051 36083 70152 67594 55057 17013 81552 74963 41693 85736 35323 68657 7011 83314 69271 84389 76660 59765 31866 18637 18759 13003 78708 90725 61830 13765 35495 77836 93339 64891 30166 78261 38972 87239 54643 2371 51892 82974 90267 88172 3013 51079 53288 37358 37373 253 56226 95619 32834 44770 19471 36083 26717 91286 44640 30001 26197 60724 25913 2781 21751 5999 91044 13012 67174 37902 45058 54097 23479 40980 27778 78264 61095 59166 8758 54057 54256 76043 54205 9338 15534 36154 86580 37644 88120 25343 79470 7780 61470 24626 81243 75666 42144 38314 97667 37381 53765 6735 7308 54271 51833 27445 51051 63691 35158 7967 65574 92108 29072 94514 23782 49958 30566 63198 26160 37496 17537 27751 58826 65540 71122 16578 89116 92914 30050 70611 98767 88958 24942 19385 65124 39209 2436 7745 54604 88074 93899 98328 73888 39731 95446 58835 41092 45119 37311 26027 44136 70038 87873 74604 95171 78602 56311 44049 1061 68930 79009 66528 844 83509 17831 10181 70260 98322 28255 99559 42494 62801 35499 84575 71483 24698 40572 44987 6033 25611 6518 82882 55188 84411 11523 10710 25728 60217 96558 88650 8348 70505 22580 90748 27072 81151 90690 30928 43121 10163 52337 25845 12922 26138 57588 59523 17877 55094 34280 4964 40216 89318 70789 87489 14078 83182 83834 28756 50005 44080 86018 52989 47232 38165 80975 33825 69725 50127 30812 41134 5406 14856 44451 53484 5305 35285 13444 66078 76700 68253 64847 77295 36560 60819 7541 73287 41103 64231 63417 21595 90912 16767 25339 18376 40702 66678 61175 87165 68504 99849 24502 45944 11849 66665 11306 97341 91361 76933 56096 9717 59532 40783 85690 62451 98454 79504 86695 81629 29063 12973 14052 99100 98533 6878 54569 84677 90912 68147 19226 44422 9233 68084 46160 87879 79774 34159 42363 45366 6042 48208 88200 94743 43830 37103 32607 73019 21087 78405 78505 39902 62777 49405 87299 54974 31864 77826 44434 55733 70656 52318 1261 23102 52592 84017 86244 54123 50705 92184 12613 49085 54638 457 69830 24026 64645 13333 36625 7434 69860 43422 26704 69758 17939 72111 3996 52044 96981 29515 49022 3454 66177 62259 49150 76916 53503 84446 28226 60807 57702 31303 18622 88049 60170 79748 40379 30684 29139 83916 11728 118 47858 58350 88645 83221 60691 45112 68039 65032 9551 56123 97284 41987 88945 95020 70496 56262 45095 18453 65958 52594 15823 43730 29792 6193 85672 37157 559 7609 53025 18429 1698 32348 96671 24395 59446 59264 9969 68543 36479 53826 53676 46976 50090 12748 35936 86278 2628 9746 53515 12468 10043 41612 18613 16428 44789 66947 17222 63760 2952 31998 83972 2130 65379 52060 60852 9874 27859 39543 67369 63892 42295 10594 21391 32417 71298 81489 29754 14818 15528 62125 72658 21683 6821 69149 52102 72318 13382 97522 72621 64718 69941 56902 63422 94273 59704 22263 15924 79236 71503 49778 87311 45968 63267 42443 58583 49796 49956 91414 83481 80096 16412 18533 23405 34648 31533 29217 75298 54594 99563 51201 25056 97884 71092 89375 65358 93108 74810 92682 17697 48646 66643 51536 57347 62171 22284 14899 91230 50212 61404 55888 91099 66659 2433 51596 86715 76122 22526 67892 82858 93705 21474 66253 11852 99550 41658 22642 22988 72669 46270 62658 90314 71855 64484 41801 30866 31146 85978 59903 1418 26747 46024 44773 42770 72193 15910 65376 33440 78813 13672 37275 82178 61881 62391 93768 11066 29512 28312 54216 27295 22730 75963 23029 46423 43141 23341 68050 78309 10742 15549 92033 78623 99002 20856 15301 85703 63095 5374 44178 87370 86301 84999 62442 69928 28004 44071 90871 55168 29602 84071 97124 38309 58340 87247 58477 37527 20332 65427 8363 26977 10224 77504 38448 65475 31339 56066 72898 6850 99705 21524 11701 37700 52911 79506 13209 53705 89349 5477 85548 43359 55026 15252 84345 88463 89540 61925 34773 61305 30164 44211 76444 79444 92814 44307 13683 95126 38352 93514 75875 8521 22479 65160 41894 94732 3183 61941 59641 1944 28935 28086 74659 53204 12564 23555 50572 79346 42671 95273 43075 68234 77929 79576 1782 88678 42540 51368 74432 52547 28221 14233 26472 10391 91128 24429 9095 61712 63845 78650 20857 29714 33449 25875 80609 32090 31123 22494 58849 66806 90192 78673 56834 19085 85402 48622 96624 79256 6734 41772 40611 91080 78692 4738 84196 7664 26117 40445 79399 5794 30970 32961 51520 10167 35555 91337 9698 74285 72901 56736 36873 73598 58826 9333 55662 12083 19576 1233 49333 6646 69640 16422 72209 79414 60096 97648 59058 66168 93472 81236 58472 11221 57218 98622 36627 88987 89748 53713 17871 46869 68834 64476 40197 31145 78927 55283 57504 63652 60717 60676 75670 15607 79523 45551 11087 29803 81367 41933 88506 58305 60720 75989 75654 76191 51880 58827 36903 49859 85602 78082 81131 77700 43082 34788 49240 96938 31672 11646 24819 93559 93234 75356 77715 68656 94381 20767 63554 42823 8411 4552 66359 9499 45569 79770 89695 60161 97356 2784 7606 51965 83204 73343 15252 2394 59007 2622 80254 99868 77051 77158 72478 71046 20581 66303 27750 20458 66013 90637 12396 37849 97084 82502 75545 92381 24622 56555 45689 89508 77236 627 45100 99258 39113 80578 37206 27318 92068 65080 97556 99906 66902 40391 58286 4998 11264 59588 42455 26673 33346 49600 34983 7639 96860 64331 41125 63803 15836 61858 55752 74678 62029 66277 77276 91381 54635 23917 85217 31734 37924 41057 31951 30034 92541 78509 36330 91206 40748 50893 24921 26182 84109 88449 4010 8779 97929 39703 22517 52493 7690 50314 87510 2857 7466 18044 40337 10603 38160 54532 66701 61891 83326 33096 37344 12978 57480 95498 86247 36651 73237 59437 24718 9047 89244 83910 59951 52486 97638 68298 94212 2891 96960 65773 64218 27092 70830 48405 30282 10319 17643 7977 6006 91241 38730 52248 95230 39806 47017 98562 94289 52518 88417 49956 96589 9321 33710 1845 22857 10125 97464 34577 5023 37460 15956 83461 32177 41280 63663 62561 22677 44280 14231 11135 18294 20360 31460 25729 33878 84465 35745 42587 88303 37749 94440 14191 10004 96632 78206 587 888 65822 71570 14889 45682 31893 60447 31441 70780 90467 94394 72171 17057 9416 36438 92185 2094 25718 96325 67706 50674 93840 84219 42173 77908 82868 87019 32835 8592 5171 41819 43574 7389 81200 96960 81582 412 26475 50126 5026 8798 86325 90048 2329 79289 8451 60396 62823 62773 91199 49108 29718 25906 90637 40037 29133 36841 1880 20403 7775 42549 796 92154 96197 15826 13549 65351 40924 80894 64058 28819 10149 657 46502 381 62383 27459 66385 82038 8256 93389 28272 12764 81665 84326 60632 69204 67832 12046 68931 31665 47135 6448 69926 15232 41999 31927 65048 52351 50421 86104 76702 16280 11936 564 7127 2580 49798 57953 98697 36373 33567 32046 26633 25293 2812 64212 85005 83543 17305 90604 10687 56152 28772 57974 23623 37895 54012 71809 87419 5075 64308 56180 64215 2340 18499 43458 34409 27525 95508 58798 36160 64499 99600 61453 50885 65650 35854 87319 13795 26426 94847 68524 43959 55515 3831 13495 24774 77359 14539 25336 28316 94111 82697 179 4144 45944 74269 11949 15144 4878 94376 33071 86802 88574 77878 15868 33523 37092 68497 83351 61599 44808 47071 96738 30651 42551 91869 12705 67432 26153 46546 33155 65704 82748 14980 35151 16791 34705 18011 99221 88900 38003 52032 29366 69075 69470 96467 55430 5905 52909 11115 22959 68904 90139 71789 19652 84551 92519 79194 75382 40506 87823 72444 89805 37346 47308 11628 59098 11288 77682 58719 52615 63553 48376 17978 20013 2290 14798 35611 45205 77883 41729 98162 39393 94564 99386 67082 44766 62342 1843 40108 58358 15491 4940 27915 6261 56647 30226 23688 50602 72709 65977 77639 53671 7838 16042 70433 32487 55292 24718 52866 53792 13453 5729 22143 10588 88353 43519 3522 16028 65062 70044 96177 77201 75949 60785 27222 44732 30316 56303 22798 94449 41802 34621 9666 3885 58106 61518 24229 78901 58393 47398 22428 92017 38513 3694 72161 48388 6957 5149 23312 24112 10553 34947 86673 97938 2376 66575 1008 70256 57818 95937 49656 37383 68375 31082 35789 34927 67811 51194 71923 17942 78105 8485 1853 86835 88727 70285 2224 30874 39721 89744 833 38584 20233 47736 49715 85381 13451 82807 70599 4022 53485 42273 71822 47834 94151 41869 27676 95405 69265 67553 68293 36122 96196 23639 49456 88869 17113 29524 92548 97919 81384 31986 90137 75588 77633 84155 94602 65406 53829 44249 47460 79723 29766 68566 13800 61118 24933 60995 59198 9849 42485 85866 9407 78632 43577 1706 19027 48065 31243 38559 54315 61247 4204 92214 27159 28411 36693 9765 21592 64164 69532 56928 28053 58775 86216 55437 32009 45353 32888 9918 32473 55008 65880 63095 63121 72148 96699 98118 92612 16933 84343 30939 90717 82566 77231 81302 34775 45315 32530 97799 48981 14834 99084 93792 17323 32079 29305 32377 12637 77231 7974 34506 90497 36580 90326 31279 4003 91128 51818 48010 63360 73865 25224 30784 80304 14887 18406 73534 92992 23009 26837 64840 80458 78706 79064 18675 20805 20458 52290 70237 94035 91271 38796 47114 62243 46468 81681 67180 86147 86927 30018 80776 73041 20530 27729 94755 79259 41663 67991 89797 14013 66277 93363 14447 73297 44161 4123 7092 18808 44390 43135 33939 80599 67008 63436 61231 86896 69940 52233 80386 42181 67064 96518 70023 71642 6158 5940 6095 83923 36667 8082 84559 52224 30643 26909 67276 40761 79480 21731 55554 84670 21705 67783 98103 99942 41387 23900 70478 7738 30614 86859 13468 16678 6496 94845 278 66792 18463 83462 98916 51533 78956 41467 40365 28911 78537 8655 24414 74460 33550 28964 60238 86608 48906 5834 89443 25070 9621 80697 90418 53678 80945 87776 73114 73976 47305 28522 29863 84870 37721 18069 83547 29815 50474 73128 27314 35737 73538 29368 98355 61199 84907 96149 95045 9374 84028 58406 19969 32320 34117 91321 87515 76877 7653 32979 8757 81145 75483 38172 5636 84901 96317 348 21192 92947 82015 48405 52676 36129 10545 98777 18452 92497 89816 94294 56123 47381 30656 2181 98696 46289 35376 64397 86411 57137 76462 81989 79962 79797 3053 42155 33005 23374 38660 28831 59484 10824 20789 17508 54066 97837 77431 46604 66613 28416 26555 87138 56127 17532 48928 13416 72167 19454 34726 6418 14353 9223 90014 28792 15429 37225 87977 46203 77788 66669 72678 71540 67881 56116 92642 14557 17072 21964 1404 86674 13015 69543 33047 87381 55051 46397 74012 85636 51117 33395 67420 15199 89167 98218 58255 47895 98681 72105 47679 51207 32651 25838 43956 57373 32117 96590 97643 60775 41631 25111 17536 9084 19099 89254 27626 45375 22183 36171 67131 95292 5884 3317 60872 33579 53257 16993 27401 66887 22345 23639 32971 59557 95016 96480 71691 86108 88145 73908 24183 34049 33643 15639 6202 1983 1865 80676 10922 64287 44557 6112 1358 14641 26779 94935 99756 3960 42261 1746 45510 87508 4091 63870 76452 49088 98354 22960 52332 97835 27985 17290 58550 11816 26859 57969 17548 28843 44102 9443 25965 9569 43065 3179 53698 5258 37273 57599 714 87724 26094 132 57477 68647 78941 12994 26188 65303 34550 10985 90576 74615 78899 37415 85198 47878 95690 90130 8641 39486 82405 88850 73536 94320 83641 90083 20490 82124 11443 74367 62821 56639 16538 92981 8817 26913 42985 44428 76750 48348 80927 5444 64491 6988 3996 28836 19635 25359 47780 35185 16020 99141 44236 31472 89467 52245 72789 52477 57869 67948 41757 34149 18876 16451 67120 97337 59928 5933 16154 47194 16491 9626 65368 65189 39179 89758 96901 46909 20673 93989 94843 3544 41939 94049 29156 85959 73447 36268 81746 66721 31570 68973 23147 15642 51101 16636 58072 56562 10587 88415 23006 97354 50891 37215 16723 64492 2247 50703 74342 47085 69599 56878 4062 26875 84042 27762 85014 94253 82173 69628 59516 3262 81193 50949 94840 96867 95551 68223 53297 88994 82001 28625 34879 84556 22873 51393 5344 42962 22612 35566 31993 3041 75527 40823 7251 52938 29457 54102 94859 17163 67028 70784 33154 98163 20155 14951 59715 32067 50267 50863 42234 12478 91230 85310 71319 65490 82092 73039 53189 60272 63511 3537 94537 65388 14780 15758 5337 81108 75852 42411 42497 80439 42287 8280 15020 28726 55604 28875 87525 9587 21523 58639 36326 67833 83949 3403 34844 14083 49183 29332 64403 70248 67414 5690 65960 83483 7521 53087 77091 81668 70038 53519 4832 2862 93171 89899 87611 53792 82696 43480 18118 49850 6797 34101 66523 5004 81519 69298 81457 74014 93727 9041 73252 77843 6808 48649 5359 25697 88518 81058 38736 3928 50629 59662 52842 18926 62955 70244 66411 30433 52265 1061 10472 5216 41617 93057 18666 79792 64648 65179 32847 51582 61262 73976 89282 33282 44755 835 1915 68513 92357 90601 80717 65456 26970 22317 65458 73999 92059 44863 86108 55009 79628 77165 85002 70171 76212 32313 10121 58511 88164 75821 25633 69333 18868 54313 83713 4172 34165 15246 77736 53106 42358 68971 87005 99167 2696 39616 16563 87147 64903 83843 67157 41670 64931 14782 87587 71005 92272 80866 12256 17720 3771 12759 41323 11301 44343 75843 23888 78948 58418 80317 51872 94776 58584 3436 85028 14115 24246 27205 63047 95635 68926 72046 94296 87073 32973 53634 6052 38289 70810 7503 1727 41810 6755 55407 10727 35653 80804 90078 3060 210 66536 46838 20507 18455 73461 7819 61899 36610 81569 35002 62876 56556 42670 11130 23823 65629 70326 91690 75774 316 27783 60018 10556 85007 14651 57156 1739 32319 22775 12037 70851 97237 21331 64740 97117 45314 86348 39218 3961 43188 34714 96708 6176 19787 97057 34270 60767 5724 1368 42581 67886 66110 94362 63043 17700 31257 85917 54566 35248 70343 55335 89017 72038 45824 98511 57699 24250 41730 96825 2943 3345 69131 11255 26034 94160 61135 59784 38216 20421 198 62454 78559 66179 29914 29785 1191 72895 81800 63097 55815 22208 61903 22595 75292 31655 70032 57558 31440 56051 8315 27095 91942 72828 30109 14437 50562 48171 77752 17574 5311 63217 29683 17 55934 41496 17039 89901 38226 62311 67105 26812 85467 7096 92809 65417 87312 72920 60290 25332 66930 99596 27112 64408 40222 56066 84280 91678 10887 38842 30856 77747 48778 55439 47265 79831 57925 82045 89131 40897 77686 79277 96991 51824 65663 48120 521 86486 11959 12434 10368 87807 734 97652 24468 82344 83249 34521 36203 5228 99 54433 60474 96958 83445 10148 56413 44927 66992 4152 62787 18445 45614 7595 24471 48695 65667 45956 98429 49909 87076 57696 74151 41117 84125 51074 9185 4220 26208 47952 32676 76847 90030 72850 36556 33621 79778 76777 1424 18814 77296 10181 77492 57067 972 87069 48867 3028 6788 3486 9367 36235 63257 23155 35841 39505 92916 32073 46138 82821 35484 55188 46156 46564 64226 22202 67288 20870 87408 95375 77586 84207 12044 54268 68939 82501 1030 3569 42505 82762 19540 56552 89235 60938 19829 99384 1224 78040 28224 91050 7603 86168 66500 13986 10368 81296 89160 60610 616 46325 64446 96514 23551 98686 77980 21206 99681 91447 6351 17152 70716 79875 24555 95782 55979 4905 23597 32926 4666 72617 90675 36879 11761 25676 75563 11556 42929 84149 75240 4981 7558 84565 95565 80867 45072 65186 79441 15242 15994 61766 53468 36556 80242 49121 50868 43903 39020 45765 62426 99460 16299 11834 14622 5843 4911 76452 5558 82939 75992 25306 15074 40525 25047 6611 37081 55739 19960 14797 38010 89694 75352 58366 54898 75857 13932 57547 34261 39740 45051 98350 92859 19060 39020 67254 53149 25645 35778 3144 33848 44419 56938 25914 48111 66420 4382 47884 82111 94907 12848 20982 68751 28308 51603 76538 96744 80936 54829 75087 65669 51470 87397 29145 39270 64625 85829 65250 32010 75327 33376 74215 13505 91165 8120 37560 21672 18982 74125 79946 98571 1827 93076 91593 85047 98998 49863 77975 22995 52700 63648 69590 79349 92730 37287 3896 30937 25403 35302 50803 74369 39666 13177 4890 62586 75146 55129 39402 10674 46103 6759 8802 17858 40686 84390 55595 91685 10322 35528 99459 35022 51082 18470 55958 40072 14183 19969 89806 77714 70046 93915 78456 10888 50475 28025 32226 95536 43635 77633 77990 53527 68529 55480 4680 37521 40869 40393 17632 59235 33976 74224 59639 87095 31212 45824 1975 58304 64044 66577 45598 23225 73973 83586 50073 40525 2994 98700 74903 12155 4900 37699 8529 55870 42067 82794 95963 18999 10043 75420 53255 66330 14485 90042 55548 17840 15277 19836 27688 79411 32161 99816 57617 10153 20691 56483 78421 86122 80557 46626 59540 79570 66486 30539 64823 64572 9892 48611 34099 63398 74838 79012 94741 63043 98396 83614 56268 12642 41363 2150 2542 6929 58493 95831 71281 30039 68618 47031 55149 92774 47438 63565 68652 78452 5519 19849 26684 12790 28055 60685 39822 88414 65267 97857 19449 76556 61680 45504 8980 54285 9636 31679 72040 41824 2893 49324 83402 8454 74513 39285 25390 17058 74217 73826 19431 46794 67583 77155 23799 12168 62855 29181 27419 56413 65852 32433 86285 17886 52912 76653 28767 78179 47368 89694 47752 52386 67080 37682 15125 26024 53643 38627 66799 51656 56466 63155 99524 20265 7860 73779 48935 36776 90818 82410 72533 38526 74841 5739 47063 43029 69817 69780 95797 13700 35208 75422 30446 27653 80431 89196 32771 18735 79349 32590 93941 18100 14066 14656 7173 96852 39933 15651 67890 81264 85456 48528 21667 6265 44994 82346 44763 49488 14473 86042 12772 8267 44762 93434 62687 72808 92015 74317 63142 68336 44396 339 67128 15844 43129 95824 52807 81320 92127 63956 89682 63751 40485 68867 75503 40669 47488 87857 13862 33334 20635 85804 30721 94138 60738 54595 27297 77963 72577 85050 98404 56809 87891 14596 15831 11647 16352 37418 27470 72288 81483 81871 85385 20482 34176 48109 25280 69376 23749 86415 49849 36305 44280 56728 66252 94537 78994 70448 79453 1439 62689 50298 59145 77601 14163 82553 7323 17228 2700 98385 14446 15705 71376 39243 56712 57170 5512 58144 31708 17389 70490 53805 57864 48471 46009 60244 92738 54180 25498 73076 3362 73002 72744 44905 94753 8398 45720 14202 76155 50908 90798 13181 73291 90069 54043 20505 19936 82121 56237 45472 83262 67765 82918 65619 23475 98926 65262 99003 8593 67135 20393 31534 96207 27132 63002 59801 35688 8147 37550 32308 79490 62168 88581 18968 9822 20573 45318 18210 92181 75392 43486 8467 75609 48059 47815 3784 24328 65330 70266 20858 80889 55722 27319 24899 42990 46523 22158 80642 40295 62977 40608 4773 7126 67874 39047 60854 80989 80520 16793 72552 12257 94243 25464 23764 53085 95130 2392 75072 67682 96011 2349 73071 74857 23000 5652 66698 13047 80351 1404 23793 49010 26804 57638 43893 60658 77335 41841 39080 59505 42753 4899 28664 95207 8362 53003 13406 18480 11640 65927 52330 32959 88005 51338 47913 69507 16676 7334 4088 98334 44717 92755 12075 6008 5460 43192 63810 96415 83942 71142 95494 61695 23705 72982 35709 50663 51779 35212 81349 84761 29990 80160 60887 12032 9183 34095 92184 84477 60081 77094 5406 82872 11793 20285 31221 27307 21127 38916 56828 96969 50606 70752 7277 91601 95712 84259 70069 24861 19565 14208 14979 11399 35233 88297 75546 38350 56822 7549 17847 67074 45017 95253 66027 14569 29423 25155 55188 61727 10916 79689 14743 41006 55859 69113 23779 36004 75140 67428 86653 19059 93717 98698 36813 83513 93311 69658 8873 83348 44139 63250 9243 34163 24759 43469 56570 47540 36856 7879 63322 87379 6258 55206 89000 34778 41529 69681 18495 70233 73769 19010 16554 8472 98436 75564 42635 48870 60973 87029 22109 83952 22673 83444 80675 21380 83499 95705 40966 18593 65794 94457 9408 89081 57112 52691 37645 44526 97530 61649 67251 39182 84236 25662 81253 83751 65817 79761 97899 62764 51791 9140 1316 5888 2830 2788 90258 64555 40725 24892 26354 60577 50471 18443 62396 23186 69660 78032 77727 92859 90303 26951 97489 10891 52228 61121 14736 85061 81089 82612 67682 13674 92909 48541 55182 91543 13467 36392 48220 22416 41035 66397 63646 92866 72577 58322 5436 31692 65935 47998 14241 60662 43674 80382 45169 49365 2142 9773 52201 80770 1409 29317 34473 99642 50338 54750 93864 71103 21611 57916 94595 78694 96266 16708 44482 68316 44419 94060 83082 50270 90129 51614 80814 77547 48240 42637 61890 52641 57696 69892 70475 31271 39291 28963 51855 3544 40200 22169 68731 23288 75406 60950 98110 51722 869 44904 23794 35594 53701 4194 40636 59797 3451 58671 21076 45104 80398 67922 36342 42717 91378 36525 50371 16878 42741 21755 32577 16841 71276 19670 78994 40367 74448 45799 96474 66131 87683 55384 45023 77533 53557 17456 34967 89803 75626 69276 5550 94298 86250 99362 53526 38979 52060 37236 17245 17575 45728 40975 64056 87635 93159 69913 25202 91067 49881 27259 99520 85456 55321 1758 32011 22456 91941 57248 29334 57355 65042 73861 55096 27203 11168 19977 7073 38113 91287 18796 60540 2803 21739 11834 97689 93714 52920 16656 26753 74124 10824 41991 81262 53832 71212 50563 14227 14306 15842 99265 47821 71900 79215 25902 79951 26974 97414 75148 86775 6095 90448 56763 11360 37835 49348 1737 45899 85939 48725 88605 99969 54841 65604 27572 82454 26762 31348 67204 81698 5715 16840 72754 55059 27324 9618 77664 54157 37953 27082 34849 52604 78348 29860 63807 85388 21070 75430 34661 86312 66430 52922 44969 82906 70980 81513 83334 92370 77741 99921 38832 69311 59217 72680 50868 78165 54708 50655 66388 24972 12963 22240 40248 46610 78898 46038 22597 26630 46837 11106 14932 60076 31773 46936 72428 44234 14032 37139 47031 93204 8379 89885 39028 40574 16176 64103 31387 58110 45283 44190 75463 72216 88763 94243 28204 67190 63926 93792 96630 39461 37168 98379 30721 90030 22825 18535 19740 5862 62713 23023 20270 89472 15702 74864 75218 29488 25203 53515 46314 40423 42871 61715 64541 47258 61124 61902 51144 47559 1017 49090 33123 36644 42956 24554 89891 93674 67199 49541 55748 91442 96092 42809 63595 67583 45378 73035 5582 82451 97607 94273 15445 81004 60003 41094 72508 1659 30899 11870 10727 87124 18683 13393 19080 53437 15538 95432 78520 96155 88696 23965 23588 99351 2758 31723 17608 48811 8416 70037 16278 95868 13247 55389 23384 62050 11907 57450 76049 26796 63220 95766 35303 43140 76994 82925 60236 67642 55618 19350 93674 70542 30098 8548 67051 53311 1562 96648 75673 74219 92879 60983 18759 66636 97597 35332 85873 89985 7213 61625 53404 46923 2538 49260 5313 86765 42273 6708 83986 96966 40374 91811 21699 75728 57326 23283 33725 6820 59454 94893 75434 5836 23750 61800 11727 33483 5258 95558 33814 34195 19501 53259 16731 13071 27047 45266 22671 20038 4967 26814 6921 1227 82358 65871 97191 379 77812 52061 40103 37934 89135 77421 5950 59625 46857 99541 95854 39635 39506 74472 96922 29044 83378 24856 21906 85574 50576 25594 72881 35525 94518 27374 17769 8418 17615 19924 31819 505 90292 15621 85165 16014 10979 61855 44930 1924 97621 89954 95887 78377 31164 46225 30619 96389 13580 46119 67665 2328 38765 40093 5293 19339 67511 61521 86574 57891 63029 57915 25846 34382 71477 24367 75881 66088 85464 92565 31483 90763 71370 38720 2609 86489 63486 71068 74428 2311 66334 86979 52288 7206 85763 83714 71986 33617 66973 27389 22792 47500 55363 91741 23090 96610 19083 32312 66299 59416 11242 72208 91271 49585 67042 59632 51111 15224 5256 47977 24140 36155 63494 91869 56181 80618 3393 24362 90274 59453 73709 57717 56118 75322 95306 4965 67766 25430 8567 52012 63031 86781 50088 80264 68512 15699 4637 25909 75445 10053 3467 61399 86951 29429 79686 61584 27268 64959 81745 23044 56650 74487 36163 6499 4124 90644 79892 96572 78308 29120 90266 24897 12628 80404 31479 83644 60091 33144 44511 1432 77039 28299 42366 73860 869 10213 62091 36694 38207 84250 43026 83141 93245 66974 67305 18189 38590 13401 97277 58916 89862 39379 28741 83009 67666 63505 64759 21136 90362 30350 39406 93703 43759 28552 28413 66030 18784 43944 60671 98064 5714 60950 17903 60552 74891 28315 48267 79123 31459 66383 5927 26561 87188 89418 46538 45734 46709 76693 38682 79840 47370 73388 87634 3529 30502 35932 67019 60920 32448 97849 79180 32350 44359 95182 3571 27377 31235 18152 13567 76627 28523 28606 76225 43365 44488 50656 5471 55523 32253 73450 87520 89198 41741 5154 7722 76704 88189 807 54784 99815 17190 3347 92358 47089 73964 34401 40536 4849 47300 61282 28038 445 16787 29782 85363 80481 38529 909 38555 53291 50058 29325 51807 94187 25114 74690 90846 96277 59385 56577 16711 74863 65610 89175 65548 64154 87070 12368 43535 47309 68929 99395 93212 16417 50495 98690 116 59351 60266 40685 49309 30237 89225 41789 29505 97109 21356 75477 6904 18074 75748 17233 73733 78647 5737 97747 13597 13731 20671 2855 79855 76964 95693 53930 97563 44485 66432 56797 35838 81575 16967 60368 67389 54939 86948 23481 72810 13335 82520 15101 81318 50530 25440 28507 64340 44469 46800 50414 6229 96748 2822 20605 2850 46092 41164 85972 95797 30850 61373 72181 90655 39917 50642 90570 89077 24816 91127 43514 62132 3080 16733 84368 59375 12541 27029 31688 64354 46174 99482 22475 92438 74955 93336 92346 9022 6688 26875 63762 61738 43586 52540 38634 21160 18400 21476 18724 76365 52114 5818 41300 18451 28777 16249 65029 80724 75191 63426 20300 71099 11280 54682 39943 89662 65729 13465 1391 91000 59683 67186 40369 43289 41142 40894 14208 26678 89440 80243 40047 45101 81831 58908 921 23625 63297 72614 25300 58190 42524 47678 63794 83268 59944 99340 49086 1094 9635 30268 28790 25408 32256 85048 91379 95793 88882 46700 86715 53107 18305 59705 2336 9837 32771 89506 1807 3759 81498 28135 20269 17702 53850 77926 45418 60657 21373 23918 63544 30512 19047 47394 77609 4915 65762 83317 82406 86781 23066 56967 62393 47717 45539 57076 37496 21815 2768 31701 47118 26905 21179 92540 39651 4541 46198 72496 41770 347 15961 21768 56395 24459 97873 98671 11825 69217 39176 32339 35088 4432 84310 60699 43808 36509 64811 70358 63640 11895 59409 16753 91878 47402 16210 95421 40179 22265 63150 67082 30737 65115 87589 86818 62094 78886 91959 16567 68949 78585 57121 80953 83739 81399 91184 9595 16112 48389 38411 50592 22937 28810 15625 87326 46524 7261 18970 53146 9031 77547 21292 66253 72105 94248 33931 93546 96119 93944 82393 59661 32624 3408 68528 17147 42675 18644 30177 19301 58107 99457 87277 79026 83550 49808 70052 51666 26158 80277 85805 36590 42550 69824 93418 84121 5938 31269 49112 10619 346 85224 89295 56866 53877 49075 62044 65212 77039 42475 99088 58948 43055 64783 12512 44974 53445 66036 7746 29114 13801 82460 47780 48016 38785 75927 91684 9227 8889 34759 37626 52659 64223 53042 73095 43497 29893 83608 83777 22419 84244 45889 45950 86784 6410 89604 90200 96908 70960 63194 68319 57396 78801 84912 56858 13804 14597 2483 64348 30076 32378 9703 95588 37063 4162 5703 45006 75673 7934 41367 6790 78747 99575 92419 3516 39044 18789 28393 94189 24242 90118 88607 55961 41497 24708 89951 82272 25621 34229 16354 94401 72718 62502 62186 67171 41948 60090 48268 1283 62449 62302 25283 30252 22750 62676 83017 30660 17846 59336 85111 51034 1727 24259 17451 80478 96417 96708 9055 10272 47955 6037 33641 87590 98936 50167 64467 56410 47734 55743 63921 37105 97731 71251 24852 60410 72337 64298 26769 21827 60846 57818 58342 55317 66845 14184 4793 59493 99451 95225 61253 81358 15258 27828 83839 63821 51365 15686 52605 48586 72105 19167 90542 76375 3370 74728 46612 36503 4505 59846 74193 43132 59599 86125 687 6562 55341 50510 55757 92125 61379 79783 49314 75932 85878 88098 28791 48192 29214 89112 9838 55294 25107 47381 61705 17610 20360 34656 52431 22293 60877 43689 35655 70077 29534 89362 73651 68655 77008 33357 7829 25595 85004 71602 65141 25956 30510 84940 87269 28254 27117 76615 77032 97189 52613 46124 71640 89521 10807 90898 73901 54848 53474 83066 85659 6071 49898 42478 15471 38736 940 15370 51643 42549 55843 47181 21823 61501 73683 24870 19380 6833 47337 90438 74275 16648 90625 71384 46992 40124 20806 8554 31007 27577 47087 80981 42996 2964 39553 85837 2771 86151 50021 49174 69990 51634 18620 8674 48064 90542 36735 56059 89668 18213 55665 75660 17919 30394 18040 26874 17599 53106 20114 89002 61111 67741 47176 57580 71555 26422 6726 9343 68241 11377 78311 18321 94460 58619 31237 73003 43354 21316 59254 29323 18671 37990 79535 76870 85057 11693 87947 26858 84691 83865 87526 37251 55765 80606 10789 11367 59083 85267 37497 82266 13954 26670 72308 4453 59102 9004 68487 48484 37622 23887 21891 60065 50105 34211 48376 3385 63259 75194 44604 30472 86866 91430 76567 44348 70800 31116 68244 21418 65079 94079 82934 63373 2420 12829 57741 31025 84907 39396 61149 40934 81734 58369 14620 82125 91569 26398 62349 82201 21862 84594 73509 94165 15566 10788 72825 5439 3397 52282 90387 26046 49624 2533 26224 20096 84920 85943 75135 40451 24210 86092 94758 32535 16380 57453 25439 69414 55842 90195 58479 31945 85450 29377 95260 56507 96266 18259 60885 90452 54909 44792 21728 58547 68971 41085 94382 94396 5126 49201 4985 17206 17350 49451 77296 26996 38025 35578 61803 62696 2371 38223 23279 42868 62421 16379 55649 72788 60146 2377 37268 3139 12039 34362 67311 98023 76980 75491 51260 82322 23769 85895 73982 34945 80153 67563 35769 40472 16155 96918 42562 94932 13891 13581 31847 6543 27087 97569 32199 23872 52404 71575 3204 89920 37364 48458 24715 33643 24359 60588 6999 91186 38507 34109 34685 90781 65245 3342 52992 54065 28429 82794 94500 58015 29264 74507 47841 66598 58440 37240 11484 25399 13244 66666 70727 41413 2710 54163 96589 78116 9180 96431 50642 83846 55249 44633 96641 18872 47146 63745 32003 68208 41116 26429 84109 69353 44131 26430 33701 4668 73426 5942 48245 28908 57486 86557 20583 97206 65796 45370 90633 18135 47244 15616 34325 20406 30424 84864 69684 46850 42500 21278 99631 43728 24324 30610 76772 41075 78872 37808 98528 17327 59627 78139 7497 88608 714 54121 7596 50997 41532 60502 80344 94250 75853 54325 12450 78993 80499 80436 11596 54107 13263 52709 10024 84156 23057 8708 39509 96965 21137 43205 7558 68767 31379 40926 43369 73885 10100 15968 79233 33915 10528 2835 927 80719 59153 25406 77267 13159 32249 8233 28639 10640 38218 32333 5231 75379 92128 35168 86253 44171 29455 74306 19321 34046 17352 71431 77052 27136 20473 28404 85641 61109 24711 4153 58966 60510 44428 78354 49161 68305 88753 18771 71906 36704 78836 72963 5941 74090 78650 33553 66880 9222 6747 79700 34912 35927 32037 38336 90106 97190 1809 99883 94026 36125 39325 95128 57029 34211 89283 94886 25337 86873 26135 9693 19495 83868 28821 37763 59319 50836 89076 80530 79021 54434 69301 41690 3588 56568 61567 47074 9291 53254 34842 96152 28105 18932 57248 77128 98254 61173 9709 36570 97559 8390 10255 64149 40639 73100 53266 72915 99085 62675 13412 76347 40400 17304 85613 75917 42011 15727 55084 87856 41639 54255 72051 94237 55167 60622 17234 4845 77569 85683 33111 74737 71400 90190 61860 93932 28105 36993 83366 68207 66800 80809 71279 52622 76287 8554 13817 75725 92626 59605 39773 10612 69628 41580 67156 68615 78070 99604 90568 49287 42641 85045 17500 88864 76984 64881 73161 13781 87751 67241 33876 74623 46449 20041 17345 88588 42261 41733 42660 38395 85544 60481 55493 80357 92015 58346 52834 67506 98401 786 90501 1994 70118 36116 39430 73665 15792 7352 73323 50434 55652 56074 54503 42491 58160 85725 39515 12658 46662 94254 62741 90714 92231 11358 33948 99389 47341 41324 52724 53307 12176 89884 14928 77798 13676 78783 47146 45123 96639 13999 56064 76919 93510 75492 47509 72910 17655 29420 12499 31568 85372 99724 1084 23190 61116 86726 95641 71124 51821 58588 10584 34069 77616 41031 28085 86497 89689 98857 99929 60690 45394 10330 71270 81401 6453 13359 25520 28721 40994 43650 40809 63616 58895 6948 98932 31171 1066 11539 43554 50289 51460 62590 63746 74685 21335 82695 43121 55656 9101 74161 86946 36182 2 87562 45423 95664 89752 87865 2107 51710 17760 28996 82426 83966 49829 93989 14853 23963 71576 50571 96350 93053 61982 72950 36922 82715 10214 61707 71048 89439 44920 42316 94490 44245 61967 65219 78950 4346 59141 65561 22547 70297 48367 48001 60013 77568 55193 23486 84774 37659 14623 88597 78419 70739 56517 22713 75298 5388 82111 30119 98304 73672 1949 44100 64865 68588 21416 22360 85749 35661 94452 10550 92650 95991 37086 9840 93653 71869 75606 89824 3924 72058 35123 19422 9337 83765 74666 56205 241 42444 94707 18835 31309 3800 18745 6376 19838 11838 96013 43283 18023 30219 28308 65850 40347 83162 26772 71548 34428 59026 74259 49763 75879 91591 23035 56851 19567 57121 66792 24371 71604 44251 58659 21709 25601 9359 27522 45259 65992 8199 70121 97753 8806 44759 44171 86979 91032 44452 11963 32447 35183 68724 11984 61365 23269 40779 48085 39406 97612 28810 2922 64249 69618 87496 77762 74841 54404 1009 86612 19241 33576 93920 37069 25666 46377 45828 31655 88520 42261 88604 24330 30909 14056 49416 13278 361 49697 71278 80209 42880 21274 21875 90659 24408 44002 74060 32411 11876 34184 64230 46302 71021 54843 69740 81901 47091 61819 84985 90170 12586 44666 5716 23724 56958 72679 9429 52166 42941 53291 11340 28074 55079 67592 83900 2318 6754 42159 18236 14323 36717 99935 25441 26036 10905 12508 68078 7037 16366 47711 8634 89997 98967 38686 42587 2278 18733 83813 28547 55851 33821 25288 33631 65359 18820 16273 19516 10309 80291 16269 62456 42279 6790 89021 31443 81751 6139 22564 49609 67930 47842 12755 13238 13492 93960 83868 79729 34765 45660 13305 19894 96732 87974 28322 1826 53510 75982 82054 69273 624 6818 58339 9703 2707 75644 89498 79122 78217 25682 48315 27303 19309 52076 31007 7947 47161 75245 42011 46946 83223 49624 86767 10557 86404 66058 1873 2048 29364 9699 22497 22319 78231 69241 97881 1781 20565 75248 50836 77292 58288 30698 48892 62042 50819 46873 13182 7058 97732 94736 55047 96882 97120 90592 81640 84558 34196 47756 34759 84187 2634 38768 93792 79154 62797 14925 55307 94644 57101 61798 30345 52619 48731 20034 84530 53085 56715 23877 54546 81244 36821 53503 45521 6182 16534 73357 52388 98190 13023 25493 86077 71102 64117 44403 97971 24482 43762 30118 65733 50726 19287 82449 83028 91457 93237 79895 44649 1217 78464 49170 66544 39063 57342 95613 78487 30923 11439 64088 18592 20975 80146 13175 3301 69580 81120 31689 61180 34449 14366 79013 83132 35490 79736 69212 25928 49789 46249 21693 14287 20828 65521 58340 3555 38251 18263 56188 53517 85056 50172 91175 67540 11156 18313 88578 11510 75588 9648 8506 13522 4368 84 20058 99109 97429 65579 41565 52106 7978 35100 46389 75099 37450 77820 42070 53164 49379 58868 74025 92616 10803 54870 20043 64498 10472 89136 60119 63098 63822 29345 38026 57703 31447 26566 57327 81202 39089 5321 30645 6245 15321 10826 86018 31806 30275 76221 1235 85213 71695 67494 52547 81377 4961 38278 31669 25306 42196 83526 68696 65652 14404 44911 78883 13075 39726 83332 59184 82826 53440 59725 9132 20727 70283 64663 11634 7487 57433 57335 55035 85607 71925 91943 81165 22783 90781 48181 81795 35801 52035 25698 60844 13205 31148 77319 18230 47232 60097 70915 66216 63620 12396 85966 49694 39811 26981 98678 60231 49450 24962 15826 25926 52901 91916 44766 64486 79728 92347 39124 31558 7212 91222 78846 95653 65225 9431 14029 14950 39553 82828 82888 57745 9315 19628 4330 27661 36121 92809 25767 2686 43101 47267 28731 635 91100 45302 52031 20103 14315 38876 45432 61375 38865 26083 84241 73572 78222 66311 89933 10726 13515 64643 86001 77831 85773 76383 79578 40242 62601 90741 75072 21142 83939 51114 88101 48058 50784 99897 67020 64905 82987 73067 23640 55879 62116 96527 46609 79936 60350 56107 3656 53066 16931 32880 54194 73743 52571 55736 84242 3934 62797 13661 56858 23865 86090 64396 36321 59419 48750 57112 90043 66310 16768 30453 98727 79301 60018 38221 14944 78012 17555 75625 6166 56884 36262 23062 78129 48993 17354 60599 77394 26096 37760 36893 81428 45012 81763 41069 8293 66869 83703 71984 45000 23369 91357 20218 45330 73390 57433 1534 39614 50173 10104 49411 56603 74859 42105 78222 10809 34857 98633 48225 3892 81482 11327 86285 17533 38152 16768 23876 35224 27672 55077 58183 70218 56784 3220 42233 85329 7 591 42718 57741 27331 54332 98135 94301 97388 15543 36308 73751 46825 34628 67628 48864 96824 29403 8373 72083 55128 68318 33220 77927 54503 99138 1875 18427 33950 18132 93165 87889 56856 99369 34582 93038 34096 26797 96243 69682 73834 46398 4487 52297 37113 40044 75492 96154 9034 32678 28270 77790 17103 62616 19074 40070 91288 24711 44731 44599 25594 47789 23508 84349 73782 58548 50358 70645 30713 99264 4049 313 78609 76910 76267 12685 35945 55225 71547 2657 98718 62377 82103 23057 96893 30585 95107 78239 39138 3215 37403 89035 15582 71933 87344 85706 58646 75475 91977 69923 68321 71572 77524 96393 99770 9827 71572 98671 3277 99074 94052 29064 39059 73352 49600 18479 7706 63732 88615 42149 61741 39937 45903 98148 21574 16685 27946 38361 88143 56208 8324 26631 51056 36317 24812 66088 61356 36503 17762 25594 84653 82559 56595 62019 50805 9170 36968 32223 23157 23182 46152 14154 19041 40317 43359 58604 69870 57561 36891 38395 92883 60137 75686 95068 3395 22968 53801 27096 70156 17331 67760 6716 41711 74609 2047 65063 29490 35001 56685 38561 11277 7240 79659 24536 34664 61014 80444 8126 96206 37042 35795 21712 87361 14492 65553 96072 58731 25914 15454 90447 75171 98091 43334 42706 4277 84444 53685 22054 65915 72946 34658 85272 80189 56406 84727 38850 35701 80295 33995 34150 33176 62228 14474 67543 59034 27007 71957 84649 16187 29199 84398 96190 2086 62376 46500 3262 16659 18013 64811 82017 92027 42362 22517 37920 24145 3258 66820 89641 11149 72455 95279 48077 56331 41039 31519 21583 72633 96024 40702 28130 19962 9254 80885 68280 33195 68506 98840 4653 52304 7510 22508 24683 68357 91465 10181 22613 18743 28549 49149 63658 77197 52804 72886 48274 52239 77748 62420 31716 67456 17799 9549 21548 91737 24709 65790 81262 39107 88953 13504 97241 17911 85792 14221 78730 13817 6646 45618 72628 7325 54925 8671 54584 89589 28438 81014 60676 5814 38522 6568 83020 63694 36543 50992 16849 5653 9043 88617 43932 53593 12009 24795 64790 43709 39375 13368 65074 52808 81760 93648 96747 62097 68163 77085 46119 69830 27989 52131 66644 5302 63100 26447 91590 83508 25542 51514 58574 86155 60715 76282 33133 52295 70586 46134 11082 86589 71023 93606 17989 91952 90319 86302 94347 18081 14949 64145 95112 97959 71466 77322 24120 96401 24591 37353 25721 70723 97608 73230 18296 484 22111 8644 67498 55225 17820 23270 5087 97326 91367 76410 53430 75441 58426 37223 19958 50471 8489 10005 79847 68048 33281 28447 15279 64730 75435 47093 38390 97820 2841 34868 97813 61780 54306 502 64382 61386 95490 11232 9576 38576 98619 43527 2937 78206 36217 75167 35917 60870 44709 55147 79727 79998 62073 78449 48598 2796 34058 30085 49958 88628 37065 30200 52339 3787 20708 37622 10962 64089 33844 43902 54542 33793 15834 57724 79808 74185 80171 56611 68240 39174 5998 70997 16090 97900 20473 66475 79790 57022 59506 46228 15061 77271 91999 57187 8230 63494 46500 62692 95453 52365 345 82648 11826 36275 25911 41806 71068 30145 66128 2080 93099 68588 67301 33653 32456 63326 18335 45748 27758 86250 6079 58006 20690 60486 88555 5324 2454 98270 96604 6915 36238 22924 44029 43002 85375 17000 28419 41633 35453 732 86294 52882 64568 14221 711 61928 56657 33382 6660 58549 81502 21421 82758 22590 18162 1053 22930 22761 4545 40169 94369 51068 22385 99920 16611 30934 38231 65628 29190 55143 88517 69477 4349 15579 70913 3312 49159 33003 49012 64965 27444 50306 36873 88598 38130 47475 87611 52515 15351 62612 95791 99084 73089 27880 83530 13041 67811 55600 15871 75661 54040 42414 4402 94347 49980 70428 5496 33617 29529 44267 32641 99009 11776 82228 81579 84362 85943 24704 29727 82017 95526 39502 20980 67432 60232 78817 90825 78015 6139 18093 98956 1273 72930 11965 39623 37345 8233 13978 6642 43245 35728 54995 30786 32272 70522 5365 80084 45566 96107 58912 50463 17358 42887 83357 36750 37497 14324 983 54332 41511 2446 23763 30787 80516 71893 20745 62739 74752 98994 91750 90334 97265 35910 62182 16285 8561 16308 78771 48865 52876 20835 92069 66545 11979 25799 29072 67937 35574 54520 25155 88615 56758 44315 45967 80745 86856 36277 94445 45811 17120 96275 42250 48114 44750 56888 48898 39641 83950 15141 84526 43166 97861 25920 84325 18526 78412 86536 6140 59841 23842 38395 57455 54917 5907 51570 77096 98079 47673 96849 63123 85496 23310 38228 55545 37907 32424 92402 62858 99364 50899 9993 31992 81829 71228 78091 70547 62219 36017 82509 23155 7668 20909 63987 88439 32354 68946 15662 46068 51128 78702 29969 21525 97034 24231 30592 40574 80345 31413 25999 60655 98085 35771 95725 75301 68870 85739 89519 64307 51041 47558 22183 61196 5475 32420 22389 10750 20162 66139 47439 41403 87459 18719 4649 71143 87497 72044 17172 27117 16601 9694 94282 37492 57931 97253 19721 68507 39209 38769 52795 66483 28995 66298 82638 82367 27966 6835 46028 7510 37099 54749 93068 49367 79179 65946 91459 21164 4858 91108 35712 8203 2951 77304 41248 33219 86769 19032 50267 67114 80462 459 96454 13086 30529 15524 41434 88999 31767 16465 4062 45349 51441 11388 27818 30706 83318 71238 53409 36499 39503 46326 75032 69624 86758 15402 82257 62730 39029 72338 24673 15889 11931 56911 7180 91975 31399 16778 51402 94463 6743 31103 30855 14162 93815 1147 97850 16430 50885 5442 6871 26978 93062 21193 92199 18140 58246 65882 16581 40242 72367 59982 50901 54413 70333 76705 80045 4646 79422 73774 63246 29782 5715 47704 9036 33342 94103 36092 62128 67763 23276 82229 59787 74838 89081 73461 79329 31867 90683 8027 46168 85260 90573 99122 22526 79475 29578 85661 87085 71266 792 15634 42096 32195 13755 99377 2235 79843 32127 28295 49709 97450 22480 88240 82596 85909 44877 48306 21980 38898 91169 52453 12660 16992 94986 16605 25242 27312 67621 43846 13304 44219 77953 87706 68634 54850 44758 25182 21033 90021 1194 26579 30084 42576 71116 14189 91468 43910 92630 8845 81067 80884 40573 74791 87988 33507 55414 26384 99581 92043 89849 22670 1378 38923 50198 99456 96541 72877 72382 76370 57420 76252 34435 33536 74170 15080 29319 73196 63991 31522 45989 20646 89593 18820 73626 32509 30915 43834 56487 81988 13745 47293 27150 26563 25795 84712 35698 16228 75452 44757 2355 51003 31915 6734 38393 93576 63355 21653 30229 67489 45619 92456 58866 87259 39078 58101 22066 66537 87568 18469 90748 98587 44589 96038 60552 37870 48804 54607 59771 40915 18596 69511 30904 53662 63669 5244 1582 91776 57507 74264 18653 71395 96493 48956 40668 9167 22910 67230 21541 33965 77833 78470 5410 73925 21225 72858 22741 38685 28527 6520 98436 96857 64647 43942 88050 65803 56349 36702 1937 3466 1681 78446 19741 6374 58617 83875 59583 96657 33956 50886 73940 15421 50119 36519 25028 86128 57137 71928 33308 89397 29245 90452 96985 28058 51650 93838 67895 99714 38669 18496 2006 99411 15341 10048 32889 56702 98545 42634 9060 17694 69661 32323 8684 23806 93031 3865 47544 476 76049 79352 54204 49996 44385 41385 28032 15993 60307 13097 74661 68967 33302 38967 56515 5331 7268 5484 36393 15876 48200 68562 90475 124 89354 34160 49447 71226 97059 58048 14659 37900 2750 99001 72714 77758 29704 63751 27353 20785 36074 91473 30952 63772 35908 46051 98302 58385 54723 38398 55136 99971 28076 7839 65614 50773 86063 63944 82165 59637 59997 34876 78852 60256 26875 47640 81813 24775 12945 12324 84279 40403 4160 59312 6331 67754 47622 19838 95007 17477 51051 24198 44937 56904 72163 52927 81564 86187 1685 91628 14447 96838 80178 68172 38645 71717 48297 26521 4495 83576 89454 58283 77621 51869 8555 17817 18381 54898 75774 42320 91371 74212 50148 46720 88911 78095 59768 64118 30027 49192 7559 3227 70143 24644 54904 73706 84814 67096 12307 39387 84308 66506 65266 46888 59853 88140 66071 57530 54394 44499 57665 35882 16945 17154 44308 94491 34150 63427 21009 87959 1832 48326 58478 23567 89508 75595 99902 87908 90684 69887 94455 6522 19515 56150 68167 64145 87221 61754 9751 98919 81943 76814 23992 58837 24377 58671 92036 59021 43712 57402 17345 50653 77918 14351 96460 72856 18288 39659 32872 79385 72496 27927 40121 55689 61844 13097 58295 58712 84973 23178 32322 30578 4492 1880 38756 10187 47416 37425 36346 72353 51560 52711 76563 25562 47104 29028 53784 44992 75356 14049 26458 54435 88825 14014 95120 70552 64185 9260 87034 9659 31433 81767 74206 39432 83307 12682 26171 70281 19010 72468 43884 60949 90647 37958 60540 33094 78848 30510 71527 84604 19885 62177 37207 96601 32101 21727 84271 54550 48817 2037 23186 43893 8933 91698 16413 97627 39158 71064 31317 46483 58614 56965 66195 55228 61651 45663 71161 49243 32493 63744 40548 79610 6753 15910 18539 36424 57999 23134 88797 87731 5943 49200 28963 95081 439 31910 5476 21121 50626 53510 64852 69437 42478 68606 86068 86592 42476 30610 69207 26429 18738 90410 39184 23227 13209 42854 91290 18390 22975 90626 83006 11662 62317 91946 75827 24618 33369 61588 92806 95048 29372 73800 70165 52376 93055 93039 77761 53083 5037 51754 22359 49870 92830 74896 78758 41547 22913 24348 19989 79820 62260 88242 37736 17256 24983 61550 67938 43054 44811 32321 23752 55685 23483 30180 74281 20616 774 55833 87926 8751 80634 58414 78535 31745 73302 83737 99238 25845 80902 40158 82700 41443 30249 70040 96079 32382 75997 49362 60560 79752 72123 97740 21315 18957 88730 4487 87032 18353 50875 80899 51140 27627 66905 52415 10143 11546 81833 27039 73592 26335 83407 77843 69003 68839 42187 60818 11930 54689 63806 7647 15314 75666 70466 19529 19588 48749 26894 81969 1096 30839 37121 12464 81923 37064 27336 16412 60798 89342 68288 42639 50325 90063 86283 26673 95758 1574 23958 67066 81886 81691 45091 12677 67543 50110 90811 87229 85194 49226 75709 15911 83660 12922 46666 59360 85792 45238 42187 44619 4284 95711 74566 58350 22301 79440 35407 45715 11595 92176 61162 84719 63496 15269 93708 37424 12261 41790 38909 73786 56139 95996 23623 37135 81204 48012 52181 35668 25349 38041 9804 1717 62252 13270 70511 39868 12625 22321 87227 31152 33271 85221 79952 68506 4437 98819 55804 92565 36663 74009 97445 22 18783 38266 83555 83774 55142 4729 5960 12343 19276 42297 61662 50994 63854 69413 48195 58929 54307 47945 33343 49535 36728 36760 96192 29824 88937 93914 90178 68536 69597 46101 97141 7687 28598 45413 63103 68964 20476 56243 59681 1580 8539 10458 80913 33076 88547 91739 34396 99148 79416 52895 17097 6381 57956 67333 26574 14406 1915 75465 32189 49280 43948 13617 94432 12577 58305 38899 61897 48057 61212 90701 33731 49375 39868 39709 26559 21304 1007 93521 10632 65692 86625 32965 4431 33238 37696 70641 44777 88759 47243 27757 13820 62774 79786 54704 26529 5927 49557 64078 74849 78232 97215 41672 87088 84096 24636 53612 65456 9898 68378 68062 32408 78972 81769 94979 69096 81532 44840 12181 23366 16127 44899 65685 38455 60738 2120 99321 84171 81127 14689 79362 61754 26954 54813 41085 12518 68647 25176 96050 76810 188 31328 27686 82168 36068 96257 80953 95307 39958 99692 63641 61580 98516 79064 75916 56925 95582 53047 66899 15707 57498 33370 57699 73957 19597 72850 86481 19890 66386 5756 68834 43204 16734 5564 17853 77351 78447 33976 16426 28928 95066 20615 89165 79137 63643 33311 65500 26519 1972 94034 12668 97897 2458 77372 21575 82190 44955 34397 86322 51722 70679 55978 59982 41460 25561 42704 33088 57669 10390 73724 9839 4130 65767 17078 50756 41582 2405 43669 52298 26856 89076 4472 92424 78134 48449 12745 66800 37105 87642 79013 28749 52836 72005 18869 43276 52630 81668 39256 76940 16621 62476 79646 31734 95899 11531 27820 51092 58819 58704 71535 97851 22561 90302 88938 24007 39374 85783 38164 5791 46313 62417 71881 8295 77257 38973 28571 78191 16164 6457 86835 51015 86822 70301 18310 58442 71139 62941 56271 83070 44850 61650 90540 32374 16945 36727 52319 3385 1735 95125 79773 56187 86701 59883 96951 74153 73107 84897 97737 58001 70477 32093 31993 14978 99885 58486 83043 59000 88224 17538 57789 34491 67715 46585 65766 34152 13844 6924 64510 70314 82298 90180 98808 8451 95418 3731 25392 30424 11218 87523 21406 39874 52358 65284 28129 74458 85145 79036 32463 36835 92459 58498 80158 99292 91325 4634 14544 58258 79247 70453 46590 9057 26287 24612 21203 57690 64282 50915 8205 94662 36837 6 50410 9259 15270 63064 95268 43530 95253 6139 12681 81819 82617 27626 36232 20350 30469 74820 90040 90189 98928 60537 26689 16641 73538 69882 45950 51631 27908 33949 67322 3670 97140 25786 2720 3728 12647 24015 87168 37210 6083 23356 4355 44250 38569 53044 45344 94140 48064 95524 96661 3846 4349 52637 24807 64697 89274 91327 31117 25630 54753 65472 92335 92747 56680 39342 67951 453 44022 79285 45452 45877 41795 10818 99045 56283 98399 10299 99417 51462 74723 57627 99995 30739 46737 11143 59789 66728 50175 17456 34132 73812 62959 19561 15682 37741 44999 67063 88746 13946 23914 21406 50069 83688 12757 13488 28304 58275 61734 64890 2750 64646 56362 64153 19413 48327 25696 2020 21666 14828 30829 78984 29347 62455 30679 7514 84120 67953 31656 9074 30033 26927 21066 53351 623 2658 35815 51727 91137 12504 60995 6109 56482 37553 89568 64766 43046 82676 67276 84297 73686 26074 96469 52561 27578 6541 13703 13348 42371 64942 79908 40743 12120 15223 14264 83360 79645 73579 82998 89699 75847 24447 32465 58423 63991 12192 91713 59143 20665 73662 90868 60 56567 77646 38244 88369 35880 10371 74354 54800 92215 19289 42430 46995 6888 49079 93345 21230 32438 43238 61736 40388 74565 31533 18255 98296 68411 74466 75224 52956 16519 7797 95425 99091 90235 70902 5116 72376 70834 23018 96135 69429 81902 9971 86512 93064 979 91923 35869 76097 20194 51374 26535 11275 12502 2387 55955 28690 88952 5344 15283 32007 2695 77211 32439 89022 55646 41927 74480 3928 33655 32370 36961 47601 79580 86119 11636 10602 63284 76207 9252 59454 39866 94785 84338 80594 72557 70899 7225 17362 4028 20749 40838 42126 10272 56941 94148 1220 9165 8762 67569 40203 67905 75784 67136 3094 83869 12265 21931 55360 1630 70953 23266 87475 68629 61085 15789 69307 22778 63048 58012 85864 17635 10248 34428 99056 1252 34910 53009 56596 37633 97952 49788 64388 78607 66252 92307 99782 60098 79076 59868 87932 51585 23221 41503 5356 62511 35440 30389 75900 24292 35549 75220 8802 36113 37112 81507 20276 83451 81772 63228 23809 33990 88333 51804 7705 74896 90246 34515 95615 51971 2858 35559 15712 27592 81803 56989 15115 79205 8549 45700 48296 12436 19581 71697 7799 48095 55401 53869 6250 49101 27202 74684 42386 37080 34241 64816 27446 9202 6410 85229 32899 22328 15269 49646 81539 60204 96224 48516 57024 29706 3834 67249 27087 59937 24655 2414 30371 71155 45701 82822 27072 1343 3311 38861 74113 20250 32682 60156 69186 25778 65095 32101 33846 8942 92724 20292 11676 49953 86251 15763 72840 2150 67390 20874 46798 68988 72858 17959 50670 2514 37860 44937 13219 6243 38639 17196 24650 12380 77543 76038 76723 11274 58037 69403 83214 44746 32066 99223 47691 35478 78477 15575 33573 33149 14237 4389 28321 98208 40011 76138 7784 21909 50149 21695 2452 99294 50096 38156 97818 30006 10573 8039 90834 23541 11671 19175 97268 43398 21248 46471 94094 96233 85502 5818 51034 76031 23209 49731 84763 20463 83086 34549 40308 20521 40721 85027 58047 7268 23540 98577 41160 16456 59569 37158 64699 17066 75167 64804 27837 53337 66209 60670 43854 10199 35211 83668 35627 56389 58820 12229 9143 62371 21147 95311 9585 47024 40783 47769 18528 59328 80229 12935 77923 62839 57954 6262 64112 36781 75610 30782 20529 91516 93487 21998 54928 128 80749 5386 26141 16198 25402 83600 50613 99203 56129 9675 24926 53937 51584 58850 4050 89512 15474 77878 4128 74141 67279 67618 14597 94110 18196 66779 83404 32641 28853 14124 55835 55282 69963 16546 97490 31054 22778 76989 98603 17576 71066 41103 72466 99074 67775 63 74413 46190 30833 10027 88093 30766 88798 60052 51592 33590 88575 2816 83425 3464 89303 53471 48270 45887 98722 44836 74849 83691 45595 94472 80838 5877 73865 17873 87100 15966 82440 94030 41952 5902 26520 58692 83678 31756 69096 78432 8003 99927 56696 69883 39019 27821 61588 81188 72941 9978 61748 51217 78204 79597 43163 57597 69171 88048 47772 75807 46926 43392 70392 10793 46308 1738 64553 48862 73323 38073 35333 95162 14718 69069 31042 90127 11101 23119 44188 1395 50774 27526 45365 98949 84314 19991 78398 59852 75553 41821 24178 75394 47871 1661 83666 50352 20287 42415 96690 97394 91904 39568 52326 84846 71359 20611 48299 8772 49993 88412 63865 21276 33684 43517 46509 76398 67265 8339 25137 91031 20695 51350 89360 39279 72154 47861 1109 96219 18637 12370 77526 96850 37241 64753 80330 9573 33737 90776 45239 63878 76611 75738 69917 59223 68081 45731 36712 3448 55131 16828 65822 78652 99176 19690 84537 34132 75657 34821 49318 2367 5870 93123 63509 69799 79505 93461 88853 75771 93661 7656 34166 96434 93273 21602 429 81465 97148 73271 21532 23253 78222 31092 15806 65620 13005 5498 65883 33591 28796 56381 45051 25353 81335 17516 1068 35271 56588 29626 10986 63542 48109 35866 53078 92299 3879 45226 5806 87778 71010 67608 52674 1459 61726 37514 40928 98343 80828 95646 71388 90259 15546 11347 66740 46443 38518 62725 11905 96193 11974 13396 37666 14131 54435 89983 11691 41910 7929 51249 40449 61001 59829 95973 41734 7923 14425 71854 17029 78554 96472 16985 50066 63239 55731 5101 47925 60861 16820 49398 4074 58985 88545 39418 21887 43058 26996 47326 70890 35020 20684 74937 27063 78377 63347 66972 71794 90287 66246 99231 15104 69393 32610 38844 57167 5938 23196 20527 60655 47782 63743 21563 40036 16294 70353 83611 51261 85799 78729 72974 73013 76959 18094 15717 37898 37801 95660 29258 45520 30347 27322 61116 63898 54755 1210 71051 57940 42560 87518 65620 78602 76996 98792 24203 62987 34696 5203 76157 45737 23904 36204 35424 96708 63906 52257 42355 3260 58324 78731 85882 43242 86261 11374 25116 68463 11233 35807 66619 44290 39845 42593 64565 20978 64464 1199 48898 90755 85527 32939 72647 88147 86103 34749 65800 76337 19794 43717 50729 37221 15730 94296 2090 15756 28158 92952 454 1621 16975 13634 90940 92308 56099 73219 5394 42281 70115 70377 77238 18202 88896 29831 1507 73155 68936 16586 27105 52932 22259 61608 3775 97507 99000 72775 14862 77238 34550 42909 57428 48355 23769 85863 58392 9542 75611 35033 22312 74912 96989 60567 71393 84592 10826 23699 87813 76985 51360 4431 64271 68144 18383 43074 84734 55113 60994 52320 92122 25238 52702 71950 63247 14335 52003 61111 82872 25615 1189 15300 31134 66540 30001 4060 52140 6820 46182 35633 72117 7130 8881 76894 87394 91253 49098 78337 77214 55605 9094 77183 76164 49444 50993 84772 70825 34016 39307 61750 4472 12005 77214 30560 54028 66922 8258 3424 21106 5764 50492 47818 72299 45554 48114 21884 8851 14821 68657 33657 53147 57819 77187 59028 24021 84896 62939 88576 35184 63127 22429 34594 37521 24533 51999 62432 65003 36277 98497 68269 99722 10902 99246 40732 90346 9013 98080 95621 71639 98142 94255 16577 21941 65771 91780 87413 68383 49419 39702 31727 63718 68000 47383 21782 48143 42637 80475 98396 61467 39059 45591 54058 43577 79907 49718 69479 40552 5494 95569 22527 24573 69626 77696 13806 16683 42526 15583 47910 60845 26950 82529 32493 12108 83563 18226 1355 51211 99001 14291 30897 69679 74568 41948 43666 27486 23488 49648 20841 10954 28734 94848 48865 35759 54183 57697 91554 32724 79745 34437 32279 68756 40750 60478 65751 44333 69498 60692 57832 23623 76528 85527 79333 20033 42418 77673 4515 9564 95909 47688 33379 16525 85168 36408 61176 24382 85787 44068 59661 48248 34388 69249 10352 16273 93735 65604 69218 94887 83472 85338 8333 8615 25 79789 35761 26158 44759 79173 21633 37168 60783 99435 76267 78400 95239 9624 16902 70330 60824 47679 272 34807 2934 31574 80094 48478 44808 12759 84845 72994 59104 42327 56984 64358 19265 99858 82004 32000 30920 43816 739 53680 80076 66914 88976 78242 68942 63073 92839 33258 64871 36995 15083 79222 40060 25686 24516 22285 70342 85258 47169 2097 47666 52538 93182 28609 56018 16015 36397 43093 35086 91752 19762 49011 89516 17142 92270 40629 48094 85694 88966 9411 91644 75609 89601 79165 68763 16576 73518 50569 28539 90437 70873 77123 79501 27825 60232 66255 26674 79759 96959 95798 73433 74243 21465 54709 73014 86685 32099 65103 96751 67753 46435 6990 68350 88616 69305 477 29604 51356 53990 40137 24554 77247 83673 29993 50952 44730 67805 6422 42984 6831 19830 99328 55514 80041 86662 22537 93821 80753 72752 93781 27307 73140 30086 85113 50307 34158 73602 79213 62820 49777 88121 6153 22975 89125 30339 56538 55966 38370 3578 77857 21582 43290 50637 77980 63646 63459 25649 88481 20007 6007 69329 42714 27260 32462 83249 91212 56794 62868 25702 4203 49246 10069 13200 6732 61389 41634 87233 10407 38896 86322 91215 93916 14832 1525 5396 96898 79479 81044 94661 59816 57038 38925 95926 70159 26239 37998 8209 13047 93122 6931 81893 87699 47032 50749 54273 85400 35410 37913 37855 37870 89509 25206 28186 98932 15351 9915 79150 39480 9938 12917 187 36354 44094 45648 99897 87639 87392 96494 8019 28722 74995 13564 60464 6396 14762 5765 60998 92662 16204 78507 63271 48484 85918 34131 91604 18343 46672 8984 64595 36668 96030 98132 7164 66740 32568 80440 65568 36832 97039 78554 58312 81346 1927 18120 95507 90011 80312 56771 24783 67153 67515 4457 60551 40166 33155 12801 97028 91195 29243 64694 13299 78874 11804 1727 55778 43082 83175 50011 5268 30582 96899 11661 94104 70045 28265 71522 42214 85264 6676 9652 46050 60333 39942 7802 92030 73792 5828 4749 29693 37130 89455 23817 17912 36062 60038 57611 74585 31624 7812 19634 74258 51669 4553 98767 34691 85872 64755 99736 66745 73618 58824 45734 93842 35518 77169 98820 33949 10244 57604 61728 76635 97627 81863 81216 97356 48234 49281 55492 93815 8073 39465 58370 91945 44029 46059 96087 71283 13164 4867 4634 83534 10819 94799 62561 5457 98703 5612 15923 60466 80454 88873 82615 42498 72329 98953 19736 27281 44218 13819 6523 39101 54934 67315 64605 65061 77010 81354 71735 9002 35949 41158 2411 91566 91295 60323 7835 72164 11900 75239 82123 44001 87088 45785 88419 56012 72054 86302 65607 81645 56494 25566 66880 1918 11293 68688 58609 77594 37469 65420 32861 42117 11184 26955 99920 20504 6242 87075 57182 39623 75093 55826 4780 76833 3345 82021 5362 36935 11607 68427 87355 16897 8976 28814 69022 47528 35316 73576 25560 30285 49139 58522 77132 11526 22389 78925 64842 54672 79567 90437 38507 21417 95909 39108 61967 90461 75405 8941 60833 91872 49095 81398 71906 71935 18170 86197 15302 66824 81528 84647 76142 44611 46868 35950 1482 13198 26328 9539 31928 48208 61274 47612 97351 81300 49606 92408 75822 25718 84249 51374 25526 60990 67008 25549 73391 91384 99524 68340 68821 49343 40906 58733 85684 19060 68765 44054 24797 75056 61231 12392 76831 25668 23473 68604 60293 75329 34115 49270 81413 92549 49131 55058 98394 29766 57314 11990 49176 87799 86427 48312 40330 84258 48272 33154 4709 15411 89286 51366 98373 48561 51211 65244 20798 97419 93044 29663 60435 70484 12881 19912 65279 94345 74766 7627 71083 35284 86984 55572 7598 7908 984 79289 27544 19216 58311 8022 89674 24327 93551 26552 67005 29132 15481 81198 49117 72065 52301 5311 70391 73885 84672 97228 93383 30874 57731 37007 12886 23734 34716 65662 16868 42246 27856 69143 96809 82903 54625 51306 69788 77221 45540 19868 90859 32059 53811 45306 98487 16023 291 79809 29375 3971 54975 37184 24311 47717 67402 2708 19396 76798 90549 13023 86871 52987 14455 54635 95538 16242 85533 65617 94588 10518 29408 43528 81712 15874 23721 9324 14037 15311 83693 69905 38335 28808 9443 40193 30837 66703 29079 29549 74537 16696 81373 51965 93907 61856 43005 83048 96437 73199 78032 45395 83862 22753 92095 72783 51104 14183 64009 64015 92256 44773 50408 73070 99085 17450 46809 1731 39562 52084 57096 81647 25653 15468 85144 90944 23915 12070 10290 96837 24892 11891 28990 78131 87925 86235 89936 87451 44808 28705 21638 224 37732 87832 46501 76685 99156 9159 88434 12564 57250 55553 97589 93870 42679 99907 70862 29221 15107 64171 26539 59609 77100 77397 31591 76663 11861 55749 17768 64897 55380 45946 58531 83254 55001 67472 34755 67948 90671 37808 89800 70653 14986 49033 578 1591 55881 79340 34781 69618 60177 26261 28207 24734 73261 2184 1308 55614 61737 18536 52978 16712 34624 16739 17785 29877 67603 93946 40630 25945 81540 75553 27084 98892 62318 12443 74308 71440 51621 18713 89889 79108 17788 73730 5147 20870 63265 20109 32368 89393 61548 51573 18788 3482 63268 27793 22888 80597 93884 36876 51184 53528 18937 25730 19669 2537 71009 42579 21272 30844 79553 97305 63912 77479 60747 76630 23709 22313 29851 96789 30930 58529 36162 69270 98089 87613 15646 16158 26632 16901 16037 30346 22671 6750 40288 20206 51773 36819 71293 38158 26598 27682 18156 85348 92157 24465 62146 85786 68096 66785 12476 53484 6454 65113 79149 30215 27913 52638 11672 10585 42013 52836 77521 6880 22559 84628 93927 63962 50418 75435 11857 9955 26728 58342 16897 98406 77655 18506 58606 77362 44180 2019 51939 88809 3498 4987 42453 71246 60968 95415 13164 89441 5063 78424 71589 88487 59663 24107 17037 72224 72537 93336 74000 68381 16347 34158 89573 35195 5330 47539 43589 50099 75350 41305 60842 86311 52972 45430 14452 82507 87608 44199 80592 60409 81452 81109 35062 67525 37032 88739 93242 31956 66720 76853 95697 94107 96709 70175 65770 48731 12398 45225 61372 56843 10406 5900 83238 23510 73268 5179 10028 99943 98143 69535 52931 55169 36687 89778 74479 51086 6870 72851 18647 23595 33856 44914 38597 68380 75108 72552 29949 17138 27659 57727 43857 81950 1651 69376 15786 54090 92827 69392 93977 34260 36908 69324 19224 78629 57417 73274 37139 6403 24494 50727 31773 5352 52637 13714 20664 15301 17886 22540 20910 27434 48601 24139 64899 27847 93039 17988 86218 1965 6496 64761 64600 68807 30806 49791 60060 67066 22648 51053 6474 82771 13336 85389 80710 64707 58011 22440 48799 81435 16909 58341 69999 46231 85166 5140 63126 92932 22760 26005 89826 33209 49130 52644 45529 11457 87318 51441 82966 74011 13990 54732 50133 97356 64168 43654 42288 75526 75570 10687 79855 53305 76866 95974 95354 28715 9192 11147 52533 28406 39426 66486 20353 465 26729 12163 47149 34230 94882 39107 44043 5881 23197 12193 25687 22022 24243 21061 19966 95810 89198 6753 599 20412 20644 23616 48603 82655 41708 23 8286 31913 98687 91492 72445 97546 78939 53809 72232 56411 98565 51395 2739 95740 14093 68988 66485 85620 54390 28390 42115 62943 19106 4219 74014 28281 85273 26308 82404 68986 22576 91190 56988 63948 71544 79241 85238 42199 13469 71480 90847 17495 61321 25102 23255 84461 80003 98453 15925 68003 23378 4616 88856 64820 8722 68777 99333 17219 78873 32051 30700 16501 45014 29013 86332 72574 67777 87208 59676 75349 80247 62211 34016 67551 11285 83352 79642 21407 32745 70799 19605 12242 71762 62093 15190 61867 99892 46355 76304 88536 35866 495 84286 15309 42423 98321 23356 79271 53021 33841 87994 64220 99614 36509 70426 41237 30327 87568 2235 19582 94037 10885 77601 61663 53004 39782 26224 38214 60695 97580 69249 77431 19003 17539 46334 43480 68193 39910 42765 5287 22073 18761 5446 20533 5565 44797 71784 84357 28260 73173 4778 53671 42814 41696 73038 27550 68208 56339 14537 41582 94284 23665 36096 39408 80162 27797 86021 598 46947 51324 51482 96496 5803 20932 40278 9806 67100 52852 36468 29111 4436 68990 36723 84919 91449 40858 85265 31659 918 8476 35439 33027 3697 46058 16845 38048 50455 72811 81611 45204 58011 62915 63124 16765 95961 38228 13633 9700 42838 45937 8676 38736 68886 61718 3398 91638 62807 39251 9325 25980 69617 38269 35164 78261 62125 22732 8198 43119 5327 29997 67700 53829 19584 29098 18903 76673 56966 57620 21051 73879 52190 3783 66436 13976 18203 32247 41921 53886 11970 80546 78619 3640 96355 30098 20646 10153 87471 58068 77549 64079 52173 50764 34799 95176 91851 83374 54806 53895 63108 13857 23246 37626 51425 67288 41641 53577 37124 55269 40356 66389 28885 25664 10322 65268 57859 96580 83397 99530 12791 41257 66253 46540 44203 66539 86461 8980 74848 31882 42718 87984 6490 34007 64852 90835 68689 97007 55793 9155 31763 64894 70454 5293 65936 16460 44419 8172 17477 90482 4972 21259 44737 84478 14059 28472 64692 57227 38759 3520 61940 40751 75084 39635 6668 53229 17757 19786 39701 44885 38048 13775 21185 10872 48210 73684 33403 77403 75712 9003 95676 24417 7414 27802 12169 67892 16722 60614 11585 29442 85053 71799 62224 11244 72937 87702 79028 84238 15577 37996 43332 79041 92923 4217 41174 47539 11081 26300 68146 19945 21554 62267 80251 63301 14156 54765 39656 11472 62816 97155 15931 63577 2327 96242 54440 68996 88247 21781 80035 4944 44944 46074 59553 3148 85178 30217 72846 32168 23333 67749 38256 56135 28398 2429 29395 39111 90986 77983 13869 79565 29530 88131 18516 34186 24720 10108 1210 86404 8147 48389 85207 45402 42651 67510 87012 25089 22524 23705 99960 60754 4559 83826 81707 98361 35202 4867 1588 15398 69240 6999 3012 79149 63311 49529 99287 70207 17722 49121 30144 52821 97702 95671 37560 508 31987 8708 54559 13726 62353 14989 7584 3387 27981 18299 51335 48842 14002 69994 5968 14609 78176 47489 70966 97787 30669 136 27641 98836 95208 31057 49365 23016 21202 10977 95357 85404 92360 27992 47654 56102 56993 61459 28796 24552 24979 25118 43455 56724 51853 59750 43752 5092 79325 90040 3946 89206 19085 84323 36516 55466 59681 6875 94377 93222 8393 71659 21629 98925 48415 27142 91266 52071 26876 8355 13871 12233 34182 57448 46230 14678 66146 68143 55636 43818 78564 81257 60067 224 48723 74902 28270 7124 17852 25522 87881 92012 18597 33083 91273 47686 29789 48879 68527 58321 52715 53571 14334 43522 40550 452 63713 41334 45590 23238 31639 65131 25886 56774 1523 64896 50313 49012 5129 43470 9962 79647 32793 34821 61219 83846 91010 54182 7300 48054 33423 8238 35061 25294 67579 25087 79460 61956 32066 3980 62774 15679 77048 26635 49040 23308 42892 40301 65297 75044 37392 69788 9880 6270 73708 41689 9317 74399 16847 68122 21964 2706 81611 753 40656 15287 12010 30676 25182 99146 57140 28710 96864 43680 32444 37496 63534 34684 42216 17347 23674 94548 46878 23579 32877 33657 17490 41486 246 9450 41508 65489 21853 43977 26245 46855 45320 40490 38609 39187 48038 82331 94695 86717 53351 11625 42749 85243 69765 53666 31204 75239 36720 80404 78621 19899 48670 84699 13115 24589 28709 12527 5989 5423 14460 87585 40195 64237 47117 40815 71483 14510 55593 73818 97260 37168 98647 79909 10482 398 7270 58896 30218 79669 13625 58390 40552 84246 92416 46983 98499 21415 93984 4576 38709 89583 79619 68855 59860 89791 20387 93639 53450 17874 6141 11760 76359 7745 30837 67059 51805 80137 38722 92682 22640 23808 7509 81357 48881 70270 63219 96174 58209 10453 54130 36076 5108 11415 35142 94 8638 42751 82145 75282 73310 32419 68859 38023 89105 38756 66919 5762 83668 9894 26987 2996 51657 102 11863 53507 5608 31170 41502 71926 92795 11290 53220 74008 97034 84030 14898 46425 59244 56766 18317 23521 53314 89591 56737 67482 47733 95994 24881 25664 80135 73420 31953 68464 89553 8112 6471 77331 81167 11834 57438 8545 20364 25706 31443 78460 22724 69269 69871 37754 43546 68743 55313 17610 52973 31669 15114 97012 64773 97206 17992 4567 71352 26438 42299 91914 71612 76725 17792 74394 99921 82035 13704 60134 29550 98969 34359 2557 97210 69043 55363 78076 60557 84312 78873 84869 81259 63403 13708 21932 76545 11413 38462 73926 30296 11656 80688 94771 6122 10972 72318 87234 92410 2379 20264 71312 45331 86873 74227 42235 29536 16646 64262 16817 90596 84533 10230 18774 30314 77748 52529 38765 31934 84031 6958 67177 87332 16596 84004 34755 43964 47864 47294 84860 81826 14823 39657 31 31028 16210 16695 19707 94039 13648 44684 32223 31692 14361 42855 55438 75794 33684 45225 98814 66469 61910 49659 17723 99932 62833 86749 82942 28456 40305 75327 10013 97717 77369 59060 9978 39435 86131 42705 60020 81207 60507 5030 25819 14904 69252 89014 57905 91276 26831 93584 82073 62103 88827 33592 24385 36945 13404 33225 42981 71829 56917 24852 58702 70258 72889 86310 17818 85063 70198 41222 94028 80499 42412 54747 50603 37938 39493 239 54800 22281 51633 10546 60562 83280 36405 83682 74235 67877 69217 44348 19275 96680 39029 3299 52671 57216 55778 14116 60581 97108 64606 45879 27224 72003 96749 26211 98276 95831 24170 54026 96708 13526 88566 38019 1476 52310 2960 29235 99877 75187 96094 89398 65363 79659 38168 47235 17013 9469 14383 58880 86422 40779 97253 87145 52127 51164 42672 93757 30352 28085 90533 40159 11823 14262 10168 31348 39561 86639 27997 16157 54808 89406 66275 7382 34263 25575 22556 6296 17605 10081 87388 55064 3234 36656 61787 8018 78794 26173 12123 82129 86513 98542 51907 39454 14055 80718 9148 30133 8773 57388 34366 12053 53764 93069 17253 33110 40838 35542 90568 68880 82954 32798 48181 79079 64479 36381 2327 24536 29701 6323 80064 70783 6305 64369 44957 54537 62941 15298 24900 23505 57160 33210 81403 58719 78111 7649 76536 75435 59309 67283 95994 84988 31741 40335 57292 70278 24438 31506 1290 99424 76443 72851 78711 55151 71954 63194 32998 19327 86189 99468 26593 76179 82846 2343 27829 71368 29864 86054 8101 14255 40681 33394 55672 75788 38501 25898 64518 19622 56763 47129 66883 33467 98231 31215 17121 65476 52531 9428 13031 17788 77980 28382 38729 15275 19722 41365 76619 39449 64686 84839 19926 70422 62559 13472 98357 63373 64337 15261 85756 29683 78018 33636 27689 62108 40669 88258 23585 10887 36524 91181 79870 81951 59259 35101 77178 55677 91126 13959 2209 18163 19753 3007 46600 83815 64274 66120 80201 60649 26406 44045 1156 49454 83645 40114 71421 79579 66792 8860 75262 16110 14837 43734 12486 88557 53214 89379 95440 72035 59802 62880 98279 30230 73666 47876 60386 77096 13316 84302 77965 34042 63469 94731 55159 1643 44389 18401 83526 45856 41543 56077 6083 13195 70594 82483 58352 79520 41908 8210 5254 54461 25164 63651 50459 92584 88628 12468 58121 66768 57415 73515 32551 4574 48999 32959 78548 73266 67486 26066 97371 15488 35225 5300 6000 83679 45945 29601 43128 95335 35071 31524 81415 88201 56140 23590 55778 93514 13980 68878 74787 1569 23975 10021 41710 48924 80712 96069 6678 73503 29847 60977 80624 48926 71218 60744 69026 51483 83595 24054 62314 16195 59746 35479 16222 15501 31424 10980 83846 28846 76176 58931 81248 21157 21121 10322 6051 49616 4687 84325 43938 21675 4695 19227 11518 86795 17162 65180 44563 17347 58100 99941 34039 65850 58542 31726 18010 86205 84537 3236 3301 24441 8205 39596 49662 83061 38792 22759 92584 98465 76498 94111 56801 13178 32653 84802 2783 55412 55754 78494 38899 22962 51032 16076 85601 19516 54654 37685 97072 24774 34364 20433 72740 26955 95911 49864 4805 47176 44704 96419 69666 10329 16847 14376 73259 35138 96544 8553 33786 6289 10445 15485 55925 45856 57802 25777 88708 73282 28761 86523 41012 77883 748 70322 65122 87803 21172 41022 89867 25388 53440 45609 27221 87425 34061 60016 42087 15216 61177 18691 92076 95529 53012 50081 56554 98003 47332 63224 92345 83226 60985 27740 45321 24041 6228 5783 88450 1615 92405 68678 68947 18356 33361 47496 63499 67105 74361 77990 71717 64842 17360 94351 88187 44445 47700 36400 83622 63417 90763 48807 76351 98975 90963 4154 96575 56274 97741 84029 47072 54951 66911 83389 92864 26370 76427 79054 7381 97346 86736 68038 45389 11823 19416 76013 79575 17987 24270 87314 8912 92609 25141 44029 27157 49756 9837 97153 39956 70326 68113 22651 68060 17602 62141 4376 42826 89025 58004 27251 57810 68366 93590 35629 67271 31585 93897 27648 6356 26831 90144 49135 33850 83941 81672 43020 78882 6808 95851 92182 42896 76974 35370 56314 76010 21757 68786 64981 38526 48982 55509 19215 54454 1831 65593 7960 34186 15585 73264 42691 64812 66158 74014 56392 39977 41742 55859 63468 10260 12862 71206 48698 22967 83593 56784 82605 61012 71212 16804 40436 5092 4653 64515 22624 27353 14985 73411 31876 65948 93156 12609 12383 73211 11071 850 72926 85143 95876 16989 45292 80700 19111 79495 93254 38697 61767 90337 7656 48462 11395 61983 13256 70930 9927 13359 96269 63528 67405 91671 72925 90606 4433 43336 81790 6733 71780 91958 67229 33353 80015 11146 29326 79270 4407 29079 86666 25459 96362 99818 77002 960 57592 61171 38382 85876 73162 48537 73589 38724 19558 45950 42043 92750 307 77783 96152 44804 51535 69480 24386 48346 91319 45718 12963 35873 74 7400 25995 44510 82033 8879 47262 20607 3903 15711 27698 64948 13115 97750 48370 94657 71662 22433 76942 81954 21844 14264 3076 94858 94824 94126 17589 28158 96172 6109 14750 16499 6515 2724 58962 99873 15599 48427 42581 93763 70187 19530 29548 25517 15863 81639 43327 48212 40439 39913 18277 14371 69760 55547 23871 5207 64894 74467 71116 23830 40902 97903 66359 83176 66041 82926 954 54170 54563 67185 45190 65720 34403 46851 72154 67008 24304 96812 46436 24595 31205 41225 71834 23777 69691 3403 80977 33848 61656 2518 385 65217 61748 35799 89458 13194 85361 42081 41817 83313 65112 69541 12388 31815 60614 19178 44197 74089 71472 22434 23498 23755 7846 65017 11946 26837 34326 3962 12154 84760 25082 5861 85726 31615 95702 96559 37874 9203 33421 19819 70768 91307 74749 61897 12458 45812 26110 29839 36744 49303 98124 66413 78226 3669 5288 50854 13359 41637 42893 17089 23143 52245 42803 30617 44048 97444 69715 9069 84347 24124 24020 7563 77386 79995 93989 78466 71544 72921 54831 14035 34400 33489 53293 42396 33825 13862 90082 7594 42717 50341 67356 1994 40311 28461 2556 27290 39239 69984 92039 87136 22868 18497 85614 39548 95018 86338 91113 56807 63068 21320 61839 39891 75323 56046 58911 85744 65892 49867 29699 68423 58043 82873 6184 87833 87708 39496 39 33048 67983 13287 16876 55881 56488 91604 98599 98929 73086 61127 63099 44375 99115 91657 20713 40658 66746 85150 73681 6388 69209 78250 76335 16829 35758 60170 71659 73692 96795 25929 72179 34688 30197 6217 69471 13569 27204 6136 98470 71390 78524 57544 24174 27639 61629 41350 20980 26819 50728 45544 85819 17993 68122 62580 56913 752 67691 47670 65558 17612 36750 34058 54121 69083 60274 61397 23049 29409 48142 30572 69552 19741 40443 79499 66491 41432 33917 43870 19415 50772 60141 650 66950 35283 98694 29567 92997 626 75877 88493 32526 402 51210 14345 65870 48682 58430 52510 76550 48292 83592 79196 2867 78124 53338 59404 65087 6546 28288 50826 1118 31111 79272 34645 68534 37321 83922 93041 23 96769 56185 52445 67066 79468 17324 67832 76165 49889 19129 34362 24810 50081 1872 36070 69708 34081 82238 49717 24525 40014 84878 68780 71372 19031 92386 42125 35010 23710 79373 76884 47256 61730 84168 57033 60843 156 61617 7917 84828 10695 75733 58552 21421 34696 31132 23077 84740 51939 10338 35987 49417 40109 8764 13769 93517 93760 74808 20435 64977 59551 76410 21331 99917 27764 7888 51962 8668 69101 66436 73256 93512 78862 13461 16514 56419 63284 59117 77201 10984 9443 63782 85985 39378 43274 80514 94447 19157 42544 39834 86770 73871 48769 16450 81693 27212 92058 52953 5562 15763 86355 74394 5884 13544 58498 22948 43362 74982 97323 85906 83581 40051 87235 63903 13044 34806 90008 86407 57913 90602 23284 86468 28265 27988 35643 61299 7586 12087 21243 23171 26603 80235 18761 79516 6251 11464 27327 73741 75617 37719 1931 64627 72701 62070 38234 11319 51370 34595 78850 80196 25817 8282 65020 87927 33317 85148 52444 10335 74672 73550 6559 49467 88672 50744 22143 25540 65274 65671 77105 80636 24840 41010 53501 33001 82072 65809 25033 39797 35703 33306 54265 51056 68008 59085 28967 39781 51202 70732 8448 57274 58542 95899 30347 37339 71681 86505 36557 17202 72701 58553 10258 11962 62957 62534 31112 98694 3586 42076 66519 39449 29459 54360 89566 82426 64498 12393 67377 40014 70202 91109 35670 92937 15547 22244 58830 10348 44876 22836 64633 48166 5698 35829 86645 478 40156 28149 12629 14554 19454 58647 96015 42437 88592 4664 9529 1362 25155 75517 4252 50910 47945 17400 66491 87016 19503 25146 44910 70874 69998 37187 77591 51350 96175 36658 25505 79457 86943 96715 97850 89547 83996 16794 42371 41279 54570 61325 30681 16427 21228 99895 20216 14837 48597 32112 89533 61090 73535 58504 46651 16035 4083 80333 43954 34872 96054 79258 88962 47189 4910 64587 49761 56561 85888 95961 22571 61604 16120 5460 3425 93919 48157 43799 14377 52564 7949 56544 81100 99261 71571 79343 19370 82662 96451 91535 73288 45317 90011 4588 64634 17590 32063 18338 47688 42728 58804 20609 872 81934 62452 35500 70211 51174 839 20108 82018 45870 54117 29679 2855 4231 6675 34779 82818 49572 33473 5741 20355 77123 42170 96966 35894 7762 17935 22570 79682 78316 1812 40127 69030 91724 34417 75103 28152 18170 35008 41415 57741 32587 10772 65797 98481 21079 93753 64950 51997 14073 14582 33358 46960 74768 11489 80127 76329 54900 82171 41176 82215 61198 11072 43757 99207 28903 97921 4852 9046 64573 21978 36090 64561 92982 88218 42030 15946 5601 69915 23556 91782 79872 70565 68459 74091 31096 23000 72787 68683 62465 57642 42386 69495 76568 50708 82235 67289 2907 43457 67138 38641 95064 80371 79309 16424 26947 50282 67237 91187 67364 23083 6019 8527 91726 7153 54050 73617 99078 53612 95298 98325 98952 48172 54691 93601 26945 42888 98091 90917 33038 44804 44840 65926 74229 95394 12592 33877 66305 70874 48702 28426 97331 44745 72009 16472 49392 61985 33645 58420 45414 82465 97546 96729 80664 73243 26083 47597 27275 7889 30828 95331 19957 89799 89252 21951 56290 85231 62709 18927 88148 74159 62897 97359 7232 90890 36283 10757 72537 30449 62284 1459 51811 89893 59843 22066 26821 36734 77866 60957 72627 85487 90894 3671 18807 11401 13989 15169 18942 88682 22994 3739 71050 81629 4937 81190 60431 46460 1100 84486 6394 23189 88534 98075 81091 70549 6663 60169 97966 37887 78007 96607 20105 30642 9628 98948 42503 98878 45515 14251 4176 70640 63179 39400 5964 14140 40194 79633 79329 49559 2866 71838 11064 10483 16522 73023 34122 59789 13191 18581 83240 45467 56241 56488 60297 2128 49696 32682 95126 47749 2997 36175 40467 62788 21122 213 15631 86125 69384 2261 57082 59103 55573 93147 69670 65353 58272 52824 54395 43691 50729 55589 16380 87033 4198 31552 38925 73940 40756 17018 48887 90450 73488 6971 5487 64848 61202 5704 94371 42773 41646 18295 72918 25573 63542 24295 42415 52568 34573 53073 52421 2999 62056 33500 32646 70361 61821 96929 37910 22172 45692 75289 87985 79842 54692 42123 77081 68285 5249 54936 13116 36229 41723 17586 19899 61116 38837 51646 51170 37572 83374 71289 82302 74438 840 65904 81076 49401 67854 1148 9499 16585 95711 87338 77768 35602 13839 48973 68538 66605 65544 12621 55453 52689 22764 58837 67541 3781 42767 51540 72851 18670 29812 41533 44525 24960 55221 18610 76139 1929 63958 69495 74768 7416 10319 69922 21571 26532 97162 40457 47883 89681 76897 81767 42942 43293 83816 32342 17539 55384 80484 21263 33873 29625 79917 40708 72178 85694 39073 2686 9494 20377 89158 13329 17480 75753 82980 99681 67842 340 40836 13842 84238 70049 63838 85473 30047 82165 25687 52771 22269 17178 30800 80296 84003 87054 11942 14792 3578 38314 62337 59697 43767 29158 27661 95899 79432 73041 32275 76081 62813 5844 84714 13341 69467 10897 18861 15976 73269 97554 45182 35140 19500 68527 32186 44585 91704 5313 64310 31192 87870 77496 15293 51309 66697 49936 12741 19983 84776 18859 29332 40550 30297 60754 29108 70961 78925 93485 37926 46678 46724 89075 73557 87296 12154 58702 89472 19976 60177 65339 63512 10033 36301 40006 73638 63658 95909 34114 26769 71101 99087 54328 61926 61656 60430 15728 36364 34237 14530 9639 80394 64102 66526 16992 11958 43257 75992 1795 63391 36923 19032 48666 25341 70699 32351 77126 88293 16932 87157 12346 328 75284 2609 73876 1475 13205 47717 47347 12386 74700 90404 54290 80260 81919 4187 64845 92918 81641 21439 13782 36943 73143 37453 45573 34404 24712 3026 84746 53810 8392 10614 58959 8886 14560 20535 68879 48144 41498 56768 47875 72345 44845 1231 96220 33728 14621 41449 5542 51462 61083 84853 17253 41542 28354 71740 95872 53131 16361 32134 81670 49271 22840 86999 31455 83918 803 76391 56860 78340 37124 30450 3022 24135 34549 72129 2411 56456 63062 20021 27107 68608 72086 24176 44279 15756 45283 37164 83510 72309 7315 7496 81856 18852 73306 46908 94376 32371 1081 77001 7668 89163 50844 2310 64307 63175 64335 92894 85940 82701 4161 45734 58288 16762 1389 63248 39718 59414 40745 84504 54929 73493 88282 51946 28010 82362 48150 80290 84118 25508 61366 43415 61297 54591 9388 35536 38726 38672 44491 62686 8269 50255 32055 90905 67877 36323 94759 99597 41036 67536 79205 70246 36301 74431 88284 53194 57025 38732 26304 59468 88785 51661 18608 19831 20857 84467 33707 40105 67494 25695 23359 11516 99240 71091 5636 9979 27356 20346 95026 35335 85451 97920 24001 99587 16863 79462 145 83306 81117 25574 34165 64149 44312 7507 88479 94381 17591 75796 65224 5741 52896 71240 38540 42938 16815 11373 97951 38536 31751 94715 26424 64729 73138 35332 95077 18888 63943 19174 74522 69778 76472 6566 34943 36273 89159 53025 17185 95185 42982 54033 25782 9776 65781 3643 19803 95265 99010 58933 61786 37817 58807 31473 34002 78343 35371 81438 34464 92105 39067 17188 88176 40482 74373 48058 7216 836 60745 77950 937 45096 83238 5466 98551 64676 33022 63835 44283 75734 71698 97217 75696 98561 8446 9889 60760 21744 3710 58362 24221 36395 36251 61775 93478 45249 97068 14460 78226 36158 98546 61452 29375 28635 31068 12730 79447 72022 99320 59825 54933 11705 82558 24146 59775 38885 67160 38592 4567 64119 23401 57070 59209 51981 73290 37250 22820 20986 14422 14802 33320 60088 14043 62030 28111 79563 50901 66287 36669 92456 91089 48400 93599 79149 69609 98236 81255 25318 72736 50068 41000 59907 31965 50008 48011 2477 88723 55846 78530 94593 39210 58000 72837 66448 40572 58660 7534 62379 64933 77650 80996 35734 27004 6834 77797 72163 68698 3912 13224 32431 4906 12671 5290 26654 40543 48316 31579 19764 53235 17381 15596 83686 54950 35793 31699 74523 23908 71570 71025 23562 25293 31359 63751 20587 55548 13405 50574 72920 57196 99299 95479 84379 22014 1705 61759 38835 82252 29296 22113 63312 18911 10015 20974 35281 63527 90525 47684 97808 58760 85378 62175 27697 95308 44727 45304 14807 438 1626 59321 54890 18738 85999 10538 78993 98585 47622 19203 38741 62598 88750 57318 68730 86270 25379 70221 48320 83090 34202 47230 23918 90289 13204 11466 2442 62007 71726 19491 66886 13101 57320 43697 89826 76555 79236 68516 77458 64317 76654 25047 43864 91218 55206 9670 11202 85659 93605 59770 40586 82011 19501 37220 18328 27694 45081 12195 2178 47710 89172 3174 95417 76076 94137 19765 44572 33617 78298 36613 17108 61960 58282 91647 50715 25986 39631 54389 29188 38071 96926 7222 58117 73954 58708 19053 90945 65479 71058 10906 23395 10618 27804 44252 40309 93172 68761 3007 15425 59344 6702 37679 21123 62371 12238 97450 85817 93635 27823 13522 3864 28987 80566 51706 21780 91028 23007 63914 41984 27655 28430 94666 79620 67429 60613 88660 84975 65503 23565 40701 3315 99528 60098 61184 83654 9716 85843 89959 88078 18108 51044 72907 61138 35534 25706 96126 94940 85343 46216 20056 10241 82007 38262 37680 58896 26046 85058 17501 91290 4698 23879 51781 32460 17733 94877 97681 50312 88398 73652 38023 29906 42955 4176 85320 1687 17798 61789 97103 87077 83795 86541 1745 9953 25646 48020 52678 14877 77734 61522 14077 95359 31215 63423 46251 32820 74853 37481 78540 54035 59366 22213 80099 21316 99323 54429 15103 26569 86491 68069 9860 78895 25973 93443 30878 72183 41318 19467 49558 90522 91151 82041 5084 95807 92109 98080 34964 11817 17415 69571 77243 53593 42337 1343 56423 88278 81457 70000 19452 59624 90071 88262 45539 48597 38544 96389 2650 43286 84650 22817 94448 18620 17175 19376 59746 25136 87447 69812 98496 39737 8090 42942 10547 91144 66417 47199 9554 6454 59805 40592 98243 45167 3469 7753 9122 57530 90094 8397 32908 4914 27717 99666 40976 43223 27905 29794 91060 21626 31905 50606 18274 72514 47373 87252 79937 21871 57033 91679 76453 67537 78209 2752 72583 90773 40379 17736 25619 71708 88541 41058 59527 52635 19319 42036 36422 2697 14807 16958 90743 64748 49874 46502 56292 68039 90842 30418 26759 5097 71003 38806 61030 69584 69715 26915 75770 55827 69849 64360 47332 93143 83304 59922 37766 25399 85122 96920 78114 12504 35182 76046 79736 38235 54884 61431 8443 22814 29208 95943 68688 62762 27643 76857 6394 12709 21315 50972 43743 61115 36123 93317 52067 477 40999 9862 87204 81991 28631 50850 22971 31393 61613 75212 49917 66419 91456 83879 39856 84874 49505 88979 61544 95666 36699 54080 9511 84157 31304 62506 79501 68955 20502 40026 93343 17174 9881 34471 96371 66696 53032 43253 97381 32941 42111 60242 77173 55040 81134 71690 20971 95137 70226 47130 84033 95215 41715 32361 32957 89404 40334 76863 93516 56568 8478 9226 64362 42506 96870 24949 12066 58426 44615 5688 35101 37613 51725 41506 68853 34694 65523 62082 37524 68801 84295 75843 1455 10192 19843 2759 86116 56388 89592 1955 97918 66681 57134 24693 25025 59672 47281 69432 73639 77087 15488 88742 50848 11168 50398 95122 26451 30776 2746 41424 79775 8770 3042 2101 48680 98039 27127 45885 99938 72960 47593 50360 50375 94727 49410 14929 35419 65738 42971 50735 46011 7638 15624 86801 80907 23677 70918 34478 62456 10541 8025 22 83763 15149 46833 36151 38059 75386 93354 25651 99077 26413 68628 81538 38206 64821 97335 65977 64405 54058 14090 97280 9390 67471 49804 90303 9578 90839 84105 40801 92678 25248 68353 97618 79917 94173 61442 3318 57181 37815 33937 49360 42673 81519 91540 75836 49559 44295 55359 63853 34293 89576 8307 79073 78127 20198 93441 28058 31593 18098 15249 41961 39692 44483 4178 7306 7008 99167 87359 372 25438 97893 54340 81079 80871 18674 31993 36921 96622 26006 91115 87201 81784 36171 74917 54878 28227 1871 90475 33178 82856 56339 14311 31371 58074 76209 73806 45523 35603 82422 79143 10194 55267 36351 89374 20677 35662 57065 1254 8853 61128 66007 63615 41457 9751 91533 57016 92791 36213 75330 92318 55462 95651 68401 84436 57159 17858 56845 34788 79820 99496 44975 96066 9516 87320 67873 74998 70030 11067 14189 2952 87635 86414 35576 76376 51227 24465 21868 20728 3077 23659 5591 57099 48895 56258 57221 73329 50830 57924 55577 63093 40268 950 43313 95472 62846 67932 74684 24979 44679 93968 39719 88924 12852 89431 31597 51967 4708 46159 11278 70197 5631 54427 65865 66274 68973 55756 43575 33561 84586 17172 86688 41682 27838 24652 40059 49209 34549 46072 74437 87016 58417 91505 28736 81789 65247 61723 14192 31613 33582 214 44904 8161 64562 17827 29110 54974 30862 98731 69898 33353 94604 3283 79773 90246 14188 44360 24553 19770 24817 69438 99708 62930 5447 93172 85309 78564 39658 56260 78338 56483 15225 52717 65119 54986 39450 12342 30687 52653 81160 21133 25984 74370 81446 58003 55073 18607 1191 63741 53152 60616 40653 17400 50762 81291 69999 16707 40214 27854 56238 36166 80254 72331 5129 87100 83910 18043 48579 46105 63706 39759 46032 56880 93163 4288 82706 76897 85773 44597 59938 15150 55611 91539 92631 51316 85108 89684 29904 27972 25253 45268 15560 83723 79169 9670 78213 76513 22243 3969 92321 91853 14852 73634 83496 39883 33824 79913 12976 71644 58020 58728 90323 75671 68060 59148 1903 49019 36816 21541 66217 32689 13051 88098 34701 17937 29763 32745 66070 72256 61905 95556 90010 43659 26440 37063 65141 99700 98047 54403 8092 78409 84069 95819 89612 26939 89875 95465 85464 46022 94644 65422 16382 55985 42662 13283 28907 92890 77895 314 99280 3053 25697 90820 28403 31461 99337 52370 30350 63900 63568 86860 8667 46831 9386 32965 98241 13971 57125 95137 36954 14662 93247 67009 68771 22999 73861 87505 73371 47933 74590 74581 76759 67881 4390 86033 71946 78503 6640 9910 11421 89080 25874 47499 93695 52384 36288 82079 32668 35619 31659 52286 47487 24527 11015 44397 34482 34832 25973 488 49100 23010 44125 36290 26777 33411 84254 19587 49590 88245 69406 71605 3093 73535 45503 15766 68661 51088 60667 93028 76762 27523 91720 7221 55238 84746 98422 42722 89020 98035 77072 10899 77305 62054 53296 96358 93749 44668 82004 49921 36413 32893 91055 41640 9988 6162 69471 3948 31706 48018 69531 98071 71469 18456 66071 50236 69706 76581 70879 83190 64486 31371 30035 32404 12854 63560 40137 36151 99942 31363 54242 75377 16142 91513 22470 42961 76663 54741 35284 61903 72090 94699 53747 50028 93806 10363 22943 28601 55372 34665 70410 63048 1962 70565 61468 19199 67886 54367 82833 64300 82969 43698 76964 94454 72682 15432 62015 72803 16392 7613 84784 98073 96707 90933 1689 58688 26207 90283 87747 59753 19498 75124 82301 82817 42227 18773 46472 11471 18558 49387 50384 10467 69623 19611 62431 25043 65179 69718 66805 73850 56885 57424 78370 20375 88072 62688 86470 36946 89445 86984 95907 40696 52649 6713 18981 60737 73572 72808 3828 537 46640 7703 37406 89302 94147 72414 34995 35147 21512 9541 64644 23617 2449 89894 3607 47529 50554 45243 42315 29786 16509 97571 19089 98057 49968 65397 53365 68443 36140 91555 84681 79586 8961 72914 75744 66399 76577 60022 41030 87156 4856 52935 12178 84947 91352 7222 56442 99816 5961 70508 61244 75513 96584 93454 80848 45367 64354 82571 65398 48697 95043 28222 71793 55757 52771 52962 2191 73240 83611 87818 38630 52551 73264 57152 8104 37657 98603 3221 38249 32174 27197 17855 68463 81896 81590 75927 55240 4763 27823 69840 49196 4071 7133 46448 21074 79148 44122 36054 43500 23490 80216 85192 10536 2889 80514 65710 31354 60546 43605 90821 65496 57345 74915 41661 96816 89649 71893 9200 28906 10801 74855 2294 93971 90083 7268 60159 91142 70708 93246 51021 31055 2124 2779 84648 84540 84512 43435 30568 77064 53885 38374 57421 98823 34232 28758 34150 96842 49468 95854 55076 40734 7872 53594 4287 7892 77438 69059 24178 9115 57107 90363 67282 24394 37111 22537 93338 39184 85501 73896 91649 50032 19775 39449 38059 4700 97218 90329 84784 43005 96757 37102 43255 43187 33440 59661 7179 32844 67150 53282 16383 43666 64489 67687 3687 46666 58455 96188 5085 96913 44927 99380 21238 939 39384 18805 45538 20478 62406 36372 70927 96049 10672 5304 39381 9259 11514 56879 63471 2448 20432 80660 61481 89402 50032 59796 10899 38898 35271 75254 71112 98445 68662 93933 48609 30098 29515 81548 12126 52127 41076 14754 98332 55986 64573 71180 10529 39381 3601 94482 64888 49001 88605 67023 58546 52340 34381 92988 66411 19171 1695 89663 70916 58323 35357 69390 99689 75718 142 98223 15909 47178 1626 74892 1714 42662 38597 69396 63652 33030 75648 52547 88626 7185 34644 40325 42664 17748 76198 83696 47161 33926 47444 33238 16772 15614 84021 60514 13674 21508 15750 25323 29304 12195 83739 72836 94735 17512 35683 81550 32188 51562 50013 54825 89964 58972 58434 12919 16606 52490 88355 99891 1482 80178 55810 68378 35179 13775 73169 37221 79206 56205 65246 4932 45220 8135 84769 43753 10792 11691 8867 65263 51671 63318 56886 1570 10081 78694 90674 86698 28006 7672 89302 66464 73313 50601 34534 54152 59847 87836 45251 64774 54264 23255 14337 20123 5181 17203 29166 73910 44543 48225 49464 66781 70092 80658 12353 65264 91510 31133 47737 16532 86267 20456 94404 66588 52810 41975 96097 9054 13943 16373 73662 42262 74192 54664 24322 53984 87189 63371 99810 17540 2632 83986 45649 32448 76256 79785 37846 15527 53035 97577 62188 93236 28587 61279 72195 98517 19700 25084 73602 17350 245 41110 12681 86376 76586 30550 35665 66519 11286 71678 19162 25843 43004 65714 73415 89082 70548 61976 3023 10028 84911 52398 84514 70960 81775 54900 70152 20228 50807 37426 86135 46521 37709 79195 9130 21686 29067 78784 35938 40686 15137 48501 12328 15577 28354 96715 39780 85978 4261 28659 52151 29673 97803 80312 89959 37419 12230 72790 97784 24461 6020 67285 6150 71692 87308 89302 62025 60760 64287 21463 12437 26595 50132 65430 21108 14402 45680 8971 10368 53728 57678 21348 68151 45243 64813 48152 10937 60886 27936 44530 81334 20989 71244 50836 72280 12288 41775 85906 71876 11466 16953 24201 25665 43346 18659 20450 24431 29080 46736 32791 12978 28411 28958 99518 13696 70023 39801 62738 77681 87059 59691 35108 31417 55994 57726 82366 41984 98647 46360 74458 22064 7432 30915 61430 39929 58609 25110 40737 14456 83484 1737 22202 76486 37727 37338 76525 51465 75497 59167 96492 80088 34524 88148 57127 30754 49479 39573 21555 3487 71646 60333 73583 16742 36083 21630 80167 32209 15858 7642 53333 40563 51044 86362 54780 69624 59146 64286 75875 66425 96234 56202 56296 29778 38055 25112 62110 71128 8825 81159 16446 22190 81281 258 86819 1182 82779 82567 33699 97495 80568 6658 75536 74579 34545 8779 19451 83462 48457 89594 57904 37805 27000 18140 39059 59396 22245 21156 1119 42517 18433 66007 10943 24803 9487 24163 73600 1866 81640 77443 92810 22485 54959 72975 2435 42769 64912 27832 44676 38559 5903 89902 59310 80727 72585 7661 39242 66060 52544 42129 42946 32495 69125 36084 92224 67598 33467 71079 45160 53050 11350 39983 7044 12533 65410 47837 77113 4829 90050 66981 1597 23026 65882 88943 21360 85286 98873 35573 80658 37684 82113 3945 6786 32152 55216 85695 82846 45930 58608 15971 73438 42491 68734 57735 61810 96004 91687 36456 55473 29815 11813 17044 56808 25672 95335 75428 64862 54050 11424 15658 35502 2405 12449 31828 85846 41748 79701 62985 30479 15847 24429 70518 88773 88447 82719 6738 68040 35038 63733 36243 62814 17901 48286 37656 55489 79492 61227 12427 95181 59249 66313 68652 41725 90522 52967 52062 7239 72145 4743 38797 82791 47860 3706 9264 43180 11596 76152 95275 940 87602 48996 81939 25154 51212 82470 80346 68002 4291 15536 71827 75216 61919 79056 44311 455 77484 58400 87013 83148 88137 44470 92098 5191 45332 34416 25324 73886 98898 34934 87315 95537 97895 39572 7537 38450 71481 18697 68395 68398 4022 81365 24259 74226 31337 85536 9881 51348 42090 1411 64364 91494 16885 41708 22382 37009 97457 63950 36172 6000 86811 16427 5596 88682 94393 92214 3003 45576 71882 45439 5465 20498 92736 14017 11028 80286 68066 97982 85085 10235 63542 90755 84551 42023 46322 23326 70215 4410 60796 56893 17470 15996 45266 66114 71953 72696 54519 45687 1585 26739 37003 72842 51488 19757 85843 2194 30664 92258 55718 89809 6317 33563 90377 40159 98317 99208 75585 21740 84824 72575 69042 234 80424 89968 34771 67831 64861 33890 64383 5052 34441 4896 90308 37253 35945 18042 8845 73343 77401 74177 28426 91831 56964 35877 5730 55443 23802 77682 2327 60604 45091 83642 13055 73739 19679 79424 96532 59934 28790 89259 44174 27633 77031 26899 86700 59639 80816 61303 73840 19269 13138 44317 63082 79484 6397 30658 10095 56892 73082 53492 43577 78142 57003 17867 72102 82874 53718 34194 2486 55902 22129 49911 782 59217 15669 33112 80696 76696 3442 35160 49627 32374 55618 28324 38702 50353 16219 5472 78342 98624 12144 30860 22061 98978 53789 29493 65863 39615 28060 65442 7125 44595 66089 56332 89487 89594 92173 47991 19739 60733 63688 8634 62927 8456 4214 24717 20652 57834 96649 85278 77629 79977 25012 24790 61331 37602 89723 27610 89191 96089 18690 41460 97909 35835 13215 4318 8245 1809 24645 71225 49435 31501 70457 28565 88225 18408 65683 12288 69560 12860 28551 96142 71900 85729 3896 52091 45382 18371 19897 66481 14523 58352 94731 80958 13254 90987 74193 80249 57493 8973 6902 15694 15077 16492 57538 31879 7522 49593 79427 77475 19911 13054 31599 77275 32956 26478 12363 21674 62911 93853 2334 74546 40090 91330 87020 86285 34987 95624 23352 38670 94295 8575 83193 1824 5897 62565 50679 32751 7090 71488 85298 20675 51244 51624 64580 16533 49569 22329 53097 84337 66565 59479 96276 77745 49138 50863 99923 99257 91753 1371 22184 27102 69934 98554 27557 8983 87455 9410 56118 99546 49173 89755 72763 47519 86019 1419 45204 65987 59756 14211 97148 49355 99086 94729 71561 9824 34756 48555 76653 52729 16043 86634 67591 46246 92172 31839 97249 29126 82692 93675 43943 61310 10303 6380 93112 31890 65466 93939 64324 44949 84766 68883 62180 65196 70727 46233 75161 39433 89689 85531 46927 94144 10261 66409 88351 89174 68088 29724 8777 64365 28726 5041 15385 31068 59141 77341 65966 2456 75659 69087 16230 74920 27314 69287 89744 48160 52076 84212 76086 19358 58936 90450 22440 14320 27097 45649 29536 3893 11226 16753 48781 64013 4390 68709 13946 56818 66324 78049 40778 7875 77547 20626 94961 97877 72156 21939 76689 58187 67897 46704 3991 44597 60999 73791 5975 57485 26317 16145 43163 55056 60432 67670 1060 71265 12125 66246 43654 8701 51749 55766 15278 8788 38027 31138 83837 32150 25228 47968 48893 24668 17116 72175 52958 47638 58454 50358 53616 73617 36068 60636 64879 9182 67041 41819 57090 29841 14841 28313 55552 12749 93492 78134 33801 3976 56686 33483 16494 50900 2784 28542 24947 56798 26441 35131 14347 16387 3525 4196 91847 24527 97374 27296 74670 24555 87830 8357 41202 50064 63905 62426 75179 45569 61817 6303 23215 65134 92382 45265 26490 76598 9051 5674 11172 48651 95395 94415 63324 73385 56308 94393 38367 67874 76547 43002 97447 66637 57360 85552 89292 30264 86312 16919 49342 98179 52126 68870 2863 46269 68950 19992 53702 13874 91638 46358 15815 55338 6568 61047 61708 14892 52810 32706 14668 13246 13180 62872 52565 68534 81882 31508 47484 20661 66808 46912 43834 16919 9634 20966 26079 78195 78313 52416 1826 16020 85503 73300 98054 15695 69805 95906 30954 1451 33962 90849 79993 71662 98805 89686 96956 59452 21911 83646 69014 68629 84404 67481 79480 75583 91190 59016 22782 24342 54617 25924 37982 26034 64009 56537 39686 14134 70834 21427 47036 57752 41613 62828 12381 79923 48318 87415 82621 75069 37163 87770 14460 50415 10635 92544 66731 84249 69802 17070 82169 14993 8479 11698 92996 68245 79929 52640 71748 71452 4044 98942 25803 76181 4313 76611 9690 27584 84922 63964 58136 70750 36960 45252 12379 43099 37354 19916 20321 78282 37905 89600 82709 78159 20482 33414 79213 36265 60405 25896 32313 24366 85754 89214 41285 82988 57052 46749 68018 91902 75707 42408 70869 75697 19281 6597 43187 83349 11792 20195 64746 29189 17940 38401 64320 50754 65668 83281 68375 65245 10500 84234 45352 19739 31869 73568 49888 56710 82440 87780 35190 28162 85075 83170 20398 44364 59784 18612 91219 51945 84308 14284 49830 26798 83260 71592 94675 33050 69454 42465 91294 91326 66074 40123 95311 17020 24336 88196 62532 27654 95623 47211 28680 36288 76230 78815 99746 33628 26469 18454 78007 5644 49846 46236 86643 80327 48762 59143 48873 46901 91232 13593 32768 69365 23180 16326 55569 63264 49042 34047 8272 41043 1537 89878 88098 30340 26082 58472 1597 32329 82544 1224 53665 30911 25736 75436 3050 26251 98193 76875 19111 59019 25709 73196 28281 75076 46614 98725 26695 50624 84218 76035 44530 54885 94268 70816 10330 773 98457 82166 40662 78299 32035 32636 33935 81602 86840 93377 89612 66286 33301 34846 44994 580 26106 41731 59014 40818 54264 85779 99952 92866 39032 3058 13394 58358 91623 20717 1924 39334 69182 52506 49018 59319 24994 8758 88994 13132 48539 27344 44138 38477 51044 13390 84676 99905 1068 30404 83313 46265 85930 34275 21672 31925 62482 86739 58380 35922 83266 94563 61653 57769 19967 77969 80947 48630 34609 47203 74713 61936 93051 37866 70531 54126 29240 26911 99855 79977 47900 12898 20301 48543 97399 6612 88460 97378 26246 23264 86642 32222 35113 86078 16327 90275 16364 30663 38800 62931 66799 23839 77975 75556 97214 60163 14840 91328 82288 77076 24130 12061 29004 46479 46303 17892 1314 34108 10960 11465 54181 25267 29741 55078 26837 14558 58682 98752 64793 4425 79661 87737 68105 57654 55023 15006 34393 46050 66877 67940 89369 66517 94991 33484 85841 56384 87357 55636 44580 97814 88145 13915 29582 86954 59628 70788 14720 81220 48897 74581 33515 54019 72455 36464 94970 35630 64952 74079 41133 63607 20988 25382 50403 64759 90237 27201 13602 17658 58091 64065 30004 65880 66772 43296 36157 60395 49995 8767 33601 5700 2265 21773 78700 98990 62710 18873 8079 14855 20489 99701 85729 38282 45015 28857 22056 52754 71439 6228 73267 71192 93045 71902 71919 3158 76641 45904 68700 53838 90833 47642 70471 71090 16628 91542 63047 34396 68650 9553 37035 40592 87591 84047 29510 9049 42748 60021 13161 16815 15018 26125 61478 26272 6974 26826 80298 31437 42245 70781 19439 78695 39427 61505 92962 68161 50492 71531 36008 6940 82378 95322 33247 20057 28566 41536 95289 39770 5137 38023 33019 44422 91145 67017 50608 31304 49418 43447 6842 56983 5786 60377 16733 17540 26117 93137 70483 41500 59373 58720 44229 17129 66287 46844 65528 95467 52195 63857 71645 14212 64979 91563 81014 31190 2204 94307 84599 12024 45456 58070 64993 36166 57409 742 7547 28215 17081 16798 32871 5763 97616 61224 42907 98515 97042 40674 48170 91574 80891 7984 65197 28176 84919 10507 4792 2482 57185 30604 77761 90022 8196 72585 11340 66776 18894 81282 22983 71112 52758 23342 3185 60261 56510 13036 59202 16818 97785 2183 96403 44883 27274 71620 60642 70080 56535 96276 53189 77544 64274 10992 42692 34543 46684 40655 36231 64854 39534 47781 71463 28169 77966 65155 82178 62282 65091 22936 1748 25961 5932 68215 17890 70328 19089 95365 95299 50013 17203 20932 30446 49620 38901 13299 41029 19127 30460 60023 60627 7792 88140 42209 52786 81430 3802 8490 47120 18102 46441 43309 37251 84815 31453 40873 59758 75393 48229 98781 6460 12665 87555 45783 87858 13242 12566 57376 2632 26511 22265 68737 5456 83071 7082 68996 89373 83289 39788 25302 18346 60093 92343 91512 91166 71738 4760 11403 9317 42383 25332 20479 63213 56479 38677 87900 98368 16310 211 39100 71539 74265 35255 23115 61122 40393 55573 6244 5268 27014 21270 18420 91958 90074 61008 47147 48636 44136 63241 12326 94852 31459 2859 88434 66099 49799 83028 25126 2395 34792 38196 98649 74824 47774 15994 95502 10139 98740 60332 19461 57615 95751 65759 41541 47802 64704 44978 16445 9295 98229 70636 66759 45439 71518 99735 26554 1417 69269 21846 22687 49450 87638 1212 19946 30349 27303 98266 64805 51798 28559 37078 69254 46085 \ No newline at end of file From b629dbccbbe2db8bd75cda4c7180550f29abae0d Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 15:56:12 +0200 Subject: [PATCH 333/815] Formatted comments --- module/mempool/herocache/backdata/heropool/pool_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index b954e693181..1da7fdcea04 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -283,11 +283,11 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject addedEntities = make(map[flow.Identifier]Indexes) for i := 0; i < numberOfOperations; i++ { - // choose between Add and Remove with a probability of 0.8 and 0.2 respectively. + // choose between Add and Remove with a probability of probabilityOfAdding and 1-probabilityOfAdding respectively. if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { // adding an entity entityToAdd := rand.Intn(int(entityCount)) - // check that entity is not already inserted + // check that entity is not already inserted. _, found := addedEntities[entities[entityToAdd].ID()] if !found { indexInThePool, _, ejectedEntity := pool.Add(entities[entityToAdd].ID(), entities[entityToAdd], uint64(ownerIds[entityToAdd])) @@ -300,7 +300,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject } } else { - // randomly select an index of an entity to remove + // randomly select an index of an entity to remove. entityToRemove := rand.Intn(len(addedEntities)) i := 0 var indexInPoolToRemove EIndex = 0 @@ -314,7 +314,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject i++ } - // Remove the selected entity from the pool + // remove the selected entity from the pool. removedEntity := pool.Remove(indexInPoolToRemove) require.Equal(t, entities[indexInEntitiesArray].ID(), removedEntity.ID(), "Removed wrong entity") delete(addedEntities, entities[indexInEntitiesArray].ID()) From 90ebef1f3c4acd320ded89389469fa76045e275e Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 16:05:55 +0200 Subject: [PATCH 334/815] fixed compilation error --- .../mempool/herocache/backdata/heropool/pool_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 1da7fdcea04..73277494503 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -828,7 +828,15 @@ func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { // discoverEntitiesBelongingToStateList discovers all entities in the pool that belong to the given list. func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, stateType StateType) []bool { - s := pool.getStateFromType(stateType) + var s *state = nil + switch stateType { + case stateFree: + s = &pool.free + case stateUsed: + s = &pool.used + default: + panic(fmt.Sprintf("unknown state type: %s", stateType)) + } result := make([]bool, len(pool.poolEntities)) for node_index := s.head; node_index != InvalidIndex; { require.False(t, result[node_index], "A node is present two times in the same state list") From 423f0854e28ba99d67f854f4cc6604ad00070af3 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 7 Jul 2023 18:46:38 +0200 Subject: [PATCH 335/815] Fixed typo --- module/mempool/herocache/backdata/heropool/pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 841932293c5..4e56d4f6bf4 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -21,7 +21,7 @@ const ( // EIndex is data type representing an entity index in Pool. type EIndex uint32 -// InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil adress. +// InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil address. const InvalidIndex EIndex = math.MaxUint32 // A type dedicated to describe possible states of placeholders for entities in the pool. From 2cce08f97fcdb4a0ea9aabcc20a8cff7564a02d6 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 13 Jul 2023 00:55:17 +0200 Subject: [PATCH 336/815] Improved test --- go.mod | 2 + .../herocache/backdata/heropool/pool_test.go | 58 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index d5890cfa995..2a92e6a415e 100644 --- a/go.mod +++ b/go.mod @@ -284,3 +284,5 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect nhooyr.io/websocket v1.8.7 // indirect ) + +replace gitHub.com/onflow/flow-emulator => ../flow-emulator diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 73277494503..66953c4a0da 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -262,6 +262,8 @@ type Indexes struct { indexInEntitiesArr int } +// testAddRemoveEntities adds and removes randomly elements in the pool, probabilityOfAdding and its counterpart 1-probabilityOfAdding are probabilities +// for an operation to be add or remove. Current timestamp is taken as a seed for a random number generator. func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { // create and log the seed. testSeed := time.Now().UnixNano() @@ -279,47 +281,73 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject ownerIds[i] = rand.Uint64() } // this map maintains entities currently stored in the pool. - var addedEntities map[flow.Identifier]Indexes - addedEntities = make(map[flow.Identifier]Indexes) + addedEntities := make(map[flow.Identifier]int) + addedEntitiesInPool := make(map[flow.Identifier]EIndex) for i := 0; i < numberOfOperations; i++ { // choose between Add and Remove with a probability of probabilityOfAdding and 1-probabilityOfAdding respectively. if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { - // adding an entity - entityToAdd := rand.Intn(int(entityCount)) - // check that entity is not already inserted. - _, found := addedEntities[entities[entityToAdd].ID()] + + // find an entity to add + entityToAddIndex := -1 + found := false + for retryTime := 0; retryTime < 100; retryTime++ { + entityToAddIndexTmp := rand.Intn(int(entityCount)) + _, found = addedEntities[entities[entityToAddIndexTmp].ID()] + if !found { + entityToAddIndex = entityToAddIndexTmp + break + } + } if !found { - indexInThePool, _, ejectedEntity := pool.Add(entities[entityToAdd].ID(), entities[entityToAdd], uint64(ownerIds[entityToAdd])) + indexInThePool, _, ejectedEntity := pool.Add(entities[entityToAddIndex].ID(), entities[entityToAddIndex], uint64(ownerIds[entityToAddIndex])) + require.True(t, indexInThePool != InvalidIndex || (ejectionMode == NoEjection && len(addedEntities) == int(limit))) + require.True(t, ejectionMode != NoEjection || ejectedEntity == nil) if indexInThePool != InvalidIndex { - addedEntities[entities[entityToAdd].ID()] = Indexes{indexInThePool, entityToAdd} + addedEntities[entities[entityToAddIndex].ID()] = entityToAddIndex + addedEntitiesInPool[entities[entityToAddIndex].ID()] = indexInThePool + actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInThePool) + require.Equal(t, entities[entityToAddIndex].ID(), actualFlowId) + require.Equal(t, entities[entityToAddIndex], actualEntity) + require.Equal(t, ownerIds[entityToAddIndex], actualOwnerId) } if ejectedEntity != nil { + require.Contains(t, addedEntities, ejectedEntity.ID(), "pool ejected an entity that was not added before") delete(addedEntities, ejectedEntity.ID()) + delete(addedEntitiesInPool, ejectedEntity.ID()) } } - } else { // randomly select an index of an entity to remove. entityToRemove := rand.Intn(len(addedEntities)) i := 0 var indexInPoolToRemove EIndex = 0 - var indexInEntitiesArray EIndex = 0 - for _, v := range addedEntities { + var indexInEntitiesArray int = 0 + for k, v := range addedEntities { if i == entityToRemove { - indexInPoolToRemove = v.indexInPool - indexInEntitiesArray = EIndex(v.indexInEntitiesArr) + indexInPoolToRemove = addedEntitiesInPool[k] + indexInEntitiesArray = v break } i++ } - // remove the selected entity from the pool. removedEntity := pool.Remove(indexInPoolToRemove) - require.Equal(t, entities[indexInEntitiesArray].ID(), removedEntity.ID(), "Removed wrong entity") + require.Equal(t, entities[indexInEntitiesArray].ID(), removedEntity.ID(), "removed wrong entity") delete(addedEntities, entities[indexInEntitiesArray].ID()) + delete(addedEntitiesInPool, entities[indexInEntitiesArray].ID()) + actualFlowId, actualEntity, _ := pool.Get(indexInPoolToRemove) + require.Equal(t, flow.ZeroID, actualFlowId) + require.Equal(t, nil, actualEntity) } } + for k, v := range addedEntities { + indexInPool := addedEntitiesInPool[k] + actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInPool) + require.Equal(t, entities[v].ID(), actualFlowId) + require.Equal(t, entities[v], actualEntity) + require.Equal(t, ownerIds[v], actualOwnerId) + } } // testInvalidatingHead keeps invalidating the head and evaluates the linked-list keeps updating its head From 65fe54a2f006f139519fe6be586d2bae4af6896f Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 14 Jul 2023 18:47:51 +0200 Subject: [PATCH 337/815] Apply suggestions --- go.mod | 2 - .../herocache/backdata/heropool/pool_test.go | 79 +++++++++++-------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index 2a92e6a415e..d5890cfa995 100644 --- a/go.mod +++ b/go.mod @@ -284,5 +284,3 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect nhooyr.io/websocket v1.8.7 // indirect ) - -replace gitHub.com/onflow/flow-emulator => ../flow-emulator diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 66953c4a0da..6f43c46dc00 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -265,58 +265,65 @@ type Indexes struct { // testAddRemoveEntities adds and removes randomly elements in the pool, probabilityOfAdding and its counterpart 1-probabilityOfAdding are probabilities // for an operation to be add or remove. Current timestamp is taken as a seed for a random number generator. func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { + + require.GreaterOrEqual(t, entityCount, 2*limit, "entityCount must be greater or equal to 2*limit to test add/remove operations") // create and log the seed. testSeed := time.Now().UnixNano() + t.Logf("seed used for test %s is %d: ", t.Name(), testSeed) rand.Seed(testSeed) - fmt.Println("Seed used for this testAddRemoveEntities execution", testSeed) - pool := NewHeroPool(limit, ejectionMode) - entities := unittest.EntityListFixture(uint(entityCount)) - + // retryLimit is the max number of retries to find an entity that is not already in the pool to add it. + // The test fails if it reaches this limit. + retryLimit := 100 // an array of random owner Ids. - ownerIds := make([]uint64, entityCount, entityCount) - // generate ownerId to index in the entities arrray. + ownerIds := make([]uint64, entityCount) + // generate ownerId to index in the entities array. for i := 0; i < int(entityCount); i++ { ownerIds[i] = rand.Uint64() } // this map maintains entities currently stored in the pool. addedEntities := make(map[flow.Identifier]int) addedEntitiesInPool := make(map[flow.Identifier]EIndex) - for i := 0; i < numberOfOperations; i++ { // choose between Add and Remove with a probability of probabilityOfAdding and 1-probabilityOfAdding respectively. if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { - - // find an entity to add - entityToAddIndex := -1 + // keeps finding an entity to add until it finds one that is not already in the pool. found := false - for retryTime := 0; retryTime < 100; retryTime++ { - entityToAddIndexTmp := rand.Intn(int(entityCount)) - _, found = addedEntities[entities[entityToAddIndexTmp].ID()] + for retryTime := 0; retryTime < retryLimit; retryTime++ { + toAddIndexTmp := rand.Intn(int(entityCount)) + _, found = addedEntities[entities[toAddIndexTmp].ID()] if !found { - entityToAddIndex = entityToAddIndexTmp + // found an entity that is not in the pool, add it. + indexInThePool, _, ejectedEntity := pool.Add(entities[toAddIndexTmp].ID(), entities[toAddIndexTmp], ownerIds[toAddIndexTmp]) + if ejectionMode != NoEjection || len(addedEntities) < int(limit) { + // when there is an ejection mode in place, and the pool is not full, the index should be valid. + require.NotEqual(t, InvalidIndex, indexInThePool) + } + if ejectionMode != NoEjection && len(addedEntities) >= int(limit) { + // when there is an ejection mode in place, the ejected entity should be valid. + require.NotNil(t, ejectedEntity) + } + if indexInThePool != InvalidIndex { + entityId := entities[toAddIndexTmp].ID() + // tracks the index of the entity in the pool and the index of the entity in the entities array. + addedEntities[entityId] = toAddIndexTmp + addedEntitiesInPool[entityId] = indexInThePool + // any entity added to the pool should be in the pool, and must be retrievable. + actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInThePool) + require.Equal(t, entityId, actualFlowId) + require.Equal(t, entities[toAddIndexTmp], actualEntity, "pool returned a different entity than the one added") + require.Equal(t, ownerIds[toAddIndexTmp], actualOwnerId, "pool returned a different owner than the one added") + } + if ejectedEntity != nil { + require.Contains(t, addedEntities, ejectedEntity.ID(), "pool ejected an entity that was not added before") + delete(addedEntities, ejectedEntity.ID()) + delete(addedEntitiesInPool, ejectedEntity.ID()) + } break } } - if !found { - indexInThePool, _, ejectedEntity := pool.Add(entities[entityToAddIndex].ID(), entities[entityToAddIndex], uint64(ownerIds[entityToAddIndex])) - require.True(t, indexInThePool != InvalidIndex || (ejectionMode == NoEjection && len(addedEntities) == int(limit))) - require.True(t, ejectionMode != NoEjection || ejectedEntity == nil) - if indexInThePool != InvalidIndex { - addedEntities[entities[entityToAddIndex].ID()] = entityToAddIndex - addedEntitiesInPool[entities[entityToAddIndex].ID()] = indexInThePool - actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInThePool) - require.Equal(t, entities[entityToAddIndex].ID(), actualFlowId) - require.Equal(t, entities[entityToAddIndex], actualEntity) - require.Equal(t, ownerIds[entityToAddIndex], actualOwnerId) - } - if ejectedEntity != nil { - require.Contains(t, addedEntities, ejectedEntity.ID(), "pool ejected an entity that was not added before") - delete(addedEntities, ejectedEntity.ID()) - delete(addedEntitiesInPool, ejectedEntity.ID()) - } - } + require.Falsef(t, found, "could not find an entity to add after %d retries", retryLimit) } else { // randomly select an index of an entity to remove. entityToRemove := rand.Intn(len(addedEntities)) @@ -333,9 +340,10 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject } // remove the selected entity from the pool. removedEntity := pool.Remove(indexInPoolToRemove) - require.Equal(t, entities[indexInEntitiesArray].ID(), removedEntity.ID(), "removed wrong entity") - delete(addedEntities, entities[indexInEntitiesArray].ID()) - delete(addedEntitiesInPool, entities[indexInEntitiesArray].ID()) + expectedRemovedEntityId := entities[indexInEntitiesArray].ID() + require.Equal(t, expectedRemovedEntityId, removedEntity.ID(), "removed wrong entity") + delete(addedEntities, expectedRemovedEntityId) + delete(addedEntitiesInPool, expectedRemovedEntityId) actualFlowId, actualEntity, _ := pool.Get(indexInPoolToRemove) require.Equal(t, flow.ZeroID, actualFlowId) require.Equal(t, nil, actualEntity) @@ -348,6 +356,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject require.Equal(t, entities[v], actualEntity) require.Equal(t, ownerIds[v], actualOwnerId) } + require.Equalf(t, len(addedEntities), int(pool.Size()), "pool size is not correct, expected %d, actual %d", len(addedEntities), pool.Size()) } // testInvalidatingHead keeps invalidating the head and evaluates the linked-list keeps updating its head From f198a641ae1ea1bccfcf168a22b0e2086bfe760b Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 14 Jul 2023 19:18:14 +0200 Subject: [PATCH 338/815] Minor modifications --- .../herocache/backdata/heropool/pool_test.go | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 6f43c46dc00..b25815225f0 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -291,29 +291,34 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject // keeps finding an entity to add until it finds one that is not already in the pool. found := false for retryTime := 0; retryTime < retryLimit; retryTime++ { - toAddIndexTmp := rand.Intn(int(entityCount)) - _, found = addedEntities[entities[toAddIndexTmp].ID()] + toAddIndex := rand.Intn(int(entityCount)) + _, found = addedEntities[entities[toAddIndex].ID()] if !found { // found an entity that is not in the pool, add it. - indexInThePool, _, ejectedEntity := pool.Add(entities[toAddIndexTmp].ID(), entities[toAddIndexTmp], ownerIds[toAddIndexTmp]) + indexInThePool, _, ejectedEntity := pool.Add(entities[toAddIndex].ID(), entities[toAddIndex], ownerIds[toAddIndex]) if ejectionMode != NoEjection || len(addedEntities) < int(limit) { - // when there is an ejection mode in place, and the pool is not full, the index should be valid. + // when there is an ejection mode in place, or the pool is not full, the index should be valid. require.NotEqual(t, InvalidIndex, indexInThePool) } + require.LessOrEqual(t, len(addedEntities), int(limit), "pool should not contain more elements than its limit") + if ejectionMode != NoEjection && len(addedEntities) == int(limit) { + // when there is an ejection mode in place, the ejected entity should be valid. + require.NotNil(t, ejectedEntity) + } if ejectionMode != NoEjection && len(addedEntities) >= int(limit) { // when there is an ejection mode in place, the ejected entity should be valid. require.NotNil(t, ejectedEntity) } if indexInThePool != InvalidIndex { - entityId := entities[toAddIndexTmp].ID() + entityId := entities[toAddIndex].ID() // tracks the index of the entity in the pool and the index of the entity in the entities array. - addedEntities[entityId] = toAddIndexTmp + addedEntities[entityId] = toAddIndex addedEntitiesInPool[entityId] = indexInThePool // any entity added to the pool should be in the pool, and must be retrievable. actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInThePool) require.Equal(t, entityId, actualFlowId) - require.Equal(t, entities[toAddIndexTmp], actualEntity, "pool returned a different entity than the one added") - require.Equal(t, ownerIds[toAddIndexTmp], actualOwnerId, "pool returned a different owner than the one added") + require.Equal(t, entities[toAddIndex], actualEntity, "pool returned a different entity than the one added") + require.Equal(t, ownerIds[toAddIndex], actualOwnerId, "pool returned a different owner than the one added") } if ejectedEntity != nil { require.Contains(t, addedEntities, ejectedEntity.ID(), "pool ejected an entity that was not added before") From 167f5de1e1a03ec7e803ff5c6af2d819f85275a5 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Mon, 17 Jul 2023 14:51:09 +0200 Subject: [PATCH 339/815] Minor clean-up --- module/mempool/herocache/backdata/heropool/pool_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index b25815225f0..987403291f4 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -255,15 +255,8 @@ func TestAddAndRemoveEntities(t *testing.T) { } } -// Indexes contains an index in the pool of an inserted entity and an index of this entity -// in the array of entities created by the EntityListFixture. -type Indexes struct { - indexInPool EIndex - indexInEntitiesArr int -} - // testAddRemoveEntities adds and removes randomly elements in the pool, probabilityOfAdding and its counterpart 1-probabilityOfAdding are probabilities -// for an operation to be add or remove. Current timestamp is taken as a seed for a random number generator. +// for an operation to be add or remove. Current timestamp is taken as a seed for the random number generator. func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { require.GreaterOrEqual(t, entityCount, 2*limit, "entityCount must be greater or equal to 2*limit to test add/remove operations") From c366b7d27b71af34b755c4744dca9da1831bd131 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 20 Jul 2023 00:26:39 +0200 Subject: [PATCH 340/815] Rebased changes, replaced rand --- .../herocache/backdata/heropool/pool.go | 15 ++-------- .../herocache/backdata/heropool/pool_test.go | 30 +++++++++++-------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 4e56d4f6bf4..1f14736e6c4 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -63,14 +63,11 @@ func (p PoolEntity) Entity() flow.Entity { } type Pool struct { - logger zerolog.Logger - // This size is a size of used linked list. As we will have it now as a part of - // linked list it can be removed - size uint32 free state // keeps track of free slots. used state // keeps track of allocated slots to cachedEntities. poolEntities []poolEntity ejectionMode EjectionMode + logger zerolog.Logger } func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { @@ -119,7 +116,6 @@ func (p *Pool) initFreeEntities() { // If the pool has no available slots and an ejection is set, ejection occurs when adding a new entity. // If an ejection occurred, ejectedEntity holds the ejected entity. -// done func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( entityIndex EIndex, slotAvailable bool, ejectedEntity flow.Entity) { entityIndex, slotAvailable, ejectedEntity = p.sliceIndexForEntity() @@ -177,20 +173,15 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn return p.claimFreeHead(), true, invalidatedEntity } - if p.free.head.isUndefined() { + if p.free.size == 0 { // the free list is empty, so we are out of space, and we need to eject. switch p.ejectionMode { case NoEjection: // pool is set for no ejection, hence, no slice index is selected, abort immediately. return InvalidIndex, false, nil - case LRUEjection: - // LRU ejection - // the used head is the oldest entity, so we turn the used head to a free head here. - invalidatedEntity := p.invalidateUsedHead() - return p.claimFreeHead(), true, invalidatedEntity case RandomEjection: // we only eject randomly when the pool is full and random ejection is on. - random, err := rand.Uint32n(p.size) + random, err := rand.Uint32n(p.used.size) if err != nil { p.logger.Fatal().Err(err). Msg("hero pool random ejection failed - falling back to LRU ejection") diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 987403291f4..b84d170ec93 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -2,9 +2,10 @@ package heropool import ( "fmt" - "math/rand" + "math" "testing" - "time" + + "github.com/onflow/flow-go/utils/rand" "github.com/stretchr/testify/require" @@ -260,11 +261,14 @@ func TestAddAndRemoveEntities(t *testing.T) { func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, ejectionMode EjectionMode, numberOfOperations int, probabilityOfAdding float32) { require.GreaterOrEqual(t, entityCount, 2*limit, "entityCount must be greater or equal to 2*limit to test add/remove operations") - // create and log the seed. - testSeed := time.Now().UnixNano() - t.Logf("seed used for test %s is %d: ", t.Name(), testSeed) - rand.Seed(testSeed) - pool := NewHeroPool(limit, ejectionMode) + + randomIntN := func(length int) int { + random, err := rand.Uintn(uint(length)) + require.Nil(t, err) + return int(random) + } + + pool := NewHeroPool(limit, ejectionMode, unittest.Logger()) entities := unittest.EntityListFixture(uint(entityCount)) // retryLimit is the max number of retries to find an entity that is not already in the pool to add it. // The test fails if it reaches this limit. @@ -273,18 +277,20 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject ownerIds := make([]uint64, entityCount) // generate ownerId to index in the entities array. for i := 0; i < int(entityCount); i++ { - ownerIds[i] = rand.Uint64() + randomOwnerId, err := rand.Uint64() + require.Nil(t, err) + ownerIds[i] = randomOwnerId } // this map maintains entities currently stored in the pool. addedEntities := make(map[flow.Identifier]int) addedEntitiesInPool := make(map[flow.Identifier]EIndex) for i := 0; i < numberOfOperations; i++ { // choose between Add and Remove with a probability of probabilityOfAdding and 1-probabilityOfAdding respectively. - if rand.Float32() < probabilityOfAdding || len(addedEntities) == 0 { + if float32(randomIntN(math.MaxInt32))/math.MaxInt32 < probabilityOfAdding || len(addedEntities) == 0 { // keeps finding an entity to add until it finds one that is not already in the pool. found := false for retryTime := 0; retryTime < retryLimit; retryTime++ { - toAddIndex := rand.Intn(int(entityCount)) + toAddIndex := randomIntN(int(entityCount)) _, found = addedEntities[entities[toAddIndex].ID()] if !found { // found an entity that is not in the pool, add it. @@ -305,7 +311,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject if indexInThePool != InvalidIndex { entityId := entities[toAddIndex].ID() // tracks the index of the entity in the pool and the index of the entity in the entities array. - addedEntities[entityId] = toAddIndex + addedEntities[entityId] = int(toAddIndex) addedEntitiesInPool[entityId] = indexInThePool // any entity added to the pool should be in the pool, and must be retrievable. actualFlowId, actualEntity, actualOwnerId := pool.Get(indexInThePool) @@ -324,7 +330,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject require.Falsef(t, found, "could not find an entity to add after %d retries", retryLimit) } else { // randomly select an index of an entity to remove. - entityToRemove := rand.Intn(len(addedEntities)) + entityToRemove := randomIntN(len(addedEntities)) i := 0 var indexInPoolToRemove EIndex = 0 var indexInEntitiesArray int = 0 From 772655e5d30706f1b4dcab3a0f0b1f3cf64090fb Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 20 Jul 2023 00:43:59 +0200 Subject: [PATCH 341/815] minor clean-up --- module/mempool/herocache/backdata/heropool/pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 1f14736e6c4..2f1a0e2ad79 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -63,11 +63,11 @@ func (p PoolEntity) Entity() flow.Entity { } type Pool struct { + logger zerolog.Logger free state // keeps track of free slots. used state // keeps track of allocated slots to cachedEntities. poolEntities []poolEntity ejectionMode EjectionMode - logger zerolog.Logger } func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { From 87599c86f7d5fbf52baca634c1e43510db881e77 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 20 Jul 2023 00:44:48 +0200 Subject: [PATCH 342/815] minor clean-up2 --- module/mempool/herocache/backdata/heropool/pool.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 2f1a0e2ad79..05595481eef 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -115,7 +115,6 @@ func (p *Pool) initFreeEntities() { // // If the pool has no available slots and an ejection is set, ejection occurs when adding a new entity. // If an ejection occurred, ejectedEntity holds the ejected entity. - func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( entityIndex EIndex, slotAvailable bool, ejectedEntity flow.Entity) { entityIndex, slotAvailable, ejectedEntity = p.sliceIndexForEntity() @@ -209,7 +208,6 @@ func (p Pool) Size() uint32 { // getHeads returns entities corresponding to the used and free heads. func (p *Pool) getHeads() (*poolEntity, *poolEntity) { var usedHead, freeHead *poolEntity - if p.used.size != 0 { usedHead = &p.poolEntities[p.used.head] } @@ -227,6 +225,7 @@ func (p *Pool) getTails() (*poolEntity, *poolEntity) { if p.used.size != 0 { usedTail = &p.poolEntities[p.used.tail] } + if p.free.size != 0 { freeTail = &p.poolEntities[p.free.tail] } From ae190fc10d381252f70b1ad1032d69181a41abdf Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Thu, 20 Jul 2023 19:33:42 +0200 Subject: [PATCH 343/815] Added godoc to hero pool constr --- module/mempool/herocache/backdata/heropool/pool.go | 2 ++ module/mempool/herocache/backdata/heropool/pool_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 05595481eef..87e21abb33e 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -70,6 +70,8 @@ type Pool struct { ejectionMode EjectionMode } +// NewHeroPool returns a pointer to a new hero pool constructed based on a provided EjectionMode, +// logger and a provided fixed size. func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { l := &Pool{ free: state{ diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index b84d170ec93..5cc19609753 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -264,7 +264,7 @@ func testAddRemoveEntities(t *testing.T, limit uint32, entityCount uint32, eject randomIntN := func(length int) int { random, err := rand.Uintn(uint(length)) - require.Nil(t, err) + require.NoError(t, err) return int(random) } From 7598df51463399f978ffa8853aeed89c99737b3b Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 31 Jul 2023 07:49:04 -0400 Subject: [PATCH 344/815] added create-dynamic-test-matrix from ci.yml --- .../workflows/test-monitor-regular-skipped.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index d9f696ab87c..ced6607d9cb 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -22,6 +22,23 @@ concurrency: cancel-in-progress: true jobs: + create-dynamic-test-matrix: + name: Create Dynamic Test Matrix + runs-on: ubuntu-latest + outputs: + dynamic-matrix: ${{ steps.set-test-matrix.outputs.dynamicMatrix }} + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Set Test Matrix + id: set-test-matrix + run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils + regular-skipped-test-run: name: Test Monitor - Regular, Skipped Tests Run strategy: From 502318b847db849f25ab1fef08d6e260e644d61e Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 31 Jul 2023 08:06:58 -0400 Subject: [PATCH 345/815] added unit-test from ci.yml --- .../test-monitor-regular-skipped.yml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index ced6607d9cb..0dd1337a0fe 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -39,6 +39,32 @@ jobs: id: set-test-matrix run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils + unit-test: + name: Unit Tests (${{ matrix.targets.name }}) + needs: create-dynamic-test-matrix + strategy: + fail-fast: false + matrix: + targets: ${{ fromJSON(needs.create-dynamic-test-matrix.outputs.dynamic-matrix)}} + # need to set image explicitly due to GitHub logging issue as described in https://github.com/onflow/flow-go/pull/3087#issuecomment-1234383202 + runs-on: ubuntu-20.04 + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Setup tests (${{ matrix.targets.name }} + run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools + - name: Run tests (${{ matrix.targets.name }}) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 25 + max_attempts: 3 + command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" test + regular-skipped-test-run: name: Test Monitor - Regular, Skipped Tests Run strategy: From 0431cf2904a0fdb60b3fc5e2d52f53bca6e6a775 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 31 Jul 2023 09:10:39 -0400 Subject: [PATCH 346/815] build fix --- network/alsp/manager/manager_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 730f29dc708..8ff4cc0609a 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/alsp" @@ -197,7 +198,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // the ALSP manager. var victimSpamRecordCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCache }), @@ -297,7 +298,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t)) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) From 52d335a61a183b079e46842b7efb70c333482123 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 31 Jul 2023 09:10:50 -0400 Subject: [PATCH 347/815] removed unused PenaltyParams --- network/alsp/model/params.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/network/alsp/model/params.go b/network/alsp/model/params.go index 0df82b1952a..7d722b2d3c3 100644 --- a/network/alsp/model/params.go +++ b/network/alsp/model/params.go @@ -45,9 +45,3 @@ const ( // speed is 1, and it takes around a day to recover from each disallow-listing. InitialDecaySpeed = 1000 // (Don't change this value) ) - -type PenaltyParams struct { - DisallowListingThreshold float64 - DefaultPenaltyValue float64 - InitialDecaySpeed int -} From a6baa911ce755f94e1f3711519c06804656c667f Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 31 Jul 2023 09:16:53 -0400 Subject: [PATCH 348/815] changed some logging messages from Info() to Trace() --- network/alsp/manager/manager.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 7b2493bc264..c0816627bd6 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -299,7 +299,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { allIds := m.cache.Identities() for _, id := range allIds { - m.logger.Info().Hex("identifier", logging.ID(id)).Msg("onHeartbeat - looping through spam records") + m.logger.Trace().Hex("identifier", logging.ID(id)).Msg("onHeartbeat - looping through spam records") penalty, err := m.cache.Adjust(id, func(record model.ProtocolSpamRecord) (model.ProtocolSpamRecord, error) { if record.Penalty > 0 { // sanity check; this should never happen. @@ -336,14 +336,14 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // each time we decay the penalty by the decay speed, the penalty is a negative number, and the decay speed // is a positive number. So the penalty is getting closer to zero. // We use math.Min() to make sure the penalty is never positive. - m.logger.Info(). + m.logger.Trace(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). Msg("onHeartbeat - before adjusting penalty via decayFunc") record.Penalty = m.DecayFunc(record) - m.logger.Info(). + m.logger.Trace(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). From 45557b076a475831be0dfb3e6866c2a8166be4e3 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 06:03:53 -0400 Subject: [PATCH 349/815] validateSyncRequestForALSP() empty implementation --- engine/common/synchronization/engine.go | 15 ++++++++++++++- engine/common/synchronization/engine_alsp_test.go | 11 +++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 engine/common/synchronization/engine_alsp_test.go diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index ec3f2e941dd..fb9e1a44b93 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -24,9 +24,11 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/alsp" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/logging" "github.com/onflow/flow-go/utils/rand" ) @@ -201,8 +203,15 @@ func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, eve // - IncompatibleInputTypeError if input has unexpected type // - All other errors are potential symptoms of internal state corruption or bugs (fatal). func (e *Engine) process(channel channels.Channel, originID flow.Identifier, event interface{}) error { - switch event.(type) { + switch resource := event.(type) { case *messages.RangeRequest, *messages.BatchRequest, *messages.SyncRequest: + report, misbehavior := e.validateSyncRequestForALSP(originID, channel, resource) { + if misbehavior { + e.con.ReportMisbehavior(report) // report misbehavior to ALSP + e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync request from %x: %v", originID[:], misbehavior) + e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncRequest) + return nil + } return e.requestHandler.Process(channel, originID, event) case *messages.SyncResponse, *messages.BlockResponse: return e.responseMessageHandler.Process(originID, event) @@ -424,3 +433,7 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy e.log.Warn().Err(err).Msg("sending range and batch requests failed") } } + +func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { + return nil, true +} diff --git a/engine/common/synchronization/engine_alsp_test.go b/engine/common/synchronization/engine_alsp_test.go new file mode 100644 index 00000000000..2f74a05db05 --- /dev/null +++ b/engine/common/synchronization/engine_alsp_test.go @@ -0,0 +1,11 @@ +package synchronization + +import "testing" + +func TestValidateSyncRequest(t *testing.T) { + +} + +func TestOnSyncRequest_DisallowListing_Integration(t *testing.T) { + +} From 69777713bee140f7e8fb8f55d43f0c1bc0493f51 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 21 Jul 2023 12:11:59 +0300 Subject: [PATCH 350/815] Add TotalComputationUsed metric in Chunk Up until now, this metric was always set to 0. --- engine/execution/block_result.go | 1 + engine/execution/ingestion/engine_test.go | 24 +++++++++++++++++++++-- model/flow/chunk.go | 3 ++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/engine/execution/block_result.go b/engine/execution/block_result.go index d2e57641d16..d83ed96bfbc 100644 --- a/engine/execution/block_result.go +++ b/engine/execution/block_result.go @@ -172,6 +172,7 @@ func (ar *BlockAttestationResult) ChunkAt(index int) *flow.Chunk { len(execRes.TransactionResults()), attestRes.eventCommit, attestRes.endStateCommit, + execRes.executionSnapshot.TotalComputationUsed(), ) } diff --git a/engine/execution/ingestion/engine_test.go b/engine/execution/ingestion/engine_test.go index a9afec0edee..541063b50a6 100644 --- a/engine/execution/ingestion/engine_test.go +++ b/engine/execution/ingestion/engine_test.go @@ -418,7 +418,9 @@ func TestChunkIndexIsSet(t *testing.T) { unittest.StateCommitmentFixture(), 21, unittest.IdentifierFixture(), - unittest.StateCommitmentFixture()) + unittest.StateCommitmentFixture(), + 17995, + ) assert.Equal(t, i, int(chunk.Index)) assert.Equal(t, i, int(chunk.CollectionIndex)) @@ -433,11 +435,29 @@ func TestChunkNumberOfTxsIsSet(t *testing.T) { unittest.StateCommitmentFixture(), i, unittest.IdentifierFixture(), - unittest.StateCommitmentFixture()) + unittest.StateCommitmentFixture(), + 17995, + ) assert.Equal(t, i, int(chunk.NumberOfTransactions)) } +func TestChunkTotalComputationUsedIsSet(t *testing.T) { + + i := mathRand.Uint64() + chunk := flow.NewChunk( + unittest.IdentifierFixture(), + 3, + unittest.StateCommitmentFixture(), + 21, + unittest.IdentifierFixture(), + unittest.StateCommitmentFixture(), + i, + ) + + assert.Equal(t, i, chunk.TotalComputationUsed) +} + func TestExecuteOneBlock(t *testing.T) { runWithEngine(t, func(ctx testingContext) { diff --git a/model/flow/chunk.go b/model/flow/chunk.go index 5fb4c0bdf68..3c81bbd2823 100644 --- a/model/flow/chunk.go +++ b/model/flow/chunk.go @@ -28,6 +28,7 @@ func NewChunk( numberOfTransactions int, eventCollection Identifier, endState StateCommitment, + totalComputationUsed uint64, ) *Chunk { return &Chunk{ ChunkBody: ChunkBody{ @@ -36,7 +37,7 @@ func NewChunk( StartState: startState, NumberOfTransactions: uint64(numberOfTransactions), EventCollection: eventCollection, - TotalComputationUsed: 0, // TODO: record gas used + TotalComputationUsed: totalComputationUsed, }, Index: uint64(collectionIndex), EndState: endState, From 28acd89ab2f8f07a5200ec1be09cc5570993fc6c Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 28 Jul 2023 15:38:49 +0300 Subject: [PATCH 351/815] Fix failing tests after adding TotalComputationUsed metric in Chunk --- engine/execution/state/unittest/fixtures.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/execution/state/unittest/fixtures.go b/engine/execution/state/unittest/fixtures.go index 71733cd8054..e792a73a4b0 100644 --- a/engine/execution/state/unittest/fixtures.go +++ b/engine/execution/state/unittest/fixtures.go @@ -3,6 +3,7 @@ package unittest import ( "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/engine/execution" + "github.com/onflow/flow-go/fvm/meter" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/mempool/entity" @@ -10,7 +11,9 @@ import ( ) func StateInteractionsFixture() *snapshot.ExecutionSnapshot { - return &snapshot.ExecutionSnapshot{} + return &snapshot.ExecutionSnapshot{ + Meter: meter.NewMeter(meter.DefaultParameters()), + } } func ComputationResultFixture( From dc004080a5373dc5e4845aede3cce515f2f083aa Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 1 Aug 2023 11:35:18 +0300 Subject: [PATCH 352/815] Add assertions to ensure that TotalComputationUsed for a Chunk is no longer 0 --- engine/execution/execution_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/execution/execution_test.go b/engine/execution/execution_test.go index c823505ebaa..4c391f8d332 100644 --- a/engine/execution/execution_test.go +++ b/engine/execution/execution_test.go @@ -566,6 +566,10 @@ func TestBroadcastToMultipleVerificationNodes(t *testing.T) { receipt, _ = args[2].(*flow.ExecutionReceipt) assert.Equal(t, block.ID(), receipt.ExecutionResult.BlockID) + for i, chunk := range receipt.ExecutionResult.Chunks { + assert.EqualValues(t, i, chunk.CollectionIndex) + assert.Greater(t, chunk.TotalComputationUsed, uint64(10)) + } }). Return(nil) From 0585f886f91a761a0497b270d1cd45042b5a1c86 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 07:08:29 -0400 Subject: [PATCH 353/815] added processing test summaries to unit-test --- .../test-monitor-regular-skipped.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 0dd1337a0fe..9f14940be92 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -9,7 +9,8 @@ on: - cron: '0 */2 * * *' # every 2 hours push: paths: - - 'tools/test_monitor/**' +# temp: for testing +# - 'tools/test_monitor/**' env: BIGQUERY_DATASET: production_src_flow_test_metrics @@ -62,8 +63,22 @@ jobs: uses: nick-fields/retry@v2 with: timeout_minutes: 25 - max_attempts: 3 + max_attempts: 1 command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" test + # test run should continue even if there are failed tests + continue-on-error: true + - name: Get commit date + id: commit_date + run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" + - name: Get job run date + id: job_run_date + run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" + - name: Process test results + run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go + env: + JOB_STARTED: ${{ steps.job_run_date.outputs.date }} + COMMIT_DATE: ${{ steps.commit_date.outputs.date }} + regular-skipped-test-run: name: Test Monitor - Regular, Skipped Tests Run From ff81efd124000cab3d9e9c42eddb89303c64b1db Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 07:12:54 -0400 Subject: [PATCH 354/815] regular-skipped-test-run job commented out --- .../test-monitor-regular-skipped.yml | 149 +++++++++--------- 1 file changed, 75 insertions(+), 74 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 9f14940be92..a5191b64ddc 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -38,7 +38,8 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix - run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils +# run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils + run: go run utils/test_matrix/test_matrix.go access unit-test: name: Unit Tests (${{ matrix.targets.name }}) @@ -80,76 +81,76 @@ jobs: COMMIT_DATE: ${{ steps.commit_date.outputs.date }} - regular-skipped-test-run: - name: Test Monitor - Regular, Skipped Tests Run - strategy: - fail-fast: false - matrix: - test-category: - - unit - - unit-crypto - - unit-insecure - - unit-integration - - integration-bft - - integration-mvp - - integration-ghost - - integration-network - - integration-epochs - - integration-access - - integration-collection - - integration-consensus - - integration-execution - - integration-verification - env: - TEST_CATEGORY: ${{ matrix.test-category }} - COMMIT_SHA: ${{ github.sha }} - RUN_ID: ${{ github.run_id }} - SKIPPED_TESTS_FILE: skipped-tests - RESULTS_FILE: test-results - runs-on: ubuntu-latest - steps: - - name: Get job run date - id: job_run_date - run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v0 - with: - service_account_key: ${{ secrets.GCP_SA_KEY }} - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: ${{ env.GO_VERSION }} - - name: Checkout repo - uses: actions/checkout@v2 - with: - ref: ${{ env.COMMIT_SHA }} - - name: Get commit date - id: commit_date - run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" - - name: Run tests - uses: nick-fields/retry@v2 - with: - timeout_minutes: 60 - max_attempts: 5 - command: ./tools/test_monitor/run-tests.sh - env: - JSON_OUTPUT: true - - name: Print test results - run: cat test-output - - name: Process test results - run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go - env: - JOB_STARTED: ${{ steps.job_run_date.outputs.date }} - COMMIT_DATE: ${{ steps.commit_date.outputs.date }} - - name: Upload results to BigQuery (skipped tests) - uses: nick-fields/retry@v2 - with: - timeout_minutes: 1 - max_attempts: 3 - command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json - - name: Upload results to BigQuery (test run) - uses: nick-fields/retry@v2 - with: - timeout_minutes: 2 - max_attempts: 3 - command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json +# regular-skipped-test-run: +# name: Test Monitor - Regular, Skipped Tests Run +# strategy: +# fail-fast: false +# matrix: +# test-category: +# - unit +# - unit-crypto +# - unit-insecure +# - unit-integration +# - integration-bft +# - integration-mvp +# - integration-ghost +# - integration-network +# - integration-epochs +# - integration-access +# - integration-collection +# - integration-consensus +# - integration-execution +# - integration-verification +# env: +# TEST_CATEGORY: ${{ matrix.test-category }} +# COMMIT_SHA: ${{ github.sha }} +# RUN_ID: ${{ github.run_id }} +# SKIPPED_TESTS_FILE: skipped-tests +# RESULTS_FILE: test-results +# runs-on: ubuntu-latest +# steps: +# - name: Get job run date +# id: job_run_date +# run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" +# - name: Set up Cloud SDK +# uses: google-github-actions/setup-gcloud@v0 +# with: +# service_account_key: ${{ secrets.GCP_SA_KEY }} +# - name: Setup Go +# uses: actions/setup-go@v2 +# with: +# go-version: ${{ env.GO_VERSION }} +# - name: Checkout repo +# uses: actions/checkout@v2 +# with: +# ref: ${{ env.COMMIT_SHA }} +# - name: Get commit date +# id: commit_date +# run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" +# - name: Run tests +# uses: nick-fields/retry@v2 +# with: +# timeout_minutes: 60 +# max_attempts: 5 +# command: ./tools/test_monitor/run-tests.sh +# env: +# JSON_OUTPUT: true +# - name: Print test results +# run: cat test-output +# - name: Process test results +# run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go +# env: +# JOB_STARTED: ${{ steps.job_run_date.outputs.date }} +# COMMIT_DATE: ${{ steps.commit_date.outputs.date }} +# - name: Upload results to BigQuery (skipped tests) +# uses: nick-fields/retry@v2 +# with: +# timeout_minutes: 1 +# max_attempts: 3 +# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json +# - name: Upload results to BigQuery (test run) +# uses: nick-fields/retry@v2 +# with: +# timeout_minutes: 2 +# max_attempts: 3 +# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json From 8b11abf3bf334c7b39c79eadf13163ff35419003 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 07:27:29 -0400 Subject: [PATCH 355/815] redirect test output to test-output --- .github/workflows/test-monitor-regular-skipped.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index a5191b64ddc..15471f554cf 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -65,7 +65,7 @@ jobs: with: timeout_minutes: 25 max_attempts: 1 - command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" test + command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" test > test-output # test run should continue even if there are failed tests continue-on-error: true - name: Get commit date @@ -79,7 +79,9 @@ jobs: env: JOB_STARTED: ${{ steps.job_run_date.outputs.date }} COMMIT_DATE: ${{ steps.commit_date.outputs.date }} - +# - name: Setup tmate session +# uses: mxschmitt/action-tmate@v3 +# if: success() || failure() # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run From f673f4976947bc6296747ba2e84d3c4a7dcd6b54 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 1 Aug 2023 16:37:47 +0300 Subject: [PATCH 356/815] Update assertion to check that `TotalComputationUsed` is greater than `0` Co-authored-by: Janez Podhostnik <67895329+janezpodhostnik@users.noreply.github.com> --- engine/execution/execution_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/execution/execution_test.go b/engine/execution/execution_test.go index 4c391f8d332..f52796f85e5 100644 --- a/engine/execution/execution_test.go +++ b/engine/execution/execution_test.go @@ -568,7 +568,7 @@ func TestBroadcastToMultipleVerificationNodes(t *testing.T) { assert.Equal(t, block.ID(), receipt.ExecutionResult.BlockID) for i, chunk := range receipt.ExecutionResult.Chunks { assert.EqualValues(t, i, chunk.CollectionIndex) - assert.Greater(t, chunk.TotalComputationUsed, uint64(10)) + assert.Greater(t, chunk.TotalComputationUsed, uint64(0)) } }). Return(nil) From 6b2b4077e7a792c632a9fbf58762b70e571eee9c Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 14:03:52 -0400 Subject: [PATCH 357/815] ssh tmate, unittest-main --- .github/workflows/test-monitor-regular-skipped.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 15471f554cf..86ff806851b 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -65,7 +65,7 @@ jobs: with: timeout_minutes: 25 max_attempts: 1 - command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" test > test-output + command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output # test run should continue even if there are failed tests continue-on-error: true - name: Get commit date @@ -79,9 +79,9 @@ jobs: env: JOB_STARTED: ${{ steps.job_run_date.outputs.date }} COMMIT_DATE: ${{ steps.commit_date.outputs.date }} -# - name: Setup tmate session -# uses: mxschmitt/action-tmate@v3 -# if: success() || failure() + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: success() || failure() # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run From c74ccf9a07312e77759a7108ae4212c23464c068 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:37:53 -0700 Subject: [PATCH 358/815] [Localnet] Fix semver for localnet builds --- integration/localnet/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/integration/localnet/Makefile b/integration/localnet/Makefile index 30af385fae3..e5f882882a4 100644 --- a/integration/localnet/Makefile +++ b/integration/localnet/Makefile @@ -22,7 +22,11 @@ LOGLEVEL=DEBUG # The Git commit hash COMMIT=$(shell git rev-parse HEAD) -VERSION=localnetbuild + +# The version to include in container builds. Must be semver compliant +ifeq ($(VERSION),) + VERSION := $(shell git describe --tags --abbrev=2 --match "v*" --match "secure-cadence*" 2>/dev/null)-localnetbuild +endif CURRENT_DIRECTORY=$(shell pwd) From c736fb9632537d458d848dfb6a73bc4d52299d1e Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 1 Aug 2023 14:39:51 -0400 Subject: [PATCH 359/815] JSON_OUTPUT, VERBOSE, silent Make --- .github/workflows/test-monitor-regular-skipped.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 86ff806851b..6518664dab2 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -65,7 +65,12 @@ jobs: with: timeout_minutes: 25 max_attempts: 1 - command: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output + command: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output + # command line from GitHub runner that works: + # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output + env: + JSON_OUTPUT: true + VERBOSE: true # test run should continue even if there are failed tests continue-on-error: true - name: Get commit date From 322aee59ecb93e6a645869793953540300409aa4 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 1 Aug 2023 14:51:43 -0400 Subject: [PATCH 360/815] Update network/p2p/inspector/validation/control_message_validation_inspector.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- .../validation/control_message_validation_inspector.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 1ed22632b71..af5ce5a68fc 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -211,6 +211,12 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.ControlIWant) error { sampleSize := uint(10 * c.rpcTracker.LastHighestIHaveRPCSize()) if sampleSize == 0 || sampleSize > c.config.IWantRPCInspectionConfig.MaxSampleSize { + c.logger.Warn(). + Uint("sample_size", sampleSize). + Uint("max_sample_size", c.config.IWantRPCInspectionConfig.MaxSampleSize). + Str(logging.KeySuspicious, "true"). // max sample size is suspicious + Str(logging.KeyNetworkingSecurity, "true"). // zero sample size is a security hole + Msg("zero or invalid sample size, using default max sample size") sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize } From 4158ed47d1547dd37ea54ad44fa42a28365fe026 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:51:25 -0700 Subject: [PATCH 361/815] [Access] Fix slice iteration bug in TrieUpdate protobuf conversion --- engine/common/rpc/convert/execution_data.go | 4 ++-- .../common/rpc/convert/execution_data_test.go | 18 ++++++++++++++++-- utils/unittest/fixtures.go | 6 ++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/engine/common/rpc/convert/execution_data.go b/engine/common/rpc/convert/execution_data.go index 1e9c6031b2c..21d2297e16a 100644 --- a/engine/common/rpc/convert/execution_data.go +++ b/engine/common/rpc/convert/execution_data.go @@ -153,8 +153,8 @@ func TrieUpdateToMessage(t *ledger.TrieUpdate) (*entities.TrieUpdate, error) { } paths := make([][]byte, len(t.Paths)) - for i, path := range t.Paths { - paths[i] = path[:] + for i := range t.Paths { + paths[i] = t.Paths[i][:] } payloads := make([]*entities.Payload, len(t.Payloads)) diff --git a/engine/common/rpc/convert/execution_data_test.go b/engine/common/rpc/convert/execution_data_test.go index 2ebfd915c28..d996e8f0c5f 100644 --- a/engine/common/rpc/convert/execution_data_test.go +++ b/engine/common/rpc/convert/execution_data_test.go @@ -7,12 +7,13 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/engine/common/rpc/convert" + "github.com/onflow/flow-go/ledger/common/testutils" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/executiondatasync/execution_data" "github.com/onflow/flow-go/utils/unittest" ) -func TestConvertBlockExecutionData1(t *testing.T) { +func TestConvertBlockExecutionData(t *testing.T) { t.Parallel() chain := flow.Testnet.Chain() // this is used by the AddressFixture @@ -21,7 +22,12 @@ func TestConvertBlockExecutionData1(t *testing.T) { chunks := 5 chunkData := make([]*execution_data.ChunkExecutionData, 0, chunks) for i := 0; i < chunks-1; i++ { - chunkData = append(chunkData, unittest.ChunkExecutionDataFixture(t, execution_data.DefaultMaxBlobSize/5, unittest.WithChunkEvents(events))) + ced := unittest.ChunkExecutionDataFixture(t, + 0, // updates set explicitly to target 5*32K per chunk + unittest.WithChunkEvents(events), + unittest.WithTrieUpdate(testutils.TrieUpdateFixture(5, 32*1024, 128*1024)), + ) + chunkData = append(chunkData, ced) } makeServiceTx := func(ced *execution_data.ChunkExecutionData) { // proposal key and payer are empty addresses for service tx @@ -46,6 +52,7 @@ func TestConvertBlockExecutionData1(t *testing.T) { require.NoError(t, err) assert.Equal(t, chunkData[0], chunkReConverted) + assert.True(t, chunkData[0].TrieUpdate.Equals(chunkReConverted.TrieUpdate)) }) t.Run("block execution data conversions", func(t *testing.T) { @@ -56,5 +63,12 @@ func TestConvertBlockExecutionData1(t *testing.T) { require.NoError(t, err) assert.Equal(t, blockData, converted) + for i, chunk := range blockData.ChunkExecutionDatas { + if chunk.TrieUpdate == nil { + assert.Nil(t, converted.ChunkExecutionDatas[i].TrieUpdate) + } else { + assert.True(t, chunk.TrieUpdate.Equals(converted.ChunkExecutionDatas[i].TrieUpdate)) + } + } }) } diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 999f090232b..f4080db8070 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -2472,6 +2472,12 @@ func WithChunkEvents(events flow.EventsList) func(*execution_data.ChunkExecution } } +func WithTrieUpdate(trieUpdate *ledger.TrieUpdate) func(*execution_data.ChunkExecutionData) { + return func(conf *execution_data.ChunkExecutionData) { + conf.TrieUpdate = trieUpdate + } +} + func ChunkExecutionDataFixture(t *testing.T, minSize int, opts ...func(*execution_data.ChunkExecutionData)) *execution_data.ChunkExecutionData { collection := CollectionFixture(5) ced := &execution_data.ChunkExecutionData{ From e74c055bc0298ffa7c44d9703bad1b13ac2a7230 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:01:55 -0700 Subject: [PATCH 362/815] use 2 tries by default, decrease max chunk size --- engine/common/rpc/convert/execution_data_test.go | 4 ++-- utils/unittest/fixtures.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/rpc/convert/execution_data_test.go b/engine/common/rpc/convert/execution_data_test.go index d996e8f0c5f..59c136c5b8a 100644 --- a/engine/common/rpc/convert/execution_data_test.go +++ b/engine/common/rpc/convert/execution_data_test.go @@ -23,9 +23,9 @@ func TestConvertBlockExecutionData(t *testing.T) { chunkData := make([]*execution_data.ChunkExecutionData, 0, chunks) for i := 0; i < chunks-1; i++ { ced := unittest.ChunkExecutionDataFixture(t, - 0, // updates set explicitly to target 5*32K per chunk + 0, // updates set explicitly to target 160-320KB per chunk unittest.WithChunkEvents(events), - unittest.WithTrieUpdate(testutils.TrieUpdateFixture(5, 32*1024, 128*1024)), + unittest.WithTrieUpdate(testutils.TrieUpdateFixture(5, 32*1024, 64*1024)), ) chunkData = append(chunkData, ced) } diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index f4080db8070..e69a263ce6d 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -2483,7 +2483,7 @@ func ChunkExecutionDataFixture(t *testing.T, minSize int, opts ...func(*executio ced := &execution_data.ChunkExecutionData{ Collection: &collection, Events: flow.EventsList{}, - TrieUpdate: testutils.TrieUpdateFixture(1, 1, 8), + TrieUpdate: testutils.TrieUpdateFixture(2, 1, 8), } for _, opt := range opts { From 21130f89693b9586ae9bf4d71154c370c7f36e84 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 2 Aug 2023 05:19:35 -0400 Subject: [PATCH 363/815] empty validate*ForALSP() methods --- engine/common/synchronization/engine.go | 59 +++++++++++++++++++++++-- network/internal/testutils/testUtil.go | 3 +- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index fb9e1a44b93..6fed092520f 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -204,8 +204,27 @@ func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, eve // - All other errors are potential symptoms of internal state corruption or bugs (fatal). func (e *Engine) process(channel channels.Channel, originID flow.Identifier, event interface{}) error { switch resource := event.(type) { - case *messages.RangeRequest, *messages.BatchRequest, *messages.SyncRequest: - report, misbehavior := e.validateSyncRequestForALSP(originID, channel, resource) { + case *messages.BatchRequest: + report, misbehavior := e.validateBatchRequestForALSP(originID, channel, resource) + if misbehavior { + e.con.ReportMisbehavior(report) // report misbehavior to ALSP + e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid batch request from %x: %v", originID[:], misbehavior) + e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBatchRequest) + return nil + } + return e.requestHandler.Process(channel, originID, event) + case *messages.RangeRequest: + report, misbehavior := e.validateRangeRequestForALSP(originID, channel, resource) + if misbehavior { + e.con.ReportMisbehavior(report) // report misbehavior to ALSP + e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid range request from %x: %v", originID[:], misbehavior) + e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageRangeRequest) + return nil + } + return e.requestHandler.Process(channel, originID, event) + + case *messages.SyncRequest: + report, misbehavior := e.validateSyncRequestForALSP(originID, channel, resource) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync request from %x: %v", originID[:], misbehavior) @@ -213,7 +232,25 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve return nil } return e.requestHandler.Process(channel, originID, event) - case *messages.SyncResponse, *messages.BlockResponse: + + case *messages.BlockResponse: + report, misbehavior := e.validateBlockResponseForALSP(originID, channel, resource) + if misbehavior { + e.con.ReportMisbehavior(report) // report misbehavior to ALSP + e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid block response from %x: %v", originID[:], misbehavior) + e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBlockResponse) + return nil + } + return e.responseMessageHandler.Process(originID, event) + + case *messages.SyncResponse: + report, misbehavior := e.validateSyncResponseForALSP(originID, channel, resource) + if misbehavior { + e.con.ReportMisbehavior(report) // report misbehavior to ALSP + e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync response from %x: %v", originID[:], misbehavior) + e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncResponse) + return nil + } return e.responseMessageHandler.Process(originID, event) default: return fmt.Errorf("received input with type %T from %x: %w", event, originID[:], engine.IncompatibleInputTypeError) @@ -434,6 +471,22 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy } } +func (e *Engine) validateBatchRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { + return nil, true +} + +func (e *Engine) validateBlockResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { + return nil, true +} + +func (e *Engine) validateRangeRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { + return nil, true +} + func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { return nil, true } + +func (e *Engine) validateSyncResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { + return nil, true +} diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 95707ee9e3c..1d76b72c945 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -114,7 +114,7 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti } // LibP2PNodeForMiddlewareFixture is a test helper that generate flow identities with a valid port and libp2p nodes. -// Note that the LibP2PNode created by this fixture is meant to used with a middleware component. +// Note that the LibP2PNode created by this fixture is meant to be used with a middleware component. // If you want to create a standalone LibP2PNode without network and middleware components, please use p2ptest.NodeFixture. // Args: // @@ -129,7 +129,6 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // []p2p.LibP2PNode - list of libp2p nodes created. // []observable.Observable - list of observables created for each node. func LibP2PNodeForMiddlewareFixture(t *testing.T, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode, []observable.Observable) { - libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) From decb46f9b551fa84c8e2c3739ac227b82c6aeffd Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 2 Aug 2023 06:40:05 -0400 Subject: [PATCH 364/815] env vars COMMIT_SHA, RUN_ID , RESULTS_FILE, SKIPPED_TESTS_FILE --- .github/workflows/test-monitor-regular-skipped.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 6518664dab2..149d67b1e40 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -17,8 +17,12 @@ env: BIGQUERY_TABLE: skipped_tests BIGQUERY_TABLE2: test_results GO_VERSION: "1.20" + SKIPPED_TESTS_FILE: skipped-tests + RESULTS_FILE: test-results + COMMIT_SHA: ${{ github.sha }} + RUN_ID: ${{ github.run_id }} -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true From f2daa8be7fe4ef52ac316600a54f9deb3a0611f1 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 2 Aug 2023 07:13:28 -0400 Subject: [PATCH 365/815] add TEST_CATEGORY --- .github/workflows/test-monitor-regular-skipped.yml | 5 +++-- tools/test_monitor/common/testdata/test_data.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 149d67b1e40..17e50a81e09 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -21,6 +21,8 @@ env: RESULTS_FILE: test-results COMMIT_SHA: ${{ github.sha }} RUN_ID: ${{ github.run_id }} + JSON_OUTPUT: true + VERBOSE: true concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} @@ -73,8 +75,7 @@ jobs: # command line from GitHub runner that works: # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output env: - JSON_OUTPUT: true - VERBOSE: true + TEST_CATEGORY: unit # test run should continue even if there are failed tests continue-on-error: true - name: Get commit date diff --git a/tools/test_monitor/common/testdata/test_data.go b/tools/test_monitor/common/testdata/test_data.go index 728184f5b63..28d8c58ba6b 100644 --- a/tools/test_monitor/common/testdata/test_data.go +++ b/tools/test_monitor/common/testdata/test_data.go @@ -225,7 +225,7 @@ func GetTestData_Level1_1Count1FailRestPass() common.Level1Summary { } } -// GetTestData_Level1_1CountAllPass represents a level 1 summary (as exptected output from level 1 parser) +// GetTestData_Level1_1CountAllPass represents a level 1 summary (as expected output from level 1 parser) // with multiple passed tests, count=1. func GetTestData_Level1_1CountAllPass() common.Level1Summary { return []common.Level1TestResult{ From 357b204f899035b8cf1ab7b78d20eb7a6a27312a Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 2 Aug 2023 07:20:04 -0400 Subject: [PATCH 366/815] upload results to BigQuery --- .../test-monitor-regular-skipped.yml | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 17e50a81e09..560ec5a482a 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -45,7 +45,7 @@ jobs: - name: Set Test Matrix id: set-test-matrix # run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils - run: go run utils/test_matrix/test_matrix.go access + run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) @@ -89,9 +89,21 @@ jobs: env: JOB_STARTED: ${{ steps.job_run_date.outputs.date }} COMMIT_DATE: ${{ steps.commit_date.outputs.date }} - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: success() || failure() +# - name: Setup tmate session +# uses: mxschmitt/action-tmate@v3 +# if: success() || failure() + - name: Upload results to BigQuery (skipped tests) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 1 + max_attempts: 3 + command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json + - name: Upload results to BigQuery (test run) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 2 + max_attempts: 3 + command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run From 6e84faa134ed4aee2575c9eadfc5637c02b82946 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 2 Aug 2023 16:13:05 -0400 Subject: [PATCH 367/815] set up cloud SDK --- .github/workflows/test-monitor-regular-skipped.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 560ec5a482a..0c53f9c599d 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -49,6 +49,8 @@ jobs: unit-test: name: Unit Tests (${{ matrix.targets.name }}) + env: + TEST_CATEGORY: unit needs: create-dynamic-test-matrix strategy: fail-fast: false @@ -74,8 +76,6 @@ jobs: command: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output # command line from GitHub runner that works: # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output - env: - TEST_CATEGORY: unit # test run should continue even if there are failed tests continue-on-error: true - name: Get commit date @@ -92,6 +92,10 @@ jobs: # - name: Setup tmate session # uses: mxschmitt/action-tmate@v3 # if: success() || failure() + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v0 + with: + service_account_key: ${{ secrets.GCP_SA_KEY }} - name: Upload results to BigQuery (skipped tests) uses: nick-fields/retry@v2 with: From 03c2e4fd17533cbd04b448f5c13712a3bde52297 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 05:50:36 -0400 Subject: [PATCH 368/815] not using nick-fields/retry@v2 --- .github/workflows/test-monitor-regular-skipped.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 0c53f9c599d..aa9a53887d2 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -69,11 +69,8 @@ jobs: - name: Setup tests (${{ matrix.targets.name }} run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - name: Run tests (${{ matrix.targets.name }}) - uses: nick-fields/retry@v2 - with: - timeout_minutes: 25 - max_attempts: 1 - command: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output + run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output + timeout-minutes: 25 # command line from GitHub runner that works: # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output # test run should continue even if there are failed tests From b5cf9e9b2f9804d85d29f9b66f67a588dee9a083 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 06:03:57 -0400 Subject: [PATCH 369/815] removed access package (no tests) --- .github/workflows/test-monitor-regular-skipped.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index aa9a53887d2..d29fdf63df3 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -45,7 +45,7 @@ jobs: - name: Set Test Matrix id: set-test-matrix # run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils - run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils + run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) From 0f82f8c7e5c0f87772ad5d7f917badabc4e7b86e Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 3 Aug 2023 11:59:13 +0100 Subject: [PATCH 370/815] 4585 Add more testable backend constructor with unit tests --- engine/access/rpc/backend/backend.go | 161 ++++++++- engine/access/rpc/backend/backend_accounts.go | 2 +- engine/access/rpc/backend/backend_events.go | 2 +- engine/access/rpc/backend/backend_scripts.go | 2 +- engine/access/rpc/backend/backend_test.go | 7 + .../rpc/backend/backend_transactions.go | 7 +- .../rpc/backend/backend_transactions_test.go | 310 ++++++++++++++++++ 7 files changed, 481 insertions(+), 10 deletions(-) create mode 100644 engine/access/rpc/backend/backend_transactions_test.go diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 9b10cc5b539..52ec7fe286b 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -8,10 +8,7 @@ import ( "time" lru "github.com/hashicorp/golang-lru" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - + lru2 "github.com/hashicorp/golang-lru/v2" "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -22,8 +19,11 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - + "github.com/onflow/flow-go/storage/badger" accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block @@ -76,6 +76,7 @@ type Backend struct { collections storage.Collections executionReceipts storage.ExecutionReceipts connFactory connection.ConnectionFactory + resultCache *badger.Cache[flow.Identifier, *access.TransactionResult] } // Config defines the configurable options for creating Backend @@ -90,6 +91,148 @@ type Config struct { CircuitBreakerConfig connection.CircuitBreakerConfig // the configuration for circuit breaker } +type Communicator interface { + CallAvailableNode( + nodes flow.IdentityList, + call NodeAction, + shouldTerminateOnError ErrorTerminator, + ) error +} + +// NewBackend creates backend accepting Communicator interfaces instead of circuitBreakerEnabled flag +// More convenient fur unit testing scenarios when you need to pass test NodeCommunicator objects. +func NewBackend(state protocol.State, + collectionRPC accessproto.AccessAPIClient, + historicalAccessNodes []accessproto.AccessAPIClient, + blocks storage.Blocks, + headers storage.Headers, + collections storage.Collections, + transactions storage.Transactions, + executionReceipts storage.ExecutionReceipts, + executionResults storage.ExecutionResults, + chainID flow.ChainID, + accessMetrics module.AccessMetrics, + connFactory connection.ConnectionFactory, + retryEnabled bool, + maxHeightRange uint, + preferredExecutionNodeIDs []string, + fixedExecutionNodeIDs []string, + log zerolog.Logger, + snapshotHistoryLimit int, + archiveAddressList []string, + communicator Communicator) *Backend { + retry := newRetry() + if retryEnabled { + retry.Activate() + } + + loggedScripts, err := lru.New(DefaultLoggedScriptsCacheSize) + if err != nil { + log.Fatal().Err(err).Msg("failed to initialize script logging cache") + } + + archivePorts := make([]uint, len(archiveAddressList)) + for idx, addr := range archiveAddressList { + port, err := findPortFromAddress(addr) + if err != nil { + log.Fatal().Err(err).Msg("failed to find archive node port") + } + archivePorts[idx] = port + } + + txResCache, err := lru2.New[flow.Identifier, *access.TransactionResult](int(badger.DefaultCacheSize)) + if err != nil { + log.Fatal().Err(err).Msg("failed to init cache for transaction results") + } + + b := &Backend{ + state: state, + // create the sub-backends + backendScripts: backendScripts{ + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + state: state, + log: log, + metrics: accessMetrics, + loggedScripts: loggedScripts, + archiveAddressList: archiveAddressList, + archivePorts: archivePorts, + nodeCommunicator: communicator, + }, + backendTransactions: backendTransactions{ + staticCollectionRPC: collectionRPC, + state: state, + chainID: chainID, + collections: collections, + blocks: blocks, + transactions: transactions, + executionReceipts: executionReceipts, + transactionValidator: configureTransactionValidator(state, chainID), + transactionMetrics: accessMetrics, + retry: retry, + connFactory: connFactory, + previousAccessNodes: historicalAccessNodes, + log: log, + nodeCommunicator: communicator, + txResultCache: txResCache, + }, + backendEvents: backendEvents{ + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + maxHeightRange: maxHeightRange, + nodeCommunicator: communicator, + }, + backendBlockHeaders: backendBlockHeaders{ + headers: headers, + state: state, + }, + backendBlockDetails: backendBlockDetails{ + blocks: blocks, + state: state, + }, + backendAccounts: backendAccounts{ + state: state, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + log: log, + nodeCommunicator: communicator, + }, + backendExecutionResults: backendExecutionResults{ + executionResults: executionResults, + }, + backendNetwork: backendNetwork{ + state: state, + chainID: chainID, + snapshotHistoryLimit: snapshotHistoryLimit, + }, + collections: collections, + executionReceipts: executionReceipts, + connFactory: connFactory, + chainID: chainID, + } + + retry.SetBackend(b) + + preferredENIdentifiers, err = identifierList(preferredExecutionNodeIDs) + if err != nil { + log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for preferred EN map") + } + + fixedENIdentifiers, err = identifierList(fixedExecutionNodeIDs) + if err != nil { + log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for fixed EN map") + } + + return b +} + +// New create new backend instance +// Deprecated: Use NewBackend for enhanced testability func New( state protocol.State, collectionRPC accessproto.AccessAPIClient, @@ -134,6 +277,11 @@ func New( // create node communicator, that will be used in sub-backend logic for interacting with API calls nodeCommunicator := NewNodeCommunicator(circuitBreakerEnabled) + txResCache, err := lru2.New[flow.Identifier, *access.TransactionResult](int(badger.DefaultCacheSize)) + if err != nil { + log.Fatal().Err(err).Msg("failed to init cache for transaction results") + } + b := &Backend{ state: state, // create the sub-backends @@ -164,6 +312,7 @@ func New( previousAccessNodes: historicalAccessNodes, log: log, nodeCommunicator: nodeCommunicator, + txResultCache: txResCache, }, backendEvents: backendEvents{ state: state, @@ -219,6 +368,8 @@ func New( return b } +//TODO: refactor cache, replace with generic lru.Cache alternative + // NewCache constructs cache for storing connections to other nodes. // No errors are expected during normal operations. func NewCache( diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index 35f8f0bf4df..470b91048ab 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -24,7 +24,7 @@ type backendAccounts struct { executionReceipts storage.ExecutionReceipts connFactory connection.ConnectionFactory log zerolog.Logger - nodeCommunicator *NodeCommunicator + nodeCommunicator Communicator } func (b *backendAccounts) GetAccount(ctx context.Context, address flow.Address) (*flow.Account, error) { diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index d0b52820ee4..43b42cde2f5 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -27,7 +27,7 @@ type backendEvents struct { connFactory connection.ConnectionFactory log zerolog.Logger maxHeightRange uint - nodeCommunicator *NodeCommunicator + nodeCommunicator Communicator } // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 62d32c56211..c1b1bfc344a 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -35,7 +35,7 @@ type backendScripts struct { loggedScripts *lru.Cache archiveAddressList []string archivePorts []uint - nodeCommunicator *NodeCommunicator + nodeCommunicator Communicator } func (b *backendScripts) ExecuteScriptAtLatestBlock( diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index d40ff45890e..83fb3020c04 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -46,11 +46,16 @@ type Suite struct { transactions *storagemock.Transactions receipts *storagemock.ExecutionReceipts results *storagemock.ExecutionResults + + colClient *access.AccessAPIClient execClient *access.ExecutionAPIClient historicalAccessClient *access.AccessAPIClient archiveClient *access.AccessAPIClient + connectionFactory *backendmock.ConnectionFactory + communicator *NodeCommunicatorMock + chainID flow.ChainID } @@ -79,6 +84,8 @@ func (suite *Suite) SetupTest() { suite.chainID = flow.Testnet suite.historicalAccessClient = new(access.AccessAPIClient) suite.connectionFactory = new(backendmock.ConnectionFactory) + + suite.communicator = new(NodeCommunicatorMock) } func (suite *Suite) TestPing() { diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 79579d420e6..b3debaaac27 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -24,6 +24,8 @@ import ( "github.com/onflow/flow-go/storage" ) + + type backendTransactions struct { staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node transactions storage.Transactions @@ -39,7 +41,7 @@ type backendTransactions struct { previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger - nodeCommunicator *NodeCommunicator + nodeCommunicator Communicator } // SendTransaction forwards the transaction to the collection node @@ -229,12 +231,13 @@ func (b *backendTransactions) GetTransactionResult( ) (*access.TransactionResult, error) { // look up transaction from storage start := time.Now() - tx, err := b.transactions.ByID(txID) + tx, err := b.transactions.ByID(txID) if err != nil { txErr := rpc.ConvertStorageError(err) if status.Code(txErr) == codes.NotFound { // Tx not found. If we have historical Sporks setup, lets look through those as well + historicalTxResult, err := b.getHistoricalTransactionResult(ctx, txID) if err != nil { // if tx not found in old access nodes either, then assume that the tx was submitted to a different AN diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go new file mode 100644 index 00000000000..d6d73e6363c --- /dev/null +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -0,0 +1,310 @@ +package backend + +import ( + "context" + "fmt" + + "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/util" + "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (suite *Suite) TestGetTransactionResultReturnsUnknown() { + + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) + util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { + epochBuilder := unittest.NewEpochBuilder(suite.T(), state) + + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(suite.T(), ok) + + // setup AtHeight mock returns for state + for _, height := range epoch1.Range() { + suite.state.On("AtHeisght", epoch1.Range()).Return(state.AtHeight(height)) + } + + snap := state.AtHeight(epoch1.Range()[0]) + suite.state.On("Final").Return(snap).Once() + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil) + + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody + + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + + suite.transactions. + On("ByID", tx.ID()). + Return(nil, storage.ErrNotFound) + + suite.blocks. + On("ByID", block.ID()). + Return(&block, nil). + Once() + + receipt := unittest.ExecutionReceiptFixture() + identity := unittest.IdentityFixture() + identity.Role = flow.RoleExecution + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + l := flow.ExecutionReceiptList{receipt} + + suite.receipts. + On("ByBlockID", block.ID()). + Return(l, nil) + + backend := NewBackend( + suite.state, + suite.colClient, + nil, + suite.blocks, + nil, + nil, + suite.transactions, + suite.receipts, + nil, + suite.chainID, + metrics.NewNoopCollector(), + nil, + false, + DefaultMaxHeightRange, + nil, + nil, + suite.log, + DefaultSnapshotHistoryLimit, + nil, + suite.communicator, + ) + res, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(res.Status, flow.TransactionStatusUnknown) + }) +} + +func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { + + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) + util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { + epochBuilder := unittest.NewEpochBuilder(suite.T(), state) + + // building 2 epochs allows us to take a snapshot at a point in time where + // an epoch transition happens + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(suite.T(), ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(suite.T(), ok) + + // setup AtHeight mock returns for state + for _, height := range append(epoch1.Range(), epoch2.Range()...) { + suite.state.On("AtHeight", height).Return(state.AtHeight(height)) + } + + // Take snapshot at height of the first block of epoch2, the sealing segment of this snapshot + // will have contain block spanning an epoch transition as well as an epoch phase transition. + // This will cause our GetLatestProtocolStateSnapshot func to return a snapshot + // at block with height 3, the first block of the staking phase of epoch1. + + snap := state.AtHeight(epoch2.Range()[0]) + suite.state.On("Final").Return(snap).Once() + + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody + + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + + suite.transactions. + On("ByID", tx.ID()). + Return(nil, fmt.Errorf("some other error")) + + suite.blocks. + On("ByID", block.ID()). + Return(&block, nil). + Once() + + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil) + + receipt := unittest.ExecutionReceiptFixture() + identity := unittest.IdentityFixture() + identity.Role = flow.RoleExecution + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + l := flow.ExecutionReceiptList{receipt} + + suite.receipts. + On("ByBlockID", block.ID()). + Return(l, nil) + + backend := NewBackend( + suite.state, + suite.colClient, + nil, + suite.blocks, + nil, + nil, + suite.transactions, + suite.receipts, + nil, + suite.chainID, + metrics.NewNoopCollector(), + nil, + false, + DefaultMaxHeightRange, + nil, + nil, + suite.log, + DefaultSnapshotHistoryLimit, + nil, + suite.communicator, + ) + _, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().Equal(err, status.Errorf(codes.Internal, "failed to find: %v", fmt.Errorf("some other error"))) + }) + +} + +func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { + + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) + util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { + epochBuilder := unittest.NewEpochBuilder(suite.T(), state) + + // building 2 epochs allows us to take a snapshot at a point in time where + // an epoch transition happens + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(suite.T(), ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(suite.T(), ok) + + // setup AtHeight mock returns for state + for _, height := range append(epoch1.Range(), epoch2.Range()...) { + suite.state.On("AtHeight", height).Return(state.AtHeight(height)) + } + + // Take snapshot at height of the first block of epoch2, the sealing segment of this snapshot + // will have contain block spanning an epoch transition as well as an epoch phase transition. + // This will cause our GetLatestProtocolStateSnapshot func to return a snapshot + // at block with height 3, the first block of the staking phase of epoch1. + + snap := state.AtHeight(epoch2.Range()[0]) + suite.state.On("Final").Return(snap).Once() + + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody + + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + + suite.transactions. + On("ByID", tx.ID()). + Return(nil, storage.ErrNotFound) + + suite.blocks. + On("ByID", block.ID()). + Return(&block, nil). + Once() + + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil) + + receipt := unittest.ExecutionReceiptFixture() + identity := unittest.IdentityFixture() + identity.Role = flow.RoleExecution + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + l := flow.ExecutionReceiptList{receipt} + + transactionResultResponse := access.TransactionResultResponse{ + Status: entities.TransactionStatus_EXECUTED, + StatusCode: uint32(entities.TransactionStatus_EXECUTED), + } + + suite.receipts. + On("ByBlockID", block.ID()). + Return(l, nil) + + suite.historicalAccessClient. + On("GetTransactionResult", mock.Anything, mock.Anything). + Return(&transactionResultResponse, nil) + + backend := NewBackend( + suite.state, + suite.colClient, + []access.AccessAPIClient{suite.historicalAccessClient}, + suite.blocks, + nil, + nil, + suite.transactions, + suite.receipts, + nil, + suite.chainID, + metrics.NewNoopCollector(), + nil, + false, + DefaultMaxHeightRange, + nil, + nil, + suite.log, + DefaultSnapshotHistoryLimit, + nil, + suite.communicator, + ) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusExecuted, resp.Status) + suite.Require().Equal(uint(flow.TransactionStatusExecuted), resp.StatusCode) + }) +} From 3d23bcf3300eed0b96b22f55a9783c84f1924ed2 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 3 Aug 2023 12:00:51 +0100 Subject: [PATCH 371/815] 4585: Add mock file generation for NodeCommunicator --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8b37127d1d..b09174f2dee 100644 --- a/Makefile +++ b/Makefile @@ -93,7 +93,7 @@ go-math-rand-check: # `exclude` should only specify non production code (test, bench..). # If this check fails, try updating your code by using: # - "crypto/rand" or "flow-go/utils/rand" for non-deterministic randomness - # - "flow-go/crypto/random" for deterministic randomness + # - "flow-go/crypto/random" for deterministic randomness grep --include=\*.go \ --exclude=*test* --exclude=*helper* --exclude=*example* --exclude=*fixture* --exclude=*benchmark* --exclude=*profiler* \ --exclude-dir=*test* --exclude-dir=*helper* --exclude-dir=*example* --exclude-dir=*fixture* --exclude-dir=*benchmark* --exclude-dir=*profiler* -rnw '"math/rand"'; \ @@ -194,6 +194,8 @@ generate-mocks: install-mock-generators mockery --name 'API' --dir="./engine/protocol" --case=underscore --output="./engine/protocol/mock" --outpkg="mock" mockery --name '.*' --dir="./engine/access/state_stream" --case=underscore --output="./engine/access/state_stream/mock" --outpkg="mock" mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/connection" --case=underscore --output="./engine/access/rpc/connection/mock" --outpkg="mock" + mockery --name 'Communicator' --structname 'NodeCommunicatorMock' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend" --inpackage + mockery --name '.*' --dir=model/fingerprint --case=underscore --output="./model/fingerprint/mock" --outpkg="mock" mockery --name 'ExecForkActor' --structname 'ExecForkActorMock' --dir=module/mempool/consensus/mock/ --case=underscore --output="./module/mempool/consensus/mock/" --outpkg="mock" mockery --name '.*' --dir=engine/verification/fetcher/ --case=underscore --output="./engine/verification/fetcher/mock" --outpkg="mockfetcher" From f018aca1c2d726305cfd9ee91a9206858c18eb0a Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 3 Aug 2023 12:04:20 +0100 Subject: [PATCH 372/815] 4585: Add simple lru caching for transaction results and refactor it's tests --- .../rpc/backend/backend_transactions.go | 29 ++-- .../rpc/backend/backend_transactions_test.go | 160 ++++++++++-------- 2 files changed, 104 insertions(+), 85 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index b3debaaac27..39f3454927d 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -6,13 +6,7 @@ import ( "fmt" "time" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - + lru2 "github.com/hashicorp/golang-lru/v2" "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -22,10 +16,14 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) - - type backendTransactions struct { staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node transactions storage.Transactions @@ -42,6 +40,7 @@ type backendTransactions struct { previousAccessNodes []accessproto.AccessAPIClient log zerolog.Logger nodeCommunicator Communicator + txResultCache *lru2.Cache[flow.Identifier, *access.TransactionResult] } // SendTransaction forwards the transaction to the collection node @@ -232,12 +231,16 @@ func (b *backendTransactions) GetTransactionResult( // look up transaction from storage start := time.Now() - tx, err := b.transactions.ByID(txID) + tx, err := b.transactions.ByID(txID) if err != nil { txErr := rpc.ConvertStorageError(err) if status.Code(txErr) == codes.NotFound { // Tx not found. If we have historical Sporks setup, lets look through those as well + val, ok := b.txResultCache.Get(txID) + if ok { + return val, nil + } historicalTxResult, err := b.getHistoricalTransactionResult(ctx, txID) if err != nil { // if tx not found in old access nodes either, then assume that the tx was submitted to a different AN @@ -248,6 +251,8 @@ func (b *backendTransactions) GetTransactionResult( StatusCode: uint(txStatus), }, nil } + + b.txResultCache.Add(txID, historicalTxResult) return historicalTxResult, nil } return nil, txErr @@ -343,8 +348,8 @@ func (b *backendTransactions) lookupCollectionIDInBlock( // followed by the collection ID lookup. If both are missing, the default lookup by transaction ID is performed. func (b *backendTransactions) retrieveBlock( - // the requested block or collection was not found. If looking up the block based solely on the txID returns - // not found, then no error is returned. +// the requested block or collection was not found. If looking up the block based solely on the txID returns +// not found, then no error is returned. blockID flow.Identifier, collectionID flow.Identifier, txID flow.Identifier, diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index d6d73e6363c..c8032ff36fe 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -7,6 +7,7 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" @@ -19,8 +20,7 @@ import ( "google.golang.org/grpc/status" ) -func (suite *Suite) TestGetTransactionResultReturnsUnknown() { - +func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { @@ -47,12 +47,20 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { mock.Anything). Return(nil) + f(snap) + }) + +} + +func (suite *Suite) TestGetTransactionResultReturnsUnknown() { + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() tx.TransactionBody = tbody coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() suite.transactions. On("ByID", tx.ID()). @@ -67,8 +75,6 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { identity := unittest.IdentityFixture() identity.Role = flow.RoleExecution - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() - l := flow.ExecutionReceiptList{receipt} suite.receipts. @@ -104,41 +110,7 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { } func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { - - identities := unittest.CompleteIdentitySet() - rootSnapshot := unittest.RootSnapshotFixture(identities) - util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { - epochBuilder := unittest.NewEpochBuilder(suite.T(), state) - - // building 2 epochs allows us to take a snapshot at a point in time where - // an epoch transition happens - epochBuilder. - BuildEpoch(). - CompleteEpoch() - - epochBuilder. - BuildEpoch(). - CompleteEpoch() - - // get heights of each phase in built epochs - epoch1, ok := epochBuilder.EpochHeights(1) - require.True(suite.T(), ok) - epoch2, ok := epochBuilder.EpochHeights(2) - require.True(suite.T(), ok) - - // setup AtHeight mock returns for state - for _, height := range append(epoch1.Range(), epoch2.Range()...) { - suite.state.On("AtHeight", height).Return(state.AtHeight(height)) - } - - // Take snapshot at height of the first block of epoch2, the sealing segment of this snapshot - // will have contain block spanning an epoch transition as well as an epoch phase transition. - // This will cause our GetLatestProtocolStateSnapshot func to return a snapshot - // at block with height 3, the first block of the staking phase of epoch1. - - snap := state.AtHeight(epoch2.Range()[0]) - suite.state.On("Final").Return(snap).Once() - + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -155,12 +127,6 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { Return(&block, nil). Once() - suite.communicator.On("CallAvailableNode", - mock.Anything, - mock.Anything, - mock.Anything). - Return(nil) - receipt := unittest.ExecutionReceiptFixture() identity := unittest.IdentityFixture() identity.Role = flow.RoleExecution @@ -197,46 +163,86 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { ) _, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().Equal(err, status.Errorf(codes.Internal, "failed to find: %v", fmt.Errorf("some other error"))) - }) + }) } func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody - identities := unittest.CompleteIdentitySet() - rootSnapshot := unittest.RootSnapshotFixture(identities) - util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { - epochBuilder := unittest.NewEpochBuilder(suite.T(), state) + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) - // building 2 epochs allows us to take a snapshot at a point in time where - // an epoch transition happens - epochBuilder. - BuildEpoch(). - CompleteEpoch() + suite.transactions. + On("ByID", tx.ID()). + Return(nil, storage.ErrNotFound) - epochBuilder. - BuildEpoch(). - CompleteEpoch() + suite.blocks. + On("ByID", block.ID()). + Return(&block, nil). + Once() - // get heights of each phase in built epochs - epoch1, ok := epochBuilder.EpochHeights(1) - require.True(suite.T(), ok) - epoch2, ok := epochBuilder.EpochHeights(2) - require.True(suite.T(), ok) + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil) - // setup AtHeight mock returns for state - for _, height := range append(epoch1.Range(), epoch2.Range()...) { - suite.state.On("AtHeight", height).Return(state.AtHeight(height)) + receipt := unittest.ExecutionReceiptFixture() + identity := unittest.IdentityFixture() + identity.Role = flow.RoleExecution + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + l := flow.ExecutionReceiptList{receipt} + + transactionResultResponse := access.TransactionResultResponse{ + Status: entities.TransactionStatus_EXECUTED, + StatusCode: uint32(entities.TransactionStatus_EXECUTED), } - // Take snapshot at height of the first block of epoch2, the sealing segment of this snapshot - // will have contain block spanning an epoch transition as well as an epoch phase transition. - // This will cause our GetLatestProtocolStateSnapshot func to return a snapshot - // at block with height 3, the first block of the staking phase of epoch1. + suite.receipts. + On("ByBlockID", block.ID()). + Return(l, nil) - snap := state.AtHeight(epoch2.Range()[0]) - suite.state.On("Final").Return(snap).Once() + suite.historicalAccessClient. + On("GetTransactionResult", mock.Anything, mock.Anything). + Return(&transactionResultResponse, nil) + backend := NewBackend( + suite.state, + suite.colClient, + []access.AccessAPIClient{suite.historicalAccessClient}, + suite.blocks, + nil, + nil, + suite.transactions, + suite.receipts, + nil, + suite.chainID, + metrics.NewNoopCollector(), + nil, + false, + DefaultMaxHeightRange, + nil, + nil, + suite.log, + DefaultSnapshotHistoryLimit, + nil, + suite.communicator, + ) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusExecuted, resp.Status) + suite.Require().Equal(uint(flow.TransactionStatusExecuted), resp.StatusCode) + }) +} + +func (suite *Suite) TestGetTransactionResultFromCache() { + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -278,7 +284,7 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). - Return(&transactionResultResponse, nil) + Return(&transactionResultResponse, nil).Once() backend := NewBackend( suite.state, @@ -302,9 +308,17 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { nil, suite.communicator, ) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusExecuted, resp.Status) suite.Require().Equal(uint(flow.TransactionStatusExecuted), resp.StatusCode) + + resp2, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusExecuted, resp2.Status) + suite.Require().Equal(uint(flow.TransactionStatusExecuted), resp2.StatusCode) + + suite.historicalAccessClient.AssertExpectations(suite.T()) }) } From 8dd08dbc39159e49bc8035436fbea245f5eae73f Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 3 Aug 2023 12:06:27 +0100 Subject: [PATCH 373/815] 4585: Ignore mock file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1be2e18a99f..024b0d9417e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /cmd/testclient/testclient /cmd/util/util /cmd/bootstrap/bootstrap +/engine/access/rpc/backend/mock_communicator.go # crypto relic folder crypto/relic/ From b3865c444b651bf15c9cd95b15425ff5c48ec0ed Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 3 Aug 2023 12:15:00 +0100 Subject: [PATCH 374/815] 4585: Fix goimport issues --- engine/access/rpc/backend/backend.go | 9 +++++---- engine/access/rpc/backend/backend_test.go | 19 +++++++++---------- .../rpc/backend/backend_transactions.go | 19 ++++++++++--------- .../rpc/backend/backend_transactions_test.go | 13 +++++++------ 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 52ec7fe286b..eea7c8fdbf6 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -9,6 +9,11 @@ import ( lru "github.com/hashicorp/golang-lru" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -20,10 +25,6 @@ import ( "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 83fb3020c04..48d658a899a 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -40,23 +40,22 @@ type Suite struct { snapshot *protocol.Snapshot log zerolog.Logger - blocks *storagemock.Blocks - headers *storagemock.Headers - collections *storagemock.Collections - transactions *storagemock.Transactions - receipts *storagemock.ExecutionReceipts - results *storagemock.ExecutionResults - + blocks *storagemock.Blocks + headers *storagemock.Headers + collections *storagemock.Collections + transactions *storagemock.Transactions + receipts *storagemock.ExecutionReceipts + results *storagemock.ExecutionResults colClient *access.AccessAPIClient execClient *access.ExecutionAPIClient historicalAccessClient *access.AccessAPIClient archiveClient *access.AccessAPIClient - connectionFactory *backendmock.ConnectionFactory - communicator *NodeCommunicatorMock + connectionFactory *backendmock.ConnectionFactory + communicator *NodeCommunicatorMock - chainID flow.ChainID + chainID flow.ChainID } func TestHandler(t *testing.T) { diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 39f3454927d..d5fed8f04d6 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -7,6 +7,13 @@ import ( "time" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -16,12 +23,6 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendTransactions struct { @@ -251,7 +252,7 @@ func (b *backendTransactions) GetTransactionResult( StatusCode: uint(txStatus), }, nil } - + b.txResultCache.Add(txID, historicalTxResult) return historicalTxResult, nil } @@ -348,8 +349,8 @@ func (b *backendTransactions) lookupCollectionIDInBlock( // followed by the collection ID lookup. If both are missing, the default lookup by transaction ID is performed. func (b *backendTransactions) retrieveBlock( -// the requested block or collection was not found. If looking up the block based solely on the txID returns -// not found, then no error is returned. + // the requested block or collection was not found. If looking up the block based solely on the txID returns + // not found, then no error is returned. blockID flow.Identifier, collectionID flow.Identifier, txID flow.Identifier, diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index c8032ff36fe..c8c3b8496f3 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,6 +5,13 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/state/protocol" @@ -12,12 +19,6 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { From 1fe2a83bc926b1ac85a34ff962e708f93b9e8992 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 17:20:25 -0400 Subject: [PATCH 375/815] reusable composite action to process tests --- .../test-monitor-process-tests/action.yml | 34 ++++++++++ .../test-monitor-regular-skipped.yml | 62 ++++++++++--------- 2 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/actions/test-monitor-process-tests/action.yml diff --git a/.github/workflows/actions/test-monitor-process-tests/action.yml b/.github/workflows/actions/test-monitor-process-tests/action.yml new file mode 100644 index 00000000000..f3db1c7bab2 --- /dev/null +++ b/.github/workflows/actions/test-monitor-process-tests/action.yml @@ -0,0 +1,34 @@ +name: Test Monitor - Reusable Workflow Steps 1 + +description: Custom action that's used in multiple Flaky Test Monitor jobs + +runs: + using : "composite" + steps: + - name: Get commit date + id: commit_date + run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" + - name: Get job run date + id: job_run_date + run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" + - name: Process test results + run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go + env: + JOB_STARTED: ${{ steps.job_run_date.outputs.date }} + COMMIT_DATE: ${{ steps.commit_date.outputs.date }} + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v0 + with: + service_account_key: ${{ secrets.GCP_SA_KEY }} + - name: Upload results to BigQuery (skipped tests) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 1 + max_attempts: 3 + command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json + - name: Upload results to BigQuery (test run) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 2 + max_attempts: 3 + command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index d29fdf63df3..16f391c80dd 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -75,36 +75,38 @@ jobs: # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output # test run should continue even if there are failed tests continue-on-error: true - - name: Get commit date - id: commit_date - run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" - - name: Get job run date - id: job_run_date - run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" - - name: Process test results - run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go - env: - JOB_STARTED: ${{ steps.job_run_date.outputs.date }} - COMMIT_DATE: ${{ steps.commit_date.outputs.date }} -# - name: Setup tmate session -# uses: mxschmitt/action-tmate@v3 -# if: success() || failure() - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v0 - with: - service_account_key: ${{ secrets.GCP_SA_KEY }} - - name: Upload results to BigQuery (skipped tests) - uses: nick-fields/retry@v2 - with: - timeout_minutes: 1 - max_attempts: 3 - command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json - - name: Upload results to BigQuery (test run) - uses: nick-fields/retry@v2 - with: - timeout_minutes: 2 - max_attempts: 3 - command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json + - name: Process test results (${{ matrix.targets.name }}) + uses: ./.github/actions/test-monitor-process-tests +# - name: Get commit date +# id: commit_date +# run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" +# - name: Get job run date +# id: job_run_date +# run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" +# - name: Process test results +# run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go +# env: +# JOB_STARTED: ${{ steps.job_run_date.outputs.date }} +# COMMIT_DATE: ${{ steps.commit_date.outputs.date }} +## - name: Setup tmate session +## uses: mxschmitt/action-tmate@v3 +## if: success() || failure() +# - name: Set up Cloud SDK +# uses: google-github-actions/setup-gcloud@v0 +# with: +# service_account_key: ${{ secrets.GCP_SA_KEY }} +# - name: Upload results to BigQuery (skipped tests) +# uses: nick-fields/retry@v2 +# with: +# timeout_minutes: 1 +# max_attempts: 3 +# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json +# - name: Upload results to BigQuery (test run) +# uses: nick-fields/retry@v2 +# with: +# timeout_minutes: 2 +# max_attempts: 3 +# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run From 660e1b6605fd81ae56afa0839e88612cf232d2d9 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 17:26:22 -0400 Subject: [PATCH 376/815] path fix for composite actions --- .github/workflows/test-monitor-regular-skipped.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 16f391c80dd..27e305093c0 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -76,7 +76,7 @@ jobs: # test run should continue even if there are failed tests continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) - uses: ./.github/actions/test-monitor-process-tests + uses: ./workflows/actions/test-monitor-process-tests # - name: Get commit date # id: commit_date # run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" From b542c062ef7db49467fac2eaafbb47b74744730c Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 17:52:58 -0400 Subject: [PATCH 377/815] add feature branch to checkout to find composite action file --- .github/workflows/test-monitor-regular-skipped.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 27e305093c0..427ff84ecce 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -61,6 +61,7 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR - name: Setup Go uses: actions/setup-go@v3 with: From 356e3982a393b95dc35c88979a8f8acaad11a0fb Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 17:56:18 -0400 Subject: [PATCH 378/815] fix checkout feature branch --- .github/workflows/test-monitor-regular-skipped.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 427ff84ecce..01735a21dba 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -61,7 +61,8 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 - ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR + with: + ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR - name: Setup Go uses: actions/setup-go@v3 with: From add0ef55d950b487e318a81f27ad6d32de1f26d6 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 18:06:47 -0400 Subject: [PATCH 379/815] debug with ssh --- .github/workflows/test-monitor-regular-skipped.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 01735a21dba..d873d39d80e 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -44,8 +44,8 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix -# run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils - run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils +# run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils + run: go run utils/test_matrix/test_matrix.go admin unit-test: name: Unit Tests (${{ matrix.targets.name }}) @@ -68,7 +68,7 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: true - - name: Setup tests (${{ matrix.targets.name }} + - name: Setup tests (${{ matrix.targets.name }}) run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - name: Run tests (${{ matrix.targets.name }}) run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output @@ -79,6 +79,10 @@ jobs: continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) uses: ./workflows/actions/test-monitor-process-tests + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: success() || failure() + # - name: Get commit date # id: commit_date # run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" From ad386e970d8c0cd52d92aa5a27508fc54c8cd2f9 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 18:17:02 -0400 Subject: [PATCH 380/815] fix path to composite action file --- .github/workflows/test-monitor-regular-skipped.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index d873d39d80e..1c5bf1a3c56 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -61,8 +61,8 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 - with: - ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR +# with: +# ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR - name: Setup Go uses: actions/setup-go@v3 with: @@ -78,7 +78,7 @@ jobs: # test run should continue even if there are failed tests continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) - uses: ./workflows/actions/test-monitor-process-tests + uses: .github/workflows/actions/test-monitor-process-tests - name: Setup tmate session uses: mxschmitt/action-tmate@v3 if: success() || failure() From bed7dfa7ffd047527e2c520ac83c615df71fcee7 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 3 Aug 2023 18:19:23 -0400 Subject: [PATCH 381/815] remove 'with; for checkout --- .github/workflows/test-monitor-regular-skipped.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 1c5bf1a3c56..6304156160f 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -61,8 +61,6 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 -# with: -# ref: misha/fix-flaky-test-monitor-ci # TODO remove this before merging PR - name: Setup Go uses: actions/setup-go@v3 with: From e97a2361bbd924b67c6c4c983d642c75e7c5a0ae Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 3 Aug 2023 19:07:15 -0600 Subject: [PATCH 382/815] unsafe random in scripts does not error --- fvm/environment/random_generator.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fvm/environment/random_generator.go b/fvm/environment/random_generator.go index 63562ff06bf..0c67ea3a7c1 100644 --- a/fvm/environment/random_generator.go +++ b/fvm/environment/random_generator.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/onflow/flow-go/crypto/random" - "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/storage/state" "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" @@ -142,5 +141,5 @@ func NewDummyRandomGenerator() RandomGenerator { // UnsafeRandom() returns an error because executing scripts // does not support randomness APIs. func (gen *dummyRandomGenerator) UnsafeRandom() (uint64, error) { - return 0, errors.NewOperationNotSupportedError("Random") + return 0, nil } From 320ea11b716edfaf8b40c6187ba5dc70c77ebfb3 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 05:30:54 -0400 Subject: [PATCH 383/815] use ./ in local path to composite action --- .github/workflows/test-monitor-regular-skipped.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 6304156160f..66a8aa7e0b4 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -76,7 +76,7 @@ jobs: # test run should continue even if there are failed tests continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) - uses: .github/workflows/actions/test-monitor-process-tests + uses: ./.github/workflows/actions/test-monitor-process-tests - name: Setup tmate session uses: mxschmitt/action-tmate@v3 if: success() || failure() From 380cc5abca84ae35137e507cca94400e0424a8fb Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 05:42:55 -0400 Subject: [PATCH 384/815] add shell: bash --- .../workflows/actions/test-monitor-process-tests/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/actions/test-monitor-process-tests/action.yml b/.github/workflows/actions/test-monitor-process-tests/action.yml index f3db1c7bab2..59f47d67976 100644 --- a/.github/workflows/actions/test-monitor-process-tests/action.yml +++ b/.github/workflows/actions/test-monitor-process-tests/action.yml @@ -8,14 +8,17 @@ runs: - name: Get commit date id: commit_date run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" + shell: bash - name: Get job run date id: job_run_date run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" + shell: bash - name: Process test results run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go env: JOB_STARTED: ${{ steps.job_run_date.outputs.date }} COMMIT_DATE: ${{ steps.commit_date.outputs.date }} + shell: bash - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v0 with: From 32cc6954c416ef308818fc9106eee5f81810fbeb Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 05:44:14 -0400 Subject: [PATCH 385/815] debug mode --- .../workflows/test-monitor-regular-skipped.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 66a8aa7e0b4..542f3a06a62 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -66,15 +66,15 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: true - - name: Setup tests (${{ matrix.targets.name }}) - run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - - name: Run tests (${{ matrix.targets.name }}) - run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output - timeout-minutes: 25 - # command line from GitHub runner that works: - # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output - # test run should continue even if there are failed tests - continue-on-error: true +# - name: Setup tests (${{ matrix.targets.name }}) +# run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools +# - name: Run tests (${{ matrix.targets.name }}) +# run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output +# timeout-minutes: 25 +# # command line from GitHub runner that works: +# # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output +# # test run should continue even if there are failed tests +# continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) uses: ./.github/workflows/actions/test-monitor-process-tests - name: Setup tmate session From 7038910a722b73f266fd06879c11aa3053c0d58f Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 05:52:08 -0400 Subject: [PATCH 386/815] pass GCP key secret to composite action as input --- .../actions/test-monitor-process-tests/action.yml | 2 +- .github/workflows/test-monitor-regular-skipped.yml | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/actions/test-monitor-process-tests/action.yml b/.github/workflows/actions/test-monitor-process-tests/action.yml index 59f47d67976..6b96f041817 100644 --- a/.github/workflows/actions/test-monitor-process-tests/action.yml +++ b/.github/workflows/actions/test-monitor-process-tests/action.yml @@ -22,7 +22,7 @@ runs: - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v0 with: - service_account_key: ${{ secrets.GCP_SA_KEY }} + service_account_key: ${{ inputs.gcp_sa_key }} - name: Upload results to BigQuery (skipped tests) uses: nick-fields/retry@v2 with: diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 542f3a06a62..e2f3a29b56f 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -77,9 +77,11 @@ jobs: # continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) uses: ./.github/workflows/actions/test-monitor-process-tests - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: success() || failure() + with: + gcp_sa_key: ${{ secrets.GCP_SA_KEY }} +# - name: Setup tmate session +# uses: mxschmitt/action-tmate@v3 +# if: success() || failure() # - name: Get commit date # id: commit_date From 679e0cc26b7afeffd13e13f19fad3bb6768ac4c7 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 05:56:39 -0400 Subject: [PATCH 387/815] removed debug mode --- .../workflows/test-monitor-regular-skipped.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index e2f3a29b56f..81664e2c347 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -66,15 +66,15 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: true -# - name: Setup tests (${{ matrix.targets.name }}) -# run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools -# - name: Run tests (${{ matrix.targets.name }}) -# run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output -# timeout-minutes: 25 -# # command line from GitHub runner that works: -# # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output -# # test run should continue even if there are failed tests -# continue-on-error: true + - name: Setup tests (${{ matrix.targets.name }}) + run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools + - name: Run tests (${{ matrix.targets.name }}) + run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output + timeout-minutes: 25 + # command line from GitHub runner that works: + # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output + # test run should continue even if there are failed tests + continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) uses: ./.github/workflows/actions/test-monitor-process-tests with: From c7b090dcd55da53756af03b433febf458f8789f7 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 06:06:19 -0400 Subject: [PATCH 388/815] added expected input to composite action --- .../actions/test-monitor-process-tests/action.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/actions/test-monitor-process-tests/action.yml b/.github/workflows/actions/test-monitor-process-tests/action.yml index 6b96f041817..83bcc362e03 100644 --- a/.github/workflows/actions/test-monitor-process-tests/action.yml +++ b/.github/workflows/actions/test-monitor-process-tests/action.yml @@ -1,6 +1,11 @@ name: Test Monitor - Reusable Workflow Steps 1 -description: Custom action that's used in multiple Flaky Test Monitor jobs +description: Custom action that's used in multiple Flaky Test Monitor jobs to process test results and upload them to BigQuery + +inputs: + gcp_sa_key: + description: 'The GCP service account key for uploading to BigQuery' + required: true runs: using : "composite" From d02ef690bea32414b854b22690c026c34e5e5123 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 06:16:53 -0400 Subject: [PATCH 389/815] revert back to testing all packages --- .../test-monitor-regular-skipped.yml | 37 +------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 81664e2c347..baeb23f696e 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -44,8 +44,7 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix -# run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils - run: go run utils/test_matrix/test_matrix.go admin + run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) @@ -79,40 +78,6 @@ jobs: uses: ./.github/workflows/actions/test-monitor-process-tests with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} -# - name: Setup tmate session -# uses: mxschmitt/action-tmate@v3 -# if: success() || failure() - -# - name: Get commit date -# id: commit_date -# run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" -# - name: Get job run date -# id: job_run_date -# run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" -# - name: Process test results -# run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go -# env: -# JOB_STARTED: ${{ steps.job_run_date.outputs.date }} -# COMMIT_DATE: ${{ steps.commit_date.outputs.date }} -## - name: Setup tmate session -## uses: mxschmitt/action-tmate@v3 -## if: success() || failure() -# - name: Set up Cloud SDK -# uses: google-github-actions/setup-gcloud@v0 -# with: -# service_account_key: ${{ secrets.GCP_SA_KEY }} -# - name: Upload results to BigQuery (skipped tests) -# uses: nick-fields/retry@v2 -# with: -# timeout_minutes: 1 -# max_attempts: 3 -# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json -# - name: Upload results to BigQuery (test run) -# uses: nick-fields/retry@v2 -# with: -# timeout_minutes: 2 -# max_attempts: 3 -# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run From a858457495f2306a9edf2f3b4b1272183fbf5618 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 06:35:31 -0400 Subject: [PATCH 390/815] update composite action path --- .../action.yml | 2 +- .github/workflows/test-monitor-regular-skipped.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/actions/{test-monitor-process-tests => test-monitor-process-results}/action.yml (97%) diff --git a/.github/workflows/actions/test-monitor-process-tests/action.yml b/.github/workflows/actions/test-monitor-process-results/action.yml similarity index 97% rename from .github/workflows/actions/test-monitor-process-tests/action.yml rename to .github/workflows/actions/test-monitor-process-results/action.yml index 83bcc362e03..723b91ea803 100644 --- a/.github/workflows/actions/test-monitor-process-tests/action.yml +++ b/.github/workflows/actions/test-monitor-process-results/action.yml @@ -1,4 +1,4 @@ -name: Test Monitor - Reusable Workflow Steps 1 +name: Test Monitor - Process Results description: Custom action that's used in multiple Flaky Test Monitor jobs to process test results and upload them to BigQuery diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index baeb23f696e..72578078c74 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -75,7 +75,7 @@ jobs: # test run should continue even if there are failed tests continue-on-error: true - name: Process test results (${{ matrix.targets.name }}) - uses: ./.github/workflows/actions/test-monitor-process-tests + uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} From 6180e838bd3bef9f7be4dec574b012b46b9b737d Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 07:32:49 -0400 Subject: [PATCH 391/815] add Unit Tests (Modules) --- .../test-monitor-regular-skipped.yml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 72578078c74..83e71d9b477 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -79,6 +79,50 @@ jobs: with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} + unit-test-modules: + name: Unit Tests (Modules) + strategy: + fail-fast: false + matrix: + include: + - name: crypto + make1: -C crypto setup + make2: unittest + race: 1 + test_category: unit-crypto + - name: insecure + make1: install-tools + make2: test + race: 0 + test_category: unit-insecure + - name: integration + make1: install-tools + make2: test + race: 0 + test_category: unit-integration + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Setup tests (${{ matrix.name }}) + run: make ${{ matrix.make1 }} + - name: Run tests (${{ matrix.name }}) + env: + RACE_DETECTOR: ${{ matrix.race }} + TEST_CATEGORY: ${{ matrix.test_category }} + run: make -esC ${{ matrix.name }} ${{ matrix.make2 }} > test-output + timeout-minutes: 25 + continue-on-error: true + - name: Process test results (${{ matrix.targets.name }}) + uses: ./.github/workflows/actions/test-monitor-process-results + with: + gcp_sa_key: ${{ secrets.GCP_SA_KEY }} + # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run # strategy: From b50694cf37a560bf9255501bda73fc00c4214c18 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 17:44:42 -0400 Subject: [PATCH 392/815] integration tests --- .../test-monitor-regular-skipped.yml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 83e71d9b477..f9483f9667c 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -123,6 +123,63 @@ jobs: with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} + integration-test: + name: Integration Tests + strategy: + fail-fast: false + matrix: + include: + - target: access-tests + test_category: integration-access + - target: bft-tests + test_category: integration-bft + - target: collection-tests + test_category: integration-collection + - target: consensus-tests + test_category: integration-consensus + - target: epochs-tests + test_category: integration-epochs + - target: execution-tests + test_category: integration-execution + - target: ghost-tests + test_category: integration-ghost + - target: mvp-tests + test_category: integration-mvp + - target: network-tests + test_category: integration-network + - target: verification-tests + test_category: integration-verification + - target: upgrades-tests + test_category: integration-upgrades + + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + # all tags are needed for integration tests + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Build relic + run: make crypto_setup_gopath + - name: Docker build + run: make docker-build-flow docker-build-flow-corrupt + + - name: Run tests + env: + TEST_CATEGORY: ${{ matrix.test_category }} + run: VERBOSE=1 make -esC integration ${{ matrix.target }} > test-output + timeout-minutes: 25 + continue-on-error: true + - name: Process test results (${{ matrix.targets.name }}) + uses: ./.github/workflows/actions/test-monitor-process-results + with: + gcp_sa_key: ${{ secrets.GCP_SA_KEY }} + # regular-skipped-test-run: # name: Test Monitor - Regular, Skipped Tests Run # strategy: From d98a24661a5652ae0c75b9652ce2622e744de759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 4 Aug 2023 15:05:51 -0700 Subject: [PATCH 393/815] auto update to onflow/cadence v0.40.0 --- go.mod | 4 ++-- go.sum | 8 ++++---- insecure/go.mod | 4 ++-- insecure/go.sum | 12 ++++++------ integration/go.mod | 4 ++-- integration/go.sum | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index d5890cfa995..05ab2e0ea2e 100644 --- a/go.mod +++ b/go.mod @@ -51,11 +51,11 @@ require ( github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multihash v0.2.3 github.com/onflow/atree v0.6.0 - github.com/onflow/cadence v0.39.14 + github.com/onflow/cadence v0.40.0 github.com/onflow/flow v0.3.4 github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 - github.com/onflow/flow-go-sdk v0.41.9 + github.com/onflow/flow-go-sdk v0.41.10 github.com/onflow/flow-go/crypto v0.24.9 github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce github.com/onflow/go-bitswap v0.0.0-20230703214630-6d3db958c73d diff --git a/go.sum b/go.sum index 73eadfddbf8..a2abc8cef6b 100644 --- a/go.sum +++ b/go.sum @@ -1240,8 +1240,8 @@ github.com/onflow/atree v0.1.0-beta1.0.20211027184039-559ee654ece9/go.mod h1:+6x github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/onflow/cadence v0.20.1/go.mod h1:7mzUvPZUIJztIbr9eTvs+fQjWWHTF8veC+yk4ihcNIA= -github.com/onflow/cadence v0.39.14 h1:YoR3YFUga49rqzVY1xwI6I2ZDBmvwGh13jENncsleC8= -github.com/onflow/cadence v0.39.14/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= +github.com/onflow/cadence v0.40.0 h1:3pTdkyVTjMx2U5+YZYvIpyw74CSxabjk9PdAZUkJ1GU= +github.com/onflow/cadence v0.40.0/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= github.com/onflow/flow v0.3.4 h1:FXUWVdYB90f/rjNcY0Owo30gL790tiYff9Pb/sycXYE= github.com/onflow/flow v0.3.4/go.mod h1:lzyAYmbu1HfkZ9cfnL5/sjrrsnJiUU8fRL26CqLP7+c= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d h1:B7PdhdUNkve5MVrekWDuQf84XsGBxNZ/D3x+QQ8XeVs= @@ -1251,8 +1251,8 @@ github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3/go.mod h1:dqAUVWwg github.com/onflow/flow-ft/lib/go/contracts v0.7.0 h1:XEKE6qJUw3luhsYmIOteXP53gtxNxrwTohgxJXCYqBE= github.com/onflow/flow-ft/lib/go/contracts v0.7.0/go.mod h1:kTMFIySzEJJeupk+7EmXs0EJ6CBWY/MV9fv9iYQk+RU= github.com/onflow/flow-go-sdk v0.24.0/go.mod h1:IoptMLPyFXWvyd9yYA6/4EmSeeozl6nJoIv4FaEMg74= -github.com/onflow/flow-go-sdk v0.41.9 h1:cyplhhhc0RnfOAan2t7I/7C9g1hVGDDLUhWj6ZHAkk4= -github.com/onflow/flow-go-sdk v0.41.9/go.mod h1:e9Q5TITCy7g08lkdQJxP8fAKBnBoC5FjALvUKr36j4I= +github.com/onflow/flow-go-sdk v0.41.10 h1:Cio6GJhtx532TUY+cqrqWglD5sZCXkWeM5QvaRha3p4= +github.com/onflow/flow-go-sdk v0.41.10/go.mod h1:0a0LiQFbFt8RW/ptoMUU7YkvW9ArVcbjLE0XS78uz1E= github.com/onflow/flow-go/crypto v0.21.3/go.mod h1:vI6V4CY3R6c4JKBxdcRiR/AnjBfL8OSD97bJc60cLuQ= github.com/onflow/flow-go/crypto v0.24.9 h1:0EQp+kSZYJepMIiSypfJVe7tzsPcb6UXOdOtsTCDhBs= github.com/onflow/flow-go/crypto v0.24.9/go.mod h1:fqCzkIBBMRRkciVrvW21rECKq1oD7Q6u+bCI78lfNX0= diff --git a/insecure/go.mod b/insecure/go.mod index b727c8ecf6c..c0f8048ecbe 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -180,11 +180,11 @@ require ( github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/onflow/atree v0.6.0 // indirect - github.com/onflow/cadence v0.39.14 // indirect + github.com/onflow/cadence v0.40.0 // indirect github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d // indirect github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 // indirect github.com/onflow/flow-ft/lib/go/contracts v0.7.0 // indirect - github.com/onflow/flow-go-sdk v0.41.9 // indirect + github.com/onflow/flow-go-sdk v0.41.10 // indirect github.com/onflow/flow-nft/lib/go/contracts v1.1.0 // indirect github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce // indirect github.com/onflow/go-bitswap v0.0.0-20230703214630-6d3db958c73d // indirect diff --git a/insecure/go.sum b/insecure/go.sum index 308ac3ab9f3..376c5d1543e 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1216,8 +1216,8 @@ github.com/onflow/atree v0.1.0-beta1.0.20211027184039-559ee654ece9/go.mod h1:+6x github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/onflow/cadence v0.20.1/go.mod h1:7mzUvPZUIJztIbr9eTvs+fQjWWHTF8veC+yk4ihcNIA= -github.com/onflow/cadence v0.39.14 h1:YoR3YFUga49rqzVY1xwI6I2ZDBmvwGh13jENncsleC8= -github.com/onflow/cadence v0.39.14/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= +github.com/onflow/cadence v0.40.0 h1:3pTdkyVTjMx2U5+YZYvIpyw74CSxabjk9PdAZUkJ1GU= +github.com/onflow/cadence v0.40.0/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d h1:B7PdhdUNkve5MVrekWDuQf84XsGBxNZ/D3x+QQ8XeVs= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d/go.mod h1:xAiV/7TKhw863r6iO3CS5RnQ4F+pBY1TxD272BsILlo= github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 h1:X25A1dNajNUtE+KoV76wQ6BR6qI7G65vuuRXxDDqX7E= @@ -1225,16 +1225,16 @@ github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3/go.mod h1:dqAUVWwg github.com/onflow/flow-ft/lib/go/contracts v0.7.0 h1:XEKE6qJUw3luhsYmIOteXP53gtxNxrwTohgxJXCYqBE= github.com/onflow/flow-ft/lib/go/contracts v0.7.0/go.mod h1:kTMFIySzEJJeupk+7EmXs0EJ6CBWY/MV9fv9iYQk+RU= github.com/onflow/flow-go-sdk v0.24.0/go.mod h1:IoptMLPyFXWvyd9yYA6/4EmSeeozl6nJoIv4FaEMg74= -github.com/onflow/flow-go-sdk v0.41.9 h1:cyplhhhc0RnfOAan2t7I/7C9g1hVGDDLUhWj6ZHAkk4= -github.com/onflow/flow-go-sdk v0.41.9/go.mod h1:e9Q5TITCy7g08lkdQJxP8fAKBnBoC5FjALvUKr36j4I= +github.com/onflow/flow-go-sdk v0.41.10 h1:Cio6GJhtx532TUY+cqrqWglD5sZCXkWeM5QvaRha3p4= +github.com/onflow/flow-go-sdk v0.41.10/go.mod h1:0a0LiQFbFt8RW/ptoMUU7YkvW9ArVcbjLE0XS78uz1E= github.com/onflow/flow-go/crypto v0.21.3/go.mod h1:vI6V4CY3R6c4JKBxdcRiR/AnjBfL8OSD97bJc60cLuQ= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce h1:YQKijiQaq8SF1ayNqp3VVcwbBGXSnuHNHq4GQmVGybE= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/flow-go/crypto v0.24.9 h1:0EQp+kSZYJepMIiSypfJVe7tzsPcb6UXOdOtsTCDhBs= github.com/onflow/flow-go/crypto v0.24.9/go.mod h1:fqCzkIBBMRRkciVrvW21rECKq1oD7Q6u+bCI78lfNX0= github.com/onflow/flow-nft/lib/go/contracts v1.1.0 h1:rhUDeD27jhLwOqQKI/23008CYfnqXErrJvc4EFRP2a0= github.com/onflow/flow-nft/lib/go/contracts v1.1.0/go.mod h1:YsvzYng4htDgRB9sa9jxdwoTuuhjK8WYWXTyLkIigZY= github.com/onflow/flow/protobuf/go/flow v0.2.2/go.mod h1:gQxYqCfkI8lpnKsmIjwtN2mV/N2PIwc1I+RUK4HPIc8= +github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce h1:YQKijiQaq8SF1ayNqp3VVcwbBGXSnuHNHq4GQmVGybE= +github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/go-bitswap v0.0.0-20230703214630-6d3db958c73d h1:QcOAeEyF3iAUHv21LQ12sdcsr0yFrJGoGLyCAzYYtvI= github.com/onflow/go-bitswap v0.0.0-20230703214630-6d3db958c73d/go.mod h1:GCPpiyRoHncdqPj++zPr9ZOYBX4hpJ0pYZRYqSE8VKk= github.com/onflow/sdks v0.5.0 h1:2HCRibwqDaQ1c9oUApnkZtEAhWiNY2GTpRD5+ftdkN8= diff --git a/integration/go.mod b/integration/go.mod index e47e8bac3c3..affeae7bc11 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -17,12 +17,12 @@ require ( github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-badger2 v0.1.3 github.com/ipfs/go-ipfs-blockstore v1.3.0 - github.com/onflow/cadence v0.39.14 + github.com/onflow/cadence v0.40.0 github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 github.com/onflow/flow-emulator v0.53.0 github.com/onflow/flow-go v0.31.1-0.20230718164039-e3411eff1e9d - github.com/onflow/flow-go-sdk v0.41.9 + github.com/onflow/flow-go-sdk v0.41.10 github.com/onflow/flow-go/crypto v0.24.9 github.com/onflow/flow-go/insecure v0.0.0-00010101000000-000000000000 github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20230628215638-83439d22e0ce diff --git a/integration/go.sum b/integration/go.sum index 4aac8d7305d..540ba104a09 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1354,8 +1354,8 @@ github.com/onflow/atree v0.1.0-beta1.0.20211027184039-559ee654ece9/go.mod h1:+6x github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/onflow/cadence v0.20.1/go.mod h1:7mzUvPZUIJztIbr9eTvs+fQjWWHTF8veC+yk4ihcNIA= -github.com/onflow/cadence v0.39.14 h1:YoR3YFUga49rqzVY1xwI6I2ZDBmvwGh13jENncsleC8= -github.com/onflow/cadence v0.39.14/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= +github.com/onflow/cadence v0.40.0 h1:3pTdkyVTjMx2U5+YZYvIpyw74CSxabjk9PdAZUkJ1GU= +github.com/onflow/cadence v0.40.0/go.mod h1:OIJLyVBPa339DCBQXBfGaorT4tBjQh9gSKe+ZAIyyh0= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d h1:B7PdhdUNkve5MVrekWDuQf84XsGBxNZ/D3x+QQ8XeVs= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d/go.mod h1:xAiV/7TKhw863r6iO3CS5RnQ4F+pBY1TxD272BsILlo= github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 h1:X25A1dNajNUtE+KoV76wQ6BR6qI7G65vuuRXxDDqX7E= @@ -1365,8 +1365,8 @@ github.com/onflow/flow-emulator v0.53.0/go.mod h1:o7O+b3fQYs26vJ+4SeMY/T9kA1rT09 github.com/onflow/flow-ft/lib/go/contracts v0.7.0 h1:XEKE6qJUw3luhsYmIOteXP53gtxNxrwTohgxJXCYqBE= github.com/onflow/flow-ft/lib/go/contracts v0.7.0/go.mod h1:kTMFIySzEJJeupk+7EmXs0EJ6CBWY/MV9fv9iYQk+RU= github.com/onflow/flow-go-sdk v0.24.0/go.mod h1:IoptMLPyFXWvyd9yYA6/4EmSeeozl6nJoIv4FaEMg74= -github.com/onflow/flow-go-sdk v0.41.9 h1:cyplhhhc0RnfOAan2t7I/7C9g1hVGDDLUhWj6ZHAkk4= -github.com/onflow/flow-go-sdk v0.41.9/go.mod h1:e9Q5TITCy7g08lkdQJxP8fAKBnBoC5FjALvUKr36j4I= +github.com/onflow/flow-go-sdk v0.41.10 h1:Cio6GJhtx532TUY+cqrqWglD5sZCXkWeM5QvaRha3p4= +github.com/onflow/flow-go-sdk v0.41.10/go.mod h1:0a0LiQFbFt8RW/ptoMUU7YkvW9ArVcbjLE0XS78uz1E= github.com/onflow/flow-go/crypto v0.21.3/go.mod h1:vI6V4CY3R6c4JKBxdcRiR/AnjBfL8OSD97bJc60cLuQ= github.com/onflow/flow-go/crypto v0.24.9 h1:0EQp+kSZYJepMIiSypfJVe7tzsPcb6UXOdOtsTCDhBs= github.com/onflow/flow-go/crypto v0.24.9/go.mod h1:fqCzkIBBMRRkciVrvW21rECKq1oD7Q6u+bCI78lfNX0= From c25475edc1bfe7f3299864a491abbcde2193c14f Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 18:13:28 -0400 Subject: [PATCH 394/815] clean up --- .../test-monitor-regular-skipped.yml | 86 ++----------------- 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index f9483f9667c..13985754b32 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -66,15 +66,13 @@ jobs: go-version: ${{ env.GO_VERSION }} cache: true - name: Setup tests (${{ matrix.targets.name }}) - run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools + run: make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - name: Run tests (${{ matrix.targets.name }}) run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output timeout-minutes: 25 - # command line from GitHub runner that works: - # make VERBOSE=1 JSON_OUTPUT=true GO_TEST_PACKAGES=github.com/onflow/flow-go/admin/commands/common -es unittest-main > test-output # test run should continue even if there are failed tests continue-on-error: true - - name: Process test results (${{ matrix.targets.name }}) + - name: Process test results uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} @@ -118,7 +116,7 @@ jobs: run: make -esC ${{ matrix.name }} ${{ matrix.make2 }} > test-output timeout-minutes: 25 continue-on-error: true - - name: Process test results (${{ matrix.targets.name }}) + - name: Process test results (${{ matrix.name }}) uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} @@ -172,84 +170,10 @@ jobs: - name: Run tests env: TEST_CATEGORY: ${{ matrix.test_category }} - run: VERBOSE=1 make -esC integration ${{ matrix.target }} > test-output + run: make -esC integration ${{ matrix.target }} > test-output timeout-minutes: 25 continue-on-error: true - - name: Process test results (${{ matrix.targets.name }}) + - name: Process test results uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} - -# regular-skipped-test-run: -# name: Test Monitor - Regular, Skipped Tests Run -# strategy: -# fail-fast: false -# matrix: -# test-category: -# - unit -# - unit-crypto -# - unit-insecure -# - unit-integration -# - integration-bft -# - integration-mvp -# - integration-ghost -# - integration-network -# - integration-epochs -# - integration-access -# - integration-collection -# - integration-consensus -# - integration-execution -# - integration-verification -# env: -# TEST_CATEGORY: ${{ matrix.test-category }} -# COMMIT_SHA: ${{ github.sha }} -# RUN_ID: ${{ github.run_id }} -# SKIPPED_TESTS_FILE: skipped-tests -# RESULTS_FILE: test-results -# runs-on: ubuntu-latest -# steps: -# - name: Get job run date -# id: job_run_date -# run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" -# - name: Set up Cloud SDK -# uses: google-github-actions/setup-gcloud@v0 -# with: -# service_account_key: ${{ secrets.GCP_SA_KEY }} -# - name: Setup Go -# uses: actions/setup-go@v2 -# with: -# go-version: ${{ env.GO_VERSION }} -# - name: Checkout repo -# uses: actions/checkout@v2 -# with: -# ref: ${{ env.COMMIT_SHA }} -# - name: Get commit date -# id: commit_date -# run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" -# - name: Run tests -# uses: nick-fields/retry@v2 -# with: -# timeout_minutes: 60 -# max_attempts: 5 -# command: ./tools/test_monitor/run-tests.sh -# env: -# JSON_OUTPUT: true -# - name: Print test results -# run: cat test-output -# - name: Process test results -# run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go -# env: -# JOB_STARTED: ${{ steps.job_run_date.outputs.date }} -# COMMIT_DATE: ${{ steps.commit_date.outputs.date }} -# - name: Upload results to BigQuery (skipped tests) -# uses: nick-fields/retry@v2 -# with: -# timeout_minutes: 1 -# max_attempts: 3 -# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $SKIPPED_TESTS_FILE tools/test_monitor/schemas/skipped_tests_schema.json -# - name: Upload results to BigQuery (test run) -# uses: nick-fields/retry@v2 -# with: -# timeout_minutes: 2 -# max_attempts: 3 -# command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE2 $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json From 181de23fcffb82d60cd27878826fc535b549624e Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 4 Aug 2023 18:15:30 -0400 Subject: [PATCH 395/815] added flaky tests, increased timeout to 100 mins --- .github/workflows/test-monitor-regular-skipped.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 13985754b32..fe02a971875 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -23,6 +23,7 @@ env: RUN_ID: ${{ github.run_id }} JSON_OUTPUT: true VERBOSE: true + TEST_FLAKY: true concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} @@ -69,7 +70,7 @@ jobs: run: make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - name: Run tests (${{ matrix.targets.name }}) run: make -es GO_TEST_PACKAGES="${{ matrix.targets.packages }}" unittest-main > test-output - timeout-minutes: 25 + timeout-minutes: 100 # test run should continue even if there are failed tests continue-on-error: true - name: Process test results @@ -114,7 +115,7 @@ jobs: RACE_DETECTOR: ${{ matrix.race }} TEST_CATEGORY: ${{ matrix.test_category }} run: make -esC ${{ matrix.name }} ${{ matrix.make2 }} > test-output - timeout-minutes: 25 + timeout-minutes: 100 continue-on-error: true - name: Process test results (${{ matrix.name }}) uses: ./.github/workflows/actions/test-monitor-process-results @@ -171,7 +172,7 @@ jobs: env: TEST_CATEGORY: ${{ matrix.test_category }} run: make -esC integration ${{ matrix.target }} > test-output - timeout-minutes: 25 + timeout-minutes: 100 continue-on-error: true - name: Process test results uses: ./.github/workflows/actions/test-monitor-process-results From d3a514503c42195df6278171424866c0b03e23ad Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 5 Aug 2023 13:46:33 +0100 Subject: [PATCH 396/815] Refactor backend constructor, replace old --- .../node_builder/access_node_builder.go | 9 +- cmd/observer/node_builder/observer_builder.go | 2 +- engine/access/access_test.go | 10 +- .../integration_unsecure_grpc_server_test.go | 9 +- engine/access/rest_api_test.go | 10 +- engine/access/rpc/backend/backend.go | 141 +----------------- engine/access/rpc/backend/backend_test.go | 67 ++++----- .../rpc/backend/backend_transactions_test.go | 8 +- .../rpc/backend/historical_access_test.go | 9 +- engine/access/rpc/backend/retry_test.go | 9 +- engine/access/rpc/rate_limit_test.go | 5 +- engine/access/secure_grpcr_test.go | 5 +- .../mock/get_execution_data_func.go | 3 +- .../mock/get_start_height_func.go | 3 +- 14 files changed, 72 insertions(+), 218 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index f083aaed0fd..9bb8472ce71 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -12,15 +12,14 @@ import ( badger "github.com/ipfs/go-ds-badger2" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/routing" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/go-bitswap" "github.com/rs/zerolog" "github.com/spf13/pflag" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/go-bitswap" - "github.com/onflow/flow-go/admin/commands" stateSyncCommands "github.com/onflow/flow-go/admin/commands/state_synchronization" storageCommands "github.com/onflow/flow-go/admin/commands/storage" @@ -1088,7 +1087,6 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { backendConfig.CircuitBreakerConfig, ), } - backend := backend.New( node.State, builder.CollectionRPC, @@ -1109,7 +1107,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { node.Logger, backend.DefaultSnapshotHistoryLimit, backendConfig.ArchiveAddressList, - backendConfig.CircuitBreakerConfig.Enabled) + backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + ) engineBuilder, err := rpc.NewBuilder( node.Logger, diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 1e7687578c2..78208d09656 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -946,7 +946,7 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { node.Logger, backend.DefaultSnapshotHistoryLimit, backendConfig.ArchiveAddressList, - backendConfig.CircuitBreakerConfig.Enabled) + backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled)) observerCollector := metrics.NewObserverCollector() restHandler, err := restapiproxy.NewRestProxyHandler( diff --git a/engine/access/access_test.go b/engine/access/access_test.go index b3979979fb9..f4d3e7c0d18 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -157,7 +157,7 @@ func (suite *Suite) RunTest( suite.log, backend.DefaultSnapshotHistoryLimit, nil, - false, + backend.NewNodeCommunicator(false), ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me, access.WithBlockSignerDecoder(suite.signerIndicesDecoder)) f(handler, db, all) @@ -330,7 +330,7 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, - false, + backend.NewNodeCommunicator(false), ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -657,7 +657,7 @@ func (suite *Suite) TestGetSealedTransaction() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, - false, + backend.NewNodeCommunicator(false), ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -797,7 +797,7 @@ func (suite *Suite) TestGetTransactionResult() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, - false, + backend.NewNodeCommunicator(false), ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -989,7 +989,7 @@ func (suite *Suite) TestExecuteScript() { suite.log, backend.DefaultSnapshotHistoryLimit, nil, - false, + backend.NewNodeCommunicator(false), ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index e2cf78ade5a..46aba64c76f 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -2,17 +2,17 @@ package access import ( "context" - "io" "os" "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -37,9 +37,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" ) // SameGRPCPortTestSuite verifies both AccessAPI and ExecutionDataAPI client continue to work when configured @@ -189,7 +186,7 @@ func (suite *SameGRPCPortTestSuite) SetupTest() { suite.log, 0, nil, - false) + backend.NewNodeCommunicator(false)) // create rpc engine builder rpcEngBuilder, err := rpc.NewBuilder( diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 091c5e2e3ad..1e4388b5a32 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -10,11 +10,6 @@ import ( "testing" "time" - "google.golang.org/grpc/credentials" - - "github.com/onflow/flow-go/module/grpcserver" - "github.com/onflow/flow-go/utils/grpcutils" - "github.com/antihax/optional" restclient "github.com/onflow/flow/openapi/go-client-generated" "github.com/rs/zerolog" @@ -22,6 +17,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "google.golang.org/grpc/credentials" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest/request" @@ -29,6 +25,7 @@ import ( "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/grpcserver" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" module "github.com/onflow/flow-go/module/mock" @@ -36,6 +33,7 @@ import ( protocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" ) @@ -170,7 +168,7 @@ func (suite *RestAPITestSuite) SetupTest() { suite.log, 0, nil, - false) + backend.NewNodeCommunicator(false)) rpcEngBuilder, err := rpc.NewBuilder( suite.log, diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index eea7c8fdbf6..998fb877e3c 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -100,9 +100,9 @@ type Communicator interface { ) error } -// NewBackend creates backend accepting Communicator interfaces instead of circuitBreakerEnabled flag +// New creates backend accepting Communicator interfaces instead of circuitBreakerEnabled flag // More convenient fur unit testing scenarios when you need to pass test NodeCommunicator objects. -func NewBackend(state protocol.State, +func New(state protocol.State, collectionRPC accessproto.AccessAPIClient, historicalAccessNodes []accessproto.AccessAPIClient, blocks storage.Blocks, @@ -232,143 +232,6 @@ func NewBackend(state protocol.State, return b } -// New create new backend instance -// Deprecated: Use NewBackend for enhanced testability -func New( - state protocol.State, - collectionRPC accessproto.AccessAPIClient, - historicalAccessNodes []accessproto.AccessAPIClient, - blocks storage.Blocks, - headers storage.Headers, - collections storage.Collections, - transactions storage.Transactions, - executionReceipts storage.ExecutionReceipts, - executionResults storage.ExecutionResults, - chainID flow.ChainID, - accessMetrics module.AccessMetrics, - connFactory connection.ConnectionFactory, - retryEnabled bool, - maxHeightRange uint, - preferredExecutionNodeIDs []string, - fixedExecutionNodeIDs []string, - log zerolog.Logger, - snapshotHistoryLimit int, - archiveAddressList []string, - circuitBreakerEnabled bool, -) *Backend { - retry := newRetry() - if retryEnabled { - retry.Activate() - } - - loggedScripts, err := lru.New(DefaultLoggedScriptsCacheSize) - if err != nil { - log.Fatal().Err(err).Msg("failed to initialize script logging cache") - } - - archivePorts := make([]uint, len(archiveAddressList)) - for idx, addr := range archiveAddressList { - port, err := findPortFromAddress(addr) - if err != nil { - log.Fatal().Err(err).Msg("failed to find archive node port") - } - archivePorts[idx] = port - } - - // create node communicator, that will be used in sub-backend logic for interacting with API calls - nodeCommunicator := NewNodeCommunicator(circuitBreakerEnabled) - - txResCache, err := lru2.New[flow.Identifier, *access.TransactionResult](int(badger.DefaultCacheSize)) - if err != nil { - log.Fatal().Err(err).Msg("failed to init cache for transaction results") - } - - b := &Backend{ - state: state, - // create the sub-backends - backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, - loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, - archivePorts: archivePorts, - nodeCommunicator: nodeCommunicator, - }, - backendTransactions: backendTransactions{ - staticCollectionRPC: collectionRPC, - state: state, - chainID: chainID, - collections: collections, - blocks: blocks, - transactions: transactions, - executionReceipts: executionReceipts, - transactionValidator: configureTransactionValidator(state, chainID), - transactionMetrics: accessMetrics, - retry: retry, - connFactory: connFactory, - previousAccessNodes: historicalAccessNodes, - log: log, - nodeCommunicator: nodeCommunicator, - txResultCache: txResCache, - }, - backendEvents: backendEvents{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - maxHeightRange: maxHeightRange, - nodeCommunicator: nodeCommunicator, - }, - backendBlockHeaders: backendBlockHeaders{ - headers: headers, - state: state, - }, - backendBlockDetails: backendBlockDetails{ - blocks: blocks, - state: state, - }, - backendAccounts: backendAccounts{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - nodeCommunicator: nodeCommunicator, - }, - backendExecutionResults: backendExecutionResults{ - executionResults: executionResults, - }, - backendNetwork: backendNetwork{ - state: state, - chainID: chainID, - snapshotHistoryLimit: snapshotHistoryLimit, - }, - collections: collections, - executionReceipts: executionReceipts, - connFactory: connFactory, - chainID: chainID, - } - - retry.SetBackend(b) - - preferredENIdentifiers, err = identifierList(preferredExecutionNodeIDs) - if err != nil { - log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for preferred EN map") - } - - fixedENIdentifiers, err = identifierList(fixedExecutionNodeIDs) - if err != nil { - log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for fixed EN map") - } - - return b -} - //TODO: refactor cache, replace with generic lru.Cache alternative // NewCache constructs cache for storing connections to other nodes. diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 48d658a899a..cc99138864e 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -6,8 +6,6 @@ import ( "strconv" "testing" - "github.com/onflow/flow-go/engine/access/rpc/connection" - "github.com/dgraph-io/badger/v2" accessproto "github.com/onflow/flow/protobuf/go/flow/access" entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" @@ -22,6 +20,7 @@ import ( access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -116,7 +115,7 @@ func (suite *Suite) TestPing() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) err := backend.Ping(context.Background()) @@ -152,7 +151,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized block @@ -218,7 +217,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized snapshot @@ -291,7 +290,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized snapshot @@ -357,7 +356,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized snapshot @@ -434,7 +433,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized snapshot @@ -495,7 +494,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { suite.log, snapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // the handler should return a snapshot history limit error @@ -534,7 +533,7 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest sealed block @@ -581,7 +580,7 @@ func (suite *Suite) TestGetTransaction() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) actual, err := backend.GetTransaction(context.Background(), transaction.ID()) @@ -622,7 +621,7 @@ func (suite *Suite) TestGetCollection() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) actual, err := backend.GetCollectionByID(context.Background(), expected.ID()) @@ -686,7 +685,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) suite.execClient. On("GetTransactionResultByIndex", ctx, exeEventReq). @@ -750,7 +749,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) suite.execClient. On("GetTransactionResultsByBlockID", ctx, exeEventReq). @@ -842,7 +841,7 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // Successfully return empty event list @@ -963,7 +962,7 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // should return pending status when we have not observed an expiry block @@ -1131,7 +1130,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -1190,7 +1189,7 @@ func (suite *Suite) TestTransactionResultUnknown() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx @@ -1245,7 +1244,7 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // query the handler for the latest finalized header @@ -1376,7 +1375,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request @@ -1409,7 +1408,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request with an empty block id list and expect an empty list of events and no error @@ -1469,7 +1468,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request @@ -1500,7 +1499,7 @@ func (suite *Suite) TestGetExecutionResultByID() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request @@ -1564,7 +1563,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request @@ -1747,7 +1746,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), maxHeight, minHeight) @@ -1787,7 +1786,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // execute request @@ -1826,7 +1825,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) actualResp, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1864,7 +1863,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, minHeight+1) @@ -1902,7 +1901,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1980,7 +1979,7 @@ func (suite *Suite) TestGetAccount() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2062,7 +2061,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2102,7 +2101,7 @@ func (suite *Suite) TestGetNetworkParameters() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) params := backend.GetNetworkParameters(context.Background()) @@ -2305,7 +2304,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // mock parameters @@ -2382,7 +2381,7 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { suite.log, DefaultSnapshotHistoryLimit, []string{fullArchiveAddress}, - false, + NewNodeCommunicator(false), ) // mock parameters diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index c8c3b8496f3..b1ccf656eeb 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -82,7 +82,7 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { On("ByBlockID", block.ID()). Return(l, nil) - backend := NewBackend( + backend := New( suite.state, suite.colClient, nil, @@ -140,7 +140,7 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { On("ByBlockID", block.ID()). Return(l, nil) - backend := NewBackend( + backend := New( suite.state, suite.colClient, nil, @@ -213,7 +213,7 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil) - backend := NewBackend( + backend := New( suite.state, suite.colClient, []access.AccessAPIClient{suite.historicalAccessClient}, @@ -287,7 +287,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil).Once() - backend := NewBackend( + backend := New( suite.state, suite.colClient, []access.AccessAPIClient{suite.historicalAccessClient}, diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 42dd829dbbc..42f26e72ffb 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -3,11 +3,10 @@ package backend import ( "context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/entities" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" @@ -56,7 +55,7 @@ func (suite *Suite) TestHistoricalTransactionResult() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // Successfully return the transaction from the historical node @@ -115,7 +114,7 @@ func (suite *Suite) TestHistoricalTransaction() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) // Successfully return the transaction from the historical node diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 2189223118a..6402d0fad44 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -3,13 +3,12 @@ package backend import ( "context" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/stretchr/testify/mock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" protocol "github.com/onflow/flow-go/state/protocol/mock" @@ -61,7 +60,7 @@ func (suite *Suite) TestTransactionRetry() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry @@ -151,7 +150,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.log, DefaultSnapshotHistoryLimit, nil, - false, + NewNodeCommunicator(false), ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index 8ff5695c3c6..f9a16cba4a5 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -31,8 +32,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - - accessproto "github.com/onflow/flow/protobuf/go/flow/access" ) type RateLimitTestSuite struct { @@ -168,7 +167,7 @@ func (suite *RateLimitTestSuite) SetupTest() { suite.log, 0, nil, - false) + backend.NewNodeCommunicator(false)) rpcEngBuilder, err := NewBuilder( suite.log, diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index 783ed0d3110..ceb4d7ba221 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -15,8 +16,6 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow-go/crypto" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -151,7 +150,7 @@ func (suite *SecureGRPCTestSuite) SetupTest() { suite.log, 0, nil, - false) + backend.NewNodeCommunicator(false)) rpcEngBuilder, err := rpc.NewBuilder( suite.log, diff --git a/engine/access/state_stream/mock/get_execution_data_func.go b/engine/access/state_stream/mock/get_execution_data_func.go index 50fe8087e21..506cc8d3d1c 100644 --- a/engine/access/state_stream/mock/get_execution_data_func.go +++ b/engine/access/state_stream/mock/get_execution_data_func.go @@ -5,8 +5,9 @@ package mock import ( context "context" - execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" mock "github.com/stretchr/testify/mock" + + execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" ) // GetExecutionDataFunc is an autogenerated mock type for the GetExecutionDataFunc type diff --git a/engine/access/state_stream/mock/get_start_height_func.go b/engine/access/state_stream/mock/get_start_height_func.go index b97a77e1d39..d1c93d2ed1c 100644 --- a/engine/access/state_stream/mock/get_start_height_func.go +++ b/engine/access/state_stream/mock/get_start_height_func.go @@ -3,8 +3,9 @@ package mock import ( - flow "github.com/onflow/flow-go/model/flow" mock "github.com/stretchr/testify/mock" + + flow "github.com/onflow/flow-go/model/flow" ) // GetStartHeightFunc is an autogenerated mock type for the GetStartHeightFunc type From e2ac1cf76ac59ff4233b5288b48facda702c7960 Mon Sep 17 00:00:00 2001 From: Amlandeep Bhadra Date: Mon, 7 Aug 2023 10:30:08 -0400 Subject: [PATCH 397/815] Create validation mode for script exec on RN/EN (#4573) * Create validation mode for script exec on RN/EN * cleanup builder * fix lint * fix observer init * add check for not found block in RN * Create validation mode for script exec on RN/EN * add check for not found block in RN * exclude errors in comparison and merge with master * chnages per feedback and add metrics * remove extra RN call * flatten code * fix and add tests * Update tests for exepected behavior * cleanup * granular debug logs * lint * goimports * fix tests --- .../node_builder/access_node_builder.go | 3 + cmd/observer/node_builder/observer_builder.go | 1 + engine/access/access_test.go | 5 + .../integration_unsecure_grpc_server_test.go | 4 +- engine/access/rest_api_test.go | 4 +- engine/access/rpc/backend/backend.go | 37 +++-- engine/access/rpc/backend/backend_scripts.go | 144 ++++++++++++++--- engine/access/rpc/backend/backend_test.go | 151 ++++++++++++++++++ .../rpc/backend/historical_access_test.go | 2 + engine/access/rpc/backend/retry_test.go | 2 + engine/access/rpc/rate_limit_test.go | 4 +- engine/access/secure_grpcr_test.go | 4 +- module/metrics.go | 8 + module/metrics/noop.go | 4 + module/metrics/transaction.go | 30 +++- module/mock/access_metrics.go | 20 +++ module/mock/backend_scripts_metrics.go | 20 +++ 17 files changed, 402 insertions(+), 41 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index f083aaed0fd..55fd48ace8e 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -162,6 +162,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { PreferredExecutionNodeIDs: nil, FixedExecutionNodeIDs: nil, ArchiveAddressList: nil, + ScriptExecValidation: false, CircuitBreakerConfig: rpcConnection.CircuitBreakerConfig{ Enabled: false, RestoreTimeout: 60 * time.Second, @@ -677,6 +678,7 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringVarP(&builder.rpcConf.CollectionAddr, "static-collection-ingress-addr", "", defaultConfig.rpcConf.CollectionAddr, "the address (of the collection node) to send transactions to") flags.StringVarP(&builder.ExecutionNodeAddress, "script-addr", "s", defaultConfig.ExecutionNodeAddress, "the address (of the execution node) forward the script to") flags.StringSliceVar(&builder.rpcConf.BackendConfig.ArchiveAddressList, "archive-address-list", defaultConfig.rpcConf.BackendConfig.ArchiveAddressList, "the list of address of the archive node to forward the script queries to") + flags.BoolVar(&builder.rpcConf.BackendConfig.ScriptExecValidation, "validate-rn-script-exec", defaultConfig.rpcConf.BackendConfig.ScriptExecValidation, "whether to validate script execution results from the archive node with results from the execution node") flags.StringVarP(&builder.rpcConf.HistoricalAccessAddrs, "historical-access-addr", "", defaultConfig.rpcConf.HistoricalAccessAddrs, "comma separated rpc addresses for historical access nodes") flags.DurationVar(&builder.rpcConf.BackendConfig.CollectionClientTimeout, "collection-client-timeout", defaultConfig.rpcConf.BackendConfig.CollectionClientTimeout, "grpc client timeout for a collection node") flags.DurationVar(&builder.rpcConf.BackendConfig.ExecutionClientTimeout, "execution-client-timeout", defaultConfig.rpcConf.BackendConfig.ExecutionClientTimeout, "grpc client timeout for an execution node") @@ -1109,6 +1111,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { node.Logger, backend.DefaultSnapshotHistoryLimit, backendConfig.ArchiveAddressList, + backendConfig.ScriptExecValidation, backendConfig.CircuitBreakerConfig.Enabled) engineBuilder, err := rpc.NewBuilder( diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 1e7687578c2..a8e7abdd4b6 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -946,6 +946,7 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { node.Logger, backend.DefaultSnapshotHistoryLimit, backendConfig.ArchiveAddressList, + backendConfig.ScriptExecValidation, backendConfig.CircuitBreakerConfig.Enabled) observerCollector := metrics.NewObserverCollector() diff --git a/engine/access/access_test.go b/engine/access/access_test.go index b3979979fb9..75a69892389 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -158,6 +158,7 @@ func (suite *Suite) RunTest( backend.DefaultSnapshotHistoryLimit, nil, false, + false, ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me, access.WithBlockSignerDecoder(suite.signerIndicesDecoder)) f(handler, db, all) @@ -331,6 +332,7 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { backend.DefaultSnapshotHistoryLimit, nil, false, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -658,6 +660,7 @@ func (suite *Suite) TestGetSealedTransaction() { backend.DefaultSnapshotHistoryLimit, nil, false, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -798,6 +801,7 @@ func (suite *Suite) TestGetTransactionResult() { backend.DefaultSnapshotHistoryLimit, nil, false, + false, ) handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) @@ -990,6 +994,7 @@ func (suite *Suite) TestExecuteScript() { backend.DefaultSnapshotHistoryLimit, nil, false, + false, ) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index e2cf78ade5a..b5660eece2d 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -189,7 +189,9 @@ func (suite *SameGRPCPortTestSuite) SetupTest() { suite.log, 0, nil, - false) + false, + false, + ) // create rpc engine builder rpcEngBuilder, err := rpc.NewBuilder( diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 091c5e2e3ad..0f7a9058dc4 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -170,7 +170,9 @@ func (suite *RestAPITestSuite) SetupTest() { suite.log, 0, nil, - false) + false, + false, + ) rpcEngBuilder, err := rpc.NewBuilder( suite.log, diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 9b10cc5b539..9c53aada9f6 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -80,13 +80,14 @@ type Backend struct { // Config defines the configurable options for creating Backend type Config struct { - ExecutionClientTimeout time.Duration // execution API GRPC client timeout - CollectionClientTimeout time.Duration // collection API GRPC client timeout - ConnectionPoolSize uint // size of the cache for storing collection and execution connections - MaxHeightRange uint // max size of height range requests - PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs - FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node ID can be chosen from the PreferredExecutionNodeIDs - ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node + ExecutionClientTimeout time.Duration // execution API GRPC client timeout + CollectionClientTimeout time.Duration // collection API GRPC client timeout + ConnectionPoolSize uint // size of the cache for storing collection and execution connections + MaxHeightRange uint // max size of height range requests + PreferredExecutionNodeIDs []string // preferred list of upstream execution node IDs + FixedExecutionNodeIDs []string // fixed list of execution node IDs to choose from if no node ID can be chosen from the PreferredExecutionNodeIDs + ArchiveAddressList []string // the archive node address list to send script executions. when configured, script executions will be all sent to the archive node + ScriptExecValidation bool CircuitBreakerConfig connection.CircuitBreakerConfig // the configuration for circuit breaker } @@ -110,6 +111,7 @@ func New( log zerolog.Logger, snapshotHistoryLimit int, archiveAddressList []string, + scriptExecValidation bool, circuitBreakerEnabled bool, ) *Backend { retry := newRetry() @@ -138,16 +140,17 @@ func New( state: state, // create the sub-backends backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, - loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, - archivePorts: archivePorts, - nodeCommunicator: nodeCommunicator, + headers: headers, + executionReceipts: executionReceipts, + connFactory: connFactory, + state: state, + log: log, + metrics: accessMetrics, + loggedScripts: loggedScripts, + archiveAddressList: archiveAddressList, + archivePorts: archivePorts, + scriptExecValidation: scriptExecValidation, + nodeCommunicator: nodeCommunicator, }, backendTransactions: backendTransactions{ staticCollectionRPC: collectionRPC, diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 62d32c56211..2d875a460ea 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -1,14 +1,15 @@ package backend import ( + "bytes" "context" "crypto/md5" //nolint:gosec "io" "time" + "github.com/hashicorp/go-multierror" lru "github.com/hashicorp/golang-lru" "github.com/onflow/flow/protobuf/go/flow/access" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "google.golang.org/grpc/codes" @@ -26,16 +27,17 @@ import ( const uniqueScriptLoggingTimeWindow = 10 * time.Minute type backendScripts struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory connection.ConnectionFactory - log zerolog.Logger - metrics module.BackendScriptsMetrics - loggedScripts *lru.Cache - archiveAddressList []string - archivePorts []uint - nodeCommunicator *NodeCommunicator + headers storage.Headers + executionReceipts storage.ExecutionReceipts + state protocol.State + connFactory connection.ConnectionFactory + log zerolog.Logger + metrics module.BackendScriptsMetrics + loggedScripts *lru.Cache + archiveAddressList []string + archivePorts []uint + scriptExecValidation bool + nodeCommunicator *NodeCommunicator } func (b *backendScripts) ExecuteScriptAtLatestBlock( @@ -86,6 +88,10 @@ func (b *backendScripts) ExecuteScriptAtBlockHeight( return b.executeScriptOnExecutor(ctx, blockID, script, arguments) } +func isCadenceScriptError(scriptExecutionErr error) bool { + return scriptExecutionErr == nil || status.Code(scriptExecutionErr) == codes.InvalidArgument +} + // executeScriptOnExecutionNode forwards the request to the execution node using the execution node // grpc client and converts the response back to the access node api response format func (b *backendScripts) executeScriptOnExecutor( @@ -94,17 +100,100 @@ func (b *backendScripts) executeScriptOnExecutor( script []byte, arguments [][]byte, ) ([]byte, error) { - // find few execution nodes which have executed the block earlier and provided an execution receipt for it - executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to find script executors at blockId %v: %v", blockID.String(), err) - } // encode to MD5 as low compute/memory lookup key // CAUTION: cryptographically insecure md5 is used here, but only to de-duplicate logs. // *DO NOT* use this hash for any protocol-related or cryptographic functions. insecureScriptHash := md5.Sum(script) //nolint:gosec + // try execution nodes if the script wasn't executed + if b.scriptExecValidation { + execNodeResult, execErr := b.executeScriptOnAvailableExecutionNodes( + ctx, blockID, script, arguments, insecureScriptHash) + // we can only compare the results if there are no errors from RN and EN + // since we cannot distinguish the EN error as caused by the block being pruned or some other reason, + // which may produce a valid RN output but an error for the EN + if isCadenceScriptError(execErr) { + archiveResult, archiveErr := b.executeScriptOnAvailableArchiveNodes(ctx, blockID, script, arguments, + insecureScriptHash) + // return EN results by default + b.compareScriptExecutionResults(execNodeResult, execErr, archiveResult, archiveErr, blockID, + script) + return execNodeResult, execErr + } + return execNodeResult, execErr + } + archiveResult, archiveErr := b.executeScriptOnAvailableArchiveNodes(ctx, blockID, script, arguments, + insecureScriptHash) + // execute on execution nodes if it's not a script error + if !isCadenceScriptError(archiveErr) { + execNodeResult, err := b.executeScriptOnAvailableExecutionNodes( + ctx, blockID, script, arguments, insecureScriptHash) + return execNodeResult, err + } + return archiveResult, archiveErr +} + +func (b *backendScripts) compareScriptExecutionResults( + execNodeResult []byte, + execErr error, + archiveResult []byte, + archiveErr error, + blockID flow.Identifier, + script []byte, +) { + // check errors first + if execErr != nil { + if archiveErr != nil && execErr == archiveErr { + b.metrics.ScriptExecutionErrorMatch() + } else { + b.metrics.ScriptExecutionErrorMismatch() + b.logScriptExecutionComparison(blockID, script, execNodeResult, archiveResult, execErr, archiveErr, + "cadence errors on Archive node and EN are not equal") + } + return + } + if bytes.Equal(execNodeResult, archiveResult) { + b.metrics.ScriptExecutionResultMatch() + } else { + b.metrics.ScriptExecutionResultMismatch() + b.logScriptExecutionComparison(blockID, script, execNodeResult, archiveResult, execErr, archiveErr, + "script execution results on Archive node and EN are not equal") + } +} + +func (b *backendScripts) logScriptExecutionComparison( + blockID flow.Identifier, + script []byte, + execNodeResult []byte, + archiveResult []byte, + executionError error, + archiveError error, + msg string, +) { + // over-log for ease of debug + if executionError != nil || archiveError != nil { + b.log.Debug().Hex("block_id", blockID[:]). + Str("script", string(script)). + AnErr("execution_node_error", executionError). + AnErr("archive_node_error", archiveError). + Msg(msg) + } else { + b.log.Debug().Hex("block_id", blockID[:]). + Str("script", string(script)). + Hex("execution_node_result", execNodeResult). + Hex("archive_node_result", archiveResult). + Msg(msg) + } +} - // try execution on Archive nodes +// executeScriptOnAvailableArchiveNodes executes the given script for a blockID on all archive nodes available +func (b *backendScripts) executeScriptOnAvailableArchiveNodes( + ctx context.Context, + blockID flow.Identifier, + script []byte, + arguments [][]byte, + insecureScriptHash [16]byte, +) ([]byte, error) { + var errors *multierror.Error if len(b.archiveAddressList) > 0 { startTime := time.Now() for idx, rnAddr := range b.archiveAddressList { @@ -133,14 +222,33 @@ func (b *backendScripts) executeScriptOnExecutor( // failures due to unavailable blocks are explicitly marked Not found b.metrics.ScriptExecutionErrorOnArchiveNode() b.log.Error().Err(err).Msg("script execution failed for archive node") + return nil, err default: + errors = multierror.Append(errors, err) continue } } } } + // don't need to distinguish error codes at this point + if errors.ErrorOrNil() != nil { + return nil, rpc.ConvertMultiError(errors, "failed to execute script on archive nodes", codes.Internal) + } + return nil, status.Errorf(codes.Unavailable, "no archive nodes in address list") +} - // try to execute the script on one of the execution nodes found +func (b *backendScripts) executeScriptOnAvailableExecutionNodes( + ctx context.Context, + blockID flow.Identifier, + script []byte, + arguments [][]byte, + insecureScriptHash [16]byte, +) ([]byte, error) { + // find few execution nodes which have executed the block earlier and provided an execution receipt for it + executors, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to find script executors at blockId %v: %v", blockID.String(), err) + } var result []byte hasInvalidArgument := false errToReturn := b.nodeCommunicator.CallAvailableNode( diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index d40ff45890e..9c87d88a4e4 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -111,6 +111,7 @@ func (suite *Suite) TestPing() { DefaultSnapshotHistoryLimit, nil, false, + false, ) err := backend.Ping(context.Background()) @@ -147,6 +148,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized block @@ -213,6 +215,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized snapshot @@ -286,6 +289,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized snapshot @@ -352,6 +356,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized snapshot @@ -429,6 +434,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized snapshot @@ -490,6 +496,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { snapshotHistoryLimit, nil, false, + false, ) // the handler should return a snapshot history limit error @@ -529,6 +536,7 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest sealed block @@ -576,6 +584,7 @@ func (suite *Suite) TestGetTransaction() { DefaultSnapshotHistoryLimit, nil, false, + false, ) actual, err := backend.GetTransaction(context.Background(), transaction.ID()) @@ -617,6 +626,7 @@ func (suite *Suite) TestGetCollection() { DefaultSnapshotHistoryLimit, nil, false, + false, ) actual, err := backend.GetCollectionByID(context.Background(), expected.ID()) @@ -681,6 +691,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { DefaultSnapshotHistoryLimit, nil, false, + false, ) suite.execClient. On("GetTransactionResultByIndex", ctx, exeEventReq). @@ -745,6 +756,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { DefaultSnapshotHistoryLimit, nil, false, + false, ) suite.execClient. On("GetTransactionResultsByBlockID", ctx, exeEventReq). @@ -837,6 +849,7 @@ func (suite *Suite) TestTransactionStatusTransition() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // Successfully return empty event list @@ -958,6 +971,7 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // should return pending status when we have not observed an expiry block @@ -1126,6 +1140,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { DefaultSnapshotHistoryLimit, nil, false, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -1185,6 +1200,7 @@ func (suite *Suite) TestTransactionResultUnknown() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx @@ -1240,6 +1256,7 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // query the handler for the latest finalized header @@ -1371,6 +1388,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1404,6 +1422,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request with an empty block id list and expect an empty list of events and no error @@ -1464,6 +1483,7 @@ func (suite *Suite) TestGetExecutionResultByID() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1495,6 +1515,7 @@ func (suite *Suite) TestGetExecutionResultByID() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1559,6 +1580,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1591,6 +1613,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1742,6 +1765,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { DefaultSnapshotHistoryLimit, nil, false, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), maxHeight, minHeight) @@ -1782,6 +1806,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // execute request @@ -1821,6 +1846,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { DefaultSnapshotHistoryLimit, nil, false, + false, ) actualResp, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1859,6 +1885,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { DefaultSnapshotHistoryLimit, nil, false, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, minHeight+1) @@ -1897,6 +1924,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { DefaultSnapshotHistoryLimit, nil, false, + false, ) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1975,6 +2003,7 @@ func (suite *Suite) TestGetAccount() { DefaultSnapshotHistoryLimit, nil, false, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2057,6 +2086,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { DefaultSnapshotHistoryLimit, nil, false, + false, ) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2097,6 +2127,7 @@ func (suite *Suite) TestGetNetworkParameters() { DefaultSnapshotHistoryLimit, nil, false, + false, ) params := backend.GetNetworkParameters(context.Background()) @@ -2300,6 +2331,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // mock parameters @@ -2377,6 +2409,7 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { DefaultSnapshotHistoryLimit, []string{fullArchiveAddress}, false, + false, ) // mock parameters @@ -2426,6 +2459,124 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { }) } +// TestExecuteScriptOnArchiveNode tests the method backend.scripts.executeScriptOnArchiveNode for script execution +func (suite *Suite) TestScriptExecutionValidationMode() { + + // create a mock connection factory + var mockPort uint = 9000 + connFactory := new(backendmock.ConnectionFactory) + connFactory.On("GetAccessAPIClientWithPort", mock.Anything, mockPort).Return(suite.archiveClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("InvalidateAccessAPIClient", mock.Anything) + archiveNode := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) + fullArchiveAddress := archiveNode.Address + ":" + strconv.FormatUint(uint64(mockPort), 10) + // create the handler with the mock + backend := New( + suite.state, + nil, + nil, + nil, + suite.headers, + nil, + nil, + suite.receipts, + suite.results, + flow.Mainnet, + metrics.NewNoopCollector(), + connFactory, // the connection factory should be used to get the execution node client + false, + DefaultMaxHeightRange, + nil, + nil, + suite.log, + DefaultSnapshotHistoryLimit, + []string{fullArchiveAddress}, + true, + false, + ) + + // mock parameters + ctx := context.Background() + block := unittest.BlockFixture() + blockID := block.ID() + _, ids := suite.setupReceipts(&block) + suite.state.On("Final").Return(suite.snapshot, nil).Maybe() + suite.snapshot.On("Identities", mock.Anything).Return(ids, nil) + suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) + + script := []byte("dummy script") + arguments := [][]byte(nil) + archiveRes := &accessproto.ExecuteScriptResponse{Value: []byte{4, 5, 6}} + archiveReq := &accessproto.ExecuteScriptAtBlockIDRequest{ + BlockId: blockID[:], + Script: script, + Arguments: arguments} + + archiveBlockUnavailableErr := status.Error(codes.NotFound, "placeholder block error") + archiveCadenceErr := status.Error(codes.InvalidArgument, "placeholder cadence error") + internalErr := status.Error(codes.Internal, "placeholder internal error") + + execReq := &execproto.ExecuteScriptAtBlockIDRequest{ + BlockId: blockID[:], + Script: script, + Arguments: arguments} + matchingExecRes := &execproto.ExecuteScriptAtBlockIDResponse{Value: []byte{4, 5, 6}} + mismatchingExecRes := &execproto.ExecuteScriptAtBlockIDResponse{Value: []byte{1, 2, 3}} + + suite.Run("happy path script execution success both en and rn return responses", func() { + suite.archiveClient.On("ExecuteScriptAtBlockID", ctx, archiveReq).Return(archiveRes, nil).Once() + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(matchingExecRes, nil).Once() + res, err := backend.executeScriptOnExecutor(ctx, blockID, script, arguments) + suite.archiveClient.AssertExpectations(suite.T()) + suite.checkResponse(res, err) + assert.Equal(suite.T(), res, matchingExecRes.Value) + }) + + suite.Run("script execution success but mismatching responses", func() { + suite.archiveClient.On("ExecuteScriptAtBlockID", ctx, archiveReq).Return(archiveRes, nil).Once() + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(mismatchingExecRes, nil).Once() + res, err := backend.executeScriptOnExecutor(ctx, blockID, script, arguments) + suite.archiveClient.AssertExpectations(suite.T()) + suite.checkResponse(res, err) + suite.Require().Equal(res, mismatchingExecRes.Value) + }) + + suite.Run("script execution failure on both nodes", func() { + suite.archiveClient.On("ExecuteScriptAtBlockID", ctx, archiveReq).Return(nil, archiveCadenceErr).Once() + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(nil, archiveCadenceErr).Once() + _, err := backend.executeScriptOnExecutor(ctx, blockID, script, arguments) + suite.archiveClient.AssertExpectations(suite.T()) + suite.Require().Error(err) + suite.Require().Equal(status.Code(err), codes.InvalidArgument) + }) + + suite.Run("script execution failure on rn but not en", func() { + suite.archiveClient.On("ExecuteScriptAtBlockID", ctx, archiveReq).Return( + nil, archiveCadenceErr).Once() + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(matchingExecRes, nil).Once() + _, err := backend.executeScriptOnExecutor(ctx, blockID, script, arguments) + suite.Require().NoError(err) + suite.archiveClient.AssertExpectations(suite.T()) + }) + + suite.Run("block not found on rn", func() { + suite.archiveClient.On("ExecuteScriptAtBlockID", ctx, archiveReq).Return( + nil, archiveBlockUnavailableErr).Once() + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(matchingExecRes, nil).Once() + _, err := backend.ExecuteScriptAtBlockID(ctx, blockID, script, arguments) + suite.Require().NoError(err) + suite.archiveClient.AssertExpectations(suite.T()) + }) + + suite.Run("block not found on en", func() { + suite.execClient.On("ExecuteScriptAtBlockID", ctx, execReq).Return(nil, internalErr). + Times(int(ids.Count())) + _, err := backend.ExecuteScriptAtBlockID(ctx, blockID, script, arguments) + suite.archiveClient.AssertExpectations(suite.T()) + suite.Require().Error(err) + }) +} + func (suite *Suite) assertAllExpectations() { suite.snapshot.AssertExpectations(suite.T()) suite.state.AssertExpectations(suite.T()) diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 42dd829dbbc..2632d216792 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -57,6 +57,7 @@ func (suite *Suite) TestHistoricalTransactionResult() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // Successfully return the transaction from the historical node @@ -116,6 +117,7 @@ func (suite *Suite) TestHistoricalTransaction() { DefaultSnapshotHistoryLimit, nil, false, + false, ) // Successfully return the transaction from the historical node diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 2189223118a..07bb77aa8a9 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -62,6 +62,7 @@ func (suite *Suite) TestTransactionRetry() { DefaultSnapshotHistoryLimit, nil, false, + false, ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry @@ -152,6 +153,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { DefaultSnapshotHistoryLimit, nil, false, + false, ) retry := newRetry().SetBackend(backend).Activate() backend.retry = retry diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index 8ff5695c3c6..b4b1d1e6980 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -168,7 +168,9 @@ func (suite *RateLimitTestSuite) SetupTest() { suite.log, 0, nil, - false) + false, + false, + ) rpcEngBuilder, err := NewBuilder( suite.log, diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index 783ed0d3110..5e122951eb0 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -151,7 +151,9 @@ func (suite *SecureGRPCTestSuite) SetupTest() { suite.log, 0, nil, - false) + false, + false, + ) rpcEngBuilder, err := rpc.NewBuilder( suite.log, diff --git a/module/metrics.go b/module/metrics.go index 2b889b98c44..3c1d6dfdc37 100644 --- a/module/metrics.go +++ b/module/metrics.go @@ -751,6 +751,14 @@ type BackendScriptsMetrics interface { // ScriptExecutionErrorOnArchiveNode records script execution failures in Archive Nodes ScriptExecutionErrorOnExecutionNode() + + ScriptExecutionResultMismatch() + + ScriptExecutionResultMatch() + + ScriptExecutionErrorMismatch() + + ScriptExecutionErrorMatch() } type TransactionMetrics interface { diff --git a/module/metrics/noop.go b/module/metrics/noop.go index 9bf5be48f0d..1d48d16882a 100644 --- a/module/metrics/noop.go +++ b/module/metrics/noop.go @@ -199,6 +199,10 @@ func (nc *NoopCollector) RuntimeTransactionProgramsCacheHit() func (nc *NoopCollector) ScriptExecuted(dur time.Duration, size int) {} func (nc *NoopCollector) ScriptExecutionErrorOnArchiveNode() {} func (nc *NoopCollector) ScriptExecutionErrorOnExecutionNode() {} +func (nc *NoopCollector) ScriptExecutionResultMismatch() {} +func (nc *NoopCollector) ScriptExecutionResultMatch() {} +func (nc *NoopCollector) ScriptExecutionErrorMismatch() {} +func (nc *NoopCollector) ScriptExecutionErrorMatch() {} func (nc *NoopCollector) TransactionResultFetched(dur time.Duration, size int) {} func (nc *NoopCollector) TransactionReceived(txID flow.Identifier, when time.Time) {} func (nc *NoopCollector) TransactionFinalized(txID flow.Identifier, when time.Time) {} diff --git a/module/metrics/transaction.go b/module/metrics/transaction.go index bbcb50414bd..50fca53bf39 100644 --- a/module/metrics/transaction.go +++ b/module/metrics/transaction.go @@ -25,6 +25,7 @@ type TransactionCollector struct { transactionSize prometheus.Histogram scriptExecutedDuration *prometheus.HistogramVec scriptExecutionErrorOnExecutor *prometheus.CounterVec + scriptExecutionComparison *prometheus.CounterVec scriptSize prometheus.Histogram transactionResultDuration *prometheus.HistogramVec } @@ -109,6 +110,12 @@ func NewTransactionCollector( Subsystem: subsystemTransactionSubmission, Help: "histogram for the internal errors for executing a script for a block on the archive node", }, []string{"source"}), + scriptExecutionComparison: promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "script_execution_comparison", + Namespace: namespaceAccess, + Subsystem: subsystemTransactionSubmission, + Help: "histogram for the comparison outcomes of executing a script on the archive and execution node", + }, []string{"outcome"}), transactionResultDuration: promauto.NewHistogramVec(prometheus.HistogramOpts{ Name: "transaction_result_fetched_duration", Namespace: namespaceAccess, @@ -144,15 +151,34 @@ func (tc *TransactionCollector) ScriptExecuted(dur time.Duration, size int) { } func (tc *TransactionCollector) ScriptExecutionErrorOnArchiveNode() { - // record the execution error along with blockID and scriptHash for Archive node + // record the execution error count tc.scriptExecutionErrorOnExecutor.WithLabelValues("archive").Inc() } func (tc *TransactionCollector) ScriptExecutionErrorOnExecutionNode() { - // record the execution error along with blockID and scriptHash for Execution node + // record the execution error count tc.scriptExecutionErrorOnExecutor.WithLabelValues("execution").Inc() } +func (tc *TransactionCollector) ScriptExecutionResultMismatch() { + // record the execution error count + tc.scriptExecutionComparison.WithLabelValues("result_mismatch").Inc() +} + +func (tc *TransactionCollector) ScriptExecutionResultMatch() { + // record the execution error count + tc.scriptExecutionComparison.WithLabelValues("result_match").Inc() +} +func (tc *TransactionCollector) ScriptExecutionErrorMismatch() { + // record the execution error count + tc.scriptExecutionComparison.WithLabelValues("error_mismatch").Inc() +} + +func (tc *TransactionCollector) ScriptExecutionErrorMatch() { + // record the execution error count + tc.scriptExecutionComparison.WithLabelValues("error_match").Inc() +} + // TransactionResult metrics func (tc *TransactionCollector) TransactionResultFetched(dur time.Duration, size int) { diff --git a/module/mock/access_metrics.go b/module/mock/access_metrics.go index 83690a36625..cde9953582c 100644 --- a/module/mock/access_metrics.go +++ b/module/mock/access_metrics.go @@ -73,6 +73,16 @@ func (_m *AccessMetrics) ScriptExecuted(dur time.Duration, size int) { _m.Called(dur, size) } +// ScriptExecutionErrorMatch provides a mock function with given fields: +func (_m *AccessMetrics) ScriptExecutionErrorMatch() { + _m.Called() +} + +// ScriptExecutionErrorMismatch provides a mock function with given fields: +func (_m *AccessMetrics) ScriptExecutionErrorMismatch() { + _m.Called() +} + // ScriptExecutionErrorOnArchiveNode provides a mock function with given fields: func (_m *AccessMetrics) ScriptExecutionErrorOnArchiveNode() { _m.Called() @@ -83,6 +93,16 @@ func (_m *AccessMetrics) ScriptExecutionErrorOnExecutionNode() { _m.Called() } +// ScriptExecutionResultMatch provides a mock function with given fields: +func (_m *AccessMetrics) ScriptExecutionResultMatch() { + _m.Called() +} + +// ScriptExecutionResultMismatch provides a mock function with given fields: +func (_m *AccessMetrics) ScriptExecutionResultMismatch() { + _m.Called() +} + // TotalConnectionsInPool provides a mock function with given fields: connectionCount, connectionPoolSize func (_m *AccessMetrics) TotalConnectionsInPool(connectionCount uint, connectionPoolSize uint) { _m.Called(connectionCount, connectionPoolSize) diff --git a/module/mock/backend_scripts_metrics.go b/module/mock/backend_scripts_metrics.go index 8a4eaf0ab33..60c3fe9e06d 100644 --- a/module/mock/backend_scripts_metrics.go +++ b/module/mock/backend_scripts_metrics.go @@ -18,6 +18,16 @@ func (_m *BackendScriptsMetrics) ScriptExecuted(dur time.Duration, size int) { _m.Called(dur, size) } +// ScriptExecutionErrorMatch provides a mock function with given fields: +func (_m *BackendScriptsMetrics) ScriptExecutionErrorMatch() { + _m.Called() +} + +// ScriptExecutionErrorMismatch provides a mock function with given fields: +func (_m *BackendScriptsMetrics) ScriptExecutionErrorMismatch() { + _m.Called() +} + // ScriptExecutionErrorOnArchiveNode provides a mock function with given fields: func (_m *BackendScriptsMetrics) ScriptExecutionErrorOnArchiveNode() { _m.Called() @@ -28,6 +38,16 @@ func (_m *BackendScriptsMetrics) ScriptExecutionErrorOnExecutionNode() { _m.Called() } +// ScriptExecutionResultMatch provides a mock function with given fields: +func (_m *BackendScriptsMetrics) ScriptExecutionResultMatch() { + _m.Called() +} + +// ScriptExecutionResultMismatch provides a mock function with given fields: +func (_m *BackendScriptsMetrics) ScriptExecutionResultMismatch() { + _m.Called() +} + type mockConstructorTestingTNewBackendScriptsMetrics interface { mock.TestingT Cleanup(func()) From 985b558a2a22fc2952073ee55781ea49518da8a0 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 8 Aug 2023 14:33:21 +0100 Subject: [PATCH 398/815] Fix goimports --- engine/access/access_test.go | 19 +++++++-------- .../integration_unsecure_grpc_server_test.go | 19 +++++++-------- engine/access/rest_api_test.go | 15 ++++++------ engine/access/rpc/backend/backend.go | 9 ++++---- engine/access/rpc/backend/backend_scripts.go | 13 ++++++----- engine/access/rpc/backend/backend_test.go | 21 +++++++++-------- .../rpc/backend/backend_transactions_test.go | 13 ++++++----- .../rpc/backend/historical_access_test.go | 9 ++++---- engine/access/rpc/backend/retry_test.go | 11 +++++---- engine/access/rpc/rate_limit_test.go | 23 ++++++++++--------- engine/access/secure_grpcr_test.go | 17 +++++++------- 11 files changed, 90 insertions(+), 79 deletions(-) diff --git a/engine/access/access_test.go b/engine/access/access_test.go index d4ef365654d..8fdf13cf5c7 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -8,6 +8,16 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/google/go-cmp/cmp" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/protobuf/testing/protocmp" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" hsmock "github.com/onflow/flow-go/consensus/hotstuff/mocks" @@ -36,15 +46,6 @@ import ( "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow-go/utils/unittest/mocks" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/protobuf/testing/protocmp" ) type Suite struct { diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index fc0e3c8f180..520da185aad 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -7,6 +7,16 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "github.com/onflow/flow-go/engine" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -27,15 +37,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) // SameGRPCPortTestSuite verifies both AccessAPI and ExecutionDataAPI client continue to work when configured diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index d85a98c0995..7ca1ef0c0f3 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -11,6 +11,14 @@ import ( "time" "github.com/antihax/optional" + restclient "github.com/onflow/flow/openapi/go-client-generated" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/credentials" + accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest/request" "github.com/onflow/flow-go/engine/access/rest/routes" @@ -27,13 +35,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - restclient "github.com/onflow/flow/openapi/go-client-generated" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/credentials" ) // RestAPITestSuite tests that the Access node serves the REST API defined via the OpenApi spec accurately diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index edeca15093d..48042d34da8 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -9,6 +9,11 @@ import ( lru "github.com/hashicorp/golang-lru" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -20,10 +25,6 @@ import ( "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index a2d411cafa5..b73aef9ce43 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -1,25 +1,26 @@ package backend import ( - "crypto/md5" //nolint:gosec "bytes" "context" + "crypto/md5" //nolint:gosec "io" "time" "github.com/hashicorp/go-multierror" lru "github.com/hashicorp/golang-lru" + "github.com/onflow/flow/protobuf/go/flow/access" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "github.com/onflow/flow/protobuf/go/flow/access" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // uniqueScriptLoggingTimeWindow is the duration for checking the uniqueness of scripts sent for execution diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index f564e24bb46..8c438a97897 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -7,6 +7,17 @@ import ( "testing" "github.com/dgraph-io/badger/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -19,16 +30,6 @@ import ( "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type Suite struct { diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 36a516a67f8..332e03a43be 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,6 +5,13 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/state/protocol" @@ -12,12 +19,6 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index b3a0fd91156..95496ab67c7 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -3,14 +3,15 @@ package backend import ( "context" - "github.com/onflow/flow-go/engine/common/rpc/convert" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/utils/unittest" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/entities" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/onflow/flow-go/engine/common/rpc/convert" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/utils/unittest" ) // TestHistoricalTransactionResult tests to see if the historical transaction status can be retrieved diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index e232c4f1c76..3f988fb2b54 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -3,16 +3,17 @@ package backend import ( "context" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - protocol "github.com/onflow/flow-go/state/protocol/mock" - realstorage "github.com/onflow/flow-go/storage" - "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/stretchr/testify/mock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + protocol "github.com/onflow/flow-go/state/protocol/mock" + realstorage "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/unittest" ) // TestTransactionRetry tests that the retry mechanism will send retries at specific times diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index 27b76955ccc..5777a4b3aee 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -8,6 +8,18 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc/backend" "github.com/onflow/flow-go/model/flow" @@ -20,17 +32,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" ) type RateLimitTestSuite struct { diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index ec5cb41643e..ecc0156b720 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -7,6 +7,15 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "github.com/onflow/flow-go/crypto" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -21,14 +30,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) // SecureGRPCTestSuite tests that Access node provides a secure GRPC server From 9864fd14d7f635942ca528236c2a30151196ccec Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 8 Aug 2023 11:18:45 -0400 Subject: [PATCH 399/815] make -C as separate flag --- .github/workflows/test-monitor-regular-skipped.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index fe02a971875..f3def969fea 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -114,7 +114,7 @@ jobs: env: RACE_DETECTOR: ${{ matrix.race }} TEST_CATEGORY: ${{ matrix.test_category }} - run: make -esC ${{ matrix.name }} ${{ matrix.make2 }} > test-output + run: make -es -C ${{ matrix.name }} ${{ matrix.make2 }} > test-output timeout-minutes: 100 continue-on-error: true - name: Process test results (${{ matrix.name }}) @@ -171,7 +171,7 @@ jobs: - name: Run tests env: TEST_CATEGORY: ${{ matrix.test_category }} - run: make -esC integration ${{ matrix.target }} > test-output + run: make -es -C integration ${{ matrix.target }} > test-output timeout-minutes: 100 continue-on-error: true - name: Process test results From 0b561f58da4e8551270fb55854ea66a2f689a6d5 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Tue, 8 Aug 2023 18:32:35 +0200 Subject: [PATCH 400/815] add comments for system transaction authorizers --- fvm/blueprints/system.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fvm/blueprints/system.go b/fvm/blueprints/system.go index eb1a0a33ba2..398624b8403 100644 --- a/fvm/blueprints/system.go +++ b/fvm/blueprints/system.go @@ -38,6 +38,8 @@ func SystemChunkTransaction(chain flow.Chain) (*flow.TransactionBody, error) { }, )), ). + // The heartbeat resources needed by the system tx have are on the service account, + // therefore, the service account is the only authorizer needed. AddAuthorizer(chain.ServiceAddress()). SetGasLimit(SystemChunkTransactionGasLimit) From 5b4843c9aa1bf57d70a7e3d90e108c45d951a91e Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 8 Aug 2023 12:43:36 -0400 Subject: [PATCH 401/815] ssh debug TEST_CATEGORY not saved --- .../test-monitor-regular-skipped.yml | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index f3def969fea..5066d8f57db 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -89,16 +89,16 @@ jobs: make2: unittest race: 1 test_category: unit-crypto - - name: insecure - make1: install-tools - make2: test - race: 0 - test_category: unit-insecure - - name: integration - make1: install-tools - make2: test - race: 0 - test_category: unit-integration +# - name: insecure +# make1: install-tools +# make2: test +# race: 0 +# test_category: unit-insecure +# - name: integration +# make1: install-tools +# make2: test +# race: 0 +# test_category: unit-integration runs-on: ubuntu-latest steps: - name: Checkout repo @@ -118,9 +118,14 @@ jobs: timeout-minutes: 100 continue-on-error: true - name: Process test results (${{ matrix.name }}) + env: + TEST_CATEGORY: ${{ matrix.test_category }} uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: success() || failure() integration-test: name: Integration Tests @@ -175,6 +180,8 @@ jobs: timeout-minutes: 100 continue-on-error: true - name: Process test results + env: + TEST_CATEGORY: ${{ matrix.test_category }} uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} From 5ebdb11fa112deb89520aa2994e2104b9ed8d07e Mon Sep 17 00:00:00 2001 From: Kay-Zee Date: Tue, 8 Aug 2023 15:36:50 -0700 Subject: [PATCH 402/815] Add merge_group to CI to allow using built in GH merge queue work flow --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e41e747e0e0..c466b118e5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,9 @@ on: - 'auto-cadence-upgrade/**' - 'feature/**' - 'v[0-9]+.[0-9]+' + merge_group: + branches: + - master env: GO_VERSION: "1.20" From 5321bcf5c535b2559e0174eaab722cf3fcf3919a Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 04:41:10 -0400 Subject: [PATCH 403/815] removed matrix.test_category from running tests --- .github/workflows/test-monitor-regular-skipped.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index 5066d8f57db..dafefd99b94 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -113,7 +113,6 @@ jobs: - name: Run tests (${{ matrix.name }}) env: RACE_DETECTOR: ${{ matrix.race }} - TEST_CATEGORY: ${{ matrix.test_category }} run: make -es -C ${{ matrix.name }} ${{ matrix.make2 }} > test-output timeout-minutes: 100 continue-on-error: true From 825b80a8c8052b19ba9352d9ccea0ab61ef3362d Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 05:01:05 -0400 Subject: [PATCH 404/815] debug done; saving TEST_CATEGORY --- .../test-monitor-regular-skipped.yml | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor-regular-skipped.yml index dafefd99b94..f22ed730a20 100644 --- a/.github/workflows/test-monitor-regular-skipped.yml +++ b/.github/workflows/test-monitor-regular-skipped.yml @@ -49,8 +49,6 @@ jobs: unit-test: name: Unit Tests (${{ matrix.targets.name }}) - env: - TEST_CATEGORY: unit needs: create-dynamic-test-matrix strategy: fail-fast: false @@ -74,6 +72,8 @@ jobs: # test run should continue even if there are failed tests continue-on-error: true - name: Process test results + env: + TEST_CATEGORY: unit uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} @@ -89,16 +89,16 @@ jobs: make2: unittest race: 1 test_category: unit-crypto -# - name: insecure -# make1: install-tools -# make2: test -# race: 0 -# test_category: unit-insecure -# - name: integration -# make1: install-tools -# make2: test -# race: 0 -# test_category: unit-integration + - name: insecure + make1: install-tools + make2: test + race: 0 + test_category: unit-insecure + - name: integration + make1: install-tools + make2: test + race: 0 + test_category: unit-integration runs-on: ubuntu-latest steps: - name: Checkout repo @@ -122,9 +122,6 @@ jobs: uses: ./.github/workflows/actions/test-monitor-process-results with: gcp_sa_key: ${{ secrets.GCP_SA_KEY }} - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - if: success() || failure() integration-test: name: Integration Tests @@ -171,10 +168,7 @@ jobs: run: make crypto_setup_gopath - name: Docker build run: make docker-build-flow docker-build-flow-corrupt - - name: Run tests - env: - TEST_CATEGORY: ${{ matrix.test_category }} run: make -es -C integration ${{ matrix.target }} > test-output timeout-minutes: 100 continue-on-error: true From 19251cd14368e4f5c923d248515991a21ad03d08 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 05:23:55 -0400 Subject: [PATCH 405/815] set job run date to UTC --- .../workflows/actions/test-monitor-process-results/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions/test-monitor-process-results/action.yml b/.github/workflows/actions/test-monitor-process-results/action.yml index 723b91ea803..08f1c16ab82 100644 --- a/.github/workflows/actions/test-monitor-process-results/action.yml +++ b/.github/workflows/actions/test-monitor-process-results/action.yml @@ -16,7 +16,7 @@ runs: shell: bash - name: Get job run date id: job_run_date - run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" + run: echo "::set-output name=date::$(TZ=":UTC" date -Iseconds)" shell: bash - name: Process test results run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go From d55c855386521c9bf6eff77b8528aef1fd3b708a Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 07:30:50 -0400 Subject: [PATCH 406/815] renamed workflow file --- .../{test-monitor-regular-skipped.yml => test-monitor.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{test-monitor-regular-skipped.yml => test-monitor.yml} (100%) diff --git a/.github/workflows/test-monitor-regular-skipped.yml b/.github/workflows/test-monitor.yml similarity index 100% rename from .github/workflows/test-monitor-regular-skipped.yml rename to .github/workflows/test-monitor.yml From d8a543f2ff90523de0b0f9637d6dd11a6ac72e39 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 07:33:15 -0400 Subject: [PATCH 407/815] deleted run-tests.sh, flaky CI workflow --- .github/workflows/test-monitor-flaky.yml | 87 ------------------------ tools/test_monitor/run-tests.sh | 63 ----------------- 2 files changed, 150 deletions(-) delete mode 100644 .github/workflows/test-monitor-flaky.yml delete mode 100755 tools/test_monitor/run-tests.sh diff --git a/.github/workflows/test-monitor-flaky.yml b/.github/workflows/test-monitor-flaky.yml deleted file mode 100644 index 442d71c3e07..00000000000 --- a/.github/workflows/test-monitor-flaky.yml +++ /dev/null @@ -1,87 +0,0 @@ -# This workflow runs ALL tests, including all tests that are skipped because they are flaky, as well as all the normal (non-skipped) tests. -# This workflow is run less frequently because running flaky tests is problematic and causes errors. - -name: Test Monitor - Flaky - -on: - schedule: - - cron: '0 */12 * * *' # every 12 hours - push: - paths: - - 'tools/test_monitor/**' - -env: - BIGQUERY_DATASET: production_src_flow_test_metrics - BIGQUERY_TABLE: test_results - GO_VERSION: "1.20" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} - cancel-in-progress: true - -jobs: - flaky-test-run: - name: Test Monitor - Flaky Tests Run - strategy: - fail-fast: false - matrix: - test-category: - - unit - - unit-crypto - - unit-insecure - - unit-integration - - integration-bft - - integration-mvp - - integration-ghost - - integration-network - - integration-epochs - - integration-access - - integration-collection - - integration-consensus - - integration-execution - - integration-verification - env: - TEST_CATEGORY: ${{ matrix.test-category }} - COMMIT_SHA: ${{ github.sha }} - RUN_ID: ${{ github.run_id }} - RESULTS_FILE: test-results - runs-on: ubuntu-latest - steps: - - name: Get job run date - id: job_run_date - run: echo "::set-output name=date::$(TZ=":America/Los_Angeles" date -Iseconds)" - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v0 - with: - service_account_key: ${{ secrets.GCP_SA_KEY }} - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: ${{ env.GO_VERSION }} - - name: Checkout repo - uses: actions/checkout@v2 - with: - ref: ${{ env.COMMIT_SHA }} - - name: Get commit date - id: commit_date - run: echo "::set-output name=date::$(git show --no-patch --no-notes --pretty='%cI' $COMMIT_SHA)" - - name: Run tests - continue-on-error: true - run: ./tools/test_monitor/run-tests.sh - env: - TEST_FLAKY: true - JSON_OUTPUT: true - RACE_DETECTOR: 1 - - name: Print test results - run: cat test-output - - name: Process test results - run: cat test-output | go run tools/test_monitor/level1/process_summary1_results.go - env: - JOB_STARTED: ${{ steps.job_run_date.outputs.date }} - COMMIT_DATE: ${{ steps.commit_date.outputs.date }} - - name: Upload results to BigQuery - uses: nick-fields/retry@v2 - with: - timeout_minutes: 2 - max_attempts: 3 - command: bq load --source_format=NEWLINE_DELIMITED_JSON $BIGQUERY_DATASET.$BIGQUERY_TABLE $RESULTS_FILE tools/test_monitor/schemas/test_results_schema.json diff --git a/tools/test_monitor/run-tests.sh b/tools/test_monitor/run-tests.sh deleted file mode 100755 index 0cbf1383b19..00000000000 --- a/tools/test_monitor/run-tests.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -# This script runs the tests in the category specified by the TEST_CATEGORY environment variable. -# Echo / logging statements send output to standard error to separate that output from test result output -# (which sends output to standard output) which needs to be parsed. - -set -e -shopt -s extglob - -echo "test category (run-tests):" $TEST_CATEGORY>&2 - -# run tests and process results - -if [[ $TEST_CATEGORY =~ integration-(bft|ghost|mvp|network|epochs|access|collection|consensus|execution|verification)$ ]] -then - echo "killing and removing orphaned containers from previous run">&2 - # kill and remove orphaned containers from previous run - containers=$(docker ps -a -q) - - if [ ! -z "$containers" ] - then - docker rm -f $containers > /dev/null - fi - - echo "preparing $TEST_CATEGORY tests">&2 - make crypto_setup_gopath - make docker-build-flow docker-build-flow-corrupt - echo "running $TEST_CATEGORY tests">&2 - make -C integration -s ${BASH_REMATCH[1]}-tests > test-output -else - case $TEST_CATEGORY in - unit) - echo "preparing unit tests">&2 - make install-tools - make verify-mocks - echo "running unit tests">&2 - make -s unittest-main > test-output - ;; - unit-crypto) - echo "preparing crypto unit tests">&2 - make -C crypto setup - echo "running crypto unit tests">&2 - make -C crypto -s unittest > test-output - ;; - unit-insecure) - echo "preparing insecure unit tests">&2 - make install-tools - echo "running insecure unit tests">&2 - make -C insecure -s test > test-output - ;; - unit-integration) - echo "preparing integration unit tests">&2 - make install-tools - echo "running integration unit tests">&2 - make -C integration -s test > test-output - ;; - *) - echo "unrecognized test category (run-tests):" $TEST_CATEGORY>&2 - exit 1 - ;; - esac -fi - From b4be5093497437f376859e72a7860d6d8f3e1203 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 07:37:48 -0400 Subject: [PATCH 408/815] updated workflow name, description --- .github/workflows/test-monitor.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-monitor.yml b/.github/workflows/test-monitor.yml index f22ed730a20..0457b5d1ffe 100644 --- a/.github/workflows/test-monitor.yml +++ b/.github/workflows/test-monitor.yml @@ -1,8 +1,6 @@ -# This workflow -# 1) runs all non-skipped (regular) tests and generates a summary. -# 2) generates a report of all skipped tests (e.g. due to flakiness). +# This workflow runs all skipped (flaky) and non-skipped (regular) tests and generates a summary. -name: Test Monitor - Regular and Skipped +name: Test Monitor - Regular and Flaky (Skipped) on: schedule: From 25863781a16b62f3c8107a6a362837a3a77bac23 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 9 Aug 2023 10:35:24 -0700 Subject: [PATCH 409/815] Update integration/localnet/Makefile Co-authored-by: Janez Podhostnik <67895329+janezpodhostnik@users.noreply.github.com> --- integration/localnet/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/localnet/Makefile b/integration/localnet/Makefile index e5f882882a4..a2a035f4a93 100644 --- a/integration/localnet/Makefile +++ b/integration/localnet/Makefile @@ -25,7 +25,7 @@ COMMIT=$(shell git rev-parse HEAD) # The version to include in container builds. Must be semver compliant ifeq ($(VERSION),) - VERSION := $(shell git describe --tags --abbrev=2 --match "v*" --match "secure-cadence*" 2>/dev/null)-localnetbuild + VERSION := $(shell git describe --tags --abbrev=2 --match "v*" 2>/dev/null)-localnetbuild endif CURRENT_DIRECTORY=$(shell pwd) From 974528a260ef43f978c647ceca5f6e6946eb29da Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 13:43:58 -0400 Subject: [PATCH 410/815] run on push to test-monitor.yml --- .github/workflows/test-monitor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-monitor.yml b/.github/workflows/test-monitor.yml index 0457b5d1ffe..63730398307 100644 --- a/.github/workflows/test-monitor.yml +++ b/.github/workflows/test-monitor.yml @@ -7,8 +7,7 @@ on: - cron: '0 */2 * * *' # every 2 hours push: paths: -# temp: for testing -# - 'tools/test_monitor/**' + - '.github/workflows/test-monitor.yml' env: BIGQUERY_DATASET: production_src_flow_test_metrics From 4d05b1769742981a070f1c20b9f346348baa0e2e Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 9 Aug 2023 13:47:50 -0400 Subject: [PATCH 411/815] renamed to Flaky Test Monitor --- .../workflows/{test-monitor.yml => flaky-test-monitor.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{test-monitor.yml => flaky-test-monitor.yml} (98%) diff --git a/.github/workflows/test-monitor.yml b/.github/workflows/flaky-test-monitor.yml similarity index 98% rename from .github/workflows/test-monitor.yml rename to .github/workflows/flaky-test-monitor.yml index 63730398307..21a99c21088 100644 --- a/.github/workflows/test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -1,6 +1,6 @@ -# This workflow runs all skipped (flaky) and non-skipped (regular) tests and generates a summary. +# This workflow runs all skipped (flaky) and non-skipped (regular) tests and generates a summary that's uploaded to BigQuery. -name: Test Monitor - Regular and Flaky (Skipped) +name: Flaky Test Monitor on: schedule: From 67e6d1293cbbd10ddf94ed0415106fb473e245d4 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:23:30 -0700 Subject: [PATCH 412/815] [Access] Update default max message size to 1GiB --- utils/grpcutils/grpc.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/grpcutils/grpc.go b/utils/grpcutils/grpc.go index 3167b025dff..c1e8897f2e1 100644 --- a/utils/grpcutils/grpc.go +++ b/utils/grpcutils/grpc.go @@ -13,9 +13,10 @@ import ( "github.com/onflow/flow-go/network/p2p/keyutils" ) -// DefaultMaxMsgSize use 20MB as the default message size limit. -// grpc library default is 4MB -const DefaultMaxMsgSize = 1024 * 1024 * 20 +// DefaultMaxMsgSize use 1 GiB as the default message size limit. +// This enforces a sane max message size, while still allowing for reasonably large messages. +// grpc library default is 4 MiB. +const DefaultMaxMsgSize = 1 << (10 * 3) // 1 GiB // CertificateConfig is used to configure an Certificate type CertificateConfig struct { From 7009b68ebfba4bf044ad0ba67aa1c0a253cf0a95 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:53:09 -0700 Subject: [PATCH 413/815] [Access] Make REST server timeouts configurable --- .../node_builder/access_node_builder.go | 13 +++++++-- cmd/observer/node_builder/observer_builder.go | 13 +++++++-- engine/access/rest/server.go | 28 +++++++++++++++---- engine/access/rest_api_test.go | 5 +++- engine/access/rpc/engine.go | 11 ++++---- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 55fd48ace8e..6ca883428d4 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -37,6 +37,7 @@ import ( "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/engine/access/ingestion" pingeng "github.com/onflow/flow-go/engine/access/ping" + "github.com/onflow/flow-go/engine/access/rest" "github.com/onflow/flow-go/engine/access/rest/routes" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" @@ -151,7 +152,6 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { UnsecureGRPCListenAddr: "0.0.0.0:9000", SecureGRPCListenAddr: "0.0.0.0:9001", HTTPListenAddr: "0.0.0.0:8000", - RESTListenAddr: "", CollectionAddr: "", HistoricalAccessAddrs: "", BackendConfig: backend.Config{ @@ -170,6 +170,12 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { MaxRequests: 1, }, }, + RestConfig: rest.Config{ + ListenAddress: "", + WriteTimeout: rest.DefaultWriteTimeout, + ReadTimeout: rest.DefaultReadTimeout, + IdleTimeout: rest.DefaultIdleTimeout, + }, MaxMsgSize: grpcutils.DefaultMaxMsgSize, }, stateStreamConf: state_stream.Config{ @@ -674,7 +680,10 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.StringVar(&builder.rpcConf.SecureGRPCListenAddr, "secure-rpc-addr", defaultConfig.rpcConf.SecureGRPCListenAddr, "the address the secure gRPC server listens on") flags.StringVar(&builder.stateStreamConf.ListenAddr, "state-stream-addr", defaultConfig.stateStreamConf.ListenAddr, "the address the state stream server listens on (if empty the server will not be started)") flags.StringVarP(&builder.rpcConf.HTTPListenAddr, "http-addr", "h", defaultConfig.rpcConf.HTTPListenAddr, "the address the http proxy server listens on") - flags.StringVar(&builder.rpcConf.RESTListenAddr, "rest-addr", defaultConfig.rpcConf.RESTListenAddr, "the address the REST server listens on (if empty the REST server will not be started)") + flags.StringVar(&builder.rpcConf.RestConfig.ListenAddress, "rest-addr", defaultConfig.rpcConf.RestConfig.ListenAddress, "the address the REST server listens on (if empty the REST server will not be started)") + flags.DurationVar(&builder.rpcConf.RestConfig.WriteTimeout, "rest-write-timeout", defaultConfig.rpcConf.RestConfig.WriteTimeout, "timeout to use when writing REST response") + flags.DurationVar(&builder.rpcConf.RestConfig.ReadTimeout, "rest-read-timeout", defaultConfig.rpcConf.RestConfig.ReadTimeout, "timeout to use when reading REST request headers") + flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") flags.StringVarP(&builder.rpcConf.CollectionAddr, "static-collection-ingress-addr", "", defaultConfig.rpcConf.CollectionAddr, "the address (of the collection node) to send transactions to") flags.StringVarP(&builder.ExecutionNodeAddress, "script-addr", "s", defaultConfig.ExecutionNodeAddress, "the address (of the execution node) forward the script to") flags.StringSliceVar(&builder.rpcConf.BackendConfig.ArchiveAddressList, "archive-address-list", defaultConfig.rpcConf.BackendConfig.ArchiveAddressList, "the list of address of the archive node to forward the script queries to") diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index a8e7abdd4b6..793ac51fcf9 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -28,6 +28,7 @@ import ( recovery "github.com/onflow/flow-go/consensus/recovery/protocol" "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/engine/access/apiproxy" + "github.com/onflow/flow-go/engine/access/rest" restapiproxy "github.com/onflow/flow-go/engine/access/rest/apiproxy" "github.com/onflow/flow-go/engine/access/rest/routes" "github.com/onflow/flow-go/engine/access/rpc" @@ -116,7 +117,6 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { UnsecureGRPCListenAddr: "0.0.0.0:9000", SecureGRPCListenAddr: "0.0.0.0:9001", HTTPListenAddr: "0.0.0.0:8000", - RESTListenAddr: "", CollectionAddr: "", HistoricalAccessAddrs: "", BackendConfig: backend.Config{ @@ -128,6 +128,12 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { FixedExecutionNodeIDs: nil, ArchiveAddressList: nil, }, + RestConfig: rest.Config{ + ListenAddress: "", + WriteTimeout: rest.DefaultWriteTimeout, + ReadTimeout: rest.DefaultReadTimeout, + IdleTimeout: rest.DefaultIdleTimeout, + }, MaxMsgSize: grpcutils.DefaultMaxMsgSize, }, rpcMetricsEnabled: false, @@ -457,7 +463,10 @@ func (builder *ObserverServiceBuilder) extraFlags() { flags.StringVarP(&builder.rpcConf.UnsecureGRPCListenAddr, "rpc-addr", "r", defaultConfig.rpcConf.UnsecureGRPCListenAddr, "the address the unsecured gRPC server listens on") flags.StringVar(&builder.rpcConf.SecureGRPCListenAddr, "secure-rpc-addr", defaultConfig.rpcConf.SecureGRPCListenAddr, "the address the secure gRPC server listens on") flags.StringVarP(&builder.rpcConf.HTTPListenAddr, "http-addr", "h", defaultConfig.rpcConf.HTTPListenAddr, "the address the http proxy server listens on") - flags.StringVar(&builder.rpcConf.RESTListenAddr, "rest-addr", defaultConfig.rpcConf.RESTListenAddr, "the address the REST server listens on (if empty the REST server will not be started)") + flags.StringVar(&builder.rpcConf.RestConfig.ListenAddress, "rest-addr", defaultConfig.rpcConf.RestConfig.ListenAddress, "the address the REST server listens on (if empty the REST server will not be started)") + flags.DurationVar(&builder.rpcConf.RestConfig.WriteTimeout, "rest-write-timeout", defaultConfig.rpcConf.RestConfig.WriteTimeout, "timeout to use when writing REST response") + flags.DurationVar(&builder.rpcConf.RestConfig.ReadTimeout, "rest-read-timeout", defaultConfig.rpcConf.RestConfig.ReadTimeout, "timeout to use when reading REST request headers") + flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") flags.UintVar(&builder.rpcConf.MaxMsgSize, "rpc-max-message-size", defaultConfig.rpcConf.MaxMsgSize, "the maximum message size in bytes for messages sent or received over grpc") flags.UintVar(&builder.rpcConf.BackendConfig.ConnectionPoolSize, "connection-pool-size", defaultConfig.rpcConf.BackendConfig.ConnectionPoolSize, "maximum number of connections allowed in the connection pool, size of 0 disables the connection pooling, and anything less than the default size will be overridden to use the default size") flags.UintVar(&builder.rpcConf.BackendConfig.MaxHeightRange, "rpc-max-height-range", defaultConfig.rpcConf.BackendConfig.MaxHeightRange, "maximum size for height range requests") diff --git a/engine/access/rest/server.go b/engine/access/rest/server.go index 4a4b1be6f0e..39124f8b87f 100644 --- a/engine/access/rest/server.go +++ b/engine/access/rest/server.go @@ -13,8 +13,26 @@ import ( "github.com/onflow/flow-go/module" ) +const ( + // DefaultReadTimeout is the default read timeout for the HTTP server + DefaultReadTimeout = time.Second * 15 + + // DefaultWriteTimeout is the default write timeout for the HTTP server + DefaultWriteTimeout = time.Second * 30 + + // DefaultIdleTimeout is the default idle timeout for the HTTP server + DefaultIdleTimeout = time.Second * 60 +) + +type Config struct { + ListenAddress string + WriteTimeout time.Duration + ReadTimeout time.Duration + IdleTimeout time.Duration +} + // NewServer returns an HTTP server initialized with the REST API handler -func NewServer(serverAPI access.API, listenAddress string, logger zerolog.Logger, chain flow.Chain, restCollector module.RestMetrics) (*http.Server, error) { +func NewServer(serverAPI access.API, config Config, logger zerolog.Logger, chain flow.Chain, restCollector module.RestMetrics) (*http.Server, error) { router, err := routes.NewRouter(serverAPI, logger, chain, restCollector) if err != nil { return nil, err @@ -31,10 +49,10 @@ func NewServer(serverAPI access.API, listenAddress string, logger zerolog.Logger }) return &http.Server{ - Addr: listenAddress, Handler: c.Handler(router), - WriteTimeout: time.Second * 15, - ReadTimeout: time.Second * 15, - IdleTimeout: time.Second * 60, + Addr: config.ListenAddress, + WriteTimeout: config.WriteTimeout, + ReadTimeout: config.ReadTimeout, + IdleTimeout: config.IdleTimeout, }, nil } diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 0f7a9058dc4..b6fb776b2e5 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/suite" accessmock "github.com/onflow/flow-go/engine/access/mock" + "github.com/onflow/flow-go/engine/access/rest" "github.com/onflow/flow-go/engine/access/rest/request" "github.com/onflow/flow-go/engine/access/rest/routes" "github.com/onflow/flow-go/engine/access/rpc" @@ -125,7 +126,9 @@ func (suite *RestAPITestSuite) SetupTest() { UnsecureGRPCListenAddr: unittest.DefaultAddress, SecureGRPCListenAddr: unittest.DefaultAddress, HTTPListenAddr: unittest.DefaultAddress, - RESTListenAddr: unittest.DefaultAddress, + RestConfig: rest.Config{ + ListenAddress: unittest.DefaultAddress, + }, } // generate a server certificate that will be served by the GRPC server diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index eea6dc5d17c..b8709ed4c88 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -9,7 +9,6 @@ import ( "sync" "github.com/rs/zerolog" - "google.golang.org/grpc/credentials" "github.com/onflow/flow-go/access" @@ -33,11 +32,11 @@ type Config struct { SecureGRPCListenAddr string // the secure GRPC server address as ip:port TransportCredentials credentials.TransportCredentials // the secure GRPC credentials HTTPListenAddr string // the HTTP web proxy address as ip:port - RESTListenAddr string // the REST server address as ip:port (if empty the REST server will not be started) CollectionAddr string // the address of the upstream collection node HistoricalAccessAddrs string // the list of all access nodes from previous spork BackendConfig backend.Config // configurable options for creating Backend + RestConfig rest.Config // the REST server configuration MaxMsgSize uint // GRPC max message size } @@ -204,15 +203,15 @@ func (e *Engine) serveGRPCWebProxyWorker(ctx irrecoverable.SignalerContext, read // serveREST is a worker routine which starts the HTTP REST server. // The ready callback is called after the server address is bound and set. func (e *Engine) serveREST(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - if e.config.RESTListenAddr == "" { + if e.config.RestConfig.ListenAddress == "" { e.log.Debug().Msg("no REST API address specified - not starting the server") ready() return } - e.log.Info().Str("rest_api_address", e.config.RESTListenAddr).Msg("starting REST server on address") + e.log.Info().Str("rest_api_address", e.config.RestConfig.ListenAddress).Msg("starting REST server on address") - r, err := rest.NewServer(e.restHandler, e.config.RESTListenAddr, e.log, e.chain, e.restCollector) + r, err := rest.NewServer(e.restHandler, e.config.RestConfig, e.log, e.chain, e.restCollector) if err != nil { e.log.Err(err).Msg("failed to initialize the REST server") ctx.Throw(err) @@ -220,7 +219,7 @@ func (e *Engine) serveREST(ctx irrecoverable.SignalerContext, ready component.Re } e.restServer = r - l, err := net.Listen("tcp", e.config.RESTListenAddr) + l, err := net.Listen("tcp", e.config.RestConfig.ListenAddress) if err != nil { e.log.Err(err).Msg("failed to start the REST server") ctx.Throw(err) From 8e62777d3226f36262b855fbd0171d47e2a44262 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 10 Aug 2023 06:56:32 -0400 Subject: [PATCH 414/815] Delete flaky-test-debug.yml --- .github/workflows/flaky-test-debug.yml | 225 ------------------------- 1 file changed, 225 deletions(-) delete mode 100644 .github/workflows/flaky-test-debug.yml diff --git a/.github/workflows/flaky-test-debug.yml b/.github/workflows/flaky-test-debug.yml deleted file mode 100644 index 8058a656f29..00000000000 --- a/.github/workflows/flaky-test-debug.yml +++ /dev/null @@ -1,225 +0,0 @@ -name: Flaky Test Debug - -on: - push: - branches: - - '**/*flaky-test-debug*' -env: - GO_VERSION: "1.20" - -#concurrency: -# group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} -# cancel-in-progress: true - -jobs: - golangci: - strategy: - fail-fast: false - matrix: - dir: [./, ./integration/, ./crypto/, ./insecure/] - name: Lint - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - name: Build relic - run: make crypto_setup_gopath - - name: Run go generate - run: go generate - working-directory: ${{ matrix.dir }} - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.51 - args: -v --build-tags relic - working-directory: ${{ matrix.dir }} - # https://github.com/golangci/golangci-lint-action/issues/244 - skip-cache: true - - tidy: - name: Tidy - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - name: Run tidy - run: make tidy - - name: Emulator no relic check - run: make emulator-norelic-check - - # shell-check: - # name: ShellCheck - # runs-on: ubuntu-latest - # steps: - # - name: Checkout repo - # uses: actions/checkout@v3 - # - name: Run ShellCheck - # uses: ludeeus/action-shellcheck@203a3fd018dfe73f8ae7e3aa8da2c149a5f41c33 - # with: - # scandir: './crypto' - # ignore: 'relic' - - create-dynamic-test-matrix: - name: Create Dynamic Test Matrix - runs-on: ubuntu-latest - outputs: - dynamic-matrix: ${{ steps.set-test-matrix.outputs.dynamicMatrix }} - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - name: Set Test Matrix - id: set-test-matrix - # modify which module to unit test by changing the argument to test_matrix.go - run: go run utils/test_matrix/test_matrix.go network - # run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils - - unit-test: - name: Unit Tests (${{ matrix.targets.name }}) - needs: create-dynamic-test-matrix - strategy: - fail-fast: false - matrix: - targets: ${{ fromJSON(needs.create-dynamic-test-matrix.outputs.dynamic-matrix)}} - # need to set image explicitly due to GitHub logging issue as described in https://github.com/onflow/flow-go/pull/3087#issuecomment-1234383202 - runs-on: ubuntu-20.04 - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - name: Run tests (${{ matrix.targets.name }}) - if: github.actor != 'bors[bot]' - run: make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" ci - # TODO(rbtz): re-enable when we fix exisiting races. - #env: - # RACE_DETECTOR: 1 - - name: Run tests (Bors) - if: github.actor == 'bors[bot]' - uses: nick-invision/retry@v2 - with: - timeout_minutes: 25 - max_attempts: 3 - command: make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" ci - - name: Upload coverage report - uses: codecov/codecov-action@v3 - with: - file: ./coverage.txt - flags: unittests - name: codecov-umbrella - - # unit-test-modules: - # name: Unit Tests (Modules) - # strategy: - # fail-fast: false - # matrix: - # include: - # - name: crypto - # make1: -C crypto setup - # make2: unittest - # retries: 1 - # race: 1 - # - name: insecure - # make1: install-tools - # make2: test - # retries: 3 - # race: 1 - # - name: integration - # make1: install-tools - # make2: test - # retries: 3 - # race: 0 - # runs-on: ubuntu-latest - # steps: - # - name: Checkout repo - # uses: actions/checkout@v3 - # - name: Setup Go - # uses: actions/setup-go@v3 - # with: - # go-version: ${{ env.GO_VERSION }} - # cache: true - # - name: Run tests (${{ matrix.name }}) - # if: github.actor != 'bors[bot]' - # env: - # RACE_DETECTOR: ${{ matrix.race }} - # # run `make1` target before running `make2` target inside each module's root - # run: | - # make ${{ matrix.make1 }} - # make -C ${{ matrix.name }} ${{ matrix.make2 }} - # - name: Run tests (Bors) - # if: github.actor == 'bors[bot]' - # uses: nick-invision/retry@v2 - # with: - # timeout_minutes: 25 - # max_attempts: ${{ matrix.retries }} - # command: | - # make ${{ matrix.make1 }} - # make -C ${{ matrix.name }} ${{ matrix.make2 }} - # - name: Upload coverage report - # uses: codecov/codecov-action@v3 - # with: - # file: ./coverage.txt - # flags: unittests - # name: codecov-umbrella - - integration-test: - name: Integration Tests - strategy: - fail-fast: false - matrix: - # modify which integration module to test by adding / removing the matrix targets - make: -# - make -C integration access-tests -# - make -C integration bft-tests -# - make -C integration collection-tests -# - make -C integration consensus-tests - - make -C integration epochs-tests -# - make -C integration execution-tests -# - make -C integration ghost-tests -# - make -C integration mvp-tests -# - make -C integration network-tests -# - make -C integration verification-tests - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - name: Build relic - run: make crypto_setup_gopath - - name: Docker build - run: make docker-build-flow docker-build-flow-corrupt - - name: Run tests - if: github.actor != 'bors[bot]' - run: VERBOSE=1 ${{ matrix.make }} - # TODO(rbtz): re-enable when we fix existing races. - #env: - # RACE_DETECTOR: 1 - - name: Run tests (Bors) - if: github.actor == 'bors[bot]' - uses: nick-invision/retry@v2 - with: - timeout_minutes: 15 - max_attempts: 2 - command: ${{ matrix.make }} From 7d94f48e31198ab21d4e0f0f1f8cb8e4a4ecf146 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 10 Aug 2023 07:20:47 -0400 Subject: [PATCH 415/815] test update to run flaky test workflow --- .github/workflows/flaky-test-monitor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index 21a99c21088..8fdd44e0662 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -21,6 +21,7 @@ env: JSON_OUTPUT: true VERBOSE: true TEST_FLAKY: true + TEMP_DIR: /tmp concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} From b19e61e35b460362507801849a5d3ae57261f37c Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 10 Aug 2023 07:29:23 -0400 Subject: [PATCH 416/815] fix to run workflow on changes to workflow --- .github/workflows/flaky-test-monitor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index 8fdd44e0662..2ede8c77e3d 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -7,7 +7,7 @@ on: - cron: '0 */2 * * *' # every 2 hours push: paths: - - '.github/workflows/test-monitor.yml' + - '.github/workflows/flaky-test-monitor.yml' env: BIGQUERY_DATASET: production_src_flow_test_metrics @@ -21,7 +21,6 @@ env: JSON_OUTPUT: true VERBOSE: true TEST_FLAKY: true - TEMP_DIR: /tmp concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} From d7b51289a9968b7f745e713cf9fce5cd4c4d54d3 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 25 Jul 2023 14:06:23 +0300 Subject: [PATCH 417/815] Add endpoint to REST API for retrieving an account key --- engine/access/rest/request/get_account.go | 34 ++++++++++++++++++++ engine/access/rest/request/request.go | 6 ++++ engine/access/rest/routes/accounts.go | 39 +++++++++++++++++++++++ engine/access/rest/routes/router.go | 5 +++ 4 files changed, 84 insertions(+) diff --git a/engine/access/rest/request/get_account.go b/engine/access/rest/request/get_account.go index fde9cf6d6e7..66be5d88c42 100644 --- a/engine/access/rest/request/get_account.go +++ b/engine/access/rest/request/get_account.go @@ -1,6 +1,9 @@ package request import ( + "fmt" + + "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) @@ -41,3 +44,34 @@ func (g *GetAccount) Parse(rawAddress string, rawHeight string) error { return nil } + +const keyVar = "keyID" + +type GetAccountKey struct { + Address flow.Address + KeyID uint64 +} + +func (g *GetAccountKey) Build(r *Request) error { + return g.Parse( + r.GetVar(addressVar), + r.GetVar(keyVar), + ) +} + +func (g *GetAccountKey) Parse(rawAddress string, rawKeyID string) error { + address, err := ParseAddress(rawAddress) + if err != nil { + return err + } + + keyID, err := util.ToUint64(rawKeyID) + if err != nil { + return fmt.Errorf("invalid key index: %w", err) + } + + g.Address = address + g.KeyID = keyID + + return nil +} diff --git a/engine/access/rest/request/request.go b/engine/access/rest/request/request.go index b7500206fac..6c7821d0b0b 100644 --- a/engine/access/rest/request/request.go +++ b/engine/access/rest/request/request.go @@ -54,6 +54,12 @@ func (rd *Request) GetAccountRequest() (GetAccount, error) { return req, err } +func (rd *Request) GetAccountKeyRequest() (GetAccountKey, error) { + var req GetAccountKey + err := req.Build(rd) + return req, err +} + func (rd *Request) GetExecutionResultByBlockIDsRequest() (GetExecutionResultByBlockIDs, error) { var req GetExecutionResultByBlockIDs err := req.Build(rd) diff --git a/engine/access/rest/routes/accounts.go b/engine/access/rest/routes/accounts.go index 972c2ba68ac..0488ea88d44 100644 --- a/engine/access/rest/routes/accounts.go +++ b/engine/access/rest/routes/accounts.go @@ -1,9 +1,12 @@ package routes import ( + "fmt" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rest/models" "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/model/flow" ) // GetAccount handler retrieves account by address and returns the response @@ -31,3 +34,39 @@ func GetAccount(r *request.Request, backend access.API, link models.LinkGenerato err = response.Build(account, link, r.ExpandFields) return response, err } + +func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := r.GetAccountKeyRequest() + if err != nil { + return nil, models.NewBadRequestError(err) + } + + header, _, err := backend.GetLatestBlockHeader(r.Context(), true) + if err != nil { + return nil, err + } + + account, err := backend.GetAccountAtBlockHeight(r.Context(), req.Address, header.Height) + if err != nil { + return nil, err + } + + var accountKey flow.AccountPublicKey + found := false + for _, key := range account.Keys { + if key.Index == int(req.KeyID) { + accountKey = key + found = true + } + } + if !found { + return nil, models.NewNotFoundError( + fmt.Sprintf("error looking up account key with ID %d", req.KeyID), + nil, + ) + } + + var response models.AccountPublicKey + response.Build(accountKey) + return response, nil +} diff --git a/engine/access/rest/routes/router.go b/engine/access/rest/routes/router.go index a2185e4e9a3..4b42292aadc 100644 --- a/engine/access/rest/routes/router.go +++ b/engine/access/rest/routes/router.go @@ -101,6 +101,11 @@ var Routes = []route{{ Pattern: "/accounts/{address}", Name: "getAccount", Handler: GetAccount, +}, { + Method: http.MethodGet, + Pattern: "/accounts/{address}/keys/{keyID}", + Name: "getAccountKeyByID", + Handler: GetAccountKeyByID, }, { Method: http.MethodGet, Pattern: "/events", From 3a59ffe8d7f652002a512ca62aed3e8095c2dd96 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 26 Jul 2023 11:10:29 +0300 Subject: [PATCH 418/815] Move code for GetAccountKeyByID to dedicated files and add some tests --- engine/access/rest/request/get_account.go | 34 ----- engine/access/rest/request/get_account_key.go | 39 +++++ .../rest/request/get_account_key_test.go | 37 +++++ engine/access/rest/routes/account_keys.go | 47 ++++++ .../access/rest/routes/account_keys_test.go | 134 ++++++++++++++++++ engine/access/rest/routes/accounts.go | 39 ----- 6 files changed, 257 insertions(+), 73 deletions(-) create mode 100644 engine/access/rest/request/get_account_key.go create mode 100644 engine/access/rest/request/get_account_key_test.go create mode 100644 engine/access/rest/routes/account_keys.go create mode 100644 engine/access/rest/routes/account_keys_test.go diff --git a/engine/access/rest/request/get_account.go b/engine/access/rest/request/get_account.go index 66be5d88c42..fde9cf6d6e7 100644 --- a/engine/access/rest/request/get_account.go +++ b/engine/access/rest/request/get_account.go @@ -1,9 +1,6 @@ package request import ( - "fmt" - - "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) @@ -44,34 +41,3 @@ func (g *GetAccount) Parse(rawAddress string, rawHeight string) error { return nil } - -const keyVar = "keyID" - -type GetAccountKey struct { - Address flow.Address - KeyID uint64 -} - -func (g *GetAccountKey) Build(r *Request) error { - return g.Parse( - r.GetVar(addressVar), - r.GetVar(keyVar), - ) -} - -func (g *GetAccountKey) Parse(rawAddress string, rawKeyID string) error { - address, err := ParseAddress(rawAddress) - if err != nil { - return err - } - - keyID, err := util.ToUint64(rawKeyID) - if err != nil { - return fmt.Errorf("invalid key index: %w", err) - } - - g.Address = address - g.KeyID = keyID - - return nil -} diff --git a/engine/access/rest/request/get_account_key.go b/engine/access/rest/request/get_account_key.go new file mode 100644 index 00000000000..27bf235aeea --- /dev/null +++ b/engine/access/rest/request/get_account_key.go @@ -0,0 +1,39 @@ +package request + +import ( + "fmt" + + "github.com/onflow/flow-go/engine/access/rest/util" + "github.com/onflow/flow-go/model/flow" +) + +const keyVar = "keyID" + +type GetAccountKey struct { + Address flow.Address + KeyID uint64 +} + +func (g *GetAccountKey) Build(r *Request) error { + return g.Parse( + r.GetVar(addressVar), + r.GetVar(keyVar), + ) +} + +func (g *GetAccountKey) Parse(rawAddress string, rawKeyID string) error { + address, err := ParseAddress(rawAddress) + if err != nil { + return err + } + + keyID, err := util.ToUint64(rawKeyID) + if err != nil { + return fmt.Errorf("invalid key index: %w", err) + } + + g.Address = address + g.KeyID = keyID + + return nil +} diff --git a/engine/access/rest/request/get_account_key_test.go b/engine/access/rest/request/get_account_key_test.go new file mode 100644 index 00000000000..c0935143606 --- /dev/null +++ b/engine/access/rest/request/get_account_key_test.go @@ -0,0 +1,37 @@ +package request + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_GetAccountKey_InvalidParse(t *testing.T) { + var getAccountKey GetAccountKey + + tests := []struct { + address string + keyID string + err string + }{ + {"", "", "invalid address"}, + {"f8d6e0586b0a20c7", "-1.2", "invalid key index: value must be an unsigned 64 bit integer"}, + } + + for i, test := range tests { + err := getAccountKey.Parse(test.address, test.keyID) + assert.EqualError(t, err, test.err, fmt.Sprintf("test #%d failed", i)) + } +} + +func Test_GetAccountKey_ValidParse(t *testing.T) { + var getAccountKey GetAccountKey + + addr := "f8d6e0586b0a20c7" + keyID := "5" + err := getAccountKey.Parse(addr, keyID) + assert.NoError(t, err) + assert.Equal(t, getAccountKey.Address.String(), addr) + assert.Equal(t, getAccountKey.KeyID, uint64(5)) +} diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/routes/account_keys.go new file mode 100644 index 00000000000..b6524347da7 --- /dev/null +++ b/engine/access/rest/routes/account_keys.go @@ -0,0 +1,47 @@ +package routes + +import ( + "fmt" + + "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/model/flow" +) + +// GetAccountKeyByID handler retrieves an account key by address and ID and returns the response +func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := r.GetAccountKeyRequest() + if err != nil { + return nil, models.NewBadRequestError(err) + } + + header, _, err := backend.GetLatestBlockHeader(r.Context(), false) + if err != nil { + return nil, err + } + + account, err := backend.GetAccountAtBlockHeight(r.Context(), req.Address, header.Height) + if err != nil { + return nil, err + } + + var accountKey flow.AccountPublicKey + found := false + for _, key := range account.Keys { + if key.Index == int(req.KeyID) { + accountKey = key + found = true + } + } + if !found { + return nil, models.NewNotFoundError( + fmt.Sprintf("error looking up account key with ID %d", req.KeyID), + nil, + ) + } + + var response models.AccountPublicKey + response.Build(accountKey) + return response, nil +} diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/routes/account_keys_test.go new file mode 100644 index 00000000000..2a016445f9b --- /dev/null +++ b/engine/access/rest/routes/account_keys_test.go @@ -0,0 +1,134 @@ +package routes + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + mocktestify "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/model/flow" + + "github.com/onflow/flow-go/utils/unittest" +) + +// TestGetAccountKeyByID tests local getAccount request. +// +// Runs the following tests: +// 1. Get key by address and ID at latest finalized block +// 2. Get missing key by address and ID at latest finalized block +// 3. Get invalid account. +func TestGetAccountKeyByID(t *testing.T) { + backend := &mock.API{} + + t.Run("get key by address and ID at latest finalized block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, "0") + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, false). + Return(block, flow.BlockStatusSealed, nil) + + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(account, nil) + + expected := expectedAccountKeyResponse(account) + + assertOKResponse(t, req, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) + }) + + t.Run("get missing key by address and ID at latest finalized block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + keyID := "2" + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, keyID) + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, false). + Return(block, flow.BlockStatusSealed, nil) + + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(account, nil) + + statusCode := 404 + expected := fmt.Sprintf(` + { + "code": %d, + "message": "error looking up account key with ID %s" + } + `, statusCode, keyID) + + assertResponse(t, req, statusCode, expected, backend) + }) + + t.Run("get invalid", func(t *testing.T) { + tests := []struct { + url string + out string + }{ + { + accountKeyURL(t, "123", "3"), `{"code":400, "message":"invalid address"}`, + }, + { + accountKeyURL( + t, + unittest.AddressFixture().String(), + "foo", + ), + `{"code":400, "message":"invalid key index: value must be an unsigned 64 bit integer"}`, + }, + } + + for i, test := range tests { + req, _ := http.NewRequest("GET", test.url, nil) + rr, err := executeRequest(req, backend) + assert.NoError(t, err) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.JSONEq(t, test.out, rr.Body.String(), fmt.Sprintf("test #%d failed: %v", i, test)) + } + }) +} + +func accountKeyURL(t *testing.T, address string, keyID string) string { + u, err := url.ParseRequestURI( + fmt.Sprintf("/v1/accounts/%s/keys/%s", address, keyID), + ) + require.NoError(t, err) + + return u.String() +} + +func getAccountKeyByIDRequest(t *testing.T, account *flow.Account, keyID string) *http.Request { + req, err := http.NewRequest("GET", accountKeyURL(t, account.Address.String(), keyID), nil) + require.NoError(t, err) + + return req +} + +func expectedAccountKeyResponse(account *flow.Account) string { + return fmt.Sprintf(` + { + "index":"0", + "public_key":"%s", + "signing_algorithm":"ECDSA_P256", + "hashing_algorithm":"SHA3_256", + "sequence_number":"0", + "weight":"1000", + "revoked":false + }`, + account.Keys[0].PublicKey.String(), + ) +} diff --git a/engine/access/rest/routes/accounts.go b/engine/access/rest/routes/accounts.go index 0488ea88d44..972c2ba68ac 100644 --- a/engine/access/rest/routes/accounts.go +++ b/engine/access/rest/routes/accounts.go @@ -1,12 +1,9 @@ package routes import ( - "fmt" - "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rest/models" "github.com/onflow/flow-go/engine/access/rest/request" - "github.com/onflow/flow-go/model/flow" ) // GetAccount handler retrieves account by address and returns the response @@ -34,39 +31,3 @@ func GetAccount(r *request.Request, backend access.API, link models.LinkGenerato err = response.Build(account, link, r.ExpandFields) return response, err } - -func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetAccountKeyRequest() - if err != nil { - return nil, models.NewBadRequestError(err) - } - - header, _, err := backend.GetLatestBlockHeader(r.Context(), true) - if err != nil { - return nil, err - } - - account, err := backend.GetAccountAtBlockHeight(r.Context(), req.Address, header.Height) - if err != nil { - return nil, err - } - - var accountKey flow.AccountPublicKey - found := false - for _, key := range account.Keys { - if key.Index == int(req.KeyID) { - accountKey = key - found = true - } - } - if !found { - return nil, models.NewNotFoundError( - fmt.Sprintf("error looking up account key with ID %d", req.KeyID), - nil, - ) - } - - var response models.AccountPublicKey - response.Build(accountKey) - return response, nil -} From f30864d5f4b98a6aff0395e1d606a525cb761272 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 26 Jul 2023 11:16:26 +0300 Subject: [PATCH 419/815] Add better message for missing account key --- engine/access/rest/routes/account_keys.go | 2 +- engine/access/rest/routes/account_keys_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/routes/account_keys.go index b6524347da7..b55a086803b 100644 --- a/engine/access/rest/routes/account_keys.go +++ b/engine/access/rest/routes/account_keys.go @@ -36,7 +36,7 @@ func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkG } if !found { return nil, models.NewNotFoundError( - fmt.Sprintf("error looking up account key with ID %d", req.KeyID), + fmt.Sprintf("account key with ID: %d does not exist", req.KeyID), nil, ) } diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/routes/account_keys_test.go index 2a016445f9b..1331d1797c7 100644 --- a/engine/access/rest/routes/account_keys_test.go +++ b/engine/access/rest/routes/account_keys_test.go @@ -66,7 +66,7 @@ func TestGetAccountKeyByID(t *testing.T) { expected := fmt.Sprintf(` { "code": %d, - "message": "error looking up account key with ID %s" + "message": "account key with ID: %s does not exist" } `, statusCode, keyID) From ad94345d40b5c55ab420d8725cb9e318262c1e08 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 2 Aug 2023 13:10:18 +0300 Subject: [PATCH 420/815] Apply suggestions from PR review --- engine/access/rest/request/get_account_key.go | 32 ++- .../rest/request/get_account_key_test.go | 48 +++- engine/access/rest/routes/account_keys.go | 37 ++- .../access/rest/routes/account_keys_test.go | 227 +++++++++++++++--- engine/access/rest/routes/router.go | 12 +- engine/access/rest/routes/router_test.go | 10 + 6 files changed, 285 insertions(+), 81 deletions(-) diff --git a/engine/access/rest/request/get_account_key.go b/engine/access/rest/request/get_account_key.go index 27bf235aeea..fc38a5e17b9 100644 --- a/engine/access/rest/request/get_account_key.go +++ b/engine/access/rest/request/get_account_key.go @@ -7,33 +7,51 @@ import ( "github.com/onflow/flow-go/model/flow" ) -const keyVar = "keyID" +const keyIndexVar = "key_index" type GetAccountKey struct { - Address flow.Address - KeyID uint64 + Address flow.Address + KeyIndex uint64 + Height uint64 } func (g *GetAccountKey) Build(r *Request) error { return g.Parse( r.GetVar(addressVar), - r.GetVar(keyVar), + r.GetVar(keyIndexVar), + r.GetQueryParam(blockHeightQuery), ) } -func (g *GetAccountKey) Parse(rawAddress string, rawKeyID string) error { +func (g *GetAccountKey) Parse( + rawAddress string, + rawKeyIndex string, + rawHeight string, +) error { address, err := ParseAddress(rawAddress) if err != nil { return err } - keyID, err := util.ToUint64(rawKeyID) + keyIndex, err := util.ToUint64(rawKeyIndex) if err != nil { return fmt.Errorf("invalid key index: %w", err) } + var height Height + err = height.Parse(rawHeight) + if err != nil { + return err + } + g.Address = address - g.KeyID = keyID + g.KeyIndex = keyIndex + g.Height = height.Flow() + + // default to last block + if g.Height == EmptyHeight { + g.Height = SealedHeight + } return nil } diff --git a/engine/access/rest/request/get_account_key_test.go b/engine/access/rest/request/get_account_key_test.go index c0935143606..3305142c355 100644 --- a/engine/access/rest/request/get_account_key_test.go +++ b/engine/access/rest/request/get_account_key_test.go @@ -1,7 +1,6 @@ package request import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -11,17 +10,40 @@ func Test_GetAccountKey_InvalidParse(t *testing.T) { var getAccountKey GetAccountKey tests := []struct { - address string - keyID string - err string + name string + address string + keyIndex string + height string + err string }{ - {"", "", "invalid address"}, - {"f8d6e0586b0a20c7", "-1.2", "invalid key index: value must be an unsigned 64 bit integer"}, + { + "parse with invalid address", + "0xxxaddr", + "1", + "100", + "invalid address", + }, + { + "parse with invalid keyIndex", + "0xf8d6e0586b0a20c7", + "-1.2", + "100", + "invalid key index: value must be an unsigned 64 bit integer", + }, + { + "parse with invalid height", + "0xf8d6e0586b0a20c7", + "2", + "-100", + "invalid height format", + }, } - for i, test := range tests { - err := getAccountKey.Parse(test.address, test.keyID) - assert.EqualError(t, err, test.err, fmt.Sprintf("test #%d failed", i)) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := getAccountKey.Parse(test.address, test.keyIndex, test.height) + assert.EqualError(t, err, test.err) + }) } } @@ -29,9 +51,11 @@ func Test_GetAccountKey_ValidParse(t *testing.T) { var getAccountKey GetAccountKey addr := "f8d6e0586b0a20c7" - keyID := "5" - err := getAccountKey.Parse(addr, keyID) + keyIndex := "5" + height := "100" + err := getAccountKey.Parse(addr, keyIndex, height) assert.NoError(t, err) assert.Equal(t, getAccountKey.Address.String(), addr) - assert.Equal(t, getAccountKey.KeyID, uint64(5)) + assert.Equal(t, getAccountKey.KeyIndex, uint64(5)) + assert.Equal(t, getAccountKey.Height, uint64(100)) } diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/routes/account_keys.go index b55a086803b..1141a8720e6 100644 --- a/engine/access/rest/routes/account_keys.go +++ b/engine/access/rest/routes/account_keys.go @@ -6,7 +6,6 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rest/models" "github.com/onflow/flow-go/engine/access/rest/request" - "github.com/onflow/flow-go/model/flow" ) // GetAccountKeyByID handler retrieves an account key by address and ID and returns the response @@ -16,32 +15,30 @@ func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkG return nil, models.NewBadRequestError(err) } - header, _, err := backend.GetLatestBlockHeader(r.Context(), false) - if err != nil { - return nil, err + // in case we receive special height values 'final' and 'sealed', fetch that height and overwrite request with it + if req.Height == request.FinalHeight || req.Height == request.SealedHeight { + isSealed := req.Height == request.SealedHeight + header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed) + if err != nil { + return nil, err + } + req.Height = header.Height } - account, err := backend.GetAccountAtBlockHeight(r.Context(), req.Address, header.Height) + account, err := backend.GetAccountAtBlockHeight(r.Context(), req.Address, req.Height) if err != nil { - return nil, err + err := fmt.Errorf("account with address: %s does not exist", req.Address) + return nil, models.NewNotFoundError(err.Error(), err) } - var accountKey flow.AccountPublicKey - found := false + var response models.AccountPublicKey for _, key := range account.Keys { - if key.Index == int(req.KeyID) { - accountKey = key - found = true + if key.Index == int(req.KeyIndex) { + response.Build(key) + return response, nil } } - if !found { - return nil, models.NewNotFoundError( - fmt.Sprintf("account key with ID: %d does not exist", req.KeyID), - nil, - ) - } - var response models.AccountPublicKey - response.Build(accountKey) - return response, nil + err = fmt.Errorf("account key with index: %d does not exist", req.KeyIndex) + return nil, models.NewNotFoundError(err.Error(), err) } diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/routes/account_keys_test.go index 1331d1797c7..d6260559cba 100644 --- a/engine/access/rest/routes/account_keys_test.go +++ b/engine/access/rest/routes/account_keys_test.go @@ -16,25 +16,50 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestGetAccountKeyByID tests local getAccount request. +// TestGetAccountKeyByID tests local getAccountKeyByID request. // // Runs the following tests: -// 1. Get key by address and ID at latest finalized block -// 2. Get missing key by address and ID at latest finalized block -// 3. Get invalid account. +// 1. Get key by address and ID at latest sealed block. +// 2. Get key by address and ID at latest finalized block. +// 3. Get missing key by address and ID at latest sealed block. +// 4. Get missing key by address and ID at latest finalized block. +// 5. Get key by missing address and ID at latest sealed block. +// 6. Get key by missing address and ID at latest finalized block. +// 7. Get key by address and ID at height. func TestGetAccountKeyByID(t *testing.T) { backend := &mock.API{} + t.Run("get key by address and ID at latest sealed block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, "0", sealedHeightQueryParam) + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, true). + Return(block, flow.BlockStatusSealed, nil) + + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(account, nil) + + expected := expectedAccountKeyResponse(account) + + assertOKResponse(t, req, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) + }) + t.Run("get key by address and ID at latest finalized block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, "0") + req := getAccountKeyByIDRequest(t, account, "0", finalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). - Return(block, flow.BlockStatusSealed, nil) + Return(block, flow.BlockStatusFinalized, nil) backend.Mock. On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). @@ -46,17 +71,44 @@ func TestGetAccountKeyByID(t *testing.T) { mocktestify.AssertExpectationsForObjects(t, backend) }) + t.Run("get missing key by address and ID at latest sealed block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + keyIndex := "2" + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, keyIndex, sealedHeightQueryParam) + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, true). + Return(block, flow.BlockStatusSealed, nil) + + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(account, nil) + + statusCode := 404 + expected := fmt.Sprintf(` + { + "code": %d, + "message": "account key with index: %s does not exist" + } + `, statusCode, keyIndex) + + assertResponse(t, req, statusCode, expected, backend) + }) + t.Run("get missing key by address and ID at latest finalized block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 - keyID := "2" + keyIndex := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, keyID) + req := getAccountKeyByIDRequest(t, account, keyIndex, finalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). - Return(block, flow.BlockStatusSealed, nil) + Return(block, flow.BlockStatusFinalized, nil) backend.Mock. On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). @@ -66,53 +118,154 @@ func TestGetAccountKeyByID(t *testing.T) { expected := fmt.Sprintf(` { "code": %d, - "message": "account key with ID: %s does not exist" + "message": "account key with index: %s does not exist" } - `, statusCode, keyID) + `, statusCode, keyIndex) assertResponse(t, req, statusCode, expected, backend) }) - t.Run("get invalid", func(t *testing.T) { - tests := []struct { - url string - out string - }{ - { - accountKeyURL(t, "123", "3"), `{"code":400, "message":"invalid address"}`, - }, - { - accountKeyURL( - t, - unittest.AddressFixture().String(), - "foo", - ), - `{"code":400, "message":"invalid key index: value must be an unsigned 64 bit integer"}`, - }, - } - - for i, test := range tests { + t.Run("get key by missing address and ID at latest sealed block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + keyIndex := "2" + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, keyIndex, sealedHeightQueryParam) + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, true). + Return(block, flow.BlockStatusSealed, nil) + + err := fmt.Errorf("account with address: %s does not exist", account.Address) + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(nil, err) + + statusCode := 404 + expected := fmt.Sprintf(` + { + "code": %d, + "message": "account with address: %s does not exist" + } + `, statusCode, account.Address) + + assertResponse(t, req, statusCode, expected, backend) + }) + + t.Run("get key by missing address and ID at latest finalized block", func(t *testing.T) { + account := accountFixture(t) + var height uint64 = 100 + keyIndex := "2" + block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) + + req := getAccountKeyByIDRequest(t, account, keyIndex, finalHeightQueryParam) + + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, false). + Return(block, flow.BlockStatusFinalized, nil) + + err := fmt.Errorf("account with address: %s does not exist", account.Address) + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(nil, err) + + statusCode := 404 + expected := fmt.Sprintf(` + { + "code": %d, + "message": "account with address: %s does not exist" + } + `, statusCode, account.Address) + + assertResponse(t, req, statusCode, expected, backend) + }) + + t.Run("get key by address and ID at height", func(t *testing.T) { + var height uint64 = 1337 + account := accountFixture(t) + req := getAccountKeyByIDRequest(t, account, "0", "1337") + + backend.Mock. + On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). + Return(account, nil) + + expected := expectedAccountKeyResponse(account) + + assertOKResponse(t, req, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) + }) + + tests := []struct { + name string + url string + out string + }{ + { + "get key with invalid address", + accountKeyURL(t, "123", "3", "100"), + `{"code":400, "message":"invalid address"}`, + }, + { + "get key with invalid ID", + accountKeyURL( + t, + unittest.AddressFixture().String(), + "foo", + "100", + ), + `{"code":400, "message":"invalid key index: value must be an unsigned 64 bit integer"}`, + }, + { + "get key with invalid height", + accountKeyURL( + t, + unittest.AddressFixture().String(), + "2", + "-100", + ), + `{"code":400, "message":"invalid height format"}`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { req, _ := http.NewRequest("GET", test.url, nil) rr, err := executeRequest(req, backend) assert.NoError(t, err) assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.JSONEq(t, test.out, rr.Body.String(), fmt.Sprintf("test #%d failed: %v", i, test)) - } - }) + assert.JSONEq(t, test.out, rr.Body.String()) + }) + } } -func accountKeyURL(t *testing.T, address string, keyID string) string { +func accountKeyURL(t *testing.T, address string, keyIndex string, height string) string { u, err := url.ParseRequestURI( - fmt.Sprintf("/v1/accounts/%s/keys/%s", address, keyID), + fmt.Sprintf("/v1/accounts/%s/keys/%s", address, keyIndex), ) require.NoError(t, err) + q := u.Query() + + if height != "" { + q.Add("block_height", height) + } + u.RawQuery = q.Encode() return u.String() } -func getAccountKeyByIDRequest(t *testing.T, account *flow.Account, keyID string) *http.Request { - req, err := http.NewRequest("GET", accountKeyURL(t, account.Address.String(), keyID), nil) +func getAccountKeyByIDRequest( + t *testing.T, + account *flow.Account, + keyIndex string, + height string, +) *http.Request { + req, err := http.NewRequest( + "GET", + accountKeyURL(t, account.Address.String(), keyIndex, height), + nil, + ) require.NoError(t, err) return req diff --git a/engine/access/rest/routes/router.go b/engine/access/rest/routes/router.go index 4b42292aadc..b9da8ee1cc1 100644 --- a/engine/access/rest/routes/router.go +++ b/engine/access/rest/routes/router.go @@ -103,7 +103,7 @@ var Routes = []route{{ Handler: GetAccount, }, { Method: http.MethodGet, - Pattern: "/accounts/{address}/keys/{keyID}", + Pattern: "/accounts/{address}/keys/{key_index}", Name: "getAccountKeyByID", Handler: GetAccountKeyByID, }, { @@ -164,17 +164,19 @@ func normalizeURL(url string) (string, error) { case 64: // id based resource. e.g. /v1/blocks/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef parts = append(parts, "{id}") + if matches[0][5] != "" { + parts = append(parts, matches[0][5]) + } case 16: // address based resource. e.g. /v1/accounts/1234567890abcdef parts = append(parts, "{address}") + if matches[0][4] != "" { + parts = append(parts, "keys", "{key_index}") + } default: // named resource. e.g. /v1/network/parameters parts = append(parts, matches[0][3]) } - if matches[0][5] != "" { - parts = append(parts, matches[0][5]) - } - return "/" + strings.Join(parts, "/"), nil } diff --git a/engine/access/rest/routes/router_test.go b/engine/access/rest/routes/router_test.go index e3c2a2c3fdd..2fd7cb1fdb9 100644 --- a/engine/access/rest/routes/router_test.go +++ b/engine/access/rest/routes/router_test.go @@ -69,6 +69,11 @@ func TestParseURL(t *testing.T) { url: "/v1/accounts/6a587be304c1224c", expected: "getAccount", }, + { + name: "/v1/accounts/{address}/keys/{key_index}", + url: "/v1/accounts/6a587be304c1224c/keys/0", + expected: "getAccountKeyByID", + }, { name: "/v1/events", url: "/v1/events", @@ -156,6 +161,11 @@ func TestBenchmarkParseURL(t *testing.T) { url: "/v1/accounts/6a587be304c1224c", expected: "getAccount", }, + { + name: "/v1/accounts/{address}/keys/{key_index}", + url: "/v1/accounts/6a587be304c1224c/keys/0", + expected: "getAccountKeyByID", + }, { name: "/v1/events", url: "/v1/events", From b0970d45ff70c176e8c5bcf6a5113b2ccce7b46f Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 2 Aug 2023 14:14:41 +0300 Subject: [PATCH 421/815] Replace ID with index for consistency --- engine/access/rest/request/get_account_key.go | 16 ++--- .../rest/request/get_account_key_test.go | 14 ++-- engine/access/rest/routes/account_keys.go | 8 +-- .../access/rest/routes/account_keys_test.go | 70 +++++++++---------- engine/access/rest/routes/router.go | 8 +-- engine/access/rest/routes/router_test.go | 8 +-- 6 files changed, 62 insertions(+), 62 deletions(-) diff --git a/engine/access/rest/request/get_account_key.go b/engine/access/rest/request/get_account_key.go index fc38a5e17b9..51c214022ff 100644 --- a/engine/access/rest/request/get_account_key.go +++ b/engine/access/rest/request/get_account_key.go @@ -7,25 +7,25 @@ import ( "github.com/onflow/flow-go/model/flow" ) -const keyIndexVar = "key_index" +const indexVar = "index" type GetAccountKey struct { - Address flow.Address - KeyIndex uint64 - Height uint64 + Address flow.Address + Index uint64 + Height uint64 } func (g *GetAccountKey) Build(r *Request) error { return g.Parse( r.GetVar(addressVar), - r.GetVar(keyIndexVar), + r.GetVar(indexVar), r.GetQueryParam(blockHeightQuery), ) } func (g *GetAccountKey) Parse( rawAddress string, - rawKeyIndex string, + rawIndex string, rawHeight string, ) error { address, err := ParseAddress(rawAddress) @@ -33,7 +33,7 @@ func (g *GetAccountKey) Parse( return err } - keyIndex, err := util.ToUint64(rawKeyIndex) + index, err := util.ToUint64(rawIndex) if err != nil { return fmt.Errorf("invalid key index: %w", err) } @@ -45,7 +45,7 @@ func (g *GetAccountKey) Parse( } g.Address = address - g.KeyIndex = keyIndex + g.Index = index g.Height = height.Flow() // default to last block diff --git a/engine/access/rest/request/get_account_key_test.go b/engine/access/rest/request/get_account_key_test.go index 3305142c355..2b74421cc91 100644 --- a/engine/access/rest/request/get_account_key_test.go +++ b/engine/access/rest/request/get_account_key_test.go @@ -10,11 +10,11 @@ func Test_GetAccountKey_InvalidParse(t *testing.T) { var getAccountKey GetAccountKey tests := []struct { - name string - address string - keyIndex string - height string - err string + name string + address string + index string + height string + err string }{ { "parse with invalid address", @@ -41,7 +41,7 @@ func Test_GetAccountKey_InvalidParse(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - err := getAccountKey.Parse(test.address, test.keyIndex, test.height) + err := getAccountKey.Parse(test.address, test.index, test.height) assert.EqualError(t, err, test.err) }) } @@ -56,6 +56,6 @@ func Test_GetAccountKey_ValidParse(t *testing.T) { err := getAccountKey.Parse(addr, keyIndex, height) assert.NoError(t, err) assert.Equal(t, getAccountKey.Address.String(), addr) - assert.Equal(t, getAccountKey.KeyIndex, uint64(5)) + assert.Equal(t, getAccountKey.Index, uint64(5)) assert.Equal(t, getAccountKey.Height, uint64(100)) } diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/routes/account_keys.go index 1141a8720e6..3f6d795ad87 100644 --- a/engine/access/rest/routes/account_keys.go +++ b/engine/access/rest/routes/account_keys.go @@ -8,8 +8,8 @@ import ( "github.com/onflow/flow-go/engine/access/rest/request" ) -// GetAccountKeyByID handler retrieves an account key by address and ID and returns the response -func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { +// GetAccountKeyByIndex handler retrieves an account key by address and index and returns the response +func GetAccountKeyByIndex(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { req, err := r.GetAccountKeyRequest() if err != nil { return nil, models.NewBadRequestError(err) @@ -33,12 +33,12 @@ func GetAccountKeyByID(r *request.Request, backend access.API, link models.LinkG var response models.AccountPublicKey for _, key := range account.Keys { - if key.Index == int(req.KeyIndex) { + if key.Index == int(req.Index) { response.Build(key) return response, nil } } - err = fmt.Errorf("account key with index: %d does not exist", req.KeyIndex) + err = fmt.Errorf("account key with index: %d does not exist", req.Index) return nil, models.NewNotFoundError(err.Error(), err) } diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/routes/account_keys_test.go index d6260559cba..c3d59324f6c 100644 --- a/engine/access/rest/routes/account_keys_test.go +++ b/engine/access/rest/routes/account_keys_test.go @@ -16,25 +16,25 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestGetAccountKeyByID tests local getAccountKeyByID request. +// TestGetAccountKeyByIndex tests local getAccountKeyByIndex request. // // Runs the following tests: -// 1. Get key by address and ID at latest sealed block. -// 2. Get key by address and ID at latest finalized block. -// 3. Get missing key by address and ID at latest sealed block. -// 4. Get missing key by address and ID at latest finalized block. -// 5. Get key by missing address and ID at latest sealed block. -// 6. Get key by missing address and ID at latest finalized block. -// 7. Get key by address and ID at height. -func TestGetAccountKeyByID(t *testing.T) { +// 1. Get key by address and index at latest sealed block. +// 2. Get key by address and index at latest finalized block. +// 3. Get missing key by address and index at latest sealed block. +// 4. Get missing key by address and index at latest finalized block. +// 5. Get key by missing address and index at latest sealed block. +// 6. Get key by missing address and index at latest finalized block. +// 7. Get key by address and index at height. +func TestGetAccountKeyByIndex(t *testing.T) { backend := &mock.API{} - t.Run("get key by address and ID at latest sealed block", func(t *testing.T) { + t.Run("get key by address and index at latest sealed block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, "0", sealedHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, "0", sealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -50,12 +50,12 @@ func TestGetAccountKeyByID(t *testing.T) { mocktestify.AssertExpectationsForObjects(t, backend) }) - t.Run("get key by address and ID at latest finalized block", func(t *testing.T) { + t.Run("get key by address and index at latest finalized block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, "0", finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -71,13 +71,13 @@ func TestGetAccountKeyByID(t *testing.T) { mocktestify.AssertExpectationsForObjects(t, backend) }) - t.Run("get missing key by address and ID at latest sealed block", func(t *testing.T) { + t.Run("get missing key by address and index at latest sealed block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 - keyIndex := "2" + index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, keyIndex, sealedHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, sealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -93,18 +93,18 @@ func TestGetAccountKeyByID(t *testing.T) { "code": %d, "message": "account key with index: %s does not exist" } - `, statusCode, keyIndex) + `, statusCode, index) assertResponse(t, req, statusCode, expected, backend) }) - t.Run("get missing key by address and ID at latest finalized block", func(t *testing.T) { + t.Run("get missing key by address and index at latest finalized block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 - keyIndex := "2" + index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, keyIndex, finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, finalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -120,18 +120,18 @@ func TestGetAccountKeyByID(t *testing.T) { "code": %d, "message": "account key with index: %s does not exist" } - `, statusCode, keyIndex) + `, statusCode, index) assertResponse(t, req, statusCode, expected, backend) }) - t.Run("get key by missing address and ID at latest sealed block", func(t *testing.T) { + t.Run("get key by missing address and index at latest sealed block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 - keyIndex := "2" + index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, keyIndex, sealedHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, sealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -153,13 +153,13 @@ func TestGetAccountKeyByID(t *testing.T) { assertResponse(t, req, statusCode, expected, backend) }) - t.Run("get key by missing address and ID at latest finalized block", func(t *testing.T) { + t.Run("get key by missing address and index at latest finalized block", func(t *testing.T) { account := accountFixture(t) var height uint64 = 100 - keyIndex := "2" + index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIDRequest(t, account, keyIndex, finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, finalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -181,10 +181,10 @@ func TestGetAccountKeyByID(t *testing.T) { assertResponse(t, req, statusCode, expected, backend) }) - t.Run("get key by address and ID at height", func(t *testing.T) { + t.Run("get key by address and index at height", func(t *testing.T) { var height uint64 = 1337 account := accountFixture(t) - req := getAccountKeyByIDRequest(t, account, "0", "1337") + req := getAccountKeyByIndexRequest(t, account, "0", "1337") backend.Mock. On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). @@ -207,7 +207,7 @@ func TestGetAccountKeyByID(t *testing.T) { `{"code":400, "message":"invalid address"}`, }, { - "get key with invalid ID", + "get key with invalid index", accountKeyURL( t, unittest.AddressFixture().String(), @@ -240,9 +240,9 @@ func TestGetAccountKeyByID(t *testing.T) { } } -func accountKeyURL(t *testing.T, address string, keyIndex string, height string) string { +func accountKeyURL(t *testing.T, address string, index string, height string) string { u, err := url.ParseRequestURI( - fmt.Sprintf("/v1/accounts/%s/keys/%s", address, keyIndex), + fmt.Sprintf("/v1/accounts/%s/keys/%s", address, index), ) require.NoError(t, err) q := u.Query() @@ -255,15 +255,15 @@ func accountKeyURL(t *testing.T, address string, keyIndex string, height string) return u.String() } -func getAccountKeyByIDRequest( +func getAccountKeyByIndexRequest( t *testing.T, account *flow.Account, - keyIndex string, + index string, height string, ) *http.Request { req, err := http.NewRequest( "GET", - accountKeyURL(t, account.Address.String(), keyIndex, height), + accountKeyURL(t, account.Address.String(), index, height), nil, ) require.NoError(t, err) diff --git a/engine/access/rest/routes/router.go b/engine/access/rest/routes/router.go index b9da8ee1cc1..5dd25140030 100644 --- a/engine/access/rest/routes/router.go +++ b/engine/access/rest/routes/router.go @@ -103,9 +103,9 @@ var Routes = []route{{ Handler: GetAccount, }, { Method: http.MethodGet, - Pattern: "/accounts/{address}/keys/{key_index}", - Name: "getAccountKeyByID", - Handler: GetAccountKeyByID, + Pattern: "/accounts/{address}/keys/{index}", + Name: "getAccountKeyByIndex", + Handler: GetAccountKeyByIndex, }, { Method: http.MethodGet, Pattern: "/events", @@ -171,7 +171,7 @@ func normalizeURL(url string) (string, error) { // address based resource. e.g. /v1/accounts/1234567890abcdef parts = append(parts, "{address}") if matches[0][4] != "" { - parts = append(parts, "keys", "{key_index}") + parts = append(parts, "keys", "{index}") } default: // named resource. e.g. /v1/network/parameters diff --git a/engine/access/rest/routes/router_test.go b/engine/access/rest/routes/router_test.go index 2fd7cb1fdb9..2b0f6e218aa 100644 --- a/engine/access/rest/routes/router_test.go +++ b/engine/access/rest/routes/router_test.go @@ -70,9 +70,9 @@ func TestParseURL(t *testing.T) { expected: "getAccount", }, { - name: "/v1/accounts/{address}/keys/{key_index}", + name: "/v1/accounts/{address}/keys/{index}", url: "/v1/accounts/6a587be304c1224c/keys/0", - expected: "getAccountKeyByID", + expected: "getAccountKeyByIndex", }, { name: "/v1/events", @@ -162,9 +162,9 @@ func TestBenchmarkParseURL(t *testing.T) { expected: "getAccount", }, { - name: "/v1/accounts/{address}/keys/{key_index}", + name: "/v1/accounts/{address}/keys/{index}", url: "/v1/accounts/6a587be304c1224c/keys/0", - expected: "getAccountKeyByID", + expected: "getAccountKeyByIndex", }, { name: "/v1/events", From 25374df29468ae28971873743a216139be57b814 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Thu, 10 Aug 2023 13:10:18 +0300 Subject: [PATCH 422/815] Add more test cases for the GetAccountKeyByIndex endpoint --- .../rest/request/get_account_key_test.go | 18 +++++++++++ engine/access/rest/routes/account_keys.go | 6 ++-- .../access/rest/routes/account_keys_test.go | 32 ++++++++++++++++++- engine/access/rest/routes/router.go | 2 +- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/engine/access/rest/request/get_account_key_test.go b/engine/access/rest/request/get_account_key_test.go index 2b74421cc91..73288aa9695 100644 --- a/engine/access/rest/request/get_account_key_test.go +++ b/engine/access/rest/request/get_account_key_test.go @@ -58,4 +58,22 @@ func Test_GetAccountKey_ValidParse(t *testing.T) { assert.Equal(t, getAccountKey.Address.String(), addr) assert.Equal(t, getAccountKey.Index, uint64(5)) assert.Equal(t, getAccountKey.Height, uint64(100)) + + err = getAccountKey.Parse(addr, keyIndex, "") + assert.NoError(t, err) + assert.Equal(t, getAccountKey.Address.String(), addr) + assert.Equal(t, getAccountKey.Index, uint64(5)) + assert.Equal(t, getAccountKey.Height, SealedHeight) + + err = getAccountKey.Parse(addr, keyIndex, "sealed") + assert.NoError(t, err) + assert.Equal(t, getAccountKey.Address.String(), addr) + assert.Equal(t, getAccountKey.Index, uint64(5)) + assert.Equal(t, getAccountKey.Height, SealedHeight) + + err = getAccountKey.Parse(addr, keyIndex, "final") + assert.NoError(t, err) + assert.Equal(t, getAccountKey.Address.String(), addr) + assert.Equal(t, getAccountKey.Index, uint64(5)) + assert.Equal(t, getAccountKey.Height, FinalHeight) } diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/routes/account_keys.go index 3f6d795ad87..be3c36a06d4 100644 --- a/engine/access/rest/routes/account_keys.go +++ b/engine/access/rest/routes/account_keys.go @@ -15,12 +15,14 @@ func GetAccountKeyByIndex(r *request.Request, backend access.API, link models.Li return nil, models.NewBadRequestError(err) } - // in case we receive special height values 'final' and 'sealed', fetch that height and overwrite request with it + // In case we receive special height values 'final' and 'sealed', + // fetch that height and overwrite request with it. if req.Height == request.FinalHeight || req.Height == request.SealedHeight { isSealed := req.Height == request.SealedHeight header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed) if err != nil { - return nil, err + err := fmt.Errorf("block with height: %d does not exist", req.Height) + return nil, models.NewNotFoundError(err.Error(), err) } req.Height = header.Height } diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/routes/account_keys_test.go index c3d59324f6c..241e40240e5 100644 --- a/engine/access/rest/routes/account_keys_test.go +++ b/engine/access/rest/routes/account_keys_test.go @@ -2,6 +2,7 @@ package routes import ( "fmt" + "math" "net/http" "net/url" "testing" @@ -26,8 +27,9 @@ import ( // 5. Get key by missing address and index at latest sealed block. // 6. Get key by missing address and index at latest finalized block. // 7. Get key by address and index at height. +// 8. Get key by address and index at missing block. func TestGetAccountKeyByIndex(t *testing.T) { - backend := &mock.API{} + backend := mock.NewAPI(t) t.Run("get key by address and index at latest sealed block", func(t *testing.T) { account := accountFixture(t) @@ -96,6 +98,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { `, statusCode, index) assertResponse(t, req, statusCode, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) }) t.Run("get missing key by address and index at latest finalized block", func(t *testing.T) { @@ -123,6 +126,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { `, statusCode, index) assertResponse(t, req, statusCode, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) }) t.Run("get key by missing address and index at latest sealed block", func(t *testing.T) { @@ -151,6 +155,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { `, statusCode, account.Address) assertResponse(t, req, statusCode, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) }) t.Run("get key by missing address and index at latest finalized block", func(t *testing.T) { @@ -179,6 +184,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { `, statusCode, account.Address) assertResponse(t, req, statusCode, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) }) t.Run("get key by address and index at height", func(t *testing.T) { @@ -196,6 +202,30 @@ func TestGetAccountKeyByIndex(t *testing.T) { mocktestify.AssertExpectationsForObjects(t, backend) }) + t.Run("get key by address and index at missing block", func(t *testing.T) { + backend := mock.NewAPI(t) + account := accountFixture(t) + const finalHeight uint64 = math.MaxUint64 - 2 + + req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) + + err := fmt.Errorf("block with height: %d does not exist", finalHeight) + backend.Mock. + On("GetLatestBlockHeader", mocktestify.Anything, false). + Return(nil, flow.BlockStatusUnknown, err) + + statusCode := 404 + expected := fmt.Sprintf(` + { + "code": %d, + "message": "block with height: %d does not exist" + } + `, statusCode, finalHeight) + + assertResponse(t, req, statusCode, expected, backend) + mocktestify.AssertExpectationsForObjects(t, backend) + }) + tests := []struct { name string url string diff --git a/engine/access/rest/routes/router.go b/engine/access/rest/routes/router.go index 5dd25140030..6c7c3965d6a 100644 --- a/engine/access/rest/routes/router.go +++ b/engine/access/rest/routes/router.go @@ -170,7 +170,7 @@ func normalizeURL(url string) (string, error) { case 16: // address based resource. e.g. /v1/accounts/1234567890abcdef parts = append(parts, "{address}") - if matches[0][4] != "" { + if matches[0][5] == "keys" { parts = append(parts, "keys", "{index}") } default: From 581b085a70b5981df149fc87636da676bb3b5b97 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Thu, 10 Aug 2023 15:22:13 -0700 Subject: [PATCH 423/815] make missing header error more specific --- engine/execution/rpc/engine.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/rpc/engine.go b/engine/execution/rpc/engine.go index 3cb49a6ca8c..a69caaaa6d8 100644 --- a/engine/execution/rpc/engine.go +++ b/engine/execution/rpc/engine.go @@ -186,7 +186,7 @@ func (h *handler) ExecuteScriptAtBlockID( // return a more user friendly error if block has not been executed if _, err = h.commits.ByBlockID(blockID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockID) } return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) } @@ -257,7 +257,7 @@ func (h *handler) GetEventsForBlockIDs( // Check if block has been executed if _, err := h.commits.ByBlockID(bID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", bID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", bID) } return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", bID) } @@ -419,7 +419,7 @@ func (h *handler) GetTransactionResultsByBlockID( // an empty slice if block does not exist if _, err = h.commits.ByBlockID(blockID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockID) } return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) } @@ -531,7 +531,7 @@ func (h *handler) GetAccountAtBlockID( // return a more user friendly error if block has not been executed if _, err = h.commits.ByBlockID(blockFlowID); err != nil { if errors.Is(err, storage.ErrNotFound) { - return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node", blockFlowID) + return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockFlowID) } return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockFlowID) } From e020c368b68a3d072cc27ea8e2a79e84c88c2724 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Fri, 11 Aug 2023 09:45:25 -0700 Subject: [PATCH 424/815] [CI] Print builds and tools job inputs --- .github/workflows/builds.yml | 6 ++++++ .github/workflows/tools.yml | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 94120bdf62c..09851a53f7f 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -51,6 +51,9 @@ jobs: outputs: matrix: ${{ steps.generate.outputs.matrix }} steps: + - name: Print all input variables + run: echo '${{ toJson(inputs) }}' | jq + - id: generate run: | roles=() @@ -74,6 +77,7 @@ jobs: fi rolesJSON=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${roles[@]}") echo "matrix={\"role\":$(echo $rolesJSON)}" >> $GITHUB_OUTPUT + docker-push: name: ${{ matrix.role }} images runs-on: ubuntu-latest @@ -89,10 +93,12 @@ jobs: uses: actions/setup-go@v2 with: go-version: '1.19' + - name: Checkout repo uses: actions/checkout@v2 with: ref: ${{ inputs.tag }} + # Provide Google Service Account credentials to Github Action, allowing interaction with the Google Container Registry # Logging in as github-actions@dl-flow.iam.gserviceaccount.com - name: Docker login diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 9f228f215ba..47ddaf519e1 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -18,6 +18,8 @@ jobs: name: Build boot tools runs-on: ubuntu-latest steps: + - name: Print all input variables + run: echo '${{ toJson(inputs) }}' | jq - id: auth uses: google-github-actions/auth@v1 with: From d5b4b4f599866e3b1839164bc327b008d39de400 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Fri, 11 Aug 2023 10:17:52 -0700 Subject: [PATCH 425/815] [BN2] Remove deprecated --fast-kg from bootstrap --- cmd/bootstrap/README.md | 1 - integration/benchnet2/Makefile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/bootstrap/README.md b/cmd/bootstrap/README.md index 9000f4d87f4..14339cc91ac 100644 --- a/cmd/bootstrap/README.md +++ b/cmd/bootstrap/README.md @@ -98,7 +98,6 @@ Each input is a config file specified as a command line parameter: #### Example ```bash go run -tags relic ./cmd/bootstrap finalize \ - --fast-kg \ --root-chain main \ --root-height 0 \ --root-parent 0000000000000000000000000000000000000000000000000000000000000000 \ diff --git a/integration/benchnet2/Makefile b/integration/benchnet2/Makefile index 62859fbf74c..0ee4b9a5fdf 100644 --- a/integration/benchnet2/Makefile +++ b/integration/benchnet2/Makefile @@ -33,7 +33,7 @@ gen-bootstrap: clone-flow cd flow-go/cmd/bootstrap && go run -tags relic . keygen --machine-account --config ../../../bootstrap/conf/node-config.json -o ../../../bootstrap/keys echo {} > ./bootstrap/conf/partner-stakes.json mkdir ./bootstrap/partner-nodes - cd flow-go/cmd/bootstrap && go run -tags relic . rootblock --root-chain bench --root-height 0 --root-parent 0000000000000000000000000000000000000000000000000000000000000000 --config ../../../bootstrap/conf/node-config.json -o ../../../bootstrap/ --fast-kg --partner-dir ../../../bootstrap/partner-nodes --partner-weights ../../../bootstrap/conf/partner-stakes.json --internal-priv-dir ../../../bootstrap/keys/private-root-information + cd flow-go/cmd/bootstrap && go run -tags relic . rootblock --root-chain bench --root-height 0 --root-parent 0000000000000000000000000000000000000000000000000000000000000000 --config ../../../bootstrap/conf/node-config.json -o ../../../bootstrap/ --partner-dir ../../../bootstrap/partner-nodes --partner-weights ../../../bootstrap/conf/partner-stakes.json --internal-priv-dir ../../../bootstrap/keys/private-root-information cd flow-go/cmd/bootstrap && go run -tags relic . finalize --root-commit 0000000000000000000000000000000000000000000000000000000000000000 --service-account-public-key-json "{\"PublicKey\":\"R7MTEDdLclRLrj2MI1hcp4ucgRTpR15PCHAWLM5nks6Y3H7+PGkfZTP2di2jbITooWO4DD1yqaBSAVK8iQ6i0A==\",\"SignAlgo\":2,\"HashAlgo\":1,\"SeqNumber\":0,\"Weight\":1000}" --config ../../../bootstrap/conf/node-config.json -o ../../../bootstrap/ --partner-dir ../../../bootstrap/partner-nodes --partner-weights ../../../bootstrap/conf/partner-stakes.json --collection-clusters 1 --epoch-counter 0 --epoch-length 30000 --epoch-staking-phase-length 20000 --epoch-dkg-phase-length 2000 --genesis-token-supply="1000000000.0" --protocol-version=0 --internal-priv-dir ../../../bootstrap/keys/private-root-information --dkg-data ../../../bootstrap/private-root-information/root-dkg-data.priv.json --root-block ../../../bootstrap/public-root-information/root-block.json --root-block-votes-dir ../../../bootstrap/public-root-information/root-block-votes/ --epoch-commit-safety-threshold=1000 gen-helm-l1: From 16855ede00508569007f1180bd78e93acd90f0ea Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Fri, 11 Aug 2023 16:37:50 -0700 Subject: [PATCH 426/815] fix unittests --- engine/access/rpc/backend/backend_test.go | 4 ++-- engine/access/rpc/backend/retry_test.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 9c87d88a4e4..544319dc937 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -856,7 +856,7 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.execClient. On("GetTransactionResult", ctx, exeEventReq). Return(exeEventResp, status.Errorf(codes.NotFound, "not found")). - Once() + Times(len(fixedENIDs)) // should call each EN once // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) @@ -1114,7 +1114,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { suite.execClient. On("GetTransactionResult", ctx, exeEventReq). Return(exeEventResp, status.Errorf(codes.NotFound, "not found")). - Once() + Times(len(enIDs)) // should call each EN once // create a mock connection factory connFactory := suite.setupConnectionFactory() diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 07bb77aa8a9..ede8d5b9332 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -163,7 +163,10 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.colClient.On("SendTransaction", mock.Anything, mock.Anything).Return(&access.SendTransactionResponse{}, nil) // return not found to return finalized status - suite.execClient.On("GetTransactionResult", ctx, &exeEventReq).Return(&exeEventResp, status.Errorf(codes.NotFound, "not found")).Once() + suite.execClient.On("GetTransactionResult", ctx, &exeEventReq). + Return(&exeEventResp, status.Errorf(codes.NotFound, "not found")). + Times(len(enIDs)) // should call each EN once + // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) suite.checkResponse(result, err) From ad83ebf8db7eb8790623f0121a2833202192fa54 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 05:34:29 -0400 Subject: [PATCH 427/815] split TestOnSyncRequest() into 3 tests --- engine/common/synchronization/engine_test.go | 36 +++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 128c4684376..097bcbe11b8 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -180,8 +180,9 @@ func (ss *SyncSuite) SetupTest() { ss.e = e } -func (ss *SyncSuite) TestOnSyncRequest() { - +// TestOnSyncRequest_WithinTolerance tests that a sync request that's within tolerance of the receiver doesn't trigger +// a response, even if request height is lower than receiver. +func (ss *SyncSuite) TestOnSyncRequest_WithinTolerance() { // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ @@ -196,14 +197,41 @@ func (ss *SyncSuite) TestOnSyncRequest() { ss.Assert().NoError(err, "same height sync request should pass") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + ss.core.AssertExpectations(ss.T()) +} + +// TestOnSyncRequest_HeightHigherThanReceiver tests that a sync request that's higher than the receiver's height doesn't +// trigger a response, even if outside tolerance. +func (ss *SyncSuite) TestOnSyncRequest_HeightHigherThanReceiver() { + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: rand.Uint64(), + Height: 0, + } + // if request height is higher than local finalized, we should not respond req.Height = ss.head.Height + 1 ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - err = ss.e.requestHandler.onSyncRequest(originID, req) + err := ss.e.requestHandler.onSyncRequest(originID, req) ss.Assert().NoError(err, "same height sync request should pass") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + ss.core.AssertExpectations(ss.T()) +} + +// TestOnSyncRequest_HeightLowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and +// lower than the receiver's height triggers a response. +func (ss *SyncSuite) TestOnSyncRequest_HeightLowerThanReceiver_OutsideTolerance() { + + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: rand.Uint64(), + Height: 0, + } + // if the request height is lower than head and outside tolerance, we should submit correct response req.Height = ss.head.Height - 1 ss.core.On("HandleHeight", ss.head, req.Height) @@ -217,7 +245,7 @@ func (ss *SyncSuite) TestOnSyncRequest() { assert.Equal(ss.T(), originID, recipientID, "should send response to original sender") }, ) - err = ss.e.requestHandler.onSyncRequest(originID, req) + err := ss.e.requestHandler.onSyncRequest(originID, req) require.NoError(ss.T(), err, "smaller height sync request should pass") ss.core.AssertExpectations(ss.T()) From f7b7b62c4c696eab9f87a66fd25a77df71f8cdad Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 07:42:15 -0400 Subject: [PATCH 428/815] CacheFactory unexported, test helpers moved to test --- network/alsp/manager/manager.go | 26 +++- network/alsp/manager/manager_helper_test.go | 126 ------------------ network/alsp/manager/manager_test.go | 138 +++++++++++++++++--- 3 files changed, 140 insertions(+), 150 deletions(-) delete mode 100644 network/alsp/manager/manager_helper_test.go diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index c0816627bd6..4c84d2b97b0 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -69,12 +69,12 @@ type MisbehaviorReportManager struct { component.Component logger zerolog.Logger metrics module.AlspMetrics - // CacheFactory is the factory for creating the spam record cache. MisbehaviorReportManager is coming with a + // cacheFactory is the factory for creating the spam record cache. MisbehaviorReportManager is coming with a // default factory that creates a new spam record cache with the given parameter. However, this factory can be // overridden with a custom factory. - CacheFactory SpamRecordCacheFactory + cacheFactory SpamRecordCacheFactory // cache is the spam record cache that stores the spam records for the authorized nodes. It is initialized by - // invoking the CacheFactory. + // invoking the cacheFactory. cache alsp.SpamRecordCache // disablePenalty indicates whether applying the penalty to the misbehaving node is disabled. // When disabled, the ALSP module logs the misbehavior reports and updates the metrics, but does not apply the penalty. @@ -170,7 +170,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n metrics: cfg.AlspMetrics, disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, - CacheFactory: defaultSpamRecordCacheFactory(), + cacheFactory: defaultSpamRecordCacheFactory(), DecayFunc: defaultSpamRecordDecayFunc(), } @@ -188,7 +188,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n opt(m) } - m.cache = m.CacheFactory( + m.cache = m.cacheFactory( lg, cfg.SpamRecordCacheSize, metrics.ApplicationLayerSpamRecordCacheMetricFactory(cfg.HeroCacheMetricsFactory, cfg.NetworkType)) @@ -449,3 +449,19 @@ func (m *MisbehaviorReportManager) processMisbehaviorReport(report internal.Repo lg.Debug().Float64("updated_penalty", updatedPenalty).Msg("misbehavior report handled") return nil } + +// WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. +// Args: +// +// f: the spam record cache factory. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the spam record cache for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default factory should be sufficient for production. +func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportManagerOption { + return func(m *MisbehaviorReportManager) { + m.cacheFactory = f + } +} diff --git a/network/alsp/manager/manager_helper_test.go b/network/alsp/manager/manager_helper_test.go deleted file mode 100644 index f089c4afffd..00000000000 --- a/network/alsp/manager/manager_helper_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package alspmgr_test - -import ( - "math" - "math/rand" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/config" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/alsp" - alspmgr "github.com/onflow/flow-go/network/alsp/manager" - "github.com/onflow/flow-go/network/alsp/model" - "github.com/onflow/flow-go/network/mocknetwork" - "github.com/onflow/flow-go/utils/unittest" -) - -// This file contains helper functions for testing the ALSP manager. - -// createRandomMisbehaviorReportsForOriginId creates a slice of random misbehavior reports for a single origin id. -// Args: -// - t: the testing.T instance -// - originID: the origin id of the misbehavior reports -// - numReports: the number of misbehavior reports to create -// Returns: -// - []network.MisbehaviorReport: the slice of misbehavior reports -// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. -func createRandomMisbehaviorReportsForOriginId(t *testing.T, originID flow.Identifier, numReports int) []network.MisbehaviorReport { - reports := make([]network.MisbehaviorReport, numReports) - - for i := 0; i < numReports; i++ { - reports[i] = misbehaviorReportFixture(t, originID) - } - - return reports -} - -// createRandomMisbehaviorReports creates a slice of random misbehavior reports. -// Args: -// - t: the testing.T instance -// - numReports: the number of misbehavior reports to create -// Returns: -// - []network.MisbehaviorReport: the slice of misbehavior reports -// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. -func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.MisbehaviorReport { - reports := make([]network.MisbehaviorReport, numReports) - - for i := 0; i < numReports; i++ { - reports[i] = misbehaviorReportFixture(t, unittest.IdentifierFixture()) - } - - return reports -} - -// managerCfgFixture creates a new MisbehaviorReportManagerConfig with default values for testing. -func managerCfgFixture(t *testing.T) *alspmgr.MisbehaviorReportManagerConfig { - c, err := config.DefaultConfig() - require.NoError(t, err) - return &alspmgr.MisbehaviorReportManagerConfig{ - Logger: unittest.Logger(), - SpamRecordCacheSize: c.NetworkConfig.AlspConfig.SpamRecordCacheSize, - SpamReportQueueSize: c.NetworkConfig.AlspConfig.SpamReportQueueSize, - HeartBeatInterval: c.NetworkConfig.AlspConfig.HearBeatInterval, - AlspMetrics: metrics.NewNoopCollector(), - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - } -} - -// misbehaviorReportFixture creates a mock misbehavior report for a single origin id. -// Args: -// - t: the testing.T instance -// - originID: the origin id of the misbehavior report -// Returns: -// - network.MisbehaviorReport: the misbehavior report -// Note: the penalty of the misbehavior report is randomly chosen between -1 and -10. -func misbehaviorReportFixture(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { - return misbehaviorReportFixtureWithPenalty(t, originID, math.Min(-1, float64(-1-rand.Intn(10)))) -} - -func misbehaviorReportFixtureWithDefaultPenalty(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { - return misbehaviorReportFixtureWithPenalty(t, originID, model.DefaultPenaltyValue) -} - -func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, penalty float64) network.MisbehaviorReport { - report := mocknetwork.NewMisbehaviorReport(t) - report.On("OriginId").Return(originID) - report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) - report.On("Penalty").Return(penalty) - - return report -} - -// WithDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. -// Args: -// -// f: the decay function. -// -// Returns: -// -// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. -// -// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. -func WithDecayFunc(f alspmgr.SpamRecordDecayFunc) alspmgr.MisbehaviorReportManagerOption { - return func(m *alspmgr.MisbehaviorReportManager) { - m.DecayFunc = f - } -} - -// WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. -// Args: -// -// f: the spam record cache factory. -// -// Returns: -// -// a MisbehaviorReportManagerOption that sets the spam record cache for the MisbehaviorReportManager. -// -// Note: this option is useful primarily for testing purposes. The default factory should be sufficient for production. -func WithSpamRecordsCacheFactory(f alspmgr.SpamRecordCacheFactory) alspmgr.MisbehaviorReportManagerOption { - return func(m *alspmgr.MisbehaviorReportManager) { - m.CacheFactory = f - } -} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 8ff4cc0609a..b98dce6f7d3 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" @@ -105,7 +106,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { // the ALSP manager. var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -198,7 +199,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // the ALSP manager. var victimSpamRecordCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCache }), @@ -289,11 +290,11 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // the ALSP manager. var victimSpamRecordCache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCache }), - WithDecayFunc(fastDecayFunc), + withDecayFunc(fastDecayFunc), } ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, @@ -621,7 +622,7 @@ func TestNewMisbehaviorReportManager(t *testing.T) { consumer := mocknetwork.NewDisallowListNotificationConsumer(t) var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -692,7 +693,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport(t *testing.T) { // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -752,7 +753,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReport_PenaltyDisable(t *testing.T // we use a mock cache but we do not expect any calls to the cache, since the penalty is disabled. var cache *mockalsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = mockalsp.NewSpamRecordCache(t) return cache }), @@ -807,7 +808,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Sequentiall // create a new MisbehaviorReportManager var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -869,7 +870,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForSinglePeer_Concurrentl var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -940,7 +941,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Sequential var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1000,7 +1001,7 @@ func TestHandleMisbehaviorReport_SinglePenaltyReportsForMultiplePeers_Concurrent var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1070,7 +1071,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Sequenti var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1151,7 +1152,7 @@ func TestHandleMisbehaviorReport_MultiplePenaltyReportsForMultiplePeers_Concurre var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1226,7 +1227,7 @@ func TestHandleMisbehaviorReport_DuplicateReportsForSinglePeer_Concurrently(t *t var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1294,7 +1295,7 @@ func TestDecayMisbehaviorPenalty_SingleHeartbeat(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1384,7 +1385,7 @@ func TestDecayMisbehaviorPenalty_MultipleHeartbeats(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1474,7 +1475,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1559,7 +1560,7 @@ func TestDecayMisbehaviorPenalty_DecayToZero_AllowListing(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1609,18 +1610,23 @@ func TestDecayMisbehaviorPenalty_DecayToZero_AllowListing(t *testing.T) { require.Eventually(t, func() bool { record, ok = cache.Get(originId) if !ok { + t.Log("spam record not found in cache") return false } if record.DisallowListed { + t.Logf("peer %s is still disallow-listed", originId) return false // the peer should not be allow-listed yet. } if record.Penalty != float64(0) { + t.Log("penalty is not decayed to zero") return false // the penalty should be decayed to zero. } if record.CutoffCounter != 1 { + t.Logf("cutoff counter is %d, expected 1", record.CutoffCounter) return false // the cutoff counter should be incremented. } if record.Decay != model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay { + t.Logf("decay is %f, expected %f", record.Decay, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay) return false // the decay should be the default decay value. } @@ -1639,7 +1645,7 @@ func TestDisallowListNotification(t *testing.T) { var cache alsp.SpamRecordCache cfg.Opts = []alspmgr.MisbehaviorReportManagerOption{ - WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { + alspmgr.WithSpamRecordsCacheFactory(func(logger zerolog.Logger, size uint32, metrics module.HeroCacheMetrics) alsp.SpamRecordCache { cache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return cache }), @@ -1711,3 +1717,97 @@ func TestDisallowListNotification(t *testing.T) { return true }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") } + +////////////////////////////// TEST HELPERS /////////////////////////////////////////////////////////////////////////////// +// The following functions are helpers for the tests. It wasn't feasible to put them in a helper file in the alspmgr_test +// package because that would break encapsulation of the ALSP manager and require making some fields exportable. +// Putting them in alspmgr package would cause a circular import cycle. Therefore, they are put in the internal test package here. + +// createRandomMisbehaviorReportsForOriginId creates a slice of random misbehavior reports for a single origin id. +// Args: +// - t: the testing.T instance +// - originID: the origin id of the misbehavior reports +// - numReports: the number of misbehavior reports to create +// Returns: +// - []network.MisbehaviorReport: the slice of misbehavior reports +// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. +func createRandomMisbehaviorReportsForOriginId(t *testing.T, originID flow.Identifier, numReports int) []network.MisbehaviorReport { + reports := make([]network.MisbehaviorReport, numReports) + + for i := 0; i < numReports; i++ { + reports[i] = misbehaviorReportFixture(t, originID) + } + + return reports +} + +// createRandomMisbehaviorReports creates a slice of random misbehavior reports. +// Args: +// - t: the testing.T instance +// - numReports: the number of misbehavior reports to create +// Returns: +// - []network.MisbehaviorReport: the slice of misbehavior reports +// Note: the penalty of the misbehavior reports is randomly chosen between -1 and -10. +func createRandomMisbehaviorReports(t *testing.T, numReports int) []network.MisbehaviorReport { + reports := make([]network.MisbehaviorReport, numReports) + + for i := 0; i < numReports; i++ { + reports[i] = misbehaviorReportFixture(t, unittest.IdentifierFixture()) + } + + return reports +} + +// managerCfgFixture creates a new MisbehaviorReportManagerConfig with default values for testing. +func managerCfgFixture(t *testing.T) *alspmgr.MisbehaviorReportManagerConfig { + c, err := config.DefaultConfig() + require.NoError(t, err) + return &alspmgr.MisbehaviorReportManagerConfig{ + Logger: unittest.Logger(), + SpamRecordCacheSize: c.NetworkConfig.AlspConfig.SpamRecordCacheSize, + SpamReportQueueSize: c.NetworkConfig.AlspConfig.SpamReportQueueSize, + HeartBeatInterval: c.NetworkConfig.AlspConfig.HearBeatInterval, + AlspMetrics: metrics.NewNoopCollector(), + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + } +} + +// misbehaviorReportFixture creates a mock misbehavior report for a single origin id. +// Args: +// - t: the testing.T instance +// - originID: the origin id of the misbehavior report +// Returns: +// - network.MisbehaviorReport: the misbehavior report +// Note: the penalty of the misbehavior report is randomly chosen between -1 and -10. +func misbehaviorReportFixture(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, math.Min(-1, float64(-1-rand.Intn(10)))) +} + +func misbehaviorReportFixtureWithDefaultPenalty(t *testing.T, originID flow.Identifier) network.MisbehaviorReport { + return misbehaviorReportFixtureWithPenalty(t, originID, model.DefaultPenaltyValue) +} + +func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, penalty float64) network.MisbehaviorReport { + report := mocknetwork.NewMisbehaviorReport(t) + report.On("OriginId").Return(originID) + report.On("Reason").Return(alsp.AllMisbehaviorTypes()[rand.Intn(len(alsp.AllMisbehaviorTypes()))]) + report.On("Penalty").Return(penalty) + + return report +} + +// withDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. +// Args: +// +// f: the decay function. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. +func withDecayFunc(f alspmgr.SpamRecordDecayFunc) alspmgr.MisbehaviorReportManagerOption { + return func(m *alspmgr.MisbehaviorReportManager) { + m.DecayFunc = f + } +} From 2e027bc40f1d064bf0a28ebc783153c5b97ea240 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 07:49:08 -0400 Subject: [PATCH 429/815] DecayFunc unexported --- network/alsp/manager/manager.go | 24 ++++++++++++++++++++---- network/alsp/manager/manager_test.go | 18 +----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 4c84d2b97b0..bf7f632cbae 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -89,8 +89,8 @@ type MisbehaviorReportManager struct { // workerPool is the worker pool for handling the misbehavior reports in a thread-safe and non-blocking manner. workerPool *worker.Pool[internal.ReportedMisbehaviorWork] - // DecayFunc is the function that calculates the decay of the spam record. - DecayFunc SpamRecordDecayFunc + // decayFunc is the function that calculates the decay of the spam record. + decayFunc SpamRecordDecayFunc } var _ network.MisbehaviorReportManager = (*MisbehaviorReportManager)(nil) @@ -171,7 +171,7 @@ func NewMisbehaviorReportManager(cfg *MisbehaviorReportManagerConfig, consumer n disablePenalty: cfg.DisablePenalty, disallowListingConsumer: consumer, cacheFactory: defaultSpamRecordCacheFactory(), - DecayFunc: defaultSpamRecordDecayFunc(), + decayFunc: defaultSpamRecordDecayFunc(), } store := queue.NewHeroStore( @@ -342,7 +342,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). Msg("onHeartbeat - before adjusting penalty via decayFunc") - record.Penalty = m.DecayFunc(record) + record.Penalty = m.decayFunc(record) m.logger.Trace(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). @@ -465,3 +465,19 @@ func WithSpamRecordsCacheFactory(f SpamRecordCacheFactory) MisbehaviorReportMana m.cacheFactory = f } } + +// WithDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. +// Args: +// +// f: the decay function. +// +// Returns: +// +// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. +// +// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. +func WithDecayFunc(f SpamRecordDecayFunc) MisbehaviorReportManagerOption { + return func(m *MisbehaviorReportManager) { + m.decayFunc = f + } +} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index b98dce6f7d3..7be30014fda 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -294,7 +294,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio victimSpamRecordCache = internal.NewSpamRecordCache(size, logger, metrics, model.SpamRecordFactory()) return victimSpamRecordCache }), - withDecayFunc(fastDecayFunc), + alspmgr.WithDecayFunc(fastDecayFunc), } ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, @@ -1795,19 +1795,3 @@ func misbehaviorReportFixtureWithPenalty(t *testing.T, originID flow.Identifier, return report } - -// withDecayFunc sets the decay function for the MisbehaviorReportManager. Useful for testing purposes to simulate the decay of the penalty without waiting for the actual decay. -// Args: -// -// f: the decay function. -// -// Returns: -// -// a MisbehaviorReportManagerOption that sets the decay function for the MisbehaviorReportManager. -// -// Note: this option is useful primarily for testing purposes. The default decay function should be used for production. -func withDecayFunc(f alspmgr.SpamRecordDecayFunc) alspmgr.MisbehaviorReportManagerOption { - return func(m *alspmgr.MisbehaviorReportManager) { - m.DecayFunc = f - } -} From e5cf8fd949cbc7cd2e5ab0b07dcf72a96cf03686 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 08:02:20 -0400 Subject: [PATCH 430/815] model.DisallowListingThreshold clean up --- network/alsp/manager/manager.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index bf7f632cbae..08b104a8417 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -312,10 +312,9 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). - // as long as record.Penalty is NOT below model.disallowListingThreshold, + // as long as record.Penalty is NOT below model.DisallowListingThreshold, // the node is considered allow-listed and can conduct inbound and outbound connections. - // Once it falls below model.disallowListingThreshold, it needs to be disallow listed. - //if record.Penalty < model.disallowListingThreshold && !record.DisallowListed { + // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. if record.Penalty < model.DisallowListingThreshold && !record.DisallowListed { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ From 133a24bdbc295f96e82a8bcd971a031f299be774 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 08:07:17 -0400 Subject: [PATCH 431/815] heartbeat interval log update --- network/alsp/manager/manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 08b104a8417..e53546846c3 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -340,14 +340,14 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). - Msg("onHeartbeat - before adjusting penalty via decayFunc") + Msg("heartbeat interval, pulled the spam record for decaying") record.Penalty = m.decayFunc(record) m.logger.Trace(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). - Msg("onHeartbeat - after adjusting penalty via decayFunc") + Msg("heartbeat interval, after adjusting penalty via decayFunc") // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). From 7e75e86659fcbe7df443d178249dc01ebe3fca88 Mon Sep 17 00:00:00 2001 From: Misha Date: Sat, 12 Aug 2023 08:09:43 -0400 Subject: [PATCH 432/815] heartbeat interval log update 2 --- network/alsp/manager/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index e53546846c3..57f99d77a7c 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -347,7 +347,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Uint64("cutoff_counter", record.CutoffCounter). Bool("disallow_listed", record.DisallowListed). Float64("penalty", record.Penalty). - Msg("heartbeat interval, after adjusting penalty via decayFunc") + Msg("heartbeat interval, spam record penalty adjusted by decay function") // TODO: this can be done in batch but at this stage let's send individual notifications. // (it requires enabling the batch mode end-to-end including the cache in middleware). From 42897b21676f04cd3be7aa52272de7b8214d4a8c Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:15:33 +0100 Subject: [PATCH 433/815] Fix failing tests --- engine/access/rpc/backend/backend_test.go | 23 +++++++++---------- .../rpc/backend/backend_transactions_test.go | 21 ++++++++--------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 8c438a97897..95e2372a44b 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -7,17 +7,6 @@ import ( "testing" "github.com/dgraph-io/badger/v2" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -30,6 +19,16 @@ import ( "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Suite struct { @@ -2497,7 +2496,7 @@ func (suite *Suite) TestScriptExecutionValidationMode() { DefaultSnapshotHistoryLimit, []string{fullArchiveAddress}, NewNodeCommunicator(true), - false, + true, ) // mock parameters diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 332e03a43be..6c4707ca020 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,13 +5,6 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/state/protocol" @@ -19,6 +12,12 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { @@ -102,7 +101,7 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { suite.log, DefaultSnapshotHistoryLimit, nil, - *suite.communicator, + suite.communicator, false, ) res, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) @@ -161,7 +160,7 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { suite.log, DefaultSnapshotHistoryLimit, nil, - *suite.communicator, + suite.communicator, false, ) _, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) @@ -235,7 +234,7 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { suite.log, DefaultSnapshotHistoryLimit, nil, - *suite.communicator, + suite.communicator, false, ) resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) @@ -310,7 +309,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { suite.log, DefaultSnapshotHistoryLimit, nil, - *suite.communicator, + suite.communicator, false, ) From 9935534a4a6aa6fd96ec2d7a4d74d572628112cb Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:31:05 +0100 Subject: [PATCH 434/815] Update backend constructor annotation --- engine/access/rpc/backend/backend.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 48042d34da8..d6d7080c38a 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -9,11 +9,6 @@ import ( lru "github.com/hashicorp/golang-lru" lru2 "github.com/hashicorp/golang-lru/v2" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -25,6 +20,10 @@ import ( "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block @@ -101,8 +100,7 @@ type Communicator interface { ) error } -// New creates backend accepting Communicator interfaces instead of circuitBreakerEnabled flag -// More convenient fur unit testing scenarios when you need to pass test NodeCommunicator objects. +// New creates backend instance func New(state protocol.State, collectionRPC accessproto.AccessAPIClient, historicalAccessNodes []accessproto.AccessAPIClient, From 1e60d8d917e01079beae6ebc2a22dccd5503320c Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:32:13 +0100 Subject: [PATCH 435/815] Update mock call --- engine/access/rpc/backend/backend_transactions_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 6c4707ca020..dfe5c644ad9 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -36,7 +36,7 @@ func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { // setup AtHeight mock returns for state for _, height := range epoch1.Range() { - suite.state.On("AtHeisght", epoch1.Range()).Return(state.AtHeight(height)) + suite.state.On("AtHeight", epoch1.Range()).Return(state.AtHeight(height)) } snap := state.AtHeight(epoch1.Range()[0]) From 0d51ec31da7b738a72309c263777f27b87200ff2 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:34:41 +0100 Subject: [PATCH 436/815] Remove unused mock setup in backend transactions --- engine/access/rpc/backend/backend_transactions_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index dfe5c644ad9..5ac2dd1fbb9 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -66,15 +66,7 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - suite.blocks. - On("ByID", block.ID()). - Return(&block, nil). - Once() - receipt := unittest.ExecutionReceiptFixture() - identity := unittest.IdentityFixture() - identity.Role = flow.RoleExecution - l := flow.ExecutionReceiptList{receipt} suite.receipts. From da2c6bef7e9102f68977f06ca28ad8de43b33609 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:39:58 +0100 Subject: [PATCH 437/815] Refine backend transaction tests --- .../rpc/backend/backend_transactions_test.go | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 5ac2dd1fbb9..82de9c22440 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -174,16 +174,11 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - suite.blocks. - On("ByID", block.ID()). - Return(&block, nil). - Once() - suite.communicator.On("CallAvailableNode", mock.Anything, mock.Anything, mock.Anything). - Return(nil) + Return(nil).Once() receipt := unittest.ExecutionReceiptFixture() identity := unittest.IdentityFixture() @@ -258,25 +253,15 @@ func (suite *Suite) TestGetTransactionResultFromCache() { mock.Anything, mock.Anything, mock.Anything). - Return(nil) - - receipt := unittest.ExecutionReceiptFixture() - identity := unittest.IdentityFixture() - identity.Role = flow.RoleExecution + Return(nil).Once() suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() - l := flow.ExecutionReceiptList{receipt} - transactionResultResponse := access.TransactionResultResponse{ Status: entities.TransactionStatus_EXECUTED, StatusCode: uint32(entities.TransactionStatus_EXECUTED), } - suite.receipts. - On("ByBlockID", block.ID()). - Return(l, nil) - suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil).Once() From de502623f37969894f7d4f87638d8afc098e1a97 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:43:06 +0100 Subject: [PATCH 438/815] Simplify backend transactiont tests --- .../rpc/backend/backend_transactions_test.go | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 82de9c22440..3fd3d679e68 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -66,13 +66,6 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - receipt := unittest.ExecutionReceiptFixture() - l := flow.ExecutionReceiptList{receipt} - - suite.receipts. - On("ByBlockID", block.ID()). - Return(l, nil) - backend := New( suite.state, suite.colClient, @@ -120,18 +113,8 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { Return(&block, nil). Once() - receipt := unittest.ExecutionReceiptFixture() - identity := unittest.IdentityFixture() - identity.Role = flow.RoleExecution - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() - l := flow.ExecutionReceiptList{receipt} - - suite.receipts. - On("ByBlockID", block.ID()). - Return(l, nil) - backend := New( suite.state, suite.colClient, @@ -180,23 +163,13 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { mock.Anything). Return(nil).Once() - receipt := unittest.ExecutionReceiptFixture() - identity := unittest.IdentityFixture() - identity.Role = flow.RoleExecution - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() - l := flow.ExecutionReceiptList{receipt} - transactionResultResponse := access.TransactionResultResponse{ Status: entities.TransactionStatus_EXECUTED, StatusCode: uint32(entities.TransactionStatus_EXECUTED), } - suite.receipts. - On("ByBlockID", block.ID()). - Return(l, nil) - suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil) From 01d423eeab783420da588169369f26d02858aa67 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:45:13 +0100 Subject: [PATCH 439/815] Rename test case --- engine/access/rpc/backend/backend_transactions_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 3fd3d679e68..40398dbc9c2 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -144,7 +144,7 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { }) } -func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResult() { +func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHistoricNode() { suite.WithPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() From 43193d6c88a8170d77aff50e0f1f4867b9037aa9 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 12 Aug 2023 16:47:31 +0100 Subject: [PATCH 440/815] Require mock to be called exactly once --- engine/access/rpc/backend/backend_transactions_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 40398dbc9c2..f5e690ddfa3 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -172,7 +172,7 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). - Return(&transactionResultResponse, nil) + Return(&transactionResultResponse, nil).Once() backend := New( suite.state, From 77e65b4e342b1c8fa3d52dd157a6ecb31cda2d84 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 14:38:03 +0100 Subject: [PATCH 441/815] Refactor Access backend constructor --- .../node_builder/access_node_builder.go | 49 +- engine/access/rpc/backend/backend.go | 173 +-- .../rpc/backend/backend_block_details.go | 23 +- .../rpc/backend/backend_block_headers.go | 23 +- engine/access/rpc/backend/backend_events.go | 19 +- engine/access/rpc/backend/backend_network.go | 12 +- engine/access/rpc/backend/backend_scripts.go | 21 +- engine/access/rpc/backend/backend_test.go | 1306 ++++++----------- .../rpc/backend/backend_transactions.go | 45 +- .../rpc/backend/backend_transactions_test.go | 145 +- .../rpc/backend/historical_access_test.go | 84 +- engine/access/rpc/backend/retry.go | 8 +- engine/access/rpc/backend/retry_test.go | 95 +- 13 files changed, 792 insertions(+), 1211 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 69d3e9474fd..b68d2ce5da9 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1088,28 +1088,29 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { backendConfig.CircuitBreakerConfig, ), } - backend := backend.New( - node.State, - builder.CollectionRPC, - builder.HistoricalAccessRPCs, - node.Storage.Blocks, - node.Storage.Headers, - node.Storage.Collections, - node.Storage.Transactions, - node.Storage.Receipts, - node.Storage.Results, - node.RootChainID, - builder.AccessMetrics, - connFactory, - builder.retryEnabled, - backendConfig.MaxHeightRange, - backendConfig.PreferredExecutionNodeIDs, - backendConfig.FixedExecutionNodeIDs, - node.Logger, - backend.DefaultSnapshotHistoryLimit, - backendConfig.ArchiveAddressList, - backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - backendConfig.ScriptExecValidation) + + bnd := backend.New(backend.BackendParams{ + State: node.State, + CollectionRPC: builder.CollectionRPC, + HistoricalAccessNodes: builder.HistoricalAccessRPCs, + Blocks: node.Storage.Blocks, + Headers: node.Storage.Headers, + Collections: node.Storage.Collections, + Transactions: node.Storage.Transactions, + ExecutionReceipts: node.Storage.Receipts, + ExecutionResults: node.Storage.Results, + ChainID: node.RootChainID, + AccessMetrics: builder.AccessMetrics, + ConnFactory: connFactory, + RetryEnabled: builder.retryEnabled, + MaxHeightRange: backendConfig.MaxHeightRange, + PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, + FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, + Log: node.Logger, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + ArchiveAddressList: backendConfig.ArchiveAddressList, + Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + ScriptExecValidation: backendConfig.ScriptExecValidation}) engineBuilder, err := rpc.NewBuilder( node.Logger, @@ -1119,8 +1120,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.AccessMetrics, builder.rpcMetricsEnabled, builder.Me, - backend, - backend, + bnd, + bnd, builder.secureGrpcServer, builder.unsecureGrpcServer, ) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index d6d7080c38a..8df05d04c8b 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -35,7 +35,7 @@ const maxAttemptsForExecutionReceipt = 3 // DefaultMaxHeightRange is the default maximum size of range requests. const DefaultMaxHeightRange = 250 -// DefaultSnapshotHistoryLimit the amount of blocks to look back in state +// DefaultSnapshotHistoryLimit the amount of Blocks to look back in State // when recursively searching for a valid snapshot const DefaultSnapshotHistoryLimit = 50 @@ -100,142 +100,143 @@ type Communicator interface { ) error } +type BackendParams struct { + State protocol.State + CollectionRPC accessproto.AccessAPIClient + HistoricalAccessNodes []accessproto.AccessAPIClient + Blocks storage.Blocks + Headers storage.Headers + Collections storage.Collections + Transactions storage.Transactions + ExecutionReceipts storage.ExecutionReceipts + ExecutionResults storage.ExecutionResults + ChainID flow.ChainID + AccessMetrics module.AccessMetrics + ConnFactory connection.ConnectionFactory + RetryEnabled bool + MaxHeightRange uint + PreferredExecutionNodeIDs []string + FixedExecutionNodeIDs []string + Log zerolog.Logger + SnapshotHistoryLimit int + ArchiveAddressList []string + Communicator Communicator + ScriptExecValidation bool +} + // New creates backend instance -func New(state protocol.State, - collectionRPC accessproto.AccessAPIClient, - historicalAccessNodes []accessproto.AccessAPIClient, - blocks storage.Blocks, - headers storage.Headers, - collections storage.Collections, - transactions storage.Transactions, - executionReceipts storage.ExecutionReceipts, - executionResults storage.ExecutionResults, - chainID flow.ChainID, - accessMetrics module.AccessMetrics, - connFactory connection.ConnectionFactory, - retryEnabled bool, - maxHeightRange uint, - preferredExecutionNodeIDs []string, - fixedExecutionNodeIDs []string, - log zerolog.Logger, - snapshotHistoryLimit int, - archiveAddressList []string, - communicator Communicator, - scriptExecValidation bool, -) *Backend { +func New(params BackendParams) *Backend { retry := newRetry() - if retryEnabled { + if params.RetryEnabled { retry.Activate() } loggedScripts, err := lru.New(DefaultLoggedScriptsCacheSize) if err != nil { - log.Fatal().Err(err).Msg("failed to initialize script logging cache") + params.Log.Fatal().Err(err).Msg("failed to initialize script logging cache") } - archivePorts := make([]uint, len(archiveAddressList)) - for idx, addr := range archiveAddressList { + archivePorts := make([]uint, len(params.ArchiveAddressList)) + for idx, addr := range params.ArchiveAddressList { port, err := findPortFromAddress(addr) if err != nil { - log.Fatal().Err(err).Msg("failed to find archive node port") + params.Log.Fatal().Err(err).Msg("failed to find archive node port") } archivePorts[idx] = port } txResCache, err := lru2.New[flow.Identifier, *access.TransactionResult](int(badger.DefaultCacheSize)) if err != nil { - log.Fatal().Err(err).Msg("failed to init cache for transaction results") + params.Log.Fatal().Err(err).Msg("failed to init cache for transaction results") } b := &Backend{ - state: state, + state: params.State, // create the sub-backends backendScripts: backendScripts{ - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - state: state, - log: log, - metrics: accessMetrics, + headers: params.Headers, + executionReceipts: params.ExecutionReceipts, + connFactory: params.ConnFactory, + state: params.State, + log: params.Log, + metrics: params.AccessMetrics, loggedScripts: loggedScripts, - archiveAddressList: archiveAddressList, + archiveAddressList: params.ArchiveAddressList, archivePorts: archivePorts, - nodeCommunicator: communicator, - scriptExecValidation: scriptExecValidation, + nodeCommunicator: params.Communicator, + scriptExecValidation: params.ScriptExecValidation, }, backendTransactions: backendTransactions{ - staticCollectionRPC: collectionRPC, - state: state, - chainID: chainID, - collections: collections, - blocks: blocks, - transactions: transactions, - executionReceipts: executionReceipts, - transactionValidator: configureTransactionValidator(state, chainID), - transactionMetrics: accessMetrics, + staticCollectionRPC: params.CollectionRPC, + state: params.State, + chainID: params.ChainID, + collections: params.Collections, + blocks: params.Blocks, + transactions: params.Transactions, + executionReceipts: params.ExecutionReceipts, + transactionValidator: configureTransactionValidator(params.State, params.ChainID), + transactionMetrics: params.AccessMetrics, retry: retry, - connFactory: connFactory, - previousAccessNodes: historicalAccessNodes, - log: log, - nodeCommunicator: communicator, + connFactory: params.ConnFactory, + previousAccessNodes: params.HistoricalAccessNodes, + log: params.Log, + nodeCommunicator: params.Communicator, txResultCache: txResCache, }, backendEvents: backendEvents{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - maxHeightRange: maxHeightRange, - nodeCommunicator: communicator, + state: params.State, + headers: params.Headers, + executionReceipts: params.ExecutionReceipts, + connFactory: params.ConnFactory, + log: params.Log, + maxHeightRange: params.MaxHeightRange, + nodeCommunicator: params.Communicator, }, backendBlockHeaders: backendBlockHeaders{ - headers: headers, - state: state, + headers: params.Headers, + state: params.State, }, backendBlockDetails: backendBlockDetails{ - blocks: blocks, - state: state, + blocks: params.Blocks, + state: params.State, }, backendAccounts: backendAccounts{ - state: state, - headers: headers, - executionReceipts: executionReceipts, - connFactory: connFactory, - log: log, - nodeCommunicator: communicator, + state: params.State, + headers: params.Headers, + executionReceipts: params.ExecutionReceipts, + connFactory: params.ConnFactory, + log: params.Log, + nodeCommunicator: params.Communicator, }, backendExecutionResults: backendExecutionResults{ - executionResults: executionResults, + executionResults: params.ExecutionResults, }, backendNetwork: backendNetwork{ - state: state, - chainID: chainID, - snapshotHistoryLimit: snapshotHistoryLimit, + state: params.State, + chainID: params.ChainID, + snapshotHistoryLimit: params.SnapshotHistoryLimit, }, - collections: collections, - executionReceipts: executionReceipts, - connFactory: connFactory, - chainID: chainID, + collections: params.Collections, + executionReceipts: params.ExecutionReceipts, + connFactory: params.ConnFactory, + chainID: params.ChainID, } retry.SetBackend(b) - preferredENIdentifiers, err = identifierList(preferredExecutionNodeIDs) + preferredENIdentifiers, err = identifierList(params.PreferredExecutionNodeIDs) if err != nil { - log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for preferred EN map") + params.Log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for preferred EN map") } - fixedENIdentifiers, err = identifierList(fixedExecutionNodeIDs) + fixedENIdentifiers, err = identifierList(params.FixedExecutionNodeIDs) if err != nil { - log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for fixed EN map") + params.Log.Fatal().Err(err).Msg("failed to convert node id string to Flow Identifier for fixed EN map") } return b } -//TODO: refactor cache, replace with generic lru.Cache alternative - // NewCache constructs cache for storing connections to other nodes. // No errors are expected during normal operations. func NewCache( @@ -331,7 +332,7 @@ func (b *Backend) GetCollectionByID(_ context.Context, colID flow.Identifier) (* // retrieve the collection from the collection storage col, err := b.collections.LightByID(colID) if err != nil { - // Collections are retrieved asynchronously as we finalize blocks, so + // Collections are retrieved asynchronously as we finalize Blocks, so // it is possible for a client to request a finalized block from us // containing some collection, then get a not found error when requesting // that collection. These clients should retry. @@ -397,7 +398,7 @@ func executionNodesForBlockID( break } - // log the attempt + // Log the attempt log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). Int("execution_receipts_found", len(executorIDs)). Str("block_id", blockID.String()). @@ -471,7 +472,7 @@ func findAllExecutionNodes( } } - // if there are more than one execution result for the same block ID, log as error + // if there are more than one execution result for the same block ID, Log as error if executionResultGroupedMetaList.NumberGroups() > 1 { identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) log.Error(). diff --git a/engine/access/rpc/backend/backend_block_details.go b/engine/access/rpc/backend/backend_block_details.go index 19336ded8a4..1398eda7a77 100644 --- a/engine/access/rpc/backend/backend_block_details.go +++ b/engine/access/rpc/backend/backend_block_details.go @@ -3,13 +3,12 @@ package backend import ( "context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendBlockDetails struct { @@ -25,21 +24,21 @@ func (b *backendBlockDetails) GetLatestBlock(_ context.Context, isSealed bool) ( // get the latest seal header from storage header, err = b.state.Sealed().Head() } else { - // get the finalized header from state + // get the finalized header from State header, err = b.state.Final().Head() } if err != nil { // node should always have the latest block - // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, + // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad state. + // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol state is widely shared, we assume that in practice another component will - // observe the protocol state error and throw an exception. + // - Since the protocol State is widely shared, we assume that in practice another component will + // observe the protocol State error and throw an exception. return nil, flow.BlockStatusUnknown, status.Errorf(codes.Internal, "could not get latest block: %v", err) } @@ -85,14 +84,14 @@ func (b *backendBlockDetails) GetBlockByHeight(_ context.Context, height uint64) func (b *backendBlockDetails) getBlockStatus(block *flow.Block) (flow.BlockStatus, error) { sealed, err := b.state.Sealed().Head() if err != nil { - // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, + // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad state. + // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol state is widely shared, we assume that in practice another component will - // observe the protocol state error and throw an exception. + // - Since the protocol State is widely shared, we assume that in practice another component will + // observe the protocol State error and throw an exception. return flow.BlockStatusUnknown, status.Errorf(codes.Internal, "failed to find latest sealed header: %v", err) } diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index 178f9064f1f..10efb30c6a2 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -3,13 +3,12 @@ package backend import ( "context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendBlockHeaders struct { @@ -25,20 +24,20 @@ func (b *backendBlockHeaders) GetLatestBlockHeader(_ context.Context, isSealed b // get the latest seal header from storage header, err = b.state.Sealed().Head() } else { - // get the finalized header from state + // get the finalized header from State header, err = b.state.Final().Head() } if err != nil { // node should always have the latest block - // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, + // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad state. + // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol state is widely shared, we assume that in practice another component will - // observe the protocol state error and throw an exception. + // - Since the protocol State is widely shared, we assume that in practice another component will + // observe the protocol State error and throw an exception. return nil, flow.BlockStatusUnknown, status.Errorf(codes.Internal, "could not get latest block header: %v", err) } @@ -78,14 +77,14 @@ func (b *backendBlockHeaders) GetBlockHeaderByHeight(_ context.Context, height u func (b *backendBlockHeaders) getBlockStatus(header *flow.Header) (flow.BlockStatus, error) { sealed, err := b.state.Sealed().Head() if err != nil { - // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, + // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad state. + // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol state is widely shared, we assume that in practice another component will - // observe the protocol state error and throw an exception. + // - Since the protocol State is widely shared, we assume that in practice another component will + // observe the protocol State error and throw an exception. return flow.BlockStatusUnknown, status.Errorf(codes.Internal, "failed to find latest sealed header: %v", err) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index 43b42cde2f5..a151e138740 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -7,17 +7,16 @@ import ( "fmt" "time" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendEvents struct { @@ -30,7 +29,7 @@ type backendEvents struct { nodeCommunicator Communicator } -// GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and +// GetEventsForHeightRange retrieves events for all sealed Blocks between the start block height and // the end block height (inclusive) that have the given type. func (b *backendEvents) GetEventsForHeightRange( ctx context.Context, @@ -65,7 +64,7 @@ func (b *backendEvents) GetEventsForHeightRange( endHeight = head.Height } - // find the block headers for all the blocks between min and max height (inclusive) + // find the block Headers for all the Blocks between min and max height (inclusive) blockHeaders := make([]*flow.Header, 0) for i := startHeight; i <= endHeight; i++ { @@ -91,7 +90,7 @@ func (b *backendEvents) GetEventsForBlockIDs( return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", len(blockIDs), b.maxHeightRange) } - // find the block headers for all the block IDs + // find the block Headers for all the block IDs blockHeaders := make([]*flow.Header, 0) for _, blockID := range blockIDs { header, err := b.headers.ByBlockID(blockID) @@ -165,7 +164,7 @@ func verifyAndConvertToAccessEvents( version execproto.EventEncodingVersion, ) ([]flow.BlockEvents, error) { if len(execEvents) != len(requestedBlockHeaders) { - return nil, errors.New("number of results does not match number of blocks requested") + return nil, errors.New("number of results does not match number of Blocks requested") } requestedBlockHeaderSet := map[string]*flow.Header{} @@ -223,7 +222,7 @@ func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, logger := b.log.With(). Str("execution_node", node.String()). Str("event", req.GetType()). - Int("blocks", len(req.BlockIds)). + Int("Blocks", len(req.BlockIds)). Int64("rtt_ms", duration.Milliseconds()). Logger() diff --git a/engine/access/rpc/backend/backend_network.go b/engine/access/rpc/backend/backend_network.go index 577ebaa8a84..ebb80bd65e8 100644 --- a/engine/access/rpc/backend/backend_network.go +++ b/engine/access/rpc/backend/backend_network.go @@ -4,15 +4,13 @@ import ( "context" "fmt" - "github.com/onflow/flow-go/cmd/build" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) var SnapshotHistoryLimitErr = fmt.Errorf("reached the snapshot history limit") @@ -86,8 +84,8 @@ func (b *backendNetwork) isEpochOrPhaseDifferent(counter1, counter2 uint64, phas } // getValidSnapshot will return a valid snapshot that has a sealing segment which -// 1. does not contain any blocks that span an epoch transition -// 2. does not contain any blocks that span an epoch phase transition +// 1. does not contain any Blocks that span an epoch transition +// 2. does not contain any Blocks that span an epoch phase transition // If a snapshot does contain an invalid sealing segment query the state // by height of each block in the segment and return a snapshot at the point // where the transition happens. diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index b73aef9ce43..97b9b3f3f1d 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -1,26 +1,25 @@ package backend import ( + "crypto/md5" //nolint:gosec "bytes" "context" - "crypto/md5" //nolint:gosec "io" "time" "github.com/hashicorp/go-multierror" lru "github.com/hashicorp/golang-lru" - "github.com/onflow/flow/protobuf/go/flow/access" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + "github.com/onflow/flow/protobuf/go/flow/access" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // uniqueScriptLoggingTimeWindow is the duration for checking the uniqueness of scripts sent for execution @@ -169,7 +168,7 @@ func (b *backendScripts) logScriptExecutionComparison( archiveError error, msg string, ) { - // over-log for ease of debug + // over-Log for ease of debug if executionError != nil || archiveError != nil { b.log.Debug().Hex("block_id", blockID[:]). Str("script", string(script)). @@ -200,7 +199,7 @@ func (b *backendScripts) executeScriptOnAvailableArchiveNodes( rnPort := b.archivePorts[idx] result, err := b.tryExecuteScriptOnArchiveNode(ctx, rnAddr, rnPort, blockID, script, arguments) if err == nil { - // log execution time + // Log execution time b.metrics.ScriptExecuted( time.Since(startTime), len(script), @@ -219,7 +218,7 @@ func (b *backendScripts) executeScriptOnAvailableArchiveNodes( Msg("script failed to execute on the execution node") return nil, err case codes.NotFound: - // failures due to unavailable blocks are explicitly marked Not found + // failures due to unavailable Blocks are explicitly marked Not found b.metrics.ScriptExecutionErrorOnArchiveNode() b.log.Error().Err(err).Msg("script execution failed for archive node") return nil, err @@ -270,7 +269,7 @@ func (b *backendScripts) executeScriptOnAvailableExecutionNodes( } } - // log execution time + // Log execution time b.metrics.ScriptExecuted( time.Since(execStartTime), len(script), diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 95e2372a44b..66f956f8ea1 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -31,6 +31,8 @@ import ( "google.golang.org/grpc/status" ) +const TEST_MAX_HEIGHT = 100 + type Suite struct { suite.Suite @@ -94,29 +96,16 @@ func (suite *Suite) TestPing() { On("Ping", mock.Anything, &execproto.PingRequest{}). Return(&execproto.PingResponse{}, nil) - backend := New( - suite.state, - suite.colClient, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) err := backend.Ping(context.Background()) @@ -132,45 +121,32 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.snapshot.On("Head").Return(block, nil).Once() backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized block - header, status, err := backend.GetLatestBlockHeader(context.Background(), false) + header, stat, err := backend.GetLatestBlockHeader(context.Background(), false) suite.checkResponse(header, err) // make sure we got the latest block suite.Require().Equal(block.ID(), header.ID()) suite.Require().Equal(block.Height, header.Height) suite.Require().Equal(block.ParentID, header.ParentID) - suite.Require().Equal(status, flow.BlockStatusSealed) + suite.Require().Equal(stat, flow.BlockStatusSealed) suite.assertAllExpectations() } // TestGetLatestProtocolStateSnapshot_NoTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint -// where the sealing segment for the state requested at latest finalized block does not contain any blocks that +// where the sealing segment for the State requested at latest finalized block does not contain any Blocks that // spans an epoch or epoch phase transition. func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { identities := unittest.CompleteIdentitySet() @@ -178,7 +154,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 - // blocks in current state + // Blocks in current State // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder. BuildEpoch(). @@ -188,7 +164,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { epoch1, ok := epochBuilder.EpochHeights(1) require.True(suite.T(), ok) - // setup AtBlockID mock returns for state + // setup AtBlockID mock returns for State for _, height := range epoch1.Range() { suite.state.On("AtHeight", height).Return(state.AtHeight(height)).Once() } @@ -198,36 +174,22 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { snap := state.AtHeight(epoch1.Range()[2]) suite.state.On("Final").Return(snap).Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - 100, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: TEST_MAX_HEIGHT, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized snapshot bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background()) suite.Require().NoError(err) // we expect the endpoint to return the snapshot at the same height we requested - // because it has a valid sealing segment with no blocks spanning an epoch or phase transition + // because it has a valid sealing segment with no Blocks spanning an epoch or phase transition expectedSnapshotBytes, err := convert.SnapshotToBytes(snap) suite.Require().NoError(err) suite.Require().Equal(expectedSnapshotBytes, bytes) @@ -235,8 +197,8 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { } // TestGetLatestProtocolStateSnapshot_TransitionSpans tests our GetLatestProtocolStateSnapshot RPC endpoint -// where the sealing segment for the state requested for latest finalized block contains a block that -// spans an epoch transition and blocks that span epoch phase transitions. +// where the sealing segment for the State requested for latest finalized block contains a block that +// spans an epoch transition and Blocks that span epoch phase transitions. func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) @@ -259,7 +221,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { epoch2, ok := epochBuilder.EpochHeights(2) require.True(suite.T(), ok) - // setup AtHeight mock returns for state + // setup AtHeight mock returns for State for _, height := range append(epoch1.Range(), epoch2.Range()...) { suite.state.On("AtHeight", height).Return(state.AtHeight(height)) } @@ -273,28 +235,15 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.state.On("Final").Return(snap).Once() backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - 100, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: TEST_MAX_HEIGHT, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized snapshot bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background()) @@ -309,7 +258,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { } // TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint -// where the sealing segment for the state requested at latest finalized block contains a blocks that +// where the sealing segment for the State requested at latest finalized block contains a Blocks that // spans an epoch phase transition. func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { identities := unittest.CompleteIdentitySet() @@ -317,7 +266,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 - // blocks in current state + // Blocks in current State // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder. BuildEpoch(). @@ -327,7 +276,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { epoch1, ok := epochBuilder.EpochHeights(1) require.True(suite.T(), ok) - // setup AtBlockID mock returns for state + // setup AtBlockID mock returns for State for _, height := range epoch1.Range() { suite.state.On("AtHeight", height).Return(state.AtHeight(height)) } @@ -339,29 +288,15 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { snap := state.AtHeight(epoch1.Range()[3]) suite.state.On("Final").Return(snap).Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - 100, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: TEST_MAX_HEIGHT, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized snapshot bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background()) @@ -375,7 +310,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { } // TestGetLatestProtocolStateSnapshot_EpochTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint -// where the sealing segment for the state requested at latest finalized block contains a blocks that +// where the sealing segment for the State requested at latest finalized block contains a Blocks that // spans an epoch transition. func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { identities := unittest.CompleteIdentitySet() @@ -383,11 +318,11 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { epochBuilder := unittest.NewEpochBuilder(suite.T(), state) // build epoch 1 - // blocks in current state + // Blocks in current State // P <- A(S_P-1) <- B(S_P) <- C(S_A) <- D(S_B) |setup| <- E(S_C) <- F(S_D) |commit| epochBuilder.BuildEpoch() - // add more blocks to our state in the commit phase, this will allow + // add more Blocks to our State in the commit phase, this will allow // us to take a snapshot at the height where the epoch1 -> epoch2 transition // and no block spans an epoch phase transition. The third block added will // have a seal for the first block in the commit phase allowing us to avoid @@ -406,7 +341,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { epoch2, ok := epochBuilder.EpochHeights(2) require.True(suite.T(), ok) - // setup AtHeight mock returns for state + // setup AtHeight mock returns for State for _, height := range append(epoch1.Range(), epoch2.Range()...) { suite.state.On("AtHeight", height).Return(state.AtHeight(height)) } @@ -417,29 +352,15 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { snap := state.AtHeight(epoch2.Range()[0]) suite.state.On("Final").Return(snap).Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - 100, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: TEST_MAX_HEIGHT, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized snapshot bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background()) @@ -454,7 +375,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { } // TestGetLatestProtocolStateSnapshot_EpochTransitionSpan tests our GetLatestProtocolStateSnapshot RPC endpoint -// where the length of the sealing segment is greater than the configured snapshotHistoryLimit +// where the length of the sealing segment is greater than the configured SnapshotHistoryLimit func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) @@ -465,7 +386,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { epoch1, ok := epochBuilder.EpochHeights(1) require.True(suite.T(), ok) - // setup AtBlockID mock returns for state + // setup AtBlockID mock returns for State for _, height := range epoch1.Range() { suite.state.On("AtHeight", height).Return(state.AtHeight(height)) } @@ -477,31 +398,17 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { snap := state.AtHeight(epoch1.Range()[4]) suite.state.On("Final").Return(snap).Once() - // very short history limit, any segment with any blocks spanning any transition should force the endpoint to return a history limit error + // very short history limit, any segment with any Blocks spanning any transition should force the endpoint to return a history limit error snapshotHistoryLimit := 1 - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - snapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: snapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // the handler should return a snapshot history limit error _, err := backend.GetLatestProtocolStateSnapshot(context.Background()) @@ -519,39 +426,25 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.state.On("Sealed").Return(suite.snapshot, nil) suite.snapshot.On("Head").Return(block, nil).Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest sealed block - header, status, err := backend.GetLatestBlockHeader(context.Background(), true) + header, stat, err := backend.GetLatestBlockHeader(context.Background(), true) suite.checkResponse(header, err) // make sure we got the latest sealed block suite.Require().Equal(block.ID(), header.ID()) suite.Require().Equal(block.Height, header.Height) suite.Require().Equal(block.ParentID, header.ParentID) - suite.Require().Equal(status, flow.BlockStatusSealed) + suite.Require().Equal(stat, flow.BlockStatusSealed) suite.assertAllExpectations() } @@ -567,29 +460,16 @@ func (suite *Suite) TestGetTransaction() { Return(&expected, nil). Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - suite.transactions, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) actual, err := backend.GetTransaction(context.Background(), transaction.ID()) suite.checkResponse(actual, err) @@ -609,29 +489,17 @@ func (suite *Suite) TestGetCollection() { Return(&expected, nil). Once() - backend := New( - suite.state, - nil, - nil, - nil, - nil, - suite.collections, - suite.transactions, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Collections: suite.collections, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) actual, err := backend.GetCollectionByID(context.Background(), expected.ID()) suite.transactions.AssertExpectations(suite.T()) @@ -674,29 +542,25 @@ func (suite *Suite) TestGetTransactionResultByIndex() { Events: nil, } - backend := New( - suite.state, - nil, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - flow.IdentifierList(fixedENIDs.NodeIDs()).Strings(), - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + // the connection factory should be used to get the execution node client + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + FixedExecutionNodeIDs: (fixedENIDs.NodeIDs()).Strings(), + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) + suite.execClient. On("GetTransactionResultByIndex", ctx, exeEventReq). Return(exeEventResp, nil). @@ -739,29 +603,25 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { TransactionResults: []*execproto.GetTransactionResultResponse{{}}, } - backend := New( - suite.state, - nil, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - flow.IdentifierList(fixedENIDs.NodeIDs()).Strings(), - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + // the connection factory should be used to get the execution node client + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + FixedExecutionNodeIDs: (fixedENIDs.NodeIDs()).Strings(), + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) + suite.execClient. On("GetTransactionResultsByBlockID", ctx, exeEventReq). Return(exeEventResp, nil). @@ -774,7 +634,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { } // TestTransactionStatusTransition tests that the status of transaction changes from Finalized to Sealed -// when the protocol state is updated +// when the protocol State is updated func (suite *Suite) TestTransactionStatusTransition() { suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() @@ -832,29 +692,24 @@ func (suite *Suite) TestTransactionStatusTransition() { Events: nil, } - backend := New( - suite.state, - nil, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - flow.IdentifierList(fixedENIDs.NodeIDs()).Strings(), - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + // the connection factory should be used to get the execution node client + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + FixedExecutionNodeIDs: (fixedENIDs.NodeIDs()).Strings(), + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // Successfully return empty event list suite.execClient. @@ -866,7 +721,7 @@ func (suite *Suite) TestTransactionStatusTransition() { result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) suite.checkResponse(result, err) - // status should be finalized since the sealed blocks is smaller in height + // status should be finalized since the sealed Blocks is smaller in height suite.Assert().Equal(flow.TransactionStatusFinalized, result.Status) // block ID should be included in the response @@ -891,7 +746,7 @@ func (suite *Suite) TestTransactionStatusTransition() { result, err = backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) suite.checkResponse(result, err) - // status should be sealed since the sealed blocks is greater in height + // status should be sealed since the sealed Blocks is greater in height suite.Assert().Equal(flow.TransactionStatusSealed, result.Status) // now go far into the future @@ -909,7 +764,7 @@ func (suite *Suite) TestTransactionStatusTransition() { } // TestTransactionExpiredStatusTransition tests that the status -// of transaction changes from Pending to Expired when enough blocks pass +// of transaction changes from Pending to Expired when enough Blocks pass func (suite *Suite) TestTransactionExpiredStatusTransition() { suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() suite.state.On("Final").Return(suite.snapshot, nil).Maybe() @@ -954,29 +809,21 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { txID := transactionBody.ID() - backend := New( - suite.state, - nil, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // should return pending status when we have not observed an expiry block suite.Run("pending", func() { @@ -988,23 +835,23 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { }) // should return pending status when we have observed an expiry block but - // have not observed all intermediary collections + // have not observed all intermediary Collections suite.Run("expiry un-confirmed", func() { suite.Run("ONLY finalized expiry block", func() { // we have finalized an expiry block headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry + 1 - // we have NOT observed all intermediary collections + // we have NOT observed all intermediary Collections fullHeight = block.Header.Height + flow.DefaultTransactionExpiry/2 result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) suite.checkResponse(result, err) suite.Assert().Equal(flow.TransactionStatusPending, result.Status) }) - suite.Run("ONLY observed intermediary collections", func() { + suite.Run("ONLY observed intermediary Collections", func() { // we have NOT finalized an expiry block headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry/2 - // we have observed all intermediary collections + // we have observed all intermediary Collections fullHeight = block.Header.Height + flow.DefaultTransactionExpiry + 1 result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) @@ -1015,11 +862,11 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { }) // should return expired status only when we have observed an expiry block - // and have observed all intermediary collections + // and have observed all intermediary Collections suite.Run("expired", func() { // we have finalized an expiry block headBlock.Header.Height = block.Header.Height + flow.DefaultTransactionExpiry + 1 - // we have observed all intermediary collections + // we have observed all intermediary Collections fullHeight = block.Header.Height + flow.DefaultTransactionExpiry + 1 result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) @@ -1077,7 +924,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { On("ByID", txID). Return(transactionBody, nil) - currentState := flow.TransactionStatusPending // marker for the current state + currentState := flow.TransactionStatusPending // marker for the current State // collection storage returns a not found error if tx is pending, else it returns the collection light reference suite.collections. On("LightByTransactionID", txID). @@ -1122,30 +969,22 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { // create a mock connection factory connFactory := suite.setupConnectionFactory() - - backend := New( - suite.state, - nil, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - 100, - nil, - flow.IdentifierList(enIDs.NodeIDs()).Strings(), - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: TEST_MAX_HEIGHT, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -1183,29 +1022,16 @@ func (suite *Suite) TestTransactionResultUnknown() { On("ByID", txID). Return(nil, storage.ErrNotFound) - backend := New( - suite.state, - nil, - nil, - nil, - nil, - nil, - suite.transactions, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) @@ -1239,37 +1065,24 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { On("ByHeight", header.Height). Return(&expected, nil) - backend := New( - suite.state, - nil, - nil, - suite.blocks, - nil, - nil, - nil, - nil, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // query the handler for the latest finalized header - actual, status, err := backend.GetLatestBlock(context.Background(), false) + actual, stat, err := backend.GetLatestBlock(context.Background(), false) suite.checkResponse(actual, err) // make sure we got the latest header suite.Require().Equal(expected, *actual) - suite.Assert().Equal(status, flow.BlockStatusFinalized) + suite.Assert().Equal(stat, flow.BlockStatusFinalized) suite.assertAllExpectations() } @@ -1371,29 +1184,22 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.Run("with an execution node chosen using block ID form the list of Fixed ENs", func() { // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + // set the fixed EN Identifiers to the generated execution IDs + FixedExecutionNodeIDs: validENIDs.Strings(), + + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request actual, err := backend.GetEventsForBlockIDs(ctx, string(flow.EventAccountCreated), blockIDs) @@ -1405,29 +1211,19 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.Run("with an empty block ID list", func() { // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - receipts, - nil, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + FixedExecutionNodeIDs: validENIDs.Strings(), + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request with an empty block id list and expect an empty list of events and no error resp, err := backend.GetEventsForBlockIDs(ctx, string(flow.EventAccountCreated), []flow.Identifier{}) @@ -1464,31 +1260,20 @@ func (suite *Suite) TestGetExecutionResultByID() { Return(executionResult, nil) suite.Run("nonexisting execution result for id", func() { - - // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + FixedExecutionNodeIDs: validENIDs.Strings(), + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request _, err := backend.GetExecutionResultByID(ctx, nonexistingID) @@ -1497,30 +1282,19 @@ func (suite *Suite) TestGetExecutionResultByID() { }) suite.Run("existing execution result id", func() { - // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - nil, - results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + FixedExecutionNodeIDs: validENIDs.Strings(), + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request er, err := backend.GetExecutionResultByID(ctx, executionResult.ID()) @@ -1562,30 +1336,20 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.Run("nonexisting execution results", func() { - // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + FixedExecutionNodeIDs: validENIDs.Strings(), + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request _, err := backend.GetExecutionResultForBlockID(ctx, nonexistingBlockID) @@ -1595,30 +1359,19 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.Run("existing execution results", func() { - // create the handler - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - nil, - results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - validENIDs.Strings(), // set the fixed EN Identifiers to the generated execution IDs - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + FixedExecutionNodeIDs: validENIDs.Strings(), + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // execute request er, err := backend.GetExecutionResultForBlockID(ctx, blockID) @@ -1665,7 +1418,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { func(flow.IdentityFilter) error { return nil }, ) - // mock headers to pull from headers backend + // mock Headers to pull from Headers backend suite.headers.On("ByHeight", mock.Anything).Return( func(height uint64) *flow.Header { return headersDB[height] @@ -1747,30 +1500,21 @@ func (suite *Suite) TestGetEventsForHeightRange() { connFactory := suite.setupConnectionFactory() + //suite.state = state suite.Run("invalid request max height < min height", func() { - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), maxHeight, minHeight) suite.Require().Error(err) @@ -1788,30 +1532,21 @@ func (suite *Suite) TestGetEventsForHeightRange() { expectedResp := setupExecClient() fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - // create handler - backend := New( - state, - nil, - nil, - suite.blocks, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - fixedENIdentifiersStr, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + FixedExecutionNodeIDs: fixedENIdentifiersStr, + }) // execute request actualResp, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) @@ -1829,29 +1564,21 @@ func (suite *Suite) TestGetEventsForHeightRange() { expectedResp := setupExecClient() fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - backend := New( - state, - nil, - nil, - suite.blocks, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - fixedENIdentifiersStr, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + FixedExecutionNodeIDs: fixedENIdentifiersStr, + }) actualResp, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) suite.checkResponse(actualResp, err) @@ -1867,30 +1594,21 @@ func (suite *Suite) TestGetEventsForHeightRange() { blockHeaders, _, nodeIdentities = setupStorage(minHeight, headHeight) fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - // create handler - backend := New( - state, - nil, - nil, - suite.blocks, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - 1, // set maximum range to 1 - nil, - fixedENIdentifiersStr, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: 1, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + FixedExecutionNodeIDs: fixedENIdentifiersStr, + }) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, minHeight+1) suite.Require().Error(err) @@ -1906,30 +1624,21 @@ func (suite *Suite) TestGetEventsForHeightRange() { blockHeaders, _, nodeIdentities = setupStorage(minHeight, maxHeight) fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - // create handler - backend := New( - state, - nil, - nil, - suite.blocks, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - fixedENIdentifiersStr, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + FixedExecutionNodeIDs: fixedENIdentifiersStr, + }) _, err := backend.GetEventsForHeightRange(ctx, string(flow.EventAccountCreated), minHeight, maxHeight) suite.Require().Error(err) @@ -1985,30 +1694,20 @@ func (suite *Suite) TestGetAccount() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // create the handler with the mock - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2037,7 +1736,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { b := unittest.BlockFixture() h := b.Header - // setup headers storage to return the header when queried by height + // setup Headers storage to return the header when queried by height suite.headers. On("ByHeight", height). Return(h, nil). @@ -2068,30 +1767,19 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { Return(exeResp, nil). Once() - // create the handler with the mock - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - flow.Testnet, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: flow.Testnet, + AccessMetrics: metrics.NewNoopCollector(), + ConnFactory: connFactory, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} @@ -2110,29 +1798,14 @@ func (suite *Suite) TestGetNetworkParameters() { expectedChainID := flow.Mainnet - backend := New( - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - flow.Mainnet, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + ChainID: flow.Mainnet, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) params := backend.GetNetworkParameters(context.Background()) @@ -2226,7 +1899,7 @@ func (suite *Suite) TestExecutionNodesForBlockID() { } } // if we don't find sufficient receipts, executionNodesForBlockID should return a list of random ENs - suite.Run("insufficient receipts return random ENs in state", func() { + suite.Run("insufficient receipts return random ENs in State", func() { // return no receipts at all attempts attempt1Receipts = flow.ExecutionReceiptList{} attempt2Receipts = flow.ExecutionReceiptList{} @@ -2313,30 +1986,19 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) - // create the handler with the mock - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - flow.Mainnet, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: flow.Mainnet, + ConnFactory: connFactory, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) // mock parameters ctx := context.Background() @@ -2391,30 +2053,20 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { archiveNode := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) fullArchiveAddress := archiveNode.Address + ":" + strconv.FormatUint(uint64(mockPort), 10) - // create the handler with the mock - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - flow.Mainnet, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - []string{fullArchiveAddress}, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: flow.Mainnet, + ConnFactory: connFactory, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + ArchiveAddressList: []string{fullArchiveAddress}, + }) // mock parameters ctx := context.Background() @@ -2474,30 +2126,22 @@ func (suite *Suite) TestScriptExecutionValidationMode() { connFactory.On("InvalidateAccessAPIClient", mock.Anything) archiveNode := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) fullArchiveAddress := archiveNode.Address + ":" + strconv.FormatUint(uint64(mockPort), 10) - // create the handler with the mock - backend := New( - suite.state, - nil, - nil, - nil, - suite.headers, - nil, - nil, - suite.receipts, - suite.results, - flow.Mainnet, - metrics.NewNoopCollector(), - connFactory, // the connection factory should be used to get the execution node client - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - []string{fullArchiveAddress}, - NewNodeCommunicator(true), - true, - ) + + backend := New(BackendParams{ + State: suite.state, + Headers: suite.headers, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: flow.Mainnet, + ConnFactory: connFactory, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + ArchiveAddressList: []string{fullArchiveAddress}, + ScriptExecValidation: true, + }) // mock parameters ctx := context.Background() diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index d5fed8f04d6..4abe1b8e1b3 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -7,13 +7,6 @@ import ( "time" lru2 "github.com/hashicorp/golang-lru/v2" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -23,6 +16,12 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendTransactions struct { @@ -95,7 +94,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T var sendError error logAnyError := func() { if sendError != nil { - b.log.Info().Err(err).Msg("failed to send transactions to collector nodes") + b.log.Info().Err(err).Msg("failed to send Transactions to collector nodes") } } defer logAnyError() @@ -404,7 +403,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( i := 0 errInsufficientResults := status.Errorf( codes.Internal, - "number of transaction results returned by execution node is less than the number of transactions in the block", + "number of transaction results returned by execution node is less than the number of Transactions in the block", ) for _, guarantee := range block.Payload.Guarantees { @@ -414,7 +413,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } for _, txID := range collection.Transactions { - // bounds check. this means the EN returned fewer transaction results than the transactions in the block + // bounds check. this means the EN returned fewer transaction results than the Transactions in the block if i >= len(resp.TransactionResults) { return nil, errInsufficientResults } @@ -447,8 +446,8 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } } - // after iterating through all transactions in each collection, i equals the total number of - // user transactions in the block + // after iterating through all Transactions in each collection, i equals the total number of + // user Transactions in the block txCount := i sporkRootBlockHeight, err := b.state.Params().SporkRootBlockHeight() @@ -468,7 +467,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } // otherwise there are extra results // TODO(bft): slashable offense - return nil, status.Errorf(codes.Internal, "number of transaction results returned by execution node is more than the number of transactions in the block") + return nil, status.Errorf(codes.Internal, "number of transaction results returned by execution node is more than the number of Transactions in the block") } systemTx, err := blueprints.SystemChunkTransaction(b.chainID.Chain()) @@ -501,7 +500,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } // GetTransactionResultByIndex returns TransactionsResults for an index in a block that is executed, -// pending or finalized transactions return errors +// pending or finalized Transactions return errors func (b *backendTransactions) GetTransactionResultByIndex( ctx context.Context, blockID flow.Identifier, @@ -568,7 +567,7 @@ func (b *backendTransactions) deriveTransactionStatus( return flow.TransactionStatusUnknown, err } refHeight := referenceBlock.Height - // get the latest finalized block from the state + // get the latest finalized block from the State finalized, err := b.state.Final().Head() if err != nil { return flow.TransactionStatusUnknown, err @@ -581,27 +580,27 @@ func (b *backendTransactions) deriveTransactionStatus( } // At this point, we have seen the expiry block for the transaction. - // This means that, if no collections prior to the expiry block contain + // This means that, if no Collections prior to the expiry block contain // the transaction, it can never be included and is expired. // - // To ensure this, we need to have received all collections up to the + // To ensure this, we need to have received all Collections up to the // expiry block to ensure the transaction did not appear in any. // the last full height is the height where we have received all - // collections for all blocks with a lower height + // Collections for all Blocks with a lower height fullHeight, err := b.blocks.GetLastFullBlockHeight() if err != nil { return flow.TransactionStatusUnknown, err } - // if we have received collections for all blocks up to the expiry block, the transaction is expired + // if we have received Collections for all Blocks up to the expiry block, the transaction is expired if b.isExpired(refHeight, fullHeight) { return flow.TransactionStatusExpired, nil } // tx found in transaction storage and collection storage but not in block storage // However, this will not happen as of now since the ingestion engine doesn't subscribe - // for collections + // for Collections return flow.TransactionStatusPending, nil } @@ -612,7 +611,7 @@ func (b *backendTransactions) deriveTransactionStatus( // From this point on, we know for sure this transaction has at least been executed - // get the latest sealed block from the state + // get the latest sealed block from the State sealed, err := b.state.Sealed().Head() if err != nil { return flow.TransactionStatusUnknown, err @@ -711,7 +710,7 @@ func (b *backendTransactions) getHistoricalTransactionResult( } if result.GetStatus() == entities.TransactionStatus_PENDING { - // This is on a historical node. No transactions from it will ever be + // This is on a historical node. No Transactions from it will ever be // executed, therefore we should consider this expired result.Status = entities.TransactionStatus_EXPIRED } @@ -838,7 +837,7 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( var errToReturn error defer func() { - // log the errors + // Log the errors if errToReturn != nil { b.log.Err(errToReturn).Msg("failed to get transaction results from execution nodes") } diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index f5e690ddfa3..f876b5d1372 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -34,7 +34,7 @@ func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { epoch1, ok := epochBuilder.EpochHeights(1) require.True(suite.T(), ok) - // setup AtHeight mock returns for state + // setup AtHeight mock returns for State for _, height := range epoch1.Range() { suite.state.On("AtHeight", epoch1.Range()).Return(state.AtHeight(height)) } @@ -67,28 +67,19 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { Return(nil, storage.ErrNotFound) backend := New( - suite.state, - suite.colClient, - nil, - suite.blocks, - nil, - nil, - suite.transactions, - suite.receipts, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - suite.communicator, - false, - ) + BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + }) res, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(res.Status, flow.TransactionStatusUnknown) @@ -116,28 +107,19 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() backend := New( - suite.state, - suite.colClient, - nil, - suite.blocks, - nil, - nil, - suite.transactions, - suite.receipts, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - suite.communicator, - false, - ) + BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + }) _, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().Equal(err, status.Errorf(codes.Internal, "failed to find: %v", fmt.Errorf("some other error"))) @@ -175,28 +157,20 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis Return(&transactionResultResponse, nil).Once() backend := New( - suite.state, - suite.colClient, - []access.AccessAPIClient{suite.historicalAccessClient}, - suite.blocks, - nil, - nil, - suite.transactions, - suite.receipts, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - suite.communicator, - false, - ) + BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + }) resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusExecuted, resp.Status) @@ -239,29 +213,20 @@ func (suite *Suite) TestGetTransactionResultFromCache() { On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil).Once() - backend := New( - suite.state, - suite.colClient, - []access.AccessAPIClient{suite.historicalAccessClient}, - suite.blocks, - nil, - nil, - suite.transactions, - suite.receipts, - nil, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - suite.communicator, - false, - ) + backend := New(BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + }) resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 95496ab67c7..4109c93e94a 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -3,15 +3,14 @@ package backend import ( "context" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/utils/unittest" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // TestHistoricalTransactionResult tests to see if the historical transaction status can be retrieved @@ -36,28 +35,21 @@ func (suite *Suite) TestHistoricalTransactionResult() { Events: nil, } - backend := New(suite.state, - nil, - []accessproto.AccessAPIClient{suite.historicalAccessClient}, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New(BackendParams{ + State: suite.state, + HistoricalAccessNodes: []accessproto.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // Successfully return the transaction from the historical node suite.historicalAccessClient. @@ -96,28 +88,22 @@ func (suite *Suite) TestHistoricalTransaction() { Transaction: convert.TransactionToMessage(*transactionBody), } - backend := New(suite.state, - nil, - []accessproto.AccessAPIClient{suite.historicalAccessClient}, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New( + BackendParams{ + State: suite.state, + HistoricalAccessNodes: []accessproto.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + }) // Successfully return the transaction from the historical node suite.historicalAccessClient. diff --git a/engine/access/rpc/backend/retry.go b/engine/access/rpc/backend/retry.go index c5765bfbbe5..5d967e657bb 100644 --- a/engine/access/rpc/backend/retry.go +++ b/engine/access/rpc/backend/retry.go @@ -10,12 +10,12 @@ import ( ) // retryFrequency has to be less than TransactionExpiry or else this module does nothing -const retryFrequency uint64 = 120 // blocks +const retryFrequency uint64 = 120 // Blocks // Retry implements a simple retry mechanism for transaction submission. type Retry struct { mu sync.RWMutex - // pending transactions + // pending Transactions transactionByReferencBlockHeight map[uint64]map[flow.Identifier]*flow.TransactionBody backend *Backend active bool @@ -47,7 +47,7 @@ func (r *Retry) Retry(height uint64) { return } - // naive cleanup for now, prune every 120 blocks + // naive cleanup for now, prune every 120 Blocks if height%retryFrequency == 0 { r.prune(height) } @@ -84,7 +84,7 @@ func (r *Retry) RegisterTransaction(height uint64, tx *flow.TransactionBody) { func (r *Retry) prune(height uint64) { r.mu.Lock() defer r.mu.Unlock() - // If height is less than the default, there will be no expired transactions + // If height is less than the default, there will be no expired Transactions if height < flow.DefaultTransactionExpiry { return } diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 3f988fb2b54..63590c219b9 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -3,17 +3,16 @@ package backend import ( "context" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/stretchr/testify/mock" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" protocol "github.com/onflow/flow-go/state/protocol/mock" realstorage "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // TestTransactionRetry tests that the retry mechanism will send retries at specific times @@ -41,28 +40,24 @@ func (suite *Suite) TestTransactionRetry() { // txID := transactionBody.ID() // blockID := block.ID() // Setup Handler + Retry - backend := New(suite.state, - suite.colClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - nil, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New( + BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) + retry := newRetry().SetBackend(backend).Activate() backend.retry = retry @@ -131,29 +126,25 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { suite.snapshot.On("Identities", mock.Anything).Return(enIDs, nil) connFactory := suite.setupConnectionFactory() - // Setup Handler + Retry - backend := New(suite.state, - suite.colClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - suite.receipts, - suite.results, - suite.chainID, - metrics.NewNoopCollector(), - connFactory, - false, - DefaultMaxHeightRange, - nil, - nil, - suite.log, - DefaultSnapshotHistoryLimit, - nil, - NewNodeCommunicator(false), - false, - ) + backend := New( + BackendParams{ + State: suite.state, + CollectionRPC: suite.colClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + ConnFactory: connFactory, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Log: suite.log, + Communicator: NewNodeCommunicator(false), + }) + retry := newRetry().SetBackend(backend).Activate() backend.retry = retry @@ -167,7 +158,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { result, err := backend.GetTransactionResult(ctx, txID, flow.ZeroID, flow.ZeroID) suite.checkResponse(result, err) - // status should be finalized since the sealed blocks is smaller in height + // status should be finalized since the sealed Blocks is smaller in height suite.Assert().Equal(flow.TransactionStatusFinalized, result.Status) // Don't retry now now that block is finalized From ed1b3b2e2283bc800b2807e83e5590b03bc749b5 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 14:44:15 +0100 Subject: [PATCH 442/815] Fix backend tests --- engine/access/rpc/backend/backend_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 66f956f8ea1..505ba4cbc1b 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -1533,7 +1533,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() backend := New(BackendParams{ - State: suite.state, + State: state, Blocks: suite.blocks, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1565,7 +1565,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() backend := New(BackendParams{ - State: suite.state, + State: state, Blocks: suite.blocks, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1625,7 +1625,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() backend := New(BackendParams{ - State: suite.state, + State: state, Blocks: suite.blocks, Headers: suite.headers, ExecutionReceipts: suite.receipts, From 4cfeafcf2d9a870e829a953b9f0189edd3ce2ac8 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 14:56:33 +0100 Subject: [PATCH 443/815] Revert comment changes, rename colliding status variable name --- engine/access/rpc/backend/backend.go | 6 ++-- .../rpc/backend/backend_block_details.go | 34 +++++++++---------- engine/access/rpc/backend/backend_events.go | 10 +++--- engine/access/rpc/backend/backend_network.go | 4 +-- engine/access/rpc/backend/backend_scripts.go | 6 ++-- .../rpc/backend/backend_transactions.go | 32 ++++++++--------- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 8df05d04c8b..93b983c8afb 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -332,7 +332,7 @@ func (b *Backend) GetCollectionByID(_ context.Context, colID flow.Identifier) (* // retrieve the collection from the collection storage col, err := b.collections.LightByID(colID) if err != nil { - // Collections are retrieved asynchronously as we finalize Blocks, so + // Collections are retrieved asynchronously as we finalize blocks, so // it is possible for a client to request a finalized block from us // containing some collection, then get a not found error when requesting // that collection. These clients should retry. @@ -398,7 +398,7 @@ func executionNodesForBlockID( break } - // Log the attempt + // log the attempt log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). Int("execution_receipts_found", len(executorIDs)). Str("block_id", blockID.String()). @@ -472,7 +472,7 @@ func findAllExecutionNodes( } } - // if there are more than one execution result for the same block ID, Log as error + // if there are more than one execution result for the same block ID, log as error if executionResultGroupedMetaList.NumberGroups() > 1 { identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) log.Error(). diff --git a/engine/access/rpc/backend/backend_block_details.go b/engine/access/rpc/backend/backend_block_details.go index 1398eda7a77..d9c4c913b40 100644 --- a/engine/access/rpc/backend/backend_block_details.go +++ b/engine/access/rpc/backend/backend_block_details.go @@ -24,21 +24,21 @@ func (b *backendBlockDetails) GetLatestBlock(_ context.Context, isSealed bool) ( // get the latest seal header from storage header, err = b.state.Sealed().Head() } else { - // get the finalized header from State + // get the finalized header from state header, err = b.state.Final().Head() } if err != nil { // node should always have the latest block - // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, + // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad State. + // - It is unsafe to process requests if we have an internally bad state. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol State is widely shared, we assume that in practice another component will - // observe the protocol State error and throw an exception. + // - Since the protocol state is widely shared, we assume that in practice another component will + // observe the protocol state error and throw an exception. return nil, flow.BlockStatusUnknown, status.Errorf(codes.Internal, "could not get latest block: %v", err) } @@ -48,11 +48,11 @@ func (b *backendBlockDetails) GetLatestBlock(_ context.Context, isSealed bool) ( return nil, flow.BlockStatusUnknown, status.Errorf(codes.Internal, "could not get latest block: %v", err) } - status, err := b.getBlockStatus(block) + stat, err := b.getBlockStatus(block) if err != nil { - return nil, status, err + return nil, stat, err } - return block, status, nil + return block, stat, nil } func (b *backendBlockDetails) GetBlockByID(_ context.Context, id flow.Identifier) (*flow.Block, flow.BlockStatus, error) { @@ -61,11 +61,11 @@ func (b *backendBlockDetails) GetBlockByID(_ context.Context, id flow.Identifier return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) } - status, err := b.getBlockStatus(block) + stat, err := b.getBlockStatus(block) if err != nil { - return nil, status, err + return nil, stat, err } - return block, status, nil + return block, stat, nil } func (b *backendBlockDetails) GetBlockByHeight(_ context.Context, height uint64) (*flow.Block, flow.BlockStatus, error) { @@ -74,24 +74,24 @@ func (b *backendBlockDetails) GetBlockByHeight(_ context.Context, height uint64) return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) } - status, err := b.getBlockStatus(block) + stat, err := b.getBlockStatus(block) if err != nil { - return nil, status, err + return nil, stat, err } - return block, status, nil + return block, stat, nil } func (b *backendBlockDetails) getBlockStatus(block *flow.Block) (flow.BlockStatus, error) { sealed, err := b.state.Sealed().Head() if err != nil { - // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, + // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, // we should halt processing requests, but do throw an exception which might cause a crash: // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol State is widely shared, we assume that in practice another component will - // observe the protocol State error and throw an exception. + // - Since the protocol state is widely shared, we assume that in practice another component will + // observe the protocol state error and throw an exception. return flow.BlockStatusUnknown, status.Errorf(codes.Internal, "failed to find latest sealed header: %v", err) } diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index a151e138740..4038e21d922 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -29,7 +29,7 @@ type backendEvents struct { nodeCommunicator Communicator } -// GetEventsForHeightRange retrieves events for all sealed Blocks between the start block height and +// GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and // the end block height (inclusive) that have the given type. func (b *backendEvents) GetEventsForHeightRange( ctx context.Context, @@ -64,7 +64,7 @@ func (b *backendEvents) GetEventsForHeightRange( endHeight = head.Height } - // find the block Headers for all the Blocks between min and max height (inclusive) + // find the block headers for all the blocks between min and max height (inclusive) blockHeaders := make([]*flow.Header, 0) for i := startHeight; i <= endHeight; i++ { @@ -90,7 +90,7 @@ func (b *backendEvents) GetEventsForBlockIDs( return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", len(blockIDs), b.maxHeightRange) } - // find the block Headers for all the block IDs + // find the block headers for all the block IDs blockHeaders := make([]*flow.Header, 0) for _, blockID := range blockIDs { header, err := b.headers.ByBlockID(blockID) @@ -164,7 +164,7 @@ func verifyAndConvertToAccessEvents( version execproto.EventEncodingVersion, ) ([]flow.BlockEvents, error) { if len(execEvents) != len(requestedBlockHeaders) { - return nil, errors.New("number of results does not match number of Blocks requested") + return nil, errors.New("number of results does not match number of blocks requested") } requestedBlockHeaderSet := map[string]*flow.Header{} @@ -222,7 +222,7 @@ func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, logger := b.log.With(). Str("execution_node", node.String()). Str("event", req.GetType()). - Int("Blocks", len(req.BlockIds)). + Int("blocks", len(req.BlockIds)). Int64("rtt_ms", duration.Milliseconds()). Logger() diff --git a/engine/access/rpc/backend/backend_network.go b/engine/access/rpc/backend/backend_network.go index ebb80bd65e8..23d9c602649 100644 --- a/engine/access/rpc/backend/backend_network.go +++ b/engine/access/rpc/backend/backend_network.go @@ -84,8 +84,8 @@ func (b *backendNetwork) isEpochOrPhaseDifferent(counter1, counter2 uint64, phas } // getValidSnapshot will return a valid snapshot that has a sealing segment which -// 1. does not contain any Blocks that span an epoch transition -// 2. does not contain any Blocks that span an epoch phase transition +// 1. does not contain any blocks that span an epoch transition +// 2. does not contain any blocks that span an epoch phase transition // If a snapshot does contain an invalid sealing segment query the state // by height of each block in the segment and return a snapshot at the point // where the transition happens. diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 97b9b3f3f1d..ba48b9cff04 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -199,7 +199,7 @@ func (b *backendScripts) executeScriptOnAvailableArchiveNodes( rnPort := b.archivePorts[idx] result, err := b.tryExecuteScriptOnArchiveNode(ctx, rnAddr, rnPort, blockID, script, arguments) if err == nil { - // Log execution time + // log execution time b.metrics.ScriptExecuted( time.Since(startTime), len(script), @@ -218,7 +218,7 @@ func (b *backendScripts) executeScriptOnAvailableArchiveNodes( Msg("script failed to execute on the execution node") return nil, err case codes.NotFound: - // failures due to unavailable Blocks are explicitly marked Not found + // failures due to unavailable blocks are explicitly marked Not found b.metrics.ScriptExecutionErrorOnArchiveNode() b.log.Error().Err(err).Msg("script execution failed for archive node") return nil, err @@ -269,7 +269,7 @@ func (b *backendScripts) executeScriptOnAvailableExecutionNodes( } } - // Log execution time + // log execution time b.metrics.ScriptExecuted( time.Since(execStartTime), len(script), diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 4abe1b8e1b3..f51d90e490f 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -94,7 +94,7 @@ func (b *backendTransactions) trySendTransaction(ctx context.Context, tx *flow.T var sendError error logAnyError := func() { if sendError != nil { - b.log.Info().Err(err).Msg("failed to send Transactions to collector nodes") + b.log.Info().Err(err).Msg("failed to send transactions to collector nodes") } } defer logAnyError() @@ -403,7 +403,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( i := 0 errInsufficientResults := status.Errorf( codes.Internal, - "number of transaction results returned by execution node is less than the number of Transactions in the block", + "number of transaction results returned by execution node is less than the number of transactions in the block", ) for _, guarantee := range block.Payload.Guarantees { @@ -413,7 +413,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } for _, txID := range collection.Transactions { - // bounds check. this means the EN returned fewer transaction results than the Transactions in the block + // bounds check. this means the EN returned fewer transaction results than the transactions in the block if i >= len(resp.TransactionResults) { return nil, errInsufficientResults } @@ -446,8 +446,8 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } } - // after iterating through all Transactions in each collection, i equals the total number of - // user Transactions in the block + // after iterating through all transactions in each collection, i equals the total number of + // user transactions in the block txCount := i sporkRootBlockHeight, err := b.state.Params().SporkRootBlockHeight() @@ -467,7 +467,7 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( } // otherwise there are extra results // TODO(bft): slashable offense - return nil, status.Errorf(codes.Internal, "number of transaction results returned by execution node is more than the number of Transactions in the block") + return nil, status.Errorf(codes.Internal, "number of transaction results returned by execution node is more than the number of transactions in the block") } systemTx, err := blueprints.SystemChunkTransaction(b.chainID.Chain()) @@ -499,8 +499,8 @@ func (b *backendTransactions) GetTransactionResultsByBlockID( return results, nil } -// GetTransactionResultByIndex returns TransactionsResults for an index in a block that is executed, -// pending or finalized Transactions return errors +// GetTransactionResultByIndex returns transactions Results for an index in a block that is executed, +// pending or finalized transactions return errors func (b *backendTransactions) GetTransactionResultByIndex( ctx context.Context, blockID flow.Identifier, @@ -567,7 +567,7 @@ func (b *backendTransactions) deriveTransactionStatus( return flow.TransactionStatusUnknown, err } refHeight := referenceBlock.Height - // get the latest finalized block from the State + // get the latest finalized block from the state finalized, err := b.state.Final().Head() if err != nil { return flow.TransactionStatusUnknown, err @@ -580,27 +580,27 @@ func (b *backendTransactions) deriveTransactionStatus( } // At this point, we have seen the expiry block for the transaction. - // This means that, if no Collections prior to the expiry block contain + // This means that, if no collections prior to the expiry block contain // the transaction, it can never be included and is expired. // - // To ensure this, we need to have received all Collections up to the + // To ensure this, we need to have received all collections up to the // expiry block to ensure the transaction did not appear in any. // the last full height is the height where we have received all - // Collections for all Blocks with a lower height + // collections for all blocks with a lower height fullHeight, err := b.blocks.GetLastFullBlockHeight() if err != nil { return flow.TransactionStatusUnknown, err } - // if we have received Collections for all Blocks up to the expiry block, the transaction is expired + // if we have received collections for all blocks up to the expiry block, the transaction is expired if b.isExpired(refHeight, fullHeight) { return flow.TransactionStatusExpired, nil } // tx found in transaction storage and collection storage but not in block storage // However, this will not happen as of now since the ingestion engine doesn't subscribe - // for Collections + // for collections return flow.TransactionStatusPending, nil } @@ -710,7 +710,7 @@ func (b *backendTransactions) getHistoricalTransactionResult( } if result.GetStatus() == entities.TransactionStatus_PENDING { - // This is on a historical node. No Transactions from it will ever be + // This is on a historical node. No transactions from it will ever be // executed, therefore we should consider this expired result.Status = entities.TransactionStatus_EXPIRED } @@ -837,7 +837,7 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromAnyExeNode( var errToReturn error defer func() { - // Log the errors + // log the errors if errToReturn != nil { b.log.Err(errToReturn).Msg("failed to get transaction results from execution nodes") } From fef906c607a0f03071c132c00f907a6829928abf Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:08:06 +0100 Subject: [PATCH 444/815] Revert comment changes, rename colliding status variable --- engine/access/rpc/backend/backend.go | 2 +- .../rpc/backend/backend_block_details.go | 2 +- .../rpc/backend/backend_block_headers.go | 28 +++++++++---------- engine/access/rpc/backend/backend_events.go | 4 +-- engine/access/rpc/backend/backend_scripts.go | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 93b983c8afb..63944a57a2d 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -35,7 +35,7 @@ const maxAttemptsForExecutionReceipt = 3 // DefaultMaxHeightRange is the default maximum size of range requests. const DefaultMaxHeightRange = 250 -// DefaultSnapshotHistoryLimit the amount of Blocks to look back in State +// DefaultSnapshotHistoryLimit the amount of blocks to look back in state // when recursively searching for a valid snapshot const DefaultSnapshotHistoryLimit = 50 diff --git a/engine/access/rpc/backend/backend_block_details.go b/engine/access/rpc/backend/backend_block_details.go index d9c4c913b40..9cf366736e1 100644 --- a/engine/access/rpc/backend/backend_block_details.go +++ b/engine/access/rpc/backend/backend_block_details.go @@ -86,7 +86,7 @@ func (b *backendBlockDetails) getBlockStatus(block *flow.Block) (flow.BlockStatu if err != nil { // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad State. + // - It is unsafe to process requests if we have an internally bad state. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index 10efb30c6a2..9e3f7354e89 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -24,28 +24,28 @@ func (b *backendBlockHeaders) GetLatestBlockHeader(_ context.Context, isSealed b // get the latest seal header from storage header, err = b.state.Sealed().Head() } else { - // get the finalized header from State + // get the finalized header from state header, err = b.state.Final().Head() } if err != nil { // node should always have the latest block - // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, + // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, // we should halt processing requests, but do throw an exception which might cause a crash: - // - It is unsafe to process requests if we have an internally bad State. + // - It is unsafe to process requests if we have an internally bad state. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol State is widely shared, we assume that in practice another component will - // observe the protocol State error and throw an exception. + // - Since the protocol state is widely shared, we assume that in practice another component will + // observe the protocol state error and throw an exception. return nil, flow.BlockStatusUnknown, status.Errorf(codes.Internal, "could not get latest block header: %v", err) } - status, err := b.getBlockStatus(header) + stat, err := b.getBlockStatus(header) if err != nil { - return nil, status, err + return nil, stat, err } - return header, status, nil + return header, stat, nil } func (b *backendBlockHeaders) GetBlockHeaderByID(_ context.Context, id flow.Identifier) (*flow.Header, flow.BlockStatus, error) { @@ -54,11 +54,11 @@ func (b *backendBlockHeaders) GetBlockHeaderByID(_ context.Context, id flow.Iden return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) } - status, err := b.getBlockStatus(header) + stat, err := b.getBlockStatus(header) if err != nil { - return nil, status, err + return nil, stat, err } - return header, status, nil + return header, stat, nil } func (b *backendBlockHeaders) GetBlockHeaderByHeight(_ context.Context, height uint64) (*flow.Header, flow.BlockStatus, error) { @@ -67,11 +67,11 @@ func (b *backendBlockHeaders) GetBlockHeaderByHeight(_ context.Context, height u return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) } - status, err := b.getBlockStatus(header) + stat, err := b.getBlockStatus(header) if err != nil { - return nil, status, err + return nil, stat, err } - return header, status, nil + return header, stat, nil } func (b *backendBlockHeaders) getBlockStatus(header *flow.Header) (flow.BlockStatus, error) { diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index 4038e21d922..c19d43846a5 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -64,7 +64,7 @@ func (b *backendEvents) GetEventsForHeightRange( endHeight = head.Height } - // find the block headers for all the blocks between min and max height (inclusive) + // find the block headers for all the blocks between min and max height (inclusive) blockHeaders := make([]*flow.Header, 0) for i := startHeight; i <= endHeight; i++ { @@ -90,7 +90,7 @@ func (b *backendEvents) GetEventsForBlockIDs( return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", len(blockIDs), b.maxHeightRange) } - // find the block headers for all the block IDs + // find the block headers for all the block IDs blockHeaders := make([]*flow.Header, 0) for _, blockID := range blockIDs { header, err := b.headers.ByBlockID(blockID) diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index ba48b9cff04..e98a95b5d8a 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -168,7 +168,7 @@ func (b *backendScripts) logScriptExecutionComparison( archiveError error, msg string, ) { - // over-Log for ease of debug + // over-log for ease of debug if executionError != nil || archiveError != nil { b.log.Debug().Hex("block_id", blockID[:]). Str("script", string(script)). From 5b31bfa7effdfba8ec118dc7251e7e944c2c79d2 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:09:50 +0100 Subject: [PATCH 445/815] Rename Backend constructor params structure --- .../node_builder/access_node_builder.go | 2 +- engine/access/rpc/backend/backend.go | 4 +- engine/access/rpc/backend/backend_test.go | 68 +++++++++---------- .../rpc/backend/backend_transactions_test.go | 8 +-- .../rpc/backend/historical_access_test.go | 4 +- engine/access/rpc/backend/retry_test.go | 6 +- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index b68d2ce5da9..35cbcd58774 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1089,7 +1089,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { ), } - bnd := backend.New(backend.BackendParams{ + bnd := backend.New(backend.Params{ State: node.State, CollectionRPC: builder.CollectionRPC, HistoricalAccessNodes: builder.HistoricalAccessRPCs, diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 63944a57a2d..561c1283a8b 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -100,7 +100,7 @@ type Communicator interface { ) error } -type BackendParams struct { +type Params struct { State protocol.State CollectionRPC accessproto.AccessAPIClient HistoricalAccessNodes []accessproto.AccessAPIClient @@ -125,7 +125,7 @@ type BackendParams struct { } // New creates backend instance -func New(params BackendParams) *Backend { +func New(params Params) *Backend { retry := newRetry() if params.RetryEnabled { retry.Activate() diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 505ba4cbc1b..796e55c150a 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -96,7 +96,7 @@ func (suite *Suite) TestPing() { On("Ping", mock.Anything, &execproto.PingRequest{}). Return(&execproto.PingResponse{}, nil) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, CollectionRPC: suite.colClient, ChainID: suite.chainID, @@ -121,7 +121,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.snapshot.On("Head").Return(block, nil).Once() backend := New( - BackendParams{ + Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -174,7 +174,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_NoTransitionSpan() { snap := state.AtHeight(epoch1.Range()[2]) suite.state.On("Final").Return(snap).Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -235,7 +235,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_TransitionSpans() { suite.state.On("Final").Return(snap).Once() backend := New( - BackendParams{ + Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -288,7 +288,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_PhaseTransitionSpan() { snap := state.AtHeight(epoch1.Range()[3]) suite.state.On("Final").Return(snap).Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -352,7 +352,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_EpochTransitionSpan() { snap := state.AtHeight(epoch2.Range()[0]) suite.state.On("Final").Return(snap).Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -400,7 +400,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot_HistoryLimit() { // very short history limit, any segment with any Blocks spanning any transition should force the endpoint to return a history limit error snapshotHistoryLimit := 1 - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -426,7 +426,7 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { suite.state.On("Sealed").Return(suite.snapshot, nil) suite.snapshot.On("Head").Return(block, nil).Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, ChainID: suite.chainID, AccessMetrics: metrics.NewNoopCollector(), @@ -460,7 +460,7 @@ func (suite *Suite) TestGetTransaction() { Return(&expected, nil). Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Transactions: suite.transactions, ChainID: suite.chainID, @@ -489,7 +489,7 @@ func (suite *Suite) TestGetCollection() { Return(&expected, nil). Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Collections: suite.collections, Transactions: suite.transactions, @@ -542,7 +542,7 @@ func (suite *Suite) TestGetTransactionResultByIndex() { Events: nil, } - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -603,7 +603,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { TransactionResults: []*execproto.GetTransactionResultResponse{{}}, } - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -692,7 +692,7 @@ func (suite *Suite) TestTransactionStatusTransition() { Events: nil, } - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -809,7 +809,7 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { txID := transactionBody.ID() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -969,7 +969,7 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { // create a mock connection factory connFactory := suite.setupConnectionFactory() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -1022,7 +1022,7 @@ func (suite *Suite) TestTransactionResultUnknown() { On("ByID", txID). Return(nil, storage.ErrNotFound) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Transactions: suite.transactions, ChainID: suite.chainID, @@ -1065,7 +1065,7 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { On("ByHeight", header.Height). Return(&expected, nil) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, ChainID: suite.chainID, @@ -1184,7 +1184,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.Run("with an execution node chosen using block ID form the list of Fixed ENs", func() { // create the handler - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1211,7 +1211,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { suite.Run("with an empty block ID list", func() { // create the handler - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: receipts, @@ -1260,7 +1260,7 @@ func (suite *Suite) TestGetExecutionResultByID() { Return(executionResult, nil) suite.Run("nonexisting execution result for id", func() { - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1282,7 +1282,7 @@ func (suite *Suite) TestGetExecutionResultByID() { }) suite.Run("existing execution result id", func() { - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionResults: results, @@ -1336,7 +1336,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.Run("nonexisting execution results", func() { - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1359,7 +1359,7 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { suite.Run("existing execution results", func() { - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionResults: results, @@ -1502,7 +1502,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { //suite.state = state suite.Run("invalid request max height < min height", func() { - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1532,7 +1532,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { expectedResp := setupExecClient() fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - backend := New(BackendParams{ + backend := New(Params{ State: state, Blocks: suite.blocks, Headers: suite.headers, @@ -1564,7 +1564,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { expectedResp := setupExecClient() fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - backend := New(BackendParams{ + backend := New(Params{ State: state, Blocks: suite.blocks, Headers: suite.headers, @@ -1594,7 +1594,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { blockHeaders, _, nodeIdentities = setupStorage(minHeight, headHeight) fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -1624,7 +1624,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { blockHeaders, _, nodeIdentities = setupStorage(minHeight, maxHeight) fixedENIdentifiersStr := flow.IdentifierList(nodeIdentities.NodeIDs()).Strings() - backend := New(BackendParams{ + backend := New(Params{ State: state, Blocks: suite.blocks, Headers: suite.headers, @@ -1694,7 +1694,7 @@ func (suite *Suite) TestGetAccount() { connFactory := new(backendmock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Blocks: suite.blocks, Headers: suite.headers, @@ -1767,7 +1767,7 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { Return(exeResp, nil). Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -1798,7 +1798,7 @@ func (suite *Suite) TestGetNetworkParameters() { expectedChainID := flow.Mainnet - backend := New(BackendParams{ + backend := New(Params{ ChainID: flow.Mainnet, AccessMetrics: metrics.NewNoopCollector(), MaxHeightRange: DefaultMaxHeightRange, @@ -1986,7 +1986,7 @@ func (suite *Suite) TestExecuteScriptOnExecutionNode() { connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) connFactory.On("InvalidateExecutionAPIClient", mock.Anything) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -2053,7 +2053,7 @@ func (suite *Suite) TestExecuteScriptOnArchiveNode() { archiveNode := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) fullArchiveAddress := archiveNode.Address + ":" + strconv.FormatUint(uint64(mockPort), 10) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, @@ -2127,7 +2127,7 @@ func (suite *Suite) TestScriptExecutionValidationMode() { archiveNode := unittest.IdentityFixture(unittest.WithRole(flow.RoleAccess)) fullArchiveAddress := archiveNode.Address + ":" + strconv.FormatUint(uint64(mockPort), 10) - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, Headers: suite.headers, ExecutionReceipts: suite.receipts, diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index f876b5d1372..bbc708423d7 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -67,7 +67,7 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { Return(nil, storage.ErrNotFound) backend := New( - BackendParams{ + Params{ State: suite.state, CollectionRPC: suite.colClient, Blocks: suite.blocks, @@ -107,7 +107,7 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() backend := New( - BackendParams{ + Params{ State: suite.state, CollectionRPC: suite.colClient, Blocks: suite.blocks, @@ -157,7 +157,7 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis Return(&transactionResultResponse, nil).Once() backend := New( - BackendParams{ + Params{ State: suite.state, CollectionRPC: suite.colClient, HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, @@ -213,7 +213,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { On("GetTransactionResult", mock.Anything, mock.Anything). Return(&transactionResultResponse, nil).Once() - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, CollectionRPC: suite.colClient, HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 4109c93e94a..3bacb44b4d8 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -35,7 +35,7 @@ func (suite *Suite) TestHistoricalTransactionResult() { Events: nil, } - backend := New(BackendParams{ + backend := New(Params{ State: suite.state, HistoricalAccessNodes: []accessproto.AccessAPIClient{suite.historicalAccessClient}, Blocks: suite.blocks, @@ -89,7 +89,7 @@ func (suite *Suite) TestHistoricalTransaction() { } backend := New( - BackendParams{ + Params{ State: suite.state, HistoricalAccessNodes: []accessproto.AccessAPIClient{suite.historicalAccessClient}, Blocks: suite.blocks, diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 63590c219b9..54b13ab4c89 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -41,7 +41,7 @@ func (suite *Suite) TestTransactionRetry() { // blockID := block.ID() // Setup Handler + Retry backend := New( - BackendParams{ + Params{ State: suite.state, CollectionRPC: suite.colClient, Blocks: suite.blocks, @@ -127,7 +127,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { connFactory := suite.setupConnectionFactory() backend := New( - BackendParams{ + Params{ State: suite.state, CollectionRPC: suite.colClient, Blocks: suite.blocks, @@ -144,7 +144,7 @@ func (suite *Suite) TestSuccessfulTransactionsDontRetry() { Log: suite.log, Communicator: NewNodeCommunicator(false), }) - + retry := newRetry().SetBackend(backend).Activate() backend.retry = retry From ae8ccaa861d4bf23a416e69f60cedbe2bce3faf4 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:15:19 +0100 Subject: [PATCH 446/815] Introduce TxResultCacheParam for configuration --- engine/access/rpc/backend/backend.go | 10 +++++++--- .../rpc/backend/backend_transactions.go | 19 +++++++++++-------- .../rpc/backend/backend_transactions_test.go | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 561c1283a8b..567f522456b 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -122,6 +122,7 @@ type Params struct { ArchiveAddressList []string Communicator Communicator ScriptExecValidation bool + TxResultCacheSize int } // New creates backend instance @@ -145,9 +146,12 @@ func New(params Params) *Backend { archivePorts[idx] = port } - txResCache, err := lru2.New[flow.Identifier, *access.TransactionResult](int(badger.DefaultCacheSize)) - if err != nil { - params.Log.Fatal().Err(err).Msg("failed to init cache for transaction results") + var txResCache *lru2.Cache[flow.Identifier, *access.TransactionResult] + if params.TxResultCacheSize > 0 { + txResCache, err = lru2.New[flow.Identifier, *access.TransactionResult](params.TxResultCacheSize) + if err != nil { + params.Log.Fatal().Err(err).Msg("failed to init cache for transaction results") + } } b := &Backend{ diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index f51d90e490f..7104928f8f0 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -236,10 +236,11 @@ func (b *backendTransactions) GetTransactionResult( txErr := rpc.ConvertStorageError(err) if status.Code(txErr) == codes.NotFound { // Tx not found. If we have historical Sporks setup, lets look through those as well - - val, ok := b.txResultCache.Get(txID) - if ok { - return val, nil + if b.txResultCache != nil { + val, ok := b.txResultCache.Get(txID) + if ok { + return val, nil + } } historicalTxResult, err := b.getHistoricalTransactionResult(ctx, txID) if err != nil { @@ -251,8 +252,10 @@ func (b *backendTransactions) GetTransactionResult( StatusCode: uint(txStatus), }, nil } - - b.txResultCache.Add(txID, historicalTxResult) + + if b.txResultCache != nil { + b.txResultCache.Add(txID, historicalTxResult) + } return historicalTxResult, nil } return nil, txErr @@ -348,8 +351,8 @@ func (b *backendTransactions) lookupCollectionIDInBlock( // followed by the collection ID lookup. If both are missing, the default lookup by transaction ID is performed. func (b *backendTransactions) retrieveBlock( - // the requested block or collection was not found. If looking up the block based solely on the txID returns - // not found, then no error is returned. +// the requested block or collection was not found. If looking up the block based solely on the txID returns +// not found, then no error is returned. blockID flow.Identifier, collectionID flow.Identifier, txID flow.Identifier, diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index bbc708423d7..9af6507aeb2 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -226,6 +226,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { Log: suite.log, SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, Communicator: suite.communicator, + TxResultCacheSize: 10, }) resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) From 37bb9a3c15cf00f45479782d6d9ce8a3445097f4 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:29:46 +0100 Subject: [PATCH 447/815] Initialise TxResultCacheSize from access node flags --- cmd/access/node_builder/access_node_builder.go | 8 +++++++- engine/access/rpc/backend/backend.go | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 35cbcd58774..010d93a0bbe 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -129,6 +129,7 @@ type AccessNodeConfig struct { executionDataStartHeight uint64 executionDataConfig edrequester.ExecutionDataConfig PublicNetworkConfig PublicNetworkConfig + TxResultCacheSize uint } type PublicNetworkConfig struct { @@ -205,6 +206,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { RetryDelay: edrequester.DefaultRetryDelay, MaxRetryDelay: edrequester.DefaultMaxRetryDelay, }, + TxResultCacheSize: 0, } } @@ -718,6 +720,8 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.DurationVar(&builder.stateStreamConf.ClientSendTimeout, "state-stream-send-timeout", defaultConfig.stateStreamConf.ClientSendTimeout, "maximum wait before timing out while sending a response to a streaming client e.g. 30s") flags.UintVar(&builder.stateStreamConf.ClientSendBufferSize, "state-stream-send-buffer-size", defaultConfig.stateStreamConf.ClientSendBufferSize, "maximum number of responses to buffer within a stream") flags.Float64Var(&builder.stateStreamConf.ResponseLimit, "state-stream-response-limit", defaultConfig.stateStreamConf.ResponseLimit, "max number of responses per second to send over streaming endpoints. this helps manage resources consumed by each client querying data not in the cache e.g. 3 or 0.5. 0 means no limit") + + flags.UintVar(&builder.TxResultCacheSize, "transaction-result-cache-size", defaultConfig.TxResultCacheSize, "transaction result cache size.(Disabled by default i.e 0)") }).ValidateFlags(func() error { if builder.supportsObserver && (builder.PublicNetworkConfig.BindAddress == cmd.NotSet || builder.PublicNetworkConfig.BindAddress == "") { return errors.New("public-network-address must be set if supports-observer is true") @@ -1110,7 +1114,9 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, ArchiveAddressList: backendConfig.ArchiveAddressList, Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - ScriptExecValidation: backendConfig.ScriptExecValidation}) + ScriptExecValidation: backendConfig.ScriptExecValidation + TxResultCacheSize: builder.TxResultCacheSize, + }) engineBuilder, err := rpc.NewBuilder( node.Logger, diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 567f522456b..053f5f30079 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -122,7 +122,7 @@ type Params struct { ArchiveAddressList []string Communicator Communicator ScriptExecValidation bool - TxResultCacheSize int + TxResultCacheSize uint } // New creates backend instance @@ -148,7 +148,7 @@ func New(params Params) *Backend { var txResCache *lru2.Cache[flow.Identifier, *access.TransactionResult] if params.TxResultCacheSize > 0 { - txResCache, err = lru2.New[flow.Identifier, *access.TransactionResult](params.TxResultCacheSize) + txResCache, err = lru2.New[flow.Identifier, *access.TransactionResult](int(params.TxResultCacheSize)) if err != nil { params.Log.Fatal().Err(err).Msg("failed to init cache for transaction results") } From 33232c28cf74664e45285b96322527ae6b89e114 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:34:42 +0100 Subject: [PATCH 448/815] Fix linter issues --- engine/access/rpc/backend/backend.go | 11 +++++----- .../rpc/backend/backend_block_details.go | 5 +++-- .../rpc/backend/backend_block_headers.go | 5 +++-- engine/access/rpc/backend/backend_events.go | 9 ++++---- engine/access/rpc/backend/backend_network.go | 5 +++-- engine/access/rpc/backend/backend_scripts.go | 13 ++++++------ engine/access/rpc/backend/backend_test.go | 21 ++++++++++--------- .../rpc/backend/backend_transactions.go | 19 +++++++++-------- .../rpc/backend/backend_transactions_test.go | 13 ++++++------ .../rpc/backend/historical_access_test.go | 9 ++++---- engine/access/rpc/backend/retry_test.go | 11 +++++----- 11 files changed, 65 insertions(+), 56 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 053f5f30079..e532be24ad5 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -9,6 +9,11 @@ import ( lru "github.com/hashicorp/golang-lru" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -19,11 +24,6 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "github.com/onflow/flow-go/storage/badger" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block @@ -76,7 +76,6 @@ type Backend struct { collections storage.Collections executionReceipts storage.ExecutionReceipts connFactory connection.ConnectionFactory - resultCache *badger.Cache[flow.Identifier, *access.TransactionResult] } // Config defines the configurable options for creating Backend diff --git a/engine/access/rpc/backend/backend_block_details.go b/engine/access/rpc/backend/backend_block_details.go index 9cf366736e1..fc9eee618c4 100644 --- a/engine/access/rpc/backend/backend_block_details.go +++ b/engine/access/rpc/backend/backend_block_details.go @@ -3,12 +3,13 @@ package backend import ( "context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendBlockDetails struct { diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index 9e3f7354e89..10c26a6a3f6 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -3,12 +3,13 @@ package backend import ( "context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendBlockHeaders struct { diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index c19d43846a5..43b42cde2f5 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -7,16 +7,17 @@ import ( "fmt" "time" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendEvents struct { diff --git a/engine/access/rpc/backend/backend_network.go b/engine/access/rpc/backend/backend_network.go index 23d9c602649..cb704d93278 100644 --- a/engine/access/rpc/backend/backend_network.go +++ b/engine/access/rpc/backend/backend_network.go @@ -4,13 +4,14 @@ import ( "context" "fmt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/build" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) var SnapshotHistoryLimitErr = fmt.Errorf("reached the snapshot history limit") diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index e98a95b5d8a..bdff9beb8d7 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -1,25 +1,26 @@ package backend import ( - "crypto/md5" //nolint:gosec "bytes" "context" + "crypto/md5" //nolint:gosec "io" "time" "github.com/hashicorp/go-multierror" lru "github.com/hashicorp/golang-lru" + "github.com/onflow/flow/protobuf/go/flow/access" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "github.com/onflow/flow/protobuf/go/flow/access" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // uniqueScriptLoggingTimeWindow is the duration for checking the uniqueness of scripts sent for execution diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 796e55c150a..8c38a34920e 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -7,6 +7,17 @@ import ( "testing" "github.com/dgraph-io/badger/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + access "github.com/onflow/flow-go/engine/access/mock" backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -19,16 +30,6 @@ import ( "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) const TEST_MAX_HEIGHT = 100 diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 7104928f8f0..25fd0071f53 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -7,6 +7,13 @@ import ( "time" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -16,12 +23,6 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendTransactions struct { @@ -252,7 +253,7 @@ func (b *backendTransactions) GetTransactionResult( StatusCode: uint(txStatus), }, nil } - + if b.txResultCache != nil { b.txResultCache.Add(txID, historicalTxResult) } @@ -351,8 +352,8 @@ func (b *backendTransactions) lookupCollectionIDInBlock( // followed by the collection ID lookup. If both are missing, the default lookup by transaction ID is performed. func (b *backendTransactions) retrieveBlock( -// the requested block or collection was not found. If looking up the block based solely on the txID returns -// not found, then no error is returned. + // the requested block or collection was not found. If looking up the block based solely on the txID returns + // not found, then no error is returned. blockID flow.Identifier, collectionID flow.Identifier, txID flow.Identifier, diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 9af6507aeb2..bf06ae56c20 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,6 +5,13 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/state/protocol" @@ -12,12 +19,6 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { diff --git a/engine/access/rpc/backend/historical_access_test.go b/engine/access/rpc/backend/historical_access_test.go index 3bacb44b4d8..16fba999a47 100644 --- a/engine/access/rpc/backend/historical_access_test.go +++ b/engine/access/rpc/backend/historical_access_test.go @@ -3,14 +3,15 @@ package backend import ( "context" - "github.com/onflow/flow-go/engine/common/rpc/convert" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/utils/unittest" accessproto "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/entities" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/onflow/flow-go/engine/common/rpc/convert" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/utils/unittest" ) // TestHistoricalTransactionResult tests to see if the historical transaction status can be retrieved diff --git a/engine/access/rpc/backend/retry_test.go b/engine/access/rpc/backend/retry_test.go index 54b13ab4c89..d656380c204 100644 --- a/engine/access/rpc/backend/retry_test.go +++ b/engine/access/rpc/backend/retry_test.go @@ -3,16 +3,17 @@ package backend import ( "context" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/metrics" - protocol "github.com/onflow/flow-go/state/protocol/mock" - realstorage "github.com/onflow/flow-go/storage" - "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow/protobuf/go/flow/access" "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/stretchr/testify/mock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + protocol "github.com/onflow/flow-go/state/protocol/mock" + realstorage "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/unittest" ) // TestTransactionRetry tests that the retry mechanism will send retries at specific times From 00768db47d4c6b0267eedae0d80e40de495cccc5 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 13 Aug 2023 15:38:29 +0100 Subject: [PATCH 449/815] Remove comment changes --- engine/access/rpc/backend/backend_block_headers.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index 10c26a6a3f6..9b51201d067 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -3,13 +3,12 @@ package backend import ( "context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendBlockHeaders struct { @@ -78,14 +77,14 @@ func (b *backendBlockHeaders) GetBlockHeaderByHeight(_ context.Context, height u func (b *backendBlockHeaders) getBlockStatus(header *flow.Header) (flow.BlockStatus, error) { sealed, err := b.state.Sealed().Head() if err != nil { - // In the RPC engine, if we encounter an error from the protocol State indicating State corruption, + // In the RPC engine, if we encounter an error from the protocol state indicating state corruption, // we should halt processing requests, but do throw an exception which might cause a crash: // - It is unsafe to process requests if we have an internally bad State. // TODO: https://github.com/onflow/flow-go/issues/4028 // - We would like to avoid throwing an exception as a result of an Access API request by policy // because this can cause DOS potential - // - Since the protocol State is widely shared, we assume that in practice another component will - // observe the protocol State error and throw an exception. + // - Since the protocol state is widely shared, we assume that in practice another component will + // observe the protocol state error and throw an exception. return flow.BlockStatusUnknown, status.Errorf(codes.Internal, "failed to find latest sealed header: %v", err) } From 05c9f9f46b4ac7e05a34bb5686e792bd976f2b39 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Aug 2023 05:27:00 -0400 Subject: [PATCH 450/815] renamed loop variables --- network/alsp/manager/manager_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 7be30014fda..9208a8ffc93 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -335,8 +335,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio expectedCutoffCounter := 0 // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected - for i := range expectedDecays { - t.Logf("starting iteration %d with expected decay %d", i, expectedDecays[i]) + for expectedDecay := range expectedDecays { + t.Logf("starting iteration %d with expected decay %d", expectedDecay, expectedDecays[expectedDecay]) // reset the decay function to the default fastDecay = false @@ -347,7 +347,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // the spammer is definitely disallow-listed. reportCount := 120 wg := sync.WaitGroup{} - for i := 0; i < reportCount; i++ { + for reportCounter := 0; reportCounter < reportCount; reportCounter++ { wg.Add(1) // reports the misbehavior r := report // capture range variable @@ -379,7 +379,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - require.Equal(t, float64(expectedDecays[i]), record.Decay) + require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) require.Equal(t, true, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) @@ -395,13 +395,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) - require.Equal(t, float64(expectedDecays[i]), record.Decay) + require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) require.Equal(t, true, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) penalty2 := record.Penalty // check that the penalty has decayed by the expected amount in one heartbeat - require.Equal(t, float64(expectedDecays[i]), penalty2-penalty1) + require.Equal(t, float64(expectedDecays[expectedDecay]), penalty2-penalty1) // decay the disallow-listing penalty of the spammer node to zero. t.Log("about to decay the disallow-listing penalty of the spammer node to zero") @@ -419,9 +419,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.True(t, ok) require.NotNil(t, record) - expectedDecayAfterCutoff := expectedDecays[i] - if i < len(expectedDecays)-1 { - expectedDecayAfterCutoff = expectedDecays[i+1] + expectedDecayAfterCutoff := expectedDecays[expectedDecay] + if expectedDecay < len(expectedDecays)-1 { + expectedDecayAfterCutoff = expectedDecays[expectedDecay+1] } require.Equal(t, float64(0), record.Penalty) From e8cb9dbe1c97a57e17e470bb9d2b5662ade643b7 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Aug 2023 05:29:20 -0400 Subject: [PATCH 451/815] Comment fix Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 9208a8ffc93..64d38c4967e 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -341,7 +341,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // reset the decay function to the default fastDecay = false - // simulates the victim node reporting the spammer node misbehavior 10 times + // simulates the victim node reporting the spammer node misbehavior 120 times // as each report has the default penalty, ideally the spammer should be disallow-listed after // 100 reports (each having 0.01 * disallow-listing penalty). But we take 120 as a safe number to ensure that // the spammer is definitely disallow-listed. From 14325cd91c9ed0693608f863ca7d418c9975f646 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Aug 2023 06:59:40 -0400 Subject: [PATCH 452/815] refactor to use adjustDecayFunc(), removed DecayList from spam record --- network/alsp/internal/cache.go | 1 - network/alsp/manager/manager.go | 29 +++++++++++++++++++++++++--- network/alsp/manager/manager_test.go | 7 +------ network/alsp/model/record.go | 20 ++----------------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/network/alsp/internal/cache.go b/network/alsp/internal/cache.go index 28b673d637a..c29ae4bd988 100644 --- a/network/alsp/internal/cache.go +++ b/network/alsp/internal/cache.go @@ -168,7 +168,6 @@ func (s *SpamRecordCache) Get(originId flow.Identifier) (*model.ProtocolSpamReco return &model.ProtocolSpamRecord{ OriginId: record.OriginId, Decay: record.Decay, - DecayList: record.DecayList, CutoffCounter: record.CutoffCounter, Penalty: record.Penalty, DisallowListed: record.DisallowListed, diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 57f99d77a7c..95c6aa2c1f1 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -319,6 +319,8 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { // cutoff counter keeps track of how many times the penalty has been below the threshold. record.CutoffCounter++ record.DisallowListed = true + // Adjusts decay dynamically based on how many times the node was disallow-listed (cutoff). + record.Decay = m.adjustDecayFunc(record.CutoffCounter) m.logger.Warn(). Str("key", logging.KeySuspicious). Hex("identifier", logging.ID(id)). @@ -326,7 +328,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Uint64("cutoff_counter", record.CutoffCounter). Float64("decay_speed", record.Decay). Bool("disallow_listed", record.DisallowListed). - Msg("node penalty is below threshold, disallow listing") + Msg("node penalty dropped below threshold, initiating disallow listing") m.disallowListingConsumer.OnDisallowListNotification(&network.DisallowListingUpdate{ FlowIds: flow.IdentifierList{id}, Cause: network.DisallowListedCauseAlsp, // sets the ALSP disallow listing cause on node @@ -354,8 +356,8 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { if record.Penalty == float64(0) && record.DisallowListed { record.DisallowListed = false - // after fully decaying the penalty, update decay for next disallow listing - record.UpdateDecay() + //// after fully decaying the penalty, update decay for next disallow listing + //record.UpdateDecay() m.logger.Info(). Hex("identifier", logging.ID(id)). @@ -449,6 +451,27 @@ func (m *MisbehaviorReportManager) processMisbehaviorReport(report internal.Repo return nil } +// adjustDecayFunc calculates the decay value of the spam record. This allows the decay to be different on subsequent disallow listings. +func (m *MisbehaviorReportManager) adjustDecayFunc(cutoffCounter uint64) float64 { + // decaySpeeds illustrates the decay speeds for different cutoff counters. + // The first cutoff does not reduce the decay speed (1000 -> 1000). However, the second, third, + // and forth cutoffs reduce the decay speed by 90% (1000 -> 100, 100 -> 10, 10 -> 1). + // All subsequent cutoffs after the fourth cutoff use the last decay speed (1). + // This is to prevent the decay speed from becoming too small and the spam record from taking too long to decay. + decaySpeeds := []float64{1000, 1000, 100, 10, 1} + + if cutoffCounter <= 0 { + // illegal state, this should never happen unless there is a bug in the code. + panic(fmt.Sprintf("illegal-state cutoff counter must be positive, it should include the current time: %d", cutoffCounter)) + } + + if int(cutoffCounter) >= len(decaySpeeds) { + return decaySpeeds[len(decaySpeeds)-1] // clamp to the last value + } + + return decaySpeeds[cutoffCounter] +} + // WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. // Args: // diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 64d38c4967e..bac726ea04a 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -419,13 +419,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.True(t, ok) require.NotNil(t, record) - expectedDecayAfterCutoff := expectedDecays[expectedDecay] - if expectedDecay < len(expectedDecays)-1 { - expectedDecayAfterCutoff = expectedDecays[expectedDecay+1] - } - require.Equal(t, float64(0), record.Penalty) - require.Equal(t, float64(expectedDecayAfterCutoff), record.Decay) + require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) require.Equal(t, false, record.DisallowListed) require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) diff --git a/network/alsp/model/record.go b/network/alsp/model/record.go index 7d5a459fea5..e0af3a199ad 100644 --- a/network/alsp/model/record.go +++ b/network/alsp/model/record.go @@ -16,11 +16,6 @@ type ProtocolSpamRecord struct { // Subsequent disallow listings of the node will decrease the Decay speed of the node so it will take longer to be allow-listed. Decay float64 - // DecayList is a list of decay values that are used to decay the Penalty value of the misbehaving node on subsequent disallow listings. - // The decay values are used from left to right (left most value is used for the first disallow listing) and once the right most decay - // value is reached, any subsequent disallow listings will continue to use the right most decay value. - DecayList []float64 - // CutoffCounter is a counter that is used to determine how many times the connections to the node has been cut due to // its Penalty value dropping below the disallow-listing threshold. // Note that the cutoff connections are recovered after a certain amount of time. @@ -35,15 +30,6 @@ type ProtocolSpamRecord struct { Penalty float64 } -// UpdateDecay updates the decay value of the record. This allows the decay to be different on subsequent disallow listings. -// The decay value is updated based on the DecayList. If the DecayList is empty, the decay value is not updated. -func (r *ProtocolSpamRecord) UpdateDecay() { - if len(r.DecayList) > 0 { - r.Decay = r.DecayList[0] - r.DecayList = r.DecayList[1:] - } -} - // RecordAdjustFunc is a function that is used to adjust the fields of a ProtocolSpamRecord. // The function is called with the current record and should return the adjusted record. // Returned error indicates that the adjustment is not applied, and the record should not be updated. @@ -64,10 +50,8 @@ type SpamRecordFactoryFunc func(flow.Identifier) ProtocolSpamRecord func SpamRecordFactory() SpamRecordFactoryFunc { return func(originId flow.Identifier) ProtocolSpamRecord { return ProtocolSpamRecord{ - OriginId: originId, - Decay: InitialDecaySpeed, - // slow down decay 10x after each disallow-listing (e.g. 1000, 100, 10, 1) - DecayList: []float64{InitialDecaySpeed * .1, InitialDecaySpeed * .01, InitialDecaySpeed * .001}, + OriginId: originId, + Decay: InitialDecaySpeed, DisallowListed: false, CutoffCounter: uint64(0), Penalty: float64(0), From 507af3e883ae4d36eb4930fe6b5cae38e588c618 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 12:18:08 +0100 Subject: [PATCH 453/815] Fix syntax error --- cmd/access/node_builder/access_node_builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 010d93a0bbe..0bcef17d6a6 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1114,8 +1114,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, ArchiveAddressList: backendConfig.ArchiveAddressList, Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - ScriptExecValidation: backendConfig.ScriptExecValidation - TxResultCacheSize: builder.TxResultCacheSize, + ScriptExecValidation: backendConfig.ScriptExecValidation, + TxResultCacheSize: builder.TxResultCacheSize, }) engineBuilder, err := rpc.NewBuilder( From d7c82aa7219a5c0fe36dc47fdac2a2a329d4287e Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 12:21:13 +0100 Subject: [PATCH 454/815] Rearrange imports according to convention --- cmd/access/node_builder/access_node_builder.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 0bcef17d6a6..68d32a9efa4 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -12,6 +12,14 @@ import ( badger "github.com/ipfs/go-ds-badger2" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/routing" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/go-bitswap" + "github.com/rs/zerolog" + "github.com/spf13/pflag" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "github.com/onflow/flow-go/admin/commands" stateSyncCommands "github.com/onflow/flow-go/admin/commands/state_synchronization" storageCommands "github.com/onflow/flow-go/admin/commands/storage" @@ -79,13 +87,6 @@ import ( "github.com/onflow/flow-go/storage" bstorage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/utils/grpcutils" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/go-bitswap" - "github.com/rs/zerolog" - "github.com/spf13/pflag" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) // AccessNodeBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node. From 55cc428829a32212c3ad3c70f54a7385d36c573f Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 14 Aug 2023 08:25:38 -0400 Subject: [PATCH 455/815] updated docs for adjustDecayFunc() --- network/alsp/manager/manager.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 95c6aa2c1f1..054f7ea3b4e 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -451,7 +451,16 @@ func (m *MisbehaviorReportManager) processMisbehaviorReport(report internal.Repo return nil } -// adjustDecayFunc calculates the decay value of the spam record. This allows the decay to be different on subsequent disallow listings. +// adjustDecayFunc calculates the decay value of the spam record cache. This allows the decay to be different on subsequent disallow listings. +// It returns the decay speed for the given cutoff counter. +// The cutoff counter is the number of times that the node has been disallow-listed. +// Args: +// cutoffCounter: the number of times that the node has been disallow-listed including the current time. Note that the cutoff counter +// must always be updated before calling this function. +// +// Returns: +// +// float64: the decay speed for the given cutoff counter. func (m *MisbehaviorReportManager) adjustDecayFunc(cutoffCounter uint64) float64 { // decaySpeeds illustrates the decay speeds for different cutoff counters. // The first cutoff does not reduce the decay speed (1000 -> 1000). However, the second, third, From 3da4a39e34c344e8767ee51d9c1a17f0570e1619 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 16:47:49 +0100 Subject: [PATCH 456/815] Add caching for transactions not found anywhere with respective tests --- .../rpc/backend/backend_transactions.go | 21 +-- .../rpc/backend/backend_transactions_test.go | 141 +++++++++++++++++- 2 files changed, 146 insertions(+), 16 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 25fd0071f53..e030093d32f 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -7,13 +7,6 @@ import ( "time" lru2 "github.com/hashicorp/golang-lru/v2" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -23,6 +16,12 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type backendTransactions struct { @@ -248,10 +247,14 @@ func (b *backendTransactions) GetTransactionResult( // if tx not found in old access nodes either, then assume that the tx was submitted to a different AN // and return status as unknown txStatus := flow.TransactionStatusUnknown - return &access.TransactionResult{ + result := &access.TransactionResult{ Status: txStatus, StatusCode: uint(txStatus), - }, nil + } + if b.txResultCache != nil { + b.txResultCache.Add(txID, result) + } + return result, nil } if b.txResultCache != nil { diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index bf06ae56c20..d4567bec44c 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,13 +5,7 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - + acc "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/state/protocol" @@ -19,6 +13,12 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { @@ -243,3 +243,130 @@ func (suite *Suite) TestGetTransactionResultFromCache() { suite.historicalAccessClient.AssertExpectations(suite.T()) }) } + +func (suite *Suite) TestGetTransactionResultCacheNonExistent() { + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody + + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + + suite.transactions. + On("ByID", tx.ID()). + Return(nil, storage.ErrNotFound) + + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil).Once() + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + suite.historicalAccessClient. + On("GetTransactionResult", mock.Anything, mock.Anything). + Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() + + backend := New(Params{ + State: suite.state, + CollectionRPC: suite.colClient, + HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + TxResultCacheSize: 10, + }) + + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusUnknown, resp.Status) + suite.Require().Equal(uint(flow.TransactionStatusUnknown), resp.StatusCode) + + // ensure the unknown transaction is cached when not found anywhere + //panic("here") + txStatus := flow.TransactionStatusUnknown + res, ok := backend.txResultCache.Get(tx.ID()) + suite.Require().True(ok) + suite.Require().Equal(res, &acc.TransactionResult{ + Status: txStatus, + StatusCode: uint(txStatus), + }) + + suite.historicalAccessClient.AssertExpectations(suite.T()) + + }) +} + +func (suite *Suite) TestGetTransactionResultUnknownFromCache() { + suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + block := unittest.BlockFixture() + tbody := unittest.TransactionBodyFixture() + tx := unittest.TransactionFixture() + tx.TransactionBody = tbody + + coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) + + suite.transactions. + On("ByID", tx.ID()). + Return(nil, storage.ErrNotFound) + + suite.communicator.On("CallAvailableNode", + mock.Anything, + mock.Anything, + mock.Anything). + Return(nil).Once() + + suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + + suite.historicalAccessClient. + On("GetTransactionResult", mock.Anything, mock.Anything). + Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() + + backend := New(Params{ + State: suite.state, + CollectionRPC: suite.colClient, + HistoricalAccessNodes: []access.AccessAPIClient{suite.historicalAccessClient}, + Blocks: suite.blocks, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ChainID: suite.chainID, + AccessMetrics: metrics.NewNoopCollector(), + MaxHeightRange: DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: suite.communicator, + TxResultCacheSize: 10, + }) + + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusUnknown, resp.Status) + suite.Require().Equal(uint(flow.TransactionStatusUnknown), resp.StatusCode) + + // ensure the unknown transaction is cached when not found anywhere + //panic("here") + txStatus := flow.TransactionStatusUnknown + res, ok := backend.txResultCache.Get(tx.ID()) + suite.Require().True(ok) + suite.Require().Equal(res, &acc.TransactionResult{ + Status: txStatus, + StatusCode: uint(txStatus), + }) + + resp2, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) + suite.Require().NoError(err) + suite.Require().Equal(flow.TransactionStatusUnknown, resp2.Status) + suite.Require().Equal(uint(flow.TransactionStatusUnknown), resp2.StatusCode) + + suite.historicalAccessClient.AssertExpectations(suite.T()) + + }) +} From 3af348b49e1c2c90e209f63f88f0019ba4fedcdf Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 16:50:34 +0100 Subject: [PATCH 457/815] Fix imports --- engine/access/rpc/backend/backend_transactions.go | 13 +++++++------ .../access/rpc/backend/backend_transactions_test.go | 2 -- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index e030093d32f..f9ef781cb63 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -7,6 +7,13 @@ import ( "time" lru2 "github.com/hashicorp/golang-lru/v2" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" @@ -16,12 +23,6 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - execproto "github.com/onflow/flow/protobuf/go/flow/execution" - "github.com/rs/zerolog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendTransactions struct { diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index d4567bec44c..fb2a1712a1a 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -291,7 +291,6 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { suite.Require().Equal(uint(flow.TransactionStatusUnknown), resp.StatusCode) // ensure the unknown transaction is cached when not found anywhere - //panic("here") txStatus := flow.TransactionStatusUnknown res, ok := backend.txResultCache.Get(tx.ID()) suite.Require().True(ok) @@ -352,7 +351,6 @@ func (suite *Suite) TestGetTransactionResultUnknownFromCache() { suite.Require().Equal(uint(flow.TransactionStatusUnknown), resp.StatusCode) // ensure the unknown transaction is cached when not found anywhere - //panic("here") txStatus := flow.TransactionStatusUnknown res, ok := backend.txResultCache.Get(tx.ID()) suite.Require().True(ok) From 6aee5d7d32dfa7be64043354452a456ec862b268 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 17:16:46 +0100 Subject: [PATCH 458/815] Deduplicate caching related backend transactions unit tests --- .../rpc/backend/backend_transactions_test.go | 62 +++++-------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index fb2a1712a1a..c0f464840e9 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -179,24 +179,17 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis }) } -func (suite *Suite) TestGetTransactionResultFromCache() { +func (suite *Suite) WithGetTransactionCachingTestSetup(f func(b *flow.Block, t *flow.Transaction)) { suite.WithPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() tx.TransactionBody = tbody - coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) - suite.transactions. On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - suite.blocks. - On("ByID", block.ID()). - Return(&block, nil). - Once() - suite.communicator.On("CallAvailableNode", mock.Anything, mock.Anything, @@ -205,6 +198,12 @@ func (suite *Suite) TestGetTransactionResultFromCache() { suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + f(&block, &tx) + }) +} + +func (suite *Suite) TestGetTransactionResultFromCache() { + suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { transactionResultResponse := access.TransactionResultResponse{ Status: entities.TransactionStatus_EXECUTED, StatusCode: uint32(entities.TransactionStatus_EXECUTED), @@ -230,6 +229,8 @@ func (suite *Suite) TestGetTransactionResultFromCache() { TxResultCacheSize: 10, }) + coll := flow.CollectionFromTransactions([]*flow.Transaction{tx}) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusExecuted, resp.Status) @@ -245,25 +246,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { } func (suite *Suite) TestGetTransactionResultCacheNonExistent() { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { - block := unittest.BlockFixture() - tbody := unittest.TransactionBodyFixture() - tx := unittest.TransactionFixture() - tx.TransactionBody = tbody - - coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) - - suite.transactions. - On("ByID", tx.ID()). - Return(nil, storage.ErrNotFound) - - suite.communicator.On("CallAvailableNode", - mock.Anything, - mock.Anything, - mock.Anything). - Return(nil).Once() - - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() + suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). @@ -285,6 +268,8 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { TxResultCacheSize: 10, }) + coll := flow.CollectionFromTransactions([]*flow.Transaction{tx}) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusUnknown, resp.Status) @@ -305,26 +290,7 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { } func (suite *Suite) TestGetTransactionResultUnknownFromCache() { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { - block := unittest.BlockFixture() - tbody := unittest.TransactionBodyFixture() - tx := unittest.TransactionFixture() - tx.TransactionBody = tbody - - coll := flow.CollectionFromTransactions([]*flow.Transaction{&tx}) - - suite.transactions. - On("ByID", tx.ID()). - Return(nil, storage.ErrNotFound) - - suite.communicator.On("CallAvailableNode", - mock.Anything, - mock.Anything, - mock.Anything). - Return(nil).Once() - - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() - + suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. On("GetTransactionResult", mock.Anything, mock.Anything). Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() @@ -345,6 +311,8 @@ func (suite *Suite) TestGetTransactionResultUnknownFromCache() { TxResultCacheSize: 10, }) + coll := flow.CollectionFromTransactions([]*flow.Transaction{tx}) + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusUnknown, resp.Status) From 33bb8b72e284a5d1be18feff14febb2dba9a5b8a Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 14 Aug 2023 17:27:03 +0100 Subject: [PATCH 459/815] Deduplicate backend transaction tests --- .../rpc/backend/backend_transactions_test.go | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index c0f464840e9..c577886912c 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -46,7 +46,7 @@ func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { mock.Anything, mock.Anything, mock.Anything). - Return(nil) + Return(nil).Once() f(snap) }) @@ -140,12 +140,6 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - suite.communicator.On("CallAvailableNode", - mock.Anything, - mock.Anything, - mock.Anything). - Return(nil).Once() - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() transactionResultResponse := access.TransactionResultResponse{ @@ -190,12 +184,6 @@ func (suite *Suite) WithGetTransactionCachingTestSetup(f func(b *flow.Block, t * On("ByID", tx.ID()). Return(nil, storage.ErrNotFound) - suite.communicator.On("CallAvailableNode", - mock.Anything, - mock.Anything, - mock.Anything). - Return(nil).Once() - suite.state.On("AtBlockID", block.ID()).Return(snap, nil).Once() f(&block, &tx) @@ -312,7 +300,7 @@ func (suite *Suite) TestGetTransactionResultUnknownFromCache() { }) coll := flow.CollectionFromTransactions([]*flow.Transaction{tx}) - + resp, err := backend.GetTransactionResult(context.Background(), tx.ID(), block.ID(), coll.ID()) suite.Require().NoError(err) suite.Require().Equal(flow.TransactionStatusUnknown, resp.Status) From c5557a7a0a30a192a35bf02e789f2c8e71a50479 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 14 Aug 2023 10:44:02 -0700 Subject: [PATCH 460/815] split epoch tests to 2 sub-suites --- .../epochs/{ => cohort1}/epoch_join_and_leave_an_test.go | 5 +++-- .../epochs/{ => cohort1}/epoch_join_and_leave_ln_test.go | 5 +++-- .../epochs/{ => cohort1}/epoch_static_transition_test.go | 5 +++-- .../epochs/{ => cohort2}/epoch_join_and_leave_sn_test.go | 5 +++-- .../epochs/{ => cohort2}/epoch_join_and_leave_vn_test.go | 5 +++-- 5 files changed, 15 insertions(+), 10 deletions(-) rename integration/tests/epochs/{ => cohort1}/epoch_join_and_leave_an_test.go (84%) rename integration/tests/epochs/{ => cohort1}/epoch_join_and_leave_ln_test.go (84%) rename integration/tests/epochs/{ => cohort1}/epoch_static_transition_test.go (96%) rename integration/tests/epochs/{ => cohort2}/epoch_join_and_leave_sn_test.go (84%) rename integration/tests/epochs/{ => cohort2}/epoch_join_and_leave_vn_test.go (91%) diff --git a/integration/tests/epochs/epoch_join_and_leave_an_test.go b/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go similarity index 84% rename from integration/tests/epochs/epoch_join_and_leave_an_test.go rename to integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go index 25b96bf425a..7c16d98dc6c 100644 --- a/integration/tests/epochs/epoch_join_and_leave_an_test.go +++ b/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go @@ -1,10 +1,11 @@ -package epochs +package cohort1 import ( "testing" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/integration/tests/epochs" "github.com/onflow/flow-go/model/flow" ) @@ -13,7 +14,7 @@ func TestEpochJoinAndLeaveAN(t *testing.T) { } type EpochJoinAndLeaveANSuite struct { - DynamicEpochTransitionSuite + epochs.DynamicEpochTransitionSuite } // TestEpochJoinAndLeaveAN should update access nodes and assert healthy network conditions diff --git a/integration/tests/epochs/epoch_join_and_leave_ln_test.go b/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go similarity index 84% rename from integration/tests/epochs/epoch_join_and_leave_ln_test.go rename to integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go index 5ca72eee9f4..72f13cd36b0 100644 --- a/integration/tests/epochs/epoch_join_and_leave_ln_test.go +++ b/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go @@ -1,10 +1,11 @@ -package epochs +package cohort1 import ( "testing" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/integration/tests/epochs" "github.com/onflow/flow-go/model/flow" ) @@ -13,7 +14,7 @@ func TestEpochJoinAndLeaveLN(t *testing.T) { } type EpochJoinAndLeaveLNSuite struct { - DynamicEpochTransitionSuite + epochs.DynamicEpochTransitionSuite } // TestEpochJoinAndLeaveLN should update collection nodes and assert healthy network conditions diff --git a/integration/tests/epochs/epoch_static_transition_test.go b/integration/tests/epochs/cohort1/epoch_static_transition_test.go similarity index 96% rename from integration/tests/epochs/epoch_static_transition_test.go rename to integration/tests/epochs/cohort1/epoch_static_transition_test.go index d8ede87166f..a84688fd1ae 100644 --- a/integration/tests/epochs/epoch_static_transition_test.go +++ b/integration/tests/epochs/cohort1/epoch_static_transition_test.go @@ -1,4 +1,4 @@ -package epochs +package cohort1 import ( "testing" @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/integration/tests/epochs" "github.com/onflow/flow-go/model/flow" ) @@ -17,7 +18,7 @@ func TestEpochStaticTransition(t *testing.T) { // StaticEpochTransitionSuite is the suite used for epoch transition tests // with a static identity table. type StaticEpochTransitionSuite struct { - Suite + epochs.Suite } func (s *StaticEpochTransitionSuite) SetupTest() { diff --git a/integration/tests/epochs/epoch_join_and_leave_sn_test.go b/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go similarity index 84% rename from integration/tests/epochs/epoch_join_and_leave_sn_test.go rename to integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go index a3763420cdc..716a47120e7 100644 --- a/integration/tests/epochs/epoch_join_and_leave_sn_test.go +++ b/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go @@ -1,10 +1,11 @@ -package epochs +package cohort2 import ( "testing" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/integration/tests/epochs" "github.com/onflow/flow-go/model/flow" ) @@ -13,7 +14,7 @@ func TestEpochJoinAndLeaveSN(t *testing.T) { } type EpochJoinAndLeaveSNSuite struct { - DynamicEpochTransitionSuite + epochs.DynamicEpochTransitionSuite } // TestEpochJoinAndLeaveSN should update consensus nodes and assert healthy network conditions diff --git a/integration/tests/epochs/epoch_join_and_leave_vn_test.go b/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go similarity index 91% rename from integration/tests/epochs/epoch_join_and_leave_vn_test.go rename to integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go index f5ea2b09de0..e249928959d 100644 --- a/integration/tests/epochs/epoch_join_and_leave_vn_test.go +++ b/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go @@ -1,10 +1,11 @@ -package epochs +package cohort2 import ( "testing" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/integration/tests/epochs" "github.com/onflow/flow-go/model/flow" ) @@ -13,7 +14,7 @@ func TestEpochJoinAndLeaveVN(t *testing.T) { } type EpochJoinAndLeaveVNSuite struct { - DynamicEpochTransitionSuite + epochs.DynamicEpochTransitionSuite } func (s *EpochJoinAndLeaveVNSuite) SetupTest() { From f3f3bb3e5166426610f697fab3dfd24a9961a837 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 14 Aug 2023 10:46:49 -0700 Subject: [PATCH 461/815] add docs, update Makefile split epochs test into cohorts 1, 2 --- integration/Makefile | 15 ++++++++++----- integration/tests/epochs/suite.go | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/integration/Makefile b/integration/Makefile index f44449f7ef4..9216ee5d8a8 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -10,10 +10,10 @@ endif # Run the integration test suite .PHONY: integration-test -integration-test: access-tests ghost-tests mvp-tests execution-tests verification-tests upgrades-tests collection-tests epochs-tests network-tests consensus-tests +integration-test: access-tests ghost-tests mvp-tests execution-tests verification-tests upgrades-tests collection-tests epochs-cohort1-tests epochs-cohort2-tests network-tests consensus-tests .PHONY: ci-integration-test -ci-integration-test: access-tests ghost-tests mvp-tests epochs-tests consensus-tests execution-tests verification-tests upgrades-tests network-tests collection-tests +ci-integration-test: access-tests ghost-tests mvp-tests epochs-cohort1-tests epochs-cohort2-tests consensus-tests execution-tests verification-tests upgrades-tests network-tests collection-tests ############################################################################################ # CAUTION: DO NOT MODIFY THE TARGETS BELOW! DOING SO WILL BREAK THE FLAKY TEST MONITOR @@ -36,10 +36,15 @@ collection-tests: consensus-tests: go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/consensus/... -.PHONY: epochs-tests -epochs-tests: +.PHONY: epochs-cohort1-tests +epochs-cohort1-tests: # Use a higher timeout of 20m for the suite of tests which span full epochs - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 30m ./tests/epochs/... + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort1... + +.PHONY: epochs-cohort2-tests +epochs-cohort2-tests: + # Use a higher timeout of 20m for the suite of tests which span full epochs + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort2... .PHONY: ghost-tests ghost-tests: diff --git a/integration/tests/epochs/suite.go b/integration/tests/epochs/suite.go index 9da32c2ebf2..dca92b5523b 100644 --- a/integration/tests/epochs/suite.go +++ b/integration/tests/epochs/suite.go @@ -1,3 +1,11 @@ +// Package epochs contains common functionality for the epoch integration test suite. +// Individual tests exist in sub-directories of this: cohort1, cohort2... +// Each cohort is run as a separate, sequential CI job. Since the epoch tests are long +// and resource-heavy, we split them into several cohorts, which can be run in parallel. +// +// If a new cohort is added in the future, it must be added to: +// - ci.yml, flaky-test-monitor.yml (ensure new cohort of tests is run) +// - Makefile (include new cohort in integration-test directive, etc.) package epochs import ( From 26942c9f63b3ee7690ec692be8345ec2de3cdd27 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 14 Aug 2023 10:47:07 -0700 Subject: [PATCH 462/815] update CI, flaky test monitor, bors --- .github/workflows/ci.yml | 3 ++- .github/workflows/flaky-test-monitor.yml | 4 +++- bors.toml | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e41e747e0e0..40f9077bddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,7 +192,8 @@ jobs: - make -C integration bft-tests - make -C integration collection-tests - make -C integration consensus-tests - - make -C integration epochs-tests + - make -C integration epochs-cohort1-tests + - make -C integration epochs-cohort2-tests - make -C integration execution-tests - make -C integration ghost-tests - make -C integration mvp-tests diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index 2ede8c77e3d..f3fa72ee47d 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -134,7 +134,9 @@ jobs: test_category: integration-collection - target: consensus-tests test_category: integration-consensus - - target: epochs-tests + - target: epochs-cohort1-tests + test_category: integration-epochs + - target: epochs-cohort2-tests test_category: integration-epochs - target: execution-tests test_category: integration-execution diff --git a/bors.toml b/bors.toml index acb31db6ed9..9f059e1c0b2 100644 --- a/bors.toml +++ b/bors.toml @@ -20,7 +20,8 @@ status = [ "Integration Tests (make -C integration bft-tests)", "Integration Tests (make -C integration collection-tests)", "Integration Tests (make -C integration consensus-tests)", - "Integration Tests (make -C integration epochs-tests)", + "Integration Tests (make -C integration epochs-cohort1-tests)", + "Integration Tests (make -C integration epochs-cohort2-tests)", "Integration Tests (make -C integration execution-tests)", "Integration Tests (make -C integration ghost-tests)", "Integration Tests (make -C integration mvp-tests)", From 6083ed3c20485806a29b45f254736fa0782c8fd5 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 14 Aug 2023 11:22:28 -0700 Subject: [PATCH 463/815] fix private references --- .../cohort1/epoch_join_and_leave_an_test.go | 2 +- .../cohort1/epoch_join_and_leave_ln_test.go | 2 +- .../cohort1/epoch_static_transition_test.go | 10 +- .../cohort2/epoch_join_and_leave_sn_test.go | 2 +- .../cohort2/epoch_join_and_leave_vn_test.go | 2 +- integration/tests/epochs/suite.go | 129 +++++++++--------- 6 files changed, 74 insertions(+), 73 deletions(-) diff --git a/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go b/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go index 7c16d98dc6c..2341e2d31ac 100644 --- a/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go +++ b/integration/tests/epochs/cohort1/epoch_join_and_leave_an_test.go @@ -20,5 +20,5 @@ type EpochJoinAndLeaveANSuite struct { // TestEpochJoinAndLeaveAN should update access nodes and assert healthy network conditions // after the epoch transition completes. See health check function for details. func (s *EpochJoinAndLeaveANSuite) TestEpochJoinAndLeaveAN() { - s.runTestEpochJoinAndLeave(flow.RoleAccess, s.assertNetworkHealthyAfterANChange) + s.RunTestEpochJoinAndLeave(flow.RoleAccess, s.AssertNetworkHealthyAfterANChange) } diff --git a/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go b/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go index 72f13cd36b0..26c40b102d1 100644 --- a/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go +++ b/integration/tests/epochs/cohort1/epoch_join_and_leave_ln_test.go @@ -20,5 +20,5 @@ type EpochJoinAndLeaveLNSuite struct { // TestEpochJoinAndLeaveLN should update collection nodes and assert healthy network conditions // after the epoch transition completes. See health check function for details. func (s *EpochJoinAndLeaveLNSuite) TestEpochJoinAndLeaveLN() { - s.runTestEpochJoinAndLeave(flow.RoleCollection, s.assertNetworkHealthyAfterLNChange) + s.RunTestEpochJoinAndLeave(flow.RoleCollection, s.AssertNetworkHealthyAfterLNChange) } diff --git a/integration/tests/epochs/cohort1/epoch_static_transition_test.go b/integration/tests/epochs/cohort1/epoch_static_transition_test.go index a84688fd1ae..ae1708f514e 100644 --- a/integration/tests/epochs/cohort1/epoch_static_transition_test.go +++ b/integration/tests/epochs/cohort1/epoch_static_transition_test.go @@ -41,10 +41,10 @@ func (s *StaticEpochTransitionSuite) SetupTest() { func (s *StaticEpochTransitionSuite) TestStaticEpochTransition() { s.TimedLogf("waiting for EpochSetup phase of first epoch to begin") - s.AwaitEpochPhase(s.ctx, 0, flow.EpochPhaseSetup, time.Minute, 500*time.Millisecond) + s.AwaitEpochPhase(s.Ctx, 0, flow.EpochPhaseSetup, time.Minute, 500*time.Millisecond) s.TimedLogf("successfully reached EpochSetup phase of first epoch") - snapshot, err := s.client.GetLatestProtocolSnapshot(s.ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(s.Ctx) require.NoError(s.T(), err) header, err := snapshot.Head() @@ -58,15 +58,15 @@ func (s *StaticEpochTransitionSuite) TestStaticEpochTransition() { // wait for the first view of the second epoch s.TimedLogf("waiting for the first view (%d) of second epoch %d", epoch1FinalView+1, epoch1Counter+1) - s.AwaitFinalizedView(s.ctx, epoch1FinalView+1, 4*time.Minute, 500*time.Millisecond) + s.AwaitFinalizedView(s.Ctx, epoch1FinalView+1, 4*time.Minute, 500*time.Millisecond) s.TimedLogf("finalized first view (%d) of second epoch %d", epoch1FinalView+1, epoch1Counter+1) // assert transition to second epoch happened as expected // if counter is still 0, epoch emergency fallback was triggered and we can fail early - s.AssertInEpoch(s.ctx, epoch1Counter+1) + s.AssertInEpoch(s.Ctx, epoch1Counter+1) // submit a smoke test transaction to verify the network can seal a transaction s.TimedLogf("sending smoke test transaction in second epoch") - s.submitSmokeTestTransaction(s.ctx) + s.SubmitSmokeTestTransaction(s.Ctx) s.TimedLogf("successfully submitted and observed sealing of smoke test transaction") } diff --git a/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go b/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go index 716a47120e7..fb825e447a6 100644 --- a/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go +++ b/integration/tests/epochs/cohort2/epoch_join_and_leave_sn_test.go @@ -20,5 +20,5 @@ type EpochJoinAndLeaveSNSuite struct { // TestEpochJoinAndLeaveSN should update consensus nodes and assert healthy network conditions // after the epoch transition completes. See health check function for details. func (s *EpochJoinAndLeaveSNSuite) TestEpochJoinAndLeaveSN() { - s.runTestEpochJoinAndLeave(flow.RoleConsensus, s.assertNetworkHealthyAfterSNChange) + s.RunTestEpochJoinAndLeave(flow.RoleConsensus, s.AssertNetworkHealthyAfterSNChange) } diff --git a/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go b/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go index e249928959d..65569dacd08 100644 --- a/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go +++ b/integration/tests/epochs/cohort2/epoch_join_and_leave_vn_test.go @@ -34,5 +34,5 @@ func (s *EpochJoinAndLeaveVNSuite) SetupTest() { // TestEpochJoinAndLeaveVN should update verification nodes and assert healthy network conditions // after the epoch transition completes. See health check function for details. func (s *EpochJoinAndLeaveVNSuite) TestEpochJoinAndLeaveVN() { - s.runTestEpochJoinAndLeave(flow.RoleVerification, s.assertNetworkHealthyAfterVNChange) + s.RunTestEpochJoinAndLeave(flow.RoleVerification, s.AssertNetworkHealthyAfterVNChange) } diff --git a/integration/tests/epochs/suite.go b/integration/tests/epochs/suite.go index dca92b5523b..8e0887c3ebf 100644 --- a/integration/tests/epochs/suite.go +++ b/integration/tests/epochs/suite.go @@ -40,7 +40,7 @@ import ( // nodeUpdateValidation func that will be used to validate the health of the network // after an identity table change during an epoch transition. This is used in -// tandem with runTestEpochJoinAndLeave. +// tandem with RunTestEpochJoinAndLeave. // NOTE: The snapshot must reference a block within the second epoch. type nodeUpdateValidation func(ctx context.Context, env templates.Environment, snapshot *inmem.Snapshot, info *StakedNodeOperationInfo) @@ -48,12 +48,13 @@ type nodeUpdateValidation func(ctx context.Context, env templates.Environment, s type Suite struct { suite.Suite lib.TestnetStateTracker - ctx context.Context cancel context.CancelFunc log zerolog.Logger net *testnet.FlowNetwork ghostID flow.Identifier - client *testnet.Client + + Client *testnet.Client + Ctx context.Context // Epoch config (lengths in views) StakingAuctionLen uint64 @@ -72,7 +73,7 @@ func (s *Suite) SetupTest() { // ensure epoch lengths are set correctly require.Greater(s.T(), s.EpochLen, minEpochLength+s.EpochCommitSafetyThreshold, "epoch too short") - s.ctx, s.cancel = context.WithCancel(context.Background()) + s.Ctx, s.cancel = context.WithCancel(context.Background()) s.log = unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel) s.log.Info().Msg("================> SetupTest") defer func() { @@ -115,24 +116,24 @@ func (s *Suite) SetupTest() { s.net = testnet.PrepareFlowNetwork(s.T(), netConf, flow.Localnet) // start the network - s.net.Start(s.ctx) + s.net.Start(s.Ctx) // start tracking blocks - s.Track(s.T(), s.ctx, s.Ghost()) + s.Track(s.T(), s.Ctx, s.Ghost()) // use AN1 for test-related queries - the AN join/leave test will replace AN2 client, err := s.net.ContainerByName(testnet.PrimaryAN).TestnetClient() require.NoError(s.T(), err) - s.client = client + s.Client = client // log network info periodically to aid in debugging future flaky tests - go lib.LogStatusPeriodically(s.T(), s.ctx, s.log, s.client, 5*time.Second) + go lib.LogStatusPeriodically(s.T(), s.Ctx, s.log, s.Client, 5*time.Second) } func (s *Suite) Ghost() *client.GhostClient { client, err := s.net.ContainerByID(s.ghostID).GhostClient() - require.NoError(s.T(), err, "could not get ghost client") + require.NoError(s.T(), err, "could not get ghost Client") return client } @@ -190,17 +191,17 @@ func (s *Suite) StakeNode(ctx context.Context, env templates.Environment, role f SetHashAlgo(sdkcrypto.SHA2_256). SetWeight(sdk.AccountKeyWeightThreshold) - _, stakeAmount, err := s.client.TokenAmountByRole(role) + _, stakeAmount, err := s.Client.TokenAmountByRole(role) require.NoError(s.T(), err) containerName := s.getTestContainerName(role) - latestBlockID, err := s.client.GetLatestBlockID(ctx) + latestBlockID, err := s.Client.GetLatestBlockID(ctx) require.NoError(s.T(), err) // create and register node tx, err := utils.MakeCreateAndSetupNodeTx( env, - s.client.Account(), + s.Client.Account(), sdk.Identifier(latestBlockID), fullStakingAcctKey, fmt.Sprintf("%f", stakeAmount+10.0), @@ -213,14 +214,14 @@ func (s *Suite) StakeNode(ctx context.Context, env templates.Environment, role f ) require.NoError(s.T(), err) - err = s.client.SignAndSendTransaction(ctx, tx) + err = s.Client.SignAndSendTransaction(ctx, tx) require.NoError(s.T(), err) - result, err := s.client.WaitForSealed(ctx, tx.ID()) + result, err := s.Client.WaitForSealed(ctx, tx.ID()) require.NoError(s.T(), err) - s.client.Account().Keys[0].SequenceNumber++ + s.Client.Account().Keys[0].SequenceNumber++ require.NoError(s.T(), result.Error) - accounts := s.client.CreatedAccounts(result) + accounts := s.Client.CreatedAccounts(result) stakingAccountAddress := accounts[0] var machineAccountAddr sdk.Address if role == flow.RoleCollection || role == flow.RoleConsensus { @@ -291,29 +292,29 @@ func (s *Suite) submitAdminRemoveNodeTx(ctx context.Context, env templates.Environment, nodeID flow.Identifier, ) (*sdk.TransactionResult, error) { - latestBlockID, err := s.client.GetLatestBlockID(ctx) + latestBlockID, err := s.Client.GetLatestBlockID(ctx) require.NoError(s.T(), err) closeStakeTx, err := utils.MakeAdminRemoveNodeTx( env, - s.client.Account(), + s.Client.Account(), 0, sdk.Identifier(latestBlockID), nodeID, ) require.NoError(s.T(), err) - err = s.client.SignAndSendTransaction(ctx, closeStakeTx) + err = s.Client.SignAndSendTransaction(ctx, closeStakeTx) require.NoError(s.T(), err) - result, err := s.client.WaitForSealed(ctx, closeStakeTx.ID()) + result, err := s.Client.WaitForSealed(ctx, closeStakeTx.ID()) require.NoError(s.T(), err) - s.client.Account().Keys[0].SequenceNumber++ + s.Client.Account().Keys[0].SequenceNumber++ return result, nil } func (s *Suite) ExecuteGetProposedTableScript(ctx context.Context, env templates.Environment, nodeID flow.Identifier) cadence.Value { - v, err := s.client.ExecuteScriptBytes(ctx, templates.GenerateReturnProposedTableScript(env), []cadence.Value{}) + v, err := s.Client.ExecuteScriptBytes(ctx, templates.GenerateReturnProposedTableScript(env), []cadence.Value{}) require.NoError(s.T(), err) return v } @@ -322,14 +323,14 @@ func (s *Suite) ExecuteGetProposedTableScript(ctx context.Context, env templates func (s *Suite) ExecuteGetNodeInfoScript(ctx context.Context, env templates.Environment, nodeID flow.Identifier) cadence.Value { cdcNodeID, err := cadence.NewString(nodeID.String()) require.NoError(s.T(), err) - v, err := s.client.ExecuteScriptBytes(ctx, templates.GenerateGetNodeInfoScript(env), []cadence.Value{cdcNodeID}) + v, err := s.Client.ExecuteScriptBytes(ctx, templates.GenerateGetNodeInfoScript(env), []cadence.Value{cdcNodeID}) require.NoError(s.T(), err) return v } // SubmitSetApprovedListTx adds a node to the approved node list, this must be done when a node joins the protocol during the epoch staking phase func (s *Suite) SubmitSetApprovedListTx(ctx context.Context, env templates.Environment, identities ...flow.Identifier) *sdk.TransactionResult { - latestBlockID, err := s.client.GetLatestBlockID(ctx) + latestBlockID, err := s.Client.GetLatestBlockID(ctx) require.NoError(s.T(), err) idTableAddress := sdk.HexToAddress(env.IDTableAddress) @@ -337,25 +338,25 @@ func (s *Suite) SubmitSetApprovedListTx(ctx context.Context, env templates.Envir SetScript(templates.GenerateSetApprovedNodesScript(env)). SetGasLimit(9999). SetReferenceBlockID(sdk.Identifier(latestBlockID)). - SetProposalKey(s.client.SDKServiceAddress(), 0, s.client.Account().Keys[0].SequenceNumber). - SetPayer(s.client.SDKServiceAddress()). + SetProposalKey(s.Client.SDKServiceAddress(), 0, s.Client.Account().Keys[0].SequenceNumber). + SetPayer(s.Client.SDKServiceAddress()). AddAuthorizer(idTableAddress) err = tx.AddArgument(blueprints.SetStakingAllowlistTxArg(identities)) require.NoError(s.T(), err) - err = s.client.SignAndSendTransaction(ctx, tx) + err = s.Client.SignAndSendTransaction(ctx, tx) require.NoError(s.T(), err) - result, err := s.client.WaitForSealed(ctx, tx.ID()) + result, err := s.Client.WaitForSealed(ctx, tx.ID()) require.NoError(s.T(), err) - s.client.Account().Keys[0].SequenceNumber++ + s.Client.Account().Keys[0].SequenceNumber++ return result } // ExecuteReadApprovedNodesScript executes the return proposal table script and returns a list of approved nodes func (s *Suite) ExecuteReadApprovedNodesScript(ctx context.Context, env templates.Environment) cadence.Value { - v, err := s.client.ExecuteScriptBytes(ctx, templates.GenerateGetApprovedNodesScript(env), []cadence.Value{}) + v, err := s.Client.ExecuteScriptBytes(ctx, templates.GenerateGetApprovedNodesScript(env), []cadence.Value{}) require.NoError(s.T(), err) return v @@ -371,7 +372,7 @@ func (s *Suite) getTestContainerName(role flow.Role) string { // and checks that the info.NodeID is in both list func (s *Suite) assertNodeApprovedAndProposed(ctx context.Context, env templates.Environment, info *StakedNodeOperationInfo) { // ensure node ID in approved list - //approvedNodes := s.ExecuteReadApprovedNodesScript(ctx, env) + //approvedNodes := s.ExecuteReadApprovedNodesScript(Ctx, env) //require.Containsf(s.T(), approvedNodes.(cadence.Array).Values, cadence.String(info.NodeID.String()), "expected new node to be in approved nodes list: %x", info.NodeID) // Access Nodes go through a separate selection process, so they do not immediately @@ -451,7 +452,7 @@ func (s *Suite) getContainerToReplace(role flow.Role) *testnet.Container { // AwaitEpochPhase waits for the given phase, in the given epoch. func (s *Suite) AwaitEpochPhase(ctx context.Context, expectedEpoch uint64, expectedPhase flow.EpochPhase, waitFor, tick time.Duration) { condition := func() bool { - snapshot, err := s.client.GetLatestProtocolSnapshot(ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(ctx) require.NoError(s.T(), err) actualEpoch, err := snapshot.Epochs().Current().Counter() @@ -466,7 +467,7 @@ func (s *Suite) AwaitEpochPhase(ctx context.Context, expectedEpoch uint64, expec // AssertInEpochPhase checks if we are in the phase of the given epoch. func (s *Suite) AssertInEpochPhase(ctx context.Context, expectedEpoch uint64, expectedPhase flow.EpochPhase) { - snapshot, err := s.client.GetLatestProtocolSnapshot(ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(ctx) require.NoError(s.T(), err) actualEpoch, err := snapshot.Epochs().Current().Counter() require.NoError(s.T(), err) @@ -482,7 +483,7 @@ func (s *Suite) AssertInEpochPhase(ctx context.Context, expectedEpoch uint64, ex // AssertInEpoch requires actual epoch counter is equal to counter provided. func (s *Suite) AssertInEpoch(ctx context.Context, expectedEpoch uint64) { - snapshot, err := s.client.GetLatestProtocolSnapshot(ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(ctx) require.NoError(s.T(), err) actualEpoch, err := snapshot.Epochs().Current().Counter() require.NoError(s.T(), err) @@ -524,7 +525,7 @@ func (s *Suite) AwaitFinalizedView(ctx context.Context, view uint64, waitFor, ti // getLatestSealedHeader retrieves the latest sealed block, as reported in LatestSnapshot. func (s *Suite) getLatestSealedHeader(ctx context.Context) *flow.Header { - snapshot, err := s.client.GetLatestProtocolSnapshot(ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(ctx) require.NoError(s.T(), err) segment, err := snapshot.SealingSegment() require.NoError(s.T(), err) @@ -534,36 +535,36 @@ func (s *Suite) getLatestSealedHeader(ctx context.Context) *flow.Header { // getLatestFinalizedHeader retrieves the latest finalized block, as reported in LatestSnapshot. func (s *Suite) getLatestFinalizedHeader(ctx context.Context) *flow.Header { - snapshot, err := s.client.GetLatestProtocolSnapshot(ctx) + snapshot, err := s.Client.GetLatestProtocolSnapshot(ctx) require.NoError(s.T(), err) finalized, err := snapshot.Head() require.NoError(s.T(), err) return finalized } -// submitSmokeTestTransaction will submit a create account transaction to smoke test network +// SubmitSmokeTestTransaction will submit a create account transaction to smoke test network // This ensures a single transaction can be sealed by the network. -func (s *Suite) submitSmokeTestTransaction(ctx context.Context) { - _, err := utils.CreateFlowAccount(ctx, s.client) +func (s *Suite) SubmitSmokeTestTransaction(ctx context.Context) { + _, err := utils.CreateFlowAccount(ctx, s.Client) require.NoError(s.T(), err) } -// assertNetworkHealthyAfterANChange performs a basic network health check after replacing an access node. +// AssertNetworkHealthyAfterANChange performs a basic network health check after replacing an access node. // 1. Check that there is no problem connecting directly to the AN provided and retrieve a protocol snapshot // 2. Check that the chain moved at least 20 blocks from when the node was bootstrapped by comparing // head of the rootSnapshot with the head of the snapshot we retrieved directly from the AN // 3. Check that we can execute a script on the AN // // TODO test sending and observing result of a transaction via the new AN (blocked by https://github.com/onflow/flow-go/issues/3642) -func (s *Suite) assertNetworkHealthyAfterANChange(ctx context.Context, env templates.Environment, snapshotInSecondEpoch *inmem.Snapshot, info *StakedNodeOperationInfo) { +func (s *Suite) AssertNetworkHealthyAfterANChange(ctx context.Context, env templates.Environment, snapshotInSecondEpoch *inmem.Snapshot, info *StakedNodeOperationInfo) { // get snapshot directly from new AN and compare head with head from the // snapshot that was used to bootstrap the node client, err := s.net.ContainerByName(info.ContainerName).TestnetClient() require.NoError(s.T(), err) - // overwrite client to point to the new AN (since we have stopped the initial AN at this point) - s.client = client + // overwrite Client to point to the new AN (since we have stopped the initial AN at this point) + s.Client = client // assert atleast 20 blocks have been finalized since the node replacement s.AwaitSealedBlockHeightExceedsSnapshot(ctx, snapshotInSecondEpoch, 10, 30*time.Second, time.Millisecond*100) @@ -573,25 +574,25 @@ func (s *Suite) assertNetworkHealthyAfterANChange(ctx context.Context, env templ require.Contains(s.T(), proposedTable.(cadence.Array).Values, cadence.String(info.NodeID.String()), "expected node ID to be present in proposed table returned by new AN.") } -// assertNetworkHealthyAfterVNChange performs a basic network health check after replacing a verification node. +// AssertNetworkHealthyAfterVNChange performs a basic network health check after replacing a verification node. // 1. Ensure sealing continues into the second epoch (post-replacement) by observing // at least 10 blocks of sealing progress within the epoch -func (s *Suite) assertNetworkHealthyAfterVNChange(ctx context.Context, _ templates.Environment, snapshotInSecondEpoch *inmem.Snapshot, _ *StakedNodeOperationInfo) { +func (s *Suite) AssertNetworkHealthyAfterVNChange(ctx context.Context, _ templates.Environment, snapshotInSecondEpoch *inmem.Snapshot, _ *StakedNodeOperationInfo) { s.AwaitSealedBlockHeightExceedsSnapshot(ctx, snapshotInSecondEpoch, 10, 30*time.Second, time.Millisecond*100) } -// assertNetworkHealthyAfterLNChange performs a basic network health check after replacing a collection node. +// AssertNetworkHealthyAfterLNChange performs a basic network health check after replacing a collection node. // 1. Submit transaction to network that will target the newly staked LN by making // sure the reference block ID is after the first epoch. -func (s *Suite) assertNetworkHealthyAfterLNChange(ctx context.Context, _ templates.Environment, _ *inmem.Snapshot, _ *StakedNodeOperationInfo) { +func (s *Suite) AssertNetworkHealthyAfterLNChange(ctx context.Context, _ templates.Environment, _ *inmem.Snapshot, _ *StakedNodeOperationInfo) { // At this point we have reached the second epoch and our new LN is the only LN in the network. // To validate the LN joined the network successfully and is processing transactions we create // an account, which submits a transaction and verifies it is sealed. - s.submitSmokeTestTransaction(ctx) + s.SubmitSmokeTestTransaction(ctx) } -// assertNetworkHealthyAfterSNChange performs a basic network health check after replacing a consensus node. -// The runTestEpochJoinAndLeave function running prior to this health check already asserts that we successfully: +// AssertNetworkHealthyAfterSNChange performs a basic network health check after replacing a consensus node. +// The RunTestEpochJoinAndLeave function running prior to this health check already asserts that we successfully: // 1. enter the second epoch (DKG succeeds; epoch fallback is not triggered) // 2. seal at least the first block within the second epoch (consensus progresses into second epoch). // @@ -599,11 +600,11 @@ func (s *Suite) assertNetworkHealthyAfterLNChange(ctx context.Context, _ templat // therefore the newly joined consensus node must be participating in consensus. // // In addition, here, we submit a transaction and verify that it is sealed. -func (s *Suite) assertNetworkHealthyAfterSNChange(ctx context.Context, _ templates.Environment, _ *inmem.Snapshot, _ *StakedNodeOperationInfo) { - s.submitSmokeTestTransaction(ctx) +func (s *Suite) AssertNetworkHealthyAfterSNChange(ctx context.Context, _ templates.Environment, _ *inmem.Snapshot, _ *StakedNodeOperationInfo) { + s.SubmitSmokeTestTransaction(ctx) } -// runTestEpochJoinAndLeave coordinates adding and removing one node with the given +// RunTestEpochJoinAndLeave coordinates adding and removing one node with the given // role during the first epoch, then running the network health validation function // once the network has successfully transitioned into the second epoch. // @@ -611,13 +612,13 @@ func (s *Suite) assertNetworkHealthyAfterSNChange(ctx context.Context, _ templat // * that nodes can stake and join the network at an epoch boundary // * that nodes can unstake and leave the network at an epoch boundary // * role-specific network health validation after the swap has completed -func (s *Suite) runTestEpochJoinAndLeave(role flow.Role, checkNetworkHealth nodeUpdateValidation) { +func (s *Suite) RunTestEpochJoinAndLeave(role flow.Role, checkNetworkHealth nodeUpdateValidation) { env := utils.LocalnetEnv() var containerToReplace *testnet.Container - // replace access_2, avoid replacing access_1 the container used for client connections + // replace access_2, avoid replacing access_1 the container used for Client connections if role == flow.RoleAccess { containerToReplace = s.net.ContainerByName("access_2") require.NotNil(s.T(), containerToReplace) @@ -629,21 +630,21 @@ func (s *Suite) runTestEpochJoinAndLeave(role flow.Role, checkNetworkHealth node // staking our new node and add get the corresponding container for that node s.TimedLogf("staking joining node with role %s", role.String()) - info, testContainer := s.StakeNewNode(s.ctx, env, role) + info, testContainer := s.StakeNewNode(s.Ctx, env, role) s.TimedLogf("successfully staked joining node: %s", info.NodeID) // use admin transaction to remove node, this simulates a node leaving the network s.TimedLogf("removing node %s with role %s", containerToReplace.Config.NodeID, role.String()) - s.removeNodeFromProtocol(s.ctx, env, containerToReplace.Config.NodeID) + s.removeNodeFromProtocol(s.Ctx, env, containerToReplace.Config.NodeID) s.TimedLogf("successfully removed node: %s", containerToReplace.Config.NodeID) // wait for epoch setup phase before we start our container and pause the old container s.TimedLogf("waiting for EpochSetup phase of first epoch to begin") - s.AwaitEpochPhase(s.ctx, 0, flow.EpochPhaseSetup, 3*time.Minute, 500*time.Millisecond) + s.AwaitEpochPhase(s.Ctx, 0, flow.EpochPhaseSetup, 3*time.Minute, 500*time.Millisecond) s.TimedLogf("successfully reached EpochSetup phase of first epoch") // get the latest snapshot and start new container with it - rootSnapshot, err := s.client.GetLatestProtocolSnapshot(s.ctx) + rootSnapshot, err := s.Client.GetLatestProtocolSnapshot(s.Ctx) require.NoError(s.T(), err) header, err := rootSnapshot.Head() @@ -657,14 +658,14 @@ func (s *Suite) runTestEpochJoinAndLeave(role flow.Role, checkNetworkHealth node segment.Sealed().Header.View, segment.Highest().Header.View) testContainer.WriteRootSnapshot(rootSnapshot) - testContainer.Container.Start(s.ctx) + testContainer.Container.Start(s.Ctx) epoch1FinalView, err := rootSnapshot.Epochs().Current().FinalView() require.NoError(s.T(), err) // wait for at least the first block of the next epoch to be sealed before we pause our container to replace s.TimedLogf("waiting for epoch transition (finalized view %d) before pausing container", epoch1FinalView+1) - s.AwaitFinalizedView(s.ctx, epoch1FinalView+1, 4*time.Minute, 500*time.Millisecond) + s.AwaitFinalizedView(s.Ctx, epoch1FinalView+1, 4*time.Minute, 500*time.Millisecond) s.TimedLogf("observed finalized view %d -> pausing container", epoch1FinalView+1) // make sure container to replace is not a member of epoch 2 @@ -672,17 +673,17 @@ func (s *Suite) runTestEpochJoinAndLeave(role flow.Role, checkNetworkHealth node // assert transition to second epoch happened as expected // if counter is still 0, epoch emergency fallback was triggered and we can fail early - s.AssertInEpoch(s.ctx, 1) + s.AssertInEpoch(s.Ctx, 1) err = containerToReplace.Pause() require.NoError(s.T(), err) // retrieve a snapshot after observing that we have entered the second epoch - secondEpochSnapshot, err := s.client.GetLatestProtocolSnapshot(s.ctx) + secondEpochSnapshot, err := s.Client.GetLatestProtocolSnapshot(s.Ctx) require.NoError(s.T(), err) // make sure the network is healthy after adding new node - checkNetworkHealth(s.ctx, env, secondEpochSnapshot, info) + checkNetworkHealth(s.Ctx, env, secondEpochSnapshot, info) } // DynamicEpochTransitionSuite is the suite used for epoch transitions tests From a0863b8ed7f25dd090acd05136519b8288af4020 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 14 Aug 2023 14:47:55 -0700 Subject: [PATCH 464/815] changes spork id to be computed with the test logic --- network/alsp/manager/manager_test.go | 57 +++++++++++++++++----- network/internal/testutils/testUtil.go | 16 +++--- network/test/blob_service_test.go | 9 +++- network/test/echoengine_test.go | 10 +++- network/test/epochtransition_test.go | 10 +++- network/test/meshengine_test.go | 10 +++- network/test/middleware_test.go | 18 +++++-- network/test/unicast_authorization_test.go | 5 +- 8 files changed, 101 insertions(+), 34 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index b013688bf8b..82ee1f001b5 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -52,10 +52,16 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { return ch }() + sporkId := unittest.IdentifierFixture() misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 1) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + mws, _ := testutils.MiddlewareFixtures( + t, + ids, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId), + mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0]) net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) @@ -111,8 +117,15 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { return cache }), } - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 1) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) + + sporkId := unittest.IdentifierFixture() + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + mws, _ := testutils.MiddlewareFixtures( + t, + ids, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId), + mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -205,9 +218,18 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) }), } - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, + sporkId := unittest.IdentifierFixture() + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture( + t, + sporkId, + 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) + mws, _ := testutils.MiddlewareFixtures( + t, + ids, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId), + mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -278,11 +300,21 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // The test ensures that despite attempting on connections, no inbound or outbound connections between the victim and // the pruned spammer nodes are established. func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t *testing.T) { - + sporkId := unittest.IdentifierFixture() // create 1 victim node, 1 honest node and a node for each slashing violation - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(managerCfgFixture(t))) + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). + mws, _ := testutils.MiddlewareFixtures( + t, + ids, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId), + mocknetwork.NewViolationsConsumer(t)) + networkCfg := testutils.NetworkConfigFixture( + t, + *ids[0], + ids, + mws[0], + p2p.WithAlspConfig(managerCfgFixture(t))) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -371,8 +403,9 @@ func TestMisbehaviorReportMetrics(t *testing.T) { alspMetrics := mockmodule.NewAlspMetrics(t) cfg.AlspMetrics = alspMetrics - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 1) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) + sporkId := unittest.IdentifierFixture() + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 95707ee9e3c..8e4d457e0e9 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -39,8 +39,6 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -var sporkID = unittest.IdentifierFixture() - // RateLimitConsumer p2p.RateLimiterConsumer fixture that invokes a callback when rate limit event is consumed. type RateLimitConsumer struct { callback func(pid peer.ID, role, msgType, topic, reason string) // callback func that will be invoked on rate limit @@ -118,9 +116,10 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // If you want to create a standalone LibP2PNode without network and middleware components, please use p2ptest.NodeFixture. // Args: // -// t: testing.T- the test object +// t: testing.T- the test object +// sporkId: flow.Identifier - the spork id to use for the nodes +// n: int - number of nodes to create // -// n: int - number of nodes to create // opts: []p2ptest.NodeFixtureParameterOption - options to configure the nodes // Returns: // @@ -128,8 +127,7 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // // []p2p.LibP2PNode - list of libp2p nodes created. // []observable.Observable - list of observables created for each node. -func LibP2PNodeForMiddlewareFixture(t *testing.T, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode, []observable.Observable) { - +func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode, []observable.Observable) { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) @@ -147,7 +145,7 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, n int, opts ...p2ptest.NodeFix opts = append(opts, p2ptest.WithConnectionManager(connManager)) node, nodeId := p2ptest.NodeFixture(t, - sporkID, + sporkId, t.Name(), idProvider, opts...) @@ -164,11 +162,11 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, n int, opts ...p2ptest.NodeFix // - t: the test instance. // Returns: // - a middleware config. -func MiddlewareConfigFixture(t *testing.T) *middleware.Config { +func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware.Config { return &middleware.Config{ Logger: unittest.Logger(), BitSwapMetrics: metrics.NewNoopCollector(), - RootBlockID: sporkID, + RootBlockID: sporkId, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, Codec: unittest.NetworkCodec(), } diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index c0979244ad8..6853e9dac2d 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -82,7 +82,9 @@ func (suite *BlobServiceTestSuite) SetupTest() { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + sporkId := unittest.IdentifierFixture() ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), + sporkId, suite.numNodes, p2ptest.WithDHTOptions(dht.AsServer()), p2ptest.WithPeerManagerEnabled(&p2pconfig.PeerManagerConfig{ @@ -90,7 +92,12 @@ func (suite *BlobServiceTestSuite) SetupTest() { ConnectionPruning: true, ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), }, nil)) - mws, _ := testutils.MiddlewareFixtures(suite.T(), ids, nodes, testutils.MiddlewareConfigFixture(suite.T()), mocknetwork.NewViolationsConsumer(suite.T())) + mws, _ := testutils.MiddlewareFixtures( + suite.T(), + ids, + nodes, + testutils.MiddlewareConfigFixture(suite.T(), sporkId), + mocknetwork.NewViolationsConsumer(suite.T())) suite.networks = testutils.NetworksFixture(suite.T(), ids, mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 55732b64d17..4e43fc6bb2e 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -54,8 +54,14 @@ func (suite *EchoEngineTestSuite) SetupTest() { // both nodes should be of the same role to get connected on epidemic dissemination var nodes []p2p.LibP2PNode - suite.ids, nodes, _ = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), count) - suite.mws, _ = testutils.MiddlewareFixtures(suite.T(), suite.ids, nodes, testutils.MiddlewareConfigFixture(suite.T()), mocknetwork.NewViolationsConsumer(suite.T())) + sporkId := unittest.IdentifierFixture() + suite.ids, nodes, _ = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + suite.mws, _ = testutils.MiddlewareFixtures( + suite.T(), + suite.ids, + nodes, + testutils.MiddlewareConfigFixture(suite.T(), sporkId), + mocknetwork.NewViolationsConsumer(suite.T())) suite.nets = testutils.NetworksFixture(suite.T(), suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 34a037a90e7..37f21236ced 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -181,8 +181,14 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) // create the ids, middlewares and networks - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), count) - mws, _ := testutils.MiddlewareFixtures(suite.T(), ids, nodes, testutils.MiddlewareConfigFixture(suite.T()), mocknetwork.NewViolationsConsumer(suite.T())) + sporkId := unittest.IdentifierFixture() + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + mws, _ := testutils.MiddlewareFixtures( + suite.T(), + ids, + nodes, + testutils.MiddlewareConfigFixture(suite.T(), sporkId), + mocknetwork.NewViolationsConsumer(suite.T())) nets := testutils.NetworksFixture(suite.T(), ids, mws) suite.cancels = append(suite.cancels, cancel) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 55a95994d45..82081a0de16 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -72,8 +72,14 @@ func (suite *MeshEngineTestSuite) SetupTest() { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) var nodes []p2p.LibP2PNode - suite.ids, nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), count) - suite.mws, _ = testutils.MiddlewareFixtures(suite.T(), suite.ids, nodes, testutils.MiddlewareConfigFixture(suite.T()), mocknetwork.NewViolationsConsumer(suite.T())) + sporkId := unittest.IdentifierFixture() + suite.ids, nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + suite.mws, _ = testutils.MiddlewareFixtures( + suite.T(), + suite.ids, + nodes, + testutils.MiddlewareConfigFixture(suite.T(), sporkId), + mocknetwork.NewViolationsConsumer(suite.T())) suite.nets = testutils.NetworksFixture(suite.T(), suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 1b42df088aa..cae2aa3aa36 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -108,8 +108,14 @@ func (m *MiddlewareTestSuite) SetupTest() { } m.slashingViolationsConsumer = mocknetwork.NewViolationsConsumer(m.T()) - m.ids, m.nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.size) - m.mws, m.providers = testutils.MiddlewareFixtures(m.T(), m.ids, m.nodes, testutils.MiddlewareConfigFixture(m.T()), m.slashingViolationsConsumer) + sporkId := unittest.IdentifierFixture() + m.ids, m.nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(m.T(), sporkId, m.size) + m.mws, m.providers = testutils.MiddlewareFixtures( + m.T(), + m.ids, + m.nodes, + testutils.MiddlewareConfigFixture(m.T(), sporkId), + m.slashingViolationsConsumer) for _, observableConnMgr := range obs { observableConnMgr.Subscribe(&ob) } @@ -159,7 +165,8 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) // create a new staked identity - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), 1) + sporkId := unittest.IdentifierFixture() + ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), sporkId, 1) mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, testutils.MiddlewareConfigFixture(m.T()), m.slashingViolationsConsumer) require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) @@ -241,8 +248,9 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { rateLimiters := ratelimit.NewRateLimiters(opts...) idProvider := unittest.NewUpdatableIDProvider(m.ids) - + sporkId := unittest.IdentifierFixture() ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), + sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { @@ -394,7 +402,9 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { idProvider := unittest.NewUpdatableIDProvider(m.ids) // create a new staked identity + sporkId := unittest.IdentifierFixture() ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), + sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 197c5f4a5a2..d1e5a83bf85 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -73,8 +73,9 @@ func (u *UnicastAuthorizationTestSuite) TearDownTest() { // setupMiddlewaresAndProviders will setup 2 middlewares that will be used as a sender and receiver in each suite test. func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingViolationsConsumer network.ViolationsConsumer) { - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(u.T(), 2) - cfg := testutils.MiddlewareConfigFixture(u.T()) + sporkId := unittest.IdentifierFixture() + ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(u.T(), sporkId, 2) + cfg := testutils.MiddlewareConfigFixture(u.T(), sporkId) mws, providers := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg, slashingViolationsConsumer) require.Len(u.T(), ids, 2) require.Len(u.T(), providers, 2) From c95dec18eef57ba766cd25be1001f7ecd3530ab0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 14 Aug 2023 15:07:41 -0700 Subject: [PATCH 465/815] lint fix --- network/test/middleware_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index cae2aa3aa36..f6a92f5a989 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -167,7 +167,12 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity sporkId := unittest.IdentifierFixture() ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), sporkId, 1) - mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, testutils.MiddlewareConfigFixture(m.T()), m.slashingViolationsConsumer) + mws, providers := testutils.MiddlewareFixtures( + m.T(), + ids, + libP2PNodes, + testutils.MiddlewareConfigFixture(m.T(), sporkId), + m.slashingViolationsConsumer) require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) @@ -265,7 +270,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T()), + testutils.MiddlewareConfigFixture(m.T(), sporkId), m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) @@ -421,7 +426,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T()), + testutils.MiddlewareConfigFixture(m.T(), sporkId), m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) From 046b6e0206966be760bdde5d42e1cd1b51829807 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 15 Aug 2023 03:57:04 -0400 Subject: [PATCH 466/815] rename TestOnSyncRequest_ tests for clarity --- engine/common/synchronization/engine.go | 10 +++++----- engine/common/synchronization/engine_test.go | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 6fed092520f..b1cdac079af 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -472,21 +472,21 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy } func (e *Engine) validateBatchRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true + return nil, false } func (e *Engine) validateBlockResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true + return nil, false } func (e *Engine) validateRangeRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true + return nil, false } func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true + return nil, false } func (e *Engine) validateSyncResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true + return nil, false } diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 097bcbe11b8..71c88ed04ee 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -180,9 +180,9 @@ func (ss *SyncSuite) SetupTest() { ss.e = e } -// TestOnSyncRequest_WithinTolerance tests that a sync request that's within tolerance of the receiver doesn't trigger +// TestOnSyncRequest_LowerThanReceiver_WithinTolerance tests that a sync request that's within tolerance of the receiver doesn't trigger // a response, even if request height is lower than receiver. -func (ss *SyncSuite) TestOnSyncRequest_WithinTolerance() { +func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ @@ -200,9 +200,9 @@ func (ss *SyncSuite) TestOnSyncRequest_WithinTolerance() { ss.core.AssertExpectations(ss.T()) } -// TestOnSyncRequest_HeightHigherThanReceiver tests that a sync request that's higher than the receiver's height doesn't +// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher than the receiver's height doesn't // trigger a response, even if outside tolerance. -func (ss *SyncSuite) TestOnSyncRequest_HeightHigherThanReceiver() { +func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ @@ -221,9 +221,9 @@ func (ss *SyncSuite) TestOnSyncRequest_HeightHigherThanReceiver() { ss.core.AssertExpectations(ss.T()) } -// TestOnSyncRequest_HeightLowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and +// TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and // lower than the receiver's height triggers a response. -func (ss *SyncSuite) TestOnSyncRequest_HeightLowerThanReceiver_OutsideTolerance() { +func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { // generate origin and request message originID := unittest.IdentifierFixture() From 800ec4cfbbf91f903d8a724c27234f6704395915 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 15 Aug 2023 11:30:42 +0100 Subject: [PATCH 467/815] Minor backend transactions history conditional refactoring --- .../rpc/backend/backend_transactions.go | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index f9ef781cb63..80bb2d07402 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -235,35 +235,37 @@ func (b *backendTransactions) GetTransactionResult( tx, err := b.transactions.ByID(txID) if err != nil { txErr := rpc.ConvertStorageError(err) - if status.Code(txErr) == codes.NotFound { - // Tx not found. If we have historical Sporks setup, lets look through those as well - if b.txResultCache != nil { - val, ok := b.txResultCache.Get(txID) - if ok { - return val, nil - } + + if status.Code(txErr) != codes.NotFound { + return nil, txErr + } + + // Tx not found. If we have historical Sporks setup, lets look through those as well + if b.txResultCache != nil { + val, ok := b.txResultCache.Get(txID) + if ok { + return val, nil } - historicalTxResult, err := b.getHistoricalTransactionResult(ctx, txID) - if err != nil { - // if tx not found in old access nodes either, then assume that the tx was submitted to a different AN - // and return status as unknown - txStatus := flow.TransactionStatusUnknown - result := &access.TransactionResult{ - Status: txStatus, - StatusCode: uint(txStatus), - } - if b.txResultCache != nil { - b.txResultCache.Add(txID, result) - } - return result, nil + } + historicalTxResult, err := b.getHistoricalTransactionResult(ctx, txID) + if err != nil { + // if tx not found in old access nodes either, then assume that the tx was submitted to a different AN + // and return status as unknown + txStatus := flow.TransactionStatusUnknown + result := &access.TransactionResult{ + Status: txStatus, + StatusCode: uint(txStatus), } - if b.txResultCache != nil { - b.txResultCache.Add(txID, historicalTxResult) + b.txResultCache.Add(txID, result) } - return historicalTxResult, nil + return result, nil } - return nil, txErr + + if b.txResultCache != nil { + b.txResultCache.Add(txID, historicalTxResult) + } + return historicalTxResult, nil } block, err := b.retrieveBlock(blockID, collectionID, txID) From 5967df8202618bd8625f5b5b7b01685f82275c78 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 15 Aug 2023 12:29:42 +0100 Subject: [PATCH 468/815] Fix backend invocation in observer builder --- cmd/observer/node_builder/observer_builder.go | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index b3245986122..8cd55297347 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -924,29 +924,30 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { backendConfig.CircuitBreakerConfig, ), } - - accessBackend := backend.New( - node.State, - nil, - nil, - node.Storage.Blocks, - node.Storage.Headers, - node.Storage.Collections, - node.Storage.Transactions, - node.Storage.Receipts, - node.Storage.Results, - node.RootChainID, - accessMetrics, - connFactory, - false, - backendConfig.MaxHeightRange, - backendConfig.PreferredExecutionNodeIDs, - backendConfig.FixedExecutionNodeIDs, - node.Logger, - backend.DefaultSnapshotHistoryLimit, - backendConfig.ArchiveAddressList, - backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - backendConfig.ScriptExecValidation) + + + + accessBackend := backend.New(backend.Params{ + State:node.State, + Blocks: node.Storage.Blocks, + Headers: node.Storage.Headers, + Collections: node.Storage.Collections, + Transactions: node.Storage.Transactions, + ExecutionReceipts: node.Storage.Receipts, + ExecutionResults: node.Storage.Results, + ChainID: node.RootChainID, + AccessMetrics: accessMetrics, + ConnFactory: connFactory, + RetryEnabled: false, + MaxHeightRange: backendConfig.MaxHeightRange, + PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, + FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, + Log:node.Logger, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + ArchiveAddressList: backendConfig.ArchiveAddressList, + Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + ScriptExecValidation: backendConfig.ScriptExecValidation, + }) observerCollector := metrics.NewObserverCollector() restHandler, err := restapiproxy.NewRestProxyHandler( From 47fe8258e59e309ca155c6ae2ed1154fa4b87f7b Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 15 Aug 2023 12:49:21 +0100 Subject: [PATCH 469/815] Fix backend invocation in access test --- engine/access/access_test.go | 201 +++++++++++++++-------------------- 1 file changed, 85 insertions(+), 116 deletions(-) diff --git a/engine/access/access_test.go b/engine/access/access_test.go index 8fdf13cf5c7..d09835a4395 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -138,28 +138,23 @@ func (suite *Suite) RunTest( unittest.RunWithBadgerDB(suite.T(), func(db *badger.DB) { all := util.StorageLayer(suite.T(), db) - suite.backend = backend.New(suite.state, - suite.collClient, - nil, - all.Blocks, - all.Headers, - all.Collections, - all.Transactions, - all.Receipts, - all.Results, - suite.chainID, - suite.metrics, - nil, - false, - backend.DefaultMaxHeightRange, - nil, - nil, - suite.log, - backend.DefaultSnapshotHistoryLimit, - nil, - backend.NewNodeCommunicator(false), - false, - ) + suite.backend = backend.New( + backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: all.Collections, + Transactions: all.Transactions, + ExecutionResults: all.Results, + ExecutionReceipts: all.Receipts, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + }) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me, access.WithBlockSignerDecoder(suite.signerIndicesDecoder)) f(handler, db, all) }) @@ -312,30 +307,19 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { connFactory.On("GetAccessAPIClient", collNode1.Address).Return(col1ApiClient, &mockCloser{}, nil) connFactory.On("GetAccessAPIClient", collNode2.Address).Return(col2ApiClient, &mockCloser{}, nil) - backend := backend.New(suite.state, - nil, - nil, - nil, - nil, - collections, - transactions, - nil, - nil, - suite.chainID, - metrics, - connFactory, - false, - backend.DefaultMaxHeightRange, - nil, - nil, - suite.log, - backend.DefaultSnapshotHistoryLimit, - nil, - backend.NewNodeCommunicator(false), - false, - ) - - handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) + bnd := backend.New(backend.Params{State: suite.state, + Collections: collections, + Transactions: transactions, + ChainID: suite.chainID, + AccessMetrics: metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + }) + + handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) // Send transaction 1 resp, err := handler.SendTransaction(context.Background(), sendReq1) @@ -640,30 +624,25 @@ func (suite *Suite) TestGetSealedTransaction() { blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(suite.T(), err) - backend := backend.New(suite.state, - suite.collClient, - nil, - all.Blocks, - all.Headers, - collections, - transactions, - receipts, - results, - suite.chainID, - suite.metrics, - connFactory, - false, - backend.DefaultMaxHeightRange, - nil, - enNodeIDs.Strings(), - suite.log, - backend.DefaultSnapshotHistoryLimit, - nil, - backend.NewNodeCommunicator(false), - false, - ) - - handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) + bnd := backend.New(backend.Params{State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + PreferredExecutionNodeIDs: enNodeIDs.Strings(), + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + }) + + handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) // create the ingest engine ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections, @@ -781,30 +760,25 @@ func (suite *Suite) TestGetTransactionResult() { blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(suite.T(), err) - backend := backend.New(suite.state, - suite.collClient, - nil, - all.Blocks, - all.Headers, - collections, - transactions, - receipts, - results, - suite.chainID, - suite.metrics, - connFactory, - false, - backend.DefaultMaxHeightRange, - nil, - enNodeIDs.Strings(), - suite.log, - backend.DefaultSnapshotHistoryLimit, - nil, - backend.NewNodeCommunicator(false), - false, - ) - - handler := access.NewHandler(backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) + bnd := backend.New(backend.Params{State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + PreferredExecutionNodeIDs: enNodeIDs.Strings(), + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + }) + + handler := access.NewHandler(bnd, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) // create the ingest engine ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections, @@ -974,28 +948,23 @@ func (suite *Suite) TestExecuteScript() { connFactory := new(factorymock.ConnectionFactory) connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - suite.backend = backend.New(suite.state, - suite.collClient, - nil, - all.Blocks, - all.Headers, - collections, - transactions, - receipts, - results, - suite.chainID, - suite.metrics, - connFactory, - false, - backend.DefaultMaxHeightRange, - nil, - flow.IdentifierList(identities.NodeIDs()).Strings(), - suite.log, - backend.DefaultSnapshotHistoryLimit, - nil, - backend.NewNodeCommunicator(false), - false, - ) + suite.backend = backend.New(backend.Params{State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + FixedExecutionNodeIDs: (identities.NodeIDs()).Strings(), + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + }) handler := access.NewHandler(suite.backend, suite.chainID.Chain(), suite.finalizedHeaderCache, suite.me) From f5b368431efb545948e33419de22e7bf750fcfce Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 15 Aug 2023 08:13:31 -0400 Subject: [PATCH 470/815] start load test WIP TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load --- engine/common/synchronization/engine.go | 6 ++ engine/common/synchronization/engine_test.go | 61 +++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index b1cdac079af..4e76235f491 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -490,3 +490,9 @@ func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels func (e *Engine) validateSyncResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } + +func primeNumbers(foo int) { + for i := 0; i < foo; i++ { + fmt.Println(i) + } +} diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 71c88ed04ee..b89d0ff4c5d 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -200,6 +200,13 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { ss.core.AssertExpectations(ss.T()) } +func BenchmarkPrimeNumbers(b *testing.B) { + foo := 5 + for i := 0; i < b.N; i++ { + primeNumbers(foo) + } +} + // TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher than the receiver's height doesn't // trigger a response, even if outside tolerance. func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { @@ -221,6 +228,58 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { ss.core.AssertExpectations(ss.T()) } +func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load() { + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: rand.Uint64(), + Height: 0, + } + + load := 30 + + // create channel to limit goroutines + //limiter := make(chan struct{}, 10) + + // create channel to count results + results := make(chan bool, load) + + //req.Height = ss.head.Height + 1 + //ss.core.On("HandleHeight", ss.head, req.Height) + //ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + + // define custom matcher + // Define a custom matcher function that takes a Person and returns true if the Name is “John” + handleHeightMatcher := mock.MatchedBy(func(head, height int) bool { + return p.Name == “John” + }) + + // loop over goroutines to simulate load + for i := 0; i < load; i++ { + // if request height is higher than local finalized, we should not respond + go func() { + + req.Height = ss.head.Height + 1 + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + + err := ss.e.requestHandler.onSyncRequest(originID, req) + ss.Assert().NoError(err, "same height sync request should pass") + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + results <- true + }() + } + + // wait for all goroutines to finish + for i := 0; i < load; i++ { + //<-results + results <- true + ss.core.AssertExpectations(ss.T()) + } + + close(results) +} + // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and // lower than the receiver's height triggers a response. func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { @@ -232,7 +291,7 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { Height: 0, } - // if the request height is lower than head and outside tolerance, we should submit correct response + // if the request height is lower than head and outside tolerance, we should expect correct response req.Height = ss.head.Height - 1 ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) From 7bda8d2e70703a546189be1b404e2a10da6cf79f Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 15 Aug 2023 08:51:22 -0400 Subject: [PATCH 471/815] clean up --- network/alsp/manager/manager.go | 3 --- network/alsp/manager/manager_test.go | 37 ++++++++++++++++------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 054f7ea3b4e..2af3f46c47d 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -356,9 +356,6 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { if record.Penalty == float64(0) && record.DisallowListed { record.DisallowListed = false - //// after fully decaying the penalty, update decay for next disallow listing - //record.UpdateDecay() - m.logger.Info(). Hex("identifier", logging.ID(id)). Uint64("cutoff_counter", record.CutoffCounter). diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index bac726ea04a..527af12ac3f 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -329,14 +329,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio // creates a misbehavior report for the spammer report := misbehaviorReportFixtureWithPenalty(t, ids[spammerIndex].NodeID, model.DefaultPenaltyValue) - expectedDecays := []int{1000, 100, 10, 1, 1, 1} // list of expected decay values after each disallow listing + expectedDecays := []float64{1000, 100, 10, 1, 1, 1} // list of expected decay values after each disallow listing t.Log("resetting cutoff counter") - expectedCutoffCounter := 0 + expectedCutoffCounter := uint64(0) // keep misbehaving until the spammer is disallow-listed and check that the decay is as expected - for expectedDecay := range expectedDecays { - t.Logf("starting iteration %d with expected decay %d", expectedDecay, expectedDecays[expectedDecay]) + for expectedDecayIndex := range expectedDecays { + t.Logf("starting iteration %d with expected decay index %f", expectedDecayIndex, expectedDecays[expectedDecayIndex]) // reset the decay function to the default fastDecay = false @@ -371,17 +371,19 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[honestIndex], nodes[spammerIndex]}, 1*time.Millisecond, 100*time.Millisecond) // ensures that the spammer is disallow-listed for the expected amount of time - record, ok := victimSpamRecordCache.Get(ids[spammerIndex].NodeID) require.True(t, ok) require.NotNil(t, record) - // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + // check the penalty of the spammer node, which should be below the disallow-listing threshold. + // i.e. spammer penalty should be more negative than the disallow-listing threshold, hence disallow-listed. + require.Less(t, record.Penalty, float64(model.DisallowListingThreshold)) + require.Equal(t, expectedDecays[expectedDecayIndex], record.Decay) - require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) + require.Equal(t, expectedDecays[expectedDecayIndex], record.Decay) + // when a node is disallow-listed, it remains disallow-listed until its penalty decays back to zero. require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + require.Equal(t, expectedCutoffCounter, record.CutoffCounter) penalty1 := record.Penalty @@ -392,16 +394,19 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.True(t, ok) require.NotNil(t, record) - // check the penalty of the spammer node. It should be greater than the disallow-listing threshold. - require.Greater(t, float64(model.DisallowListingThreshold), record.Penalty) + // check the penalty of the spammer node, which should be below the disallow-listing threshold. + // i.e. spammer penalty should be more negative than the disallow-listing threshold, hence disallow-listed. + require.Less(t, record.Penalty, float64(model.DisallowListingThreshold)) + require.Equal(t, expectedDecays[expectedDecayIndex], record.Decay) - require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) + require.Equal(t, expectedDecays[expectedDecayIndex], record.Decay) + // when a node is disallow-listed, it remains disallow-listed until its penalty decays back to zero. require.Equal(t, true, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + require.Equal(t, expectedCutoffCounter, record.CutoffCounter) penalty2 := record.Penalty // check that the penalty has decayed by the expected amount in one heartbeat - require.Equal(t, float64(expectedDecays[expectedDecay]), penalty2-penalty1) + require.Equal(t, expectedDecays[expectedDecayIndex], penalty2-penalty1) // decay the disallow-listing penalty of the spammer node to zero. t.Log("about to decay the disallow-listing penalty of the spammer node to zero") @@ -420,9 +425,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio require.NotNil(t, record) require.Equal(t, float64(0), record.Penalty) - require.Equal(t, float64(expectedDecays[expectedDecay]), record.Decay) + require.Equal(t, expectedDecays[expectedDecayIndex], record.Decay) require.Equal(t, false, record.DisallowListed) - require.Equal(t, uint64(expectedCutoffCounter), record.CutoffCounter) + require.Equal(t, expectedCutoffCounter, record.CutoffCounter) // go back to regular decay to prepare for the next set of misbehavior reports. fastDecay = false From 77678a422b5d6cf93fce5525b08568a10051d847 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 15 Aug 2023 17:29:28 +0100 Subject: [PATCH 472/815] Fix backend instance invocation --- .../integration_unsecure_grpc_server_test.go | 41 +++++++----------- engine/access/rest_api_test.go | 42 ++++++++----------- engine/access/secure_grpcr_test.go | 37 +++++++--------- 3 files changed, 48 insertions(+), 72 deletions(-) diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index 520da185aad..cf9ec1fa744 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -166,29 +166,20 @@ func (suite *SameGRPCPortTestSuite) SetupTest() { block := unittest.BlockHeaderFixture() suite.snapshot.On("Head").Return(block, nil) - backend := backend.New( - suite.state, - suite.collClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - nil, - nil, - suite.chainID, - suite.metrics, - nil, - false, - 0, - nil, - nil, - suite.log, - 0, - nil, - backend.NewNodeCommunicator(false), - false, - ) + bnd := backend.New(backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + MaxHeightRange: 0, + Log: suite.log, + SnapshotHistoryLimit: 0, + Communicator: backend.NewNodeCommunicator(false), + }) // create rpc engine builder rpcEngBuilder, err := rpc.NewBuilder( @@ -199,8 +190,8 @@ func (suite *SameGRPCPortTestSuite) SetupTest() { suite.metrics, false, suite.me, - backend, - backend, + bnd, + bnd, suite.secureGrpcServer, suite.unsecureGrpcServer, ) diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 7ca1ef0c0f3..1b891832249 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -149,28 +149,22 @@ func (suite *RestAPITestSuite) SetupTest() { nil, nil).Build() - backend := backend.New(suite.state, - suite.collClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - nil, - suite.executionResults, - suite.chainID, - suite.metrics, - nil, - false, - 0, - nil, - nil, - suite.log, - 0, - nil, - backend.NewNodeCommunicator(false), - false, - ) + bnd := backend.New( + backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionResults: suite.executionResults, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + MaxHeightRange: 0, + Log: suite.log, + SnapshotHistoryLimit: 0, + Communicator: backend.NewNodeCommunicator(false), + }) rpcEngBuilder, err := rpc.NewBuilder( suite.log, @@ -180,8 +174,8 @@ func (suite *RestAPITestSuite) SetupTest() { suite.metrics, false, suite.me, - backend, - backend, + bnd, + bnd, suite.secureGrpcServer, suite.unsecureGrpcServer, ) diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index ecc0156b720..d5f64352cf6 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -130,29 +130,20 @@ func (suite *SecureGRPCTestSuite) SetupTest() { block := unittest.BlockHeaderFixture() suite.snapshot.On("Head").Return(block, nil) - bnd := backend.New( - suite.state, - suite.collClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - nil, - nil, - suite.chainID, - suite.metrics, - nil, - false, - 0, - nil, - nil, - suite.log, - 0, - nil, - backend.NewNodeCommunicator(false), - false, - ) + bnd := backend.New(backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + MaxHeightRange: 0, + Log: suite.log, + SnapshotHistoryLimit: 0, + Communicator: backend.NewNodeCommunicator(false), + }) rpcEngBuilder, err := rpc.NewBuilder( suite.log, From a1d926834672517fdab685b9181e4ee9e503b633 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 15 Aug 2023 18:19:04 +0100 Subject: [PATCH 473/815] wip --- .../integration_unsecure_grpc_server_test.go | 19 +++++++++---------- engine/access/rest_api_test.go | 15 +++++++-------- engine/access/secure_grpcr_test.go | 17 ++++++++--------- module/metrics.go | 15 +++++++++++++-- module/metrics/access.go | 7 ++++--- 5 files changed, 41 insertions(+), 32 deletions(-) diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index cf9ec1fa744..9ad3e7f54df 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -7,16 +7,6 @@ import ( "testing" "time" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "github.com/onflow/flow-go/engine" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -37,6 +27,15 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) // SameGRPCPortTestSuite verifies both AccessAPI and ExecutionDataAPI client continue to work when configured diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 1b891832249..5164bba6106 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -11,14 +11,6 @@ import ( "time" "github.com/antihax/optional" - restclient "github.com/onflow/flow/openapi/go-client-generated" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/credentials" - accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest/request" "github.com/onflow/flow-go/engine/access/rest/routes" @@ -35,6 +27,13 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" + restclient "github.com/onflow/flow/openapi/go-client-generated" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/credentials" ) // RestAPITestSuite tests that the Access node serves the REST API defined via the OpenApi spec accurately diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index d5f64352cf6..d8d4f71b732 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -7,15 +7,6 @@ import ( "testing" "time" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "github.com/onflow/flow-go/crypto" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -30,6 +21,14 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) // SecureGRPCTestSuite tests that Access node provides a secure GRPC server diff --git a/module/metrics.go b/module/metrics.go index 3c1d6dfdc37..e5bc5b6fecd 100644 --- a/module/metrics.go +++ b/module/metrics.go @@ -6,12 +6,11 @@ import ( "github.com/libp2p/go-libp2p/core/peer" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" - httpmetrics "github.com/slok/go-http-metrics/metrics" - "github.com/onflow/flow-go/model/chainsync" "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/channels" + httpmetrics "github.com/slok/go-http-metrics/metrics" ) type EntriesFunc func() uint @@ -647,6 +646,8 @@ type AccessMetrics interface { TransactionMetrics BackendScriptsMetrics + //TrnasactionResultMetrics + // UpdateExecutionReceiptMaxHeight is called whenever we store an execution receipt from a block from a newer height UpdateExecutionReceiptMaxHeight(height uint64) @@ -654,6 +655,16 @@ type AccessMetrics interface { UpdateLastFullBlockHeight(height uint64) } +type TrnasactionResultMetrics interface { + OnKeyHitSuccess() + OnKeyHitFailure() + + OnKeyAddSuccess() + OnKeyEviction() + + OnKeyAddFailureDueToFullCache() +} + type ExecutionResultStats struct { ComputationUsed uint64 MemoryUsed uint64 diff --git a/module/metrics/access.go b/module/metrics/access.go index 1116f87f433..56e24b70da2 100644 --- a/module/metrics/access.go +++ b/module/metrics/access.go @@ -1,11 +1,10 @@ package metrics import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/counters" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) type AccessCollectorOpts func(*AccessCollector) @@ -33,6 +32,8 @@ type AccessCollector struct { module.TransactionMetrics module.BackendScriptsMetrics + //module.TransactionResultMetrics + connectionReused prometheus.Counter connectionsInPool *prometheus.GaugeVec connectionAdded prometheus.Counter From 78adc273ba11d4069dddab0f5f606274ec5bae85 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 10:41:39 -0700 Subject: [PATCH 474/815] wip --- network/test/meshengine_test.go | 40 ++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 82081a0de16..e7a5684f2f9 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -18,10 +18,12 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/observable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" @@ -30,6 +32,7 @@ import ( "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pnode" + p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" ) @@ -59,7 +62,6 @@ func (suite *MeshEngineTestSuite) SetupTest() { log.SetAllLoggers(log.LevelError) // set up a channel to receive pubsub tags from connManagers of the nodes - var obs []observable.Observable peerChannel := make(chan string) ob := tagsObserver{ tags: peerChannel, @@ -71,19 +73,45 @@ func (suite *MeshEngineTestSuite) SetupTest() { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() - suite.ids, nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + libP2PNodes := make([]p2p.LibP2PNode, 0) + identities := make(flow.IdentityList, 0) + tagObservables := make([]observable.Observable, 0) + idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) + defaultFlowConfig, err := config.DefaultConfig() + require.NoError(suite.T(), err) + opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} + + for i := 0; i < count; i++ { + connManager, err := testutils.NewTagWatchingConnManager( + unittest.Logger(), + metrics.NewNoopCollector(), + &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) + require.NoError(suite.T(), err) + + opts = append(opts, p2ptest.WithConnectionManager(connManager)) + node, nodeId := p2ptest.NodeFixture(suite.T(), + sporkId, + suite.T().Name(), + idProvider, + opts...) + libP2PNodes = append(libP2PNodes, node) + identities = append(identities, &nodeId) + tagObservables = append(tagObservables, connManager) + } + idProvider.SetIdentities(identities) + + suite.ids = identities suite.mws, _ = testutils.MiddlewareFixtures( suite.T(), suite.ids, - nodes, + libP2PNodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) suite.nets = testutils.NetworksFixture(suite.T(), suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) - for _, observableConnMgr := range obs { + for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } suite.obs = peerChannel From 710235771af046ef126ff87ae529be242772a551 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 10:55:15 -0700 Subject: [PATCH 475/815] fixes tests --- network/test/middleware_test.go | 41 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index f6a92f5a989..e721cb12af6 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -71,16 +71,16 @@ func (co *tagsObserver) OnComplete() { type MiddlewareTestSuite struct { suite.Suite sync.RWMutex - size int // used to determine number of middlewares under test - nodes []p2p.LibP2PNode - mws []network.Middleware // used to keep track of middlewares under test - ov []*mocknetwork.Overlay - obs chan string // used to keep track of Protect events tagged by pubsub messages - ids []*flow.Identity - metrics *metrics.NoopCollector // no-op performance monitoring simulation - logger zerolog.Logger - providers []*unittest.UpdatableIDProvider - + size int // used to determine number of middlewares under test + nodes []p2p.LibP2PNode + mws []network.Middleware // used to keep track of middlewares under test + ov []*mocknetwork.Overlay + obs chan string // used to keep track of Protect events tagged by pubsub messages + ids []*flow.Identity + metrics *metrics.NoopCollector // no-op performance monitoring simulation + logger zerolog.Logger + providers []*unittest.UpdatableIDProvider + sporkId flow.Identifier mwCancel context.CancelFunc mwCtx irrecoverable.SignalerContext slashingViolationsConsumer network.ViolationsConsumer @@ -108,13 +108,13 @@ func (m *MiddlewareTestSuite) SetupTest() { } m.slashingViolationsConsumer = mocknetwork.NewViolationsConsumer(m.T()) - sporkId := unittest.IdentifierFixture() - m.ids, m.nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(m.T(), sporkId, m.size) + m.sporkId = unittest.IdentifierFixture() + m.ids, m.nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, m.size) m.mws, m.providers = testutils.MiddlewareFixtures( m.T(), m.ids, m.nodes, - testutils.MiddlewareConfigFixture(m.T(), sporkId), + testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer) for _, observableConnMgr := range obs { observableConnMgr.Subscribe(&ob) @@ -165,13 +165,12 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) // create a new staked identity - sporkId := unittest.IdentifierFixture() - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), sporkId, 1) + ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) mws, providers := testutils.MiddlewareFixtures( m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), sporkId), + testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer) require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) @@ -253,9 +252,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { rateLimiters := ratelimit.NewRateLimiters(opts...) idProvider := unittest.NewUpdatableIDProvider(m.ids) - sporkId := unittest.IdentifierFixture() ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), - sporkId, + m.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { @@ -270,7 +268,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), sporkId), + testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) @@ -407,9 +405,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { idProvider := unittest.NewUpdatableIDProvider(m.ids) // create a new staked identity - sporkId := unittest.IdentifierFixture() ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), - sporkId, + m.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { @@ -426,7 +423,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { mws, providers := testutils.MiddlewareFixtures(m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), sporkId), + testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) From b460fe77d5b48f97bf4846ff3f96bfb13c2d1d25 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 11:26:12 -0700 Subject: [PATCH 476/815] relocates tag observable logic to test suite --- network/test/middleware_test.go | 39 +++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index e721cb12af6..e2775a6c91d 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -20,6 +20,7 @@ import ( "go.uber.org/atomic" "golang.org/x/time/rate" + "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" @@ -100,7 +101,6 @@ func (m *MiddlewareTestSuite) SetupTest() { m.metrics = metrics.NewNoopCollector() // create and start the middlewares and inject a connection observer - var obs []observable.Observable peerChannel := make(chan string) ob := tagsObserver{ tags: peerChannel, @@ -109,19 +109,50 @@ func (m *MiddlewareTestSuite) SetupTest() { m.slashingViolationsConsumer = mocknetwork.NewViolationsConsumer(m.T()) m.sporkId = unittest.IdentifierFixture() - m.ids, m.nodes, obs = testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, m.size) + + libP2PNodes := make([]p2p.LibP2PNode, 0) + identities := make(flow.IdentityList, 0) + tagObservables := make([]observable.Observable, 0) + idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) + defaultFlowConfig, err := config.DefaultConfig() + require.NoError(m.T(), err) + + opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} + + for i := 0; i < m.size; i++ { + connManager, err := testutils.NewTagWatchingConnManager( + unittest.Logger(), + metrics.NewNoopCollector(), + &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) + require.NoError(m.T(), err) + + opts = append(opts, p2ptest.WithConnectionManager(connManager)) + node, nodeId := p2ptest.NodeFixture(m.T(), + m.sporkId, + m.T().Name(), + idProvider, + opts...) + libP2PNodes = append(libP2PNodes, node) + identities = append(identities, &nodeId) + tagObservables = append(tagObservables, connManager) + } + idProvider.SetIdentities(identities) + + m.ids = identities + m.nodes = libP2PNodes + m.mws, m.providers = testutils.MiddlewareFixtures( m.T(), m.ids, m.nodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer) - for _, observableConnMgr := range obs { + for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } m.obs = peerChannel - require.Len(m.Suite.T(), obs, m.size) + require.Len(m.Suite.T(), tagObservables, m.size) require.Len(m.Suite.T(), m.ids, m.size) require.Len(m.Suite.T(), m.mws, m.size) From ed3cb28c5aba0cc0f412e2337a9e4c607303b439 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 11:28:08 -0700 Subject: [PATCH 477/815] removes unused returned value --- network/alsp/manager/manager_test.go | 10 +++++----- network/internal/testutils/testUtil.go | 16 ++-------------- network/test/blob_service_test.go | 2 +- network/test/echoengine_test.go | 2 +- network/test/epochtransition_test.go | 2 +- network/test/middleware_test.go | 6 +++--- network/test/unicast_authorization_test.go | 2 +- 7 files changed, 14 insertions(+), 26 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 82ee1f001b5..09def778e51 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -55,7 +55,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { sporkId := unittest.IdentifierFixture() misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) mws, _ := testutils.MiddlewareFixtures( t, ids, @@ -119,7 +119,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { } sporkId := unittest.IdentifierFixture() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) mws, _ := testutils.MiddlewareFixtures( t, ids, @@ -219,7 +219,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) } sporkId := unittest.IdentifierFixture() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture( + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture( t, sporkId, 3, @@ -302,7 +302,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() // create 1 victim node, 1 honest node and a node for each slashing violation - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). mws, _ := testutils.MiddlewareFixtures( t, ids, @@ -404,7 +404,7 @@ func TestMisbehaviorReportMetrics(t *testing.T) { cfg.AlspMetrics = alspMetrics sporkId := unittest.IdentifierFixture() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 8e4d457e0e9..bc4da0ea972 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -126,24 +126,13 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // flow.IdentityList - list of identities created for the nodes, one for each node. // // []p2p.LibP2PNode - list of libp2p nodes created. -// []observable.Observable - list of observables created for each node. -func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode, []observable.Observable) { +func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode) { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) - tagObservables := make([]observable.Observable, 0) idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) - defaultFlowConfig, err := config.DefaultConfig() - require.NoError(t, err) - opts = append(opts, p2ptest.WithUnicastHandlerFunc(nil)) for i := 0; i < n; i++ { - // TODO: generating a tag watching connection manager can be moved to a separate function, as only a few tests need this. - // For the rest of tests, the node can run on the default connection manager without setting and option. - connManager, err := NewTagWatchingConnManager(unittest.Logger(), metrics.NewNoopCollector(), &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) - require.NoError(t, err) - - opts = append(opts, p2ptest.WithConnectionManager(connManager)) node, nodeId := p2ptest.NodeFixture(t, sporkId, t.Name(), @@ -151,10 +140,9 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int opts...) libP2PNodes = append(libP2PNodes, node) identities = append(identities, &nodeId) - tagObservables = append(tagObservables, connManager) } idProvider.SetIdentities(identities) - return identities, libP2PNodes, tagObservables + return identities, libP2PNodes } // MiddlewareConfigFixture is a test helper that generates a middleware config for testing. diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 6853e9dac2d..7e3c40f96d8 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -83,7 +83,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) sporkId := unittest.IdentifierFixture() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, suite.numNodes, p2ptest.WithDHTOptions(dht.AsServer()), diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 4e43fc6bb2e..3c544279b6f 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -55,7 +55,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { // both nodes should be of the same role to get connected on epidemic dissemination var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() - suite.ids, nodes, _ = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) suite.mws, _ = testutils.MiddlewareFixtures( suite.T(), suite.ids, diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 37f21236ced..26169f91843 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -182,7 +182,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // create the ids, middlewares and networks sporkId := unittest.IdentifierFixture() - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) mws, _ := testutils.MiddlewareFixtures( suite.T(), ids, diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index e2775a6c91d..65ce4f7cca3 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -196,7 +196,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) // create a new staked identity - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) mws, providers := testutils.MiddlewareFixtures( m.T(), ids, @@ -283,7 +283,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { rateLimiters := ratelimit.NewRateLimiters(opts...) idProvider := unittest.NewUpdatableIDProvider(m.ids) - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), @@ -436,7 +436,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { idProvider := unittest.NewUpdatableIDProvider(m.ids) // create a new staked identity - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(m.T(), + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index d1e5a83bf85..976f702c9de 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -74,7 +74,7 @@ func (u *UnicastAuthorizationTestSuite) TearDownTest() { // setupMiddlewaresAndProviders will setup 2 middlewares that will be used as a sender and receiver in each suite test. func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingViolationsConsumer network.ViolationsConsumer) { sporkId := unittest.IdentifierFixture() - ids, libP2PNodes, _ := testutils.LibP2PNodeForMiddlewareFixture(u.T(), sporkId, 2) + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), sporkId, 2) cfg := testutils.MiddlewareConfigFixture(u.T(), sporkId) mws, providers := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg, slashingViolationsConsumer) require.Len(u.T(), ids, 2) From 99233ab14dd3cb0f5d2fc2072c757c6336273a16 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:29:34 -0700 Subject: [PATCH 478/815] [Network] Fix chunk datapack size and timeout limits (#4623) --- network/p2p/middleware/middleware.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index c908e8d7f18..ba8239363df 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -818,7 +818,7 @@ func (m *Middleware) IsConnected(nodeID flow.Identifier) (bool, error) { // unicastMaxMsgSize returns the max permissible size for a unicast message func unicastMaxMsgSize(messageType string) int { switch messageType { - case "*messages.ChunkDataResponse": + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": return LargeMsgMaxUnicastMsgSize default: return DefaultMaxUnicastMsgSize @@ -843,7 +843,7 @@ func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { // unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete func (m *Middleware) unicastMaxMsgDuration(messageType string) time.Duration { switch messageType { - case "messages.ChunkDataResponse": + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": if LargeMsgUnicastTimeout > m.unicastMessageTimeout { return LargeMsgUnicastTimeout } From 3b4b810a1450305eb49d5f9328679d5892ec1957 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 12:46:06 -0700 Subject: [PATCH 479/815] cleans up is connected test helper --- network/middleware.go | 2 - network/p2p/middleware/middleware.go | 10 ----- network/test/blob_service_test.go | 7 ++-- network/test/epochtransition_test.go | 61 ++++++++++++++++------------ 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/network/middleware.go b/network/middleware.go index d8e14ee82c1..be2c65281e7 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -57,8 +57,6 @@ type Middleware interface { // NewPingService creates a new PingService for the given ping protocol ID. NewPingService(pingProtocol protocol.ID, provider PingInfoProvider) PingService - - IsConnected(nodeID flow.Identifier) (bool, error) } // Overlay represents the interface that middleware uses to interact with the diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index c908e8d7f18..802bb00aa43 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -805,16 +805,6 @@ func (m *Middleware) Publish(msg *network.OutgoingMessageScope) error { return nil } -// IsConnected returns true if this node is connected to the node with id nodeID. -// All errors returned from this function can be considered benign. -func (m *Middleware) IsConnected(nodeID flow.Identifier) (bool, error) { - peerID, err := m.idTranslator.GetPeerID(nodeID) - if err != nil { - return false, fmt.Errorf("could not find peer id for target id: %w", err) - } - return m.libP2PNode.IsConnected(peerID) -} - // unicastMaxMsgSize returns the max permissible size for a unicast message func unicastMaxMsgSize(messageType string) int { switch messageType { diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 7e3c40f96d8..3a401ad4b0b 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -10,6 +10,7 @@ import ( "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/atomic" @@ -118,10 +119,10 @@ func (suite *BlobServiceTestSuite) SetupTest() { // let nodes connect to each other only after they are all listening on Bitswap topologyActive.Store(true) suite.Require().Eventually(func() bool { - for i, mw := range mws { + for i, libp2pNode := range nodes { for j := i + 1; j < suite.numNodes; j++ { - connected, err := mw.IsConnected(ids[j].NodeID) - suite.Require().NoError(err) + connected, err := libp2pNode.IsConnected(nodes[j].Host().ID()) + require.NoError(suite.T(), err) if !connected { return false } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 26169f91843..46eb1ae1b79 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -25,6 +25,7 @@ import ( "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/mocknetwork" + "github.com/onflow/flow-go/network/p2p" mockprotocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/utils/unittest" ) @@ -48,10 +49,11 @@ type MutableIdentityTableSuite struct { // testNode encapsulates the node state which includes its identity, middleware, network, // mesh engine and the id refresher type testNode struct { - id *flow.Identity - mw network.Middleware - net network.Network - engine *testutils.MeshEngine + id *flow.Identity + libp2pNode p2p.LibP2PNode + mw network.Middleware + net network.Network + engine *testutils.MeshEngine } // testNodeList encapsulates a list of test node and @@ -120,6 +122,16 @@ func (t *testNodeList) networks() []network.Network { return nets } +func (t *testNodeList) libp2pNodes() []p2p.LibP2PNode { + t.RLock() + defer t.RUnlock() + nodes := make([]p2p.LibP2PNode, len(t.nodes)) + for i, node := range t.nodes { + nodes[i] = node.libp2pNode + } + return nodes +} + func TestMutableIdentityTable(t *testing.T) { unittest.SkipUnless(t, unittest.TEST_TODO, "broken test") suite.Run(t, new(MutableIdentityTableSuite)) @@ -200,10 +212,11 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // create the test engines for i := 0; i < count; i++ { node := testNode{ - id: ids[i], - mw: mws[i], - net: nets[i], - engine: engines[i], + id: ids[i], + libp2pNode: nodes[i], + mw: mws[i], + net: nets[i], + engine: engines[i], } suite.testNodes.append(node) } @@ -226,7 +239,6 @@ func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { newNode, err := suite.testNodes.lastAdded() require.NoError(suite.T(), err) newID := newNode.id - newMiddleware := newNode.mw suite.logger.Debug(). Str("new_node", newID.NodeID.String()). @@ -240,7 +252,7 @@ func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { // check if the new node has sufficient connections with the existing nodes // if it does, then it has been inducted successfully in the network - suite.assertConnected(newMiddleware, ids.Filter(filter.Not(filter.HasNodeID(newID.NodeID)))) + suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) // check that all the engines on this new epoch can talk to each other using any of the three networking primitives suite.assertNetworkPrimitives(ids, engs, nil, nil) @@ -250,11 +262,9 @@ func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { // list (ie. as a result of an ejection or transition into an epoch where that node // has un-staked) then it cannot connect to the network. func (suite *MutableIdentityTableSuite) TestNodeRemoved() { - // removed a node removedNode := suite.removeNode() removedID := removedNode.id - removedMiddleware := removedNode.mw removedEngine := removedNode.engine // update IDs for all the remaining nodes @@ -265,7 +275,7 @@ func (suite *MutableIdentityTableSuite) TestNodeRemoved() { remainingEngs := suite.testNodes.engines() // assert that the removed node has no connections with any of the other nodes - suite.assertDisconnected(removedMiddleware, remainingIDs) + suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) // check that all remaining engines can still talk to each other while the ones removed can't // using any of the three networking primitives @@ -284,15 +294,12 @@ func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { // remove a node removedNode := suite.removeNode() removedID := removedNode.id - removedMiddleware := removedNode.mw removedEngine := removedNode.engine // add a node suite.addNodes(1) newNode, err := suite.testNodes.lastAdded() require.NoError(suite.T(), err) - newID := newNode.id - newMiddleware := newNode.mw // update all current nodes suite.signalIdentityChanged() @@ -301,10 +308,10 @@ func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { remainingEngs := suite.testNodes.engines() // check if the new node has sufficient connections with the existing nodes - suite.assertConnected(newMiddleware, remainingIDs.Filter(filter.Not(filter.HasNodeID(newID.NodeID)))) + suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) // assert that the removed node has no connections with any of the other nodes - suite.assertDisconnected(removedMiddleware, remainingIDs) + suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) // check that all remaining engines can still talk to each other while the ones removed can't // using any of the three networking primitives @@ -317,13 +324,17 @@ func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { // assertConnected checks that the middleware of a node is directly connected // to at least half of the other nodes. -func (suite *MutableIdentityTableSuite) assertConnected(mw network.Middleware, ids flow.IdentityList) { +func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { t := suite.T() - threshold := len(ids) / 2 + threshold := len(allNodes) / 2 require.Eventuallyf(t, func() bool { connections := 0 - for _, id := range ids { - connected, err := mw.IsConnected(id.NodeID) + for _, node := range allNodes { + if node == thisNode { + // we don't want to check if a node is connected to itself + continue + } + connected, err := thisNode.IsConnected(node.Host().ID()) require.NoError(t, err) if connected { connections++ @@ -339,11 +350,11 @@ func (suite *MutableIdentityTableSuite) assertConnected(mw network.Middleware, i // assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the // ids list -func (suite *MutableIdentityTableSuite) assertDisconnected(mw network.Middleware, ids flow.IdentityList) { +func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { t := suite.T() require.Eventuallyf(t, func() bool { - for _, id := range ids { - connected, err := mw.IsConnected(id.NodeID) + for _, node := range allNodes { + connected, err := thisNode.IsConnected(node.Host().ID()) require.NoError(t, err) if connected { return false From dbc14e5b3fe539dabd652f647b20b3159b385382 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 14:03:18 -0700 Subject: [PATCH 480/815] adds spork id to libp2p node --- insecure/corruptlibp2p/p2p_node.go | 4 +++- network/p2p/builder.go | 2 +- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 12 +++--------- network/p2p/p2pnode/libp2pNode.go | 6 ++++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/insecure/corruptlibp2p/p2p_node.go b/insecure/corruptlibp2p/p2p_node.go index b3fbd0cb36d..02ce4e3a3f3 100644 --- a/insecure/corruptlibp2p/p2p_node.go +++ b/insecure/corruptlibp2p/p2p_node.go @@ -8,6 +8,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/codec/cbor" @@ -53,10 +54,11 @@ func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ p2p.TopicValidatorFun // NewCorruptLibP2PNode returns corrupted libP2PNode that will subscribe to topics using the AcceptAllTopicValidator. func NewCorruptLibP2PNode( logger zerolog.Logger, + sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - node := p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) + node := p2pnode.NewNode(logger, sporkId, host, pCache, peerManager, disallowListCacheCfg) return &CorruptP2PNode{Node: node, logger: logger, codec: cbor.NewCodec()} } diff --git a/network/p2p/builder.go b/network/p2p/builder.go index b856f931a29..b530f4cc49c 100644 --- a/network/p2p/builder.go +++ b/network/p2p/builder.go @@ -23,7 +23,7 @@ import ( ) type GossipSubFactoryFunc func(context.Context, zerolog.Logger, host.Host, PubSubAdapterConfig, CollectionClusterChangesConsumer) (PubSubAdapter, error) -type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager, *DisallowListCacheConfig) LibP2PNode +type CreateNodeFunc func(zerolog.Logger, flow.Identifier, host.Host, ProtocolPeerCache, PeerManager, *DisallowListCacheConfig) LibP2PNode type GossipSubAdapterConfigFunc func(*BasePubSubAdapterConfig) PubSubAdapterConfig // GossipSubBuilder provides a builder pattern for creating a GossipSub pubsub system. diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index ac3f30fd4d0..42e22a2fb02 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -44,13 +44,6 @@ import ( "github.com/onflow/flow-go/network/p2p/utils" ) -type GossipSubFactoryFunc func(context.Context, zerolog.Logger, host.Host, p2p.PubSubAdapterConfig) (p2p.PubSubAdapter, error) -type CreateNodeFunc func(logger zerolog.Logger, - host host.Host, - pCache *p2pnode.ProtocolPeerCache, - peerManager *connection.PeerManager) p2p.LibP2PNode -type GossipSubAdapterConfigFunc func(*p2p.BasePubSubAdapterConfig) p2p.PubSubAdapterConfig - type LibP2PNodeBuilder struct { gossipSubBuilder p2p.GossipSubBuilder sporkId flow.Identifier @@ -308,7 +301,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { } } - node := builder.createNode(builder.logger, h, pCache, peerManager, builder.disallowListCacheCfg) + node := builder.createNode(builder.logger, builder.sporkId, h, pCache, peerManager, builder.disallowListCacheCfg) if builder.connGater != nil { builder.connGater.SetDisallowListOracle(node) @@ -421,11 +414,12 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti // DefaultCreateNodeFunc returns new libP2P node. func DefaultCreateNodeFunc(logger zerolog.Logger, + sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - return p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) + return p2pnode.NewNode(logger, sporkId, host, pCache, peerManager, disallowListCacheCfg) } // DefaultNodeBuilder returns a node builder. diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 38746521430..c20551f28c4 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -66,20 +66,22 @@ type Node struct { // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer // are closed and further connections are not allowed till the peer is removed from the disallow-list. disallowListedCache p2p.DisallowListCache + sporkId flow.Identifier } // NewNode creates a new libp2p node and sets its parameters. func NewNode( logger zerolog.Logger, + sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, disallowLstCacheCfg *p2p.DisallowListCacheConfig, ) *Node { - lg := logger.With().Str("component", "libp2p-node").Logger() return &Node{ host: host, - logger: lg, + logger: logger.With().Str("component", "libp2p-node").Logger(), + sporkId: sporkId, topics: make(map[channels.Topic]p2p.Topic), subs: make(map[channels.Topic]p2p.Subscription), pCache: pCache, From 72f4d96468bf79401486ed94fd9ac0bc9c4d1bf5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 14:12:40 -0700 Subject: [PATCH 481/815] moves publish logic --- network/p2p/libp2pNode.go | 2 +- network/p2p/middleware/middleware.go | 31 +--------------------------- network/p2p/p2pnode/libp2pNode.go | 29 ++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 061e45a43ff..62555af935d 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -67,7 +67,7 @@ type LibP2PNode interface { // UnSubscribe cancels the subscriber and closes the topic. UnSubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. - Publish(ctx context.Context, topic channels.Topic, data []byte) error + Publish(ctx context.Context, msgScope *network.OutgoingMessageScope) error // Host returns pointer to host object of node. Host() host.Host // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 802bb00aa43..1eb55b0b591 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -30,7 +30,6 @@ import ( "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/blob" - "github.com/onflow/flow-go/network/p2p/p2pnode" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" @@ -774,35 +773,7 @@ func (m *Middleware) processMessage(scope *network.IncomingMessageScope) { // // All errors returned from this function can be considered benign. func (m *Middleware) Publish(msg *network.OutgoingMessageScope) error { - m.log.Debug(). - Str("channel", msg.Channel().String()). - Interface("msg", msg.Proto()). - Str("type", msg.PayloadType()). - Int("msg_size", msg.Size()). - Msg("publishing new message") - - // convert the message to bytes to be put on the wire. - data, err := msg.Proto().Marshal() - if err != nil { - return fmt.Errorf("failed to marshal the message: %w", err) - } - - msgSize := len(data) - if msgSize > p2pnode.DefaultMaxPubSubMsgSize { - // libp2p pubsub will silently drop the message if its size is greater than the configured pubsub max message size - // hence return an error as this message is undeliverable - return fmt.Errorf("message size %d exceeds configured max message size %d", msgSize, p2pnode.DefaultMaxPubSubMsgSize) - } - - topic := channels.TopicFromChannel(msg.Channel(), m.rootBlockID) - - // publish the bytes on the topic - err = m.libP2PNode.Publish(m.ctx, topic, data) - if err != nil { - return fmt.Errorf("failed to publish the message: %w", err) - } - - return nil + return m.libP2PNode.Publish(m.ctx, msg) } // unicastMaxMsgSize returns the max permissible size for a unicast message diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index c20551f28c4..50216155b71 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -319,15 +319,40 @@ func (n *Node) UnSubscribe(topic channels.Topic) error { // Publish publishes the given payload on the topic. // All errors returned from this function can be considered benign. -func (n *Node) Publish(ctx context.Context, topic channels.Topic, data []byte) error { +func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageScope) error { + lg := n.logger.With(). + Str("channel", msgScope.Channel().String()). + Interface("proto_message", msgScope.Proto()). + Str("payload_type", msgScope.PayloadType()). + Int("message_size", msgScope.Size()).Logger() + lg.Debug().Msg("received message to publish") + + // convert the message to bytes to be put on the wire. + data, err := msgScope.Proto().Marshal() + if err != nil { + return fmt.Errorf("failed to marshal the message: %w", err) + } + + msgSize := len(data) + if msgSize > DefaultMaxPubSubMsgSize { + // libp2p pubsub will silently drop the message if its size is greater than the configured pubsub max message size + // hence return an error as this message is undeliverable + return fmt.Errorf("message size %d exceeds configured max message size %d", msgSize, DefaultMaxPubSubMsgSize) + } + + topic := channels.TopicFromChannel(msgScope.Channel(), n.sporkId) + lg = lg.With().Str("topic", topic.String()).Logger() + ps, found := n.topics[topic] if !found { return fmt.Errorf("could not find topic (%s)", topic) } - err := ps.Publish(ctx, data) + err = ps.Publish(ctx, data) if err != nil { return fmt.Errorf("could not publish to topic (%s): %w", topic, err) } + + lg.Debug().Msg("published message to topic") return nil } From 68e225f9543a2a72a5c66cfe2d2d7291a46cef89 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 15 Aug 2023 15:16:02 -0700 Subject: [PATCH 482/815] fixes broken tests for publish migration --- network/p2p/dht/dht_test.go | 24 ++++----- network/p2p/libp2pNode.go | 4 +- network/p2p/middleware/middleware.go | 11 +--- network/p2p/p2pnode/libp2pNode.go | 30 +++++++++-- network/p2p/test/fixtures.go | 50 ++++++++++++++----- network/p2p/test/sporking_test.go | 2 +- .../p2p/tracer/gossipSubMeshTracer_test.go | 6 +-- 7 files changed, 82 insertions(+), 45 deletions(-) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 5ea0ee70e6a..a959a5a0496 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -15,6 +15,7 @@ import ( libp2pmsg "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/module/irrecoverable" mockmodule "github.com/onflow/flow-go/module/mock" + flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" @@ -102,7 +103,6 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - topic := channels.Topic("/flow/" + unittest.IdentifierFixture().String()) count := 5 golog.SetAllLoggers(golog.LevelFatal) // change this to Debug if libp2p logs are needed @@ -155,18 +155,16 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { // hence expect count and not count - 1 messages to be received (one by each node, including the sender) ch := make(chan peer.ID, count) - codec := unittest.NetworkCodec() - - payload, _ := codec.Encode(&libp2pmsg.TestMessage{}) - msg := &message.Message{ - Payload: payload, - } - - data, err := msg.Marshal() + messageScope, err := flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.TestNetworkChannel, + &libp2pmsg.TestMessage{}, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) require.NoError(t, err) logger := unittest.Logger() - + topic := channels.TopicFromChannel(channels.TestNetworkChannel, sporkId) topicValidator := flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter()) for _, n := range nodes { s, err := n.Subscribe(topic, topicValidator) @@ -176,7 +174,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { msg, err := s.Next(ctx) require.NoError(t, err) require.NotNil(t, msg) - assert.Equal(t, data, msg.Data) + assert.Equal(t, messageScope.Proto().Payload, msg.Data) ch <- nodeID }(s, n.Host().ID()) } @@ -196,7 +194,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { require.Eventually(t, fullyConnectedGraph, time.Second*5, ticksForAssertEventually, "nodes failed to discover each other") // Step 4: publish a message to the topic - require.NoError(t, dhtServerNode.Publish(ctx, topic, data)) + require.NoError(t, dhtServerNode.Publish(ctx, messageScope)) // Step 5: By now, all peers would have been discovered and the message should have been successfully published // A hash set to keep track of the nodes who received the message @@ -221,6 +219,6 @@ loop: // Step 6: unsubscribes all nodes from the topic for _, n := range nodes { - assert.NoError(t, n.UnSubscribe(topic)) + assert.NoError(t, n.Unsubscribe(channels.TestNetworkChannel)) } } diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 62555af935d..898a9c6916e 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -64,8 +64,8 @@ type LibP2PNode interface { ListPeers(topic string) []peer.ID // Subscribe subscribes the node to the given topic and returns the subscription Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) - // UnSubscribe cancels the subscriber and closes the topic. - UnSubscribe(topic channels.Topic) error + // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. + Unsubscribe(channel channels.Channel) error // Publish publishes the given payload on the topic. Publish(ctx context.Context, msgScope *network.OutgoingMessageScope) error // Host returns pointer to host object of node. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 1eb55b0b591..4d3ae590247 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -624,16 +624,7 @@ func (m *Middleware) processPubSubMessages(msg *message.Message, peerID peer.ID) // // All errors returned from this function can be considered benign. func (m *Middleware) Unsubscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, m.rootBlockID) - err := m.libP2PNode.UnSubscribe(topic) - if err != nil { - return fmt.Errorf("failed to unsubscribe from channel (%s): %w", channel, err) - } - - // update peers to remove nodes subscribed to channel - m.libP2PNode.RequestPeerUpdate() - - return nil + return m.libP2PNode.Unsubscribe(channel) } // processUnicastStreamMessage will decode, perform authorized sender validation and process a message diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 50216155b71..e2362e3fe69 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -106,7 +106,7 @@ func (n *Node) Stop() error { n.logger.Debug().Msg("unsubscribing from all topics") for t := range n.topics { - err := n.UnSubscribe(t) + err := n.unsubscribeTopic(t) // context cancelled errors are expected while unsubscribing from topics during shutdown if err != nil && !errors.Is(err, context.Canceled) { result = multierror.Append(result, err) @@ -280,11 +280,32 @@ func (n *Node) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidator return s, err } -// UnSubscribe cancels the subscriber and closes the topic. +// Unsubscribe cancels the subscriber and closes the topic. // All errors returned from this function can be considered benign. -func (n *Node) UnSubscribe(topic channels.Topic) error { +func (n *Node) Unsubscribe(channel channels.Channel) error { + topic := channels.TopicFromChannel(channel, n.sporkId) + err := n.unsubscribeTopic(topic) + if err != nil { + return fmt.Errorf("failed to unsubscribe from topic: %w", err) + } + + n.RequestPeerUpdate() + + return nil +} + +// unsubscribeTopic cancels the subscriber and closes the topic. +// All errors returned from this function can be considered benign. +// Args: +// +// topic: topic to unsubscribe from +// +// Returns: +// error: error if any. +func (n *Node) unsubscribeTopic(topic channels.Topic) error { n.Lock() defer n.Unlock() + // Remove the Subscriber from the cache if s, found := n.subs[topic]; found { s.Cancel() @@ -314,7 +335,8 @@ func (n *Node) UnSubscribe(topic channels.Topic) error { n.logger.Debug(). Str("topic", topic.String()). Msg("unsubscribed from topic") - return err + + return nil } // Publish publishes the given payload on the topic. diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 50ecb5a568f..5be17d248eb 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -30,6 +30,7 @@ import ( flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/connection" p2pdht "github.com/onflow/flow-go/network/p2p/dht" @@ -582,19 +583,28 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. // let subscriptions propagate time.Sleep(1 * time.Second) + // TODO: remove this and use channel as an argument channel, ok := channels.ChannelFromTopic(topic) require.True(t, ok) for _, node := range nodes { for i := 0; i < count; i++ { // creates a unique message to be published by the node - msg := messageFactory() - data := p2pfixtures.MustEncodeEvent(t, msg, channel) - require.NoError(t, node.Publish(ctx, topic, data)) + payload := messageFactory() + outgoingMessageScope, err := flownet.NewOutgoingScope( + flow.IdentifierList{unittest.IdentifierFixture()}, + channel, + payload, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, node.Publish(ctx, outgoingMessageScope)) // wait for the message to be received by all nodes ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - p2pfixtures.SubsMustReceiveMessage(t, ctx, data, subs) + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + p2pfixtures.SubsMustReceiveMessage(t, ctx, expectedReceivedData, subs) cancel() } } @@ -620,18 +630,27 @@ func EnsurePubsubMessageExchangeFromNode(t *testing.T, ctx context.Context, send // let subscriptions propagate time.Sleep(1 * time.Second) + // TODO: remove this and use channel as an argument channel, ok := channels.ChannelFromTopic(topic) require.True(t, ok) for i := 0; i < count; i++ { // creates a unique message to be published by the node - msg := messageFactory() - data := p2pfixtures.MustEncodeEvent(t, msg, channel) - require.NoError(t, sender.Publish(ctx, topic, data)) + payload := messageFactory() + outgoingMessageScope, err := flownet.NewOutgoingScope( + flow.IdentifierList{}, + channel, + payload, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, sender.Publish(ctx, outgoingMessageScope)) // wait for the message to be received by all nodes ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - p2pfixtures.SubsMustReceiveMessage(t, ctx, data, []p2p.Subscription{toSub}) + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + p2pfixtures.SubsMustReceiveMessage(t, ctx, expectedReceivedData, []p2p.Subscription{toSub}) cancel() } } @@ -692,13 +711,20 @@ func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p wg.Add(1) go func() { // creates a unique message to be published by the node. - msg := messageFactory() + // TODO: remove this and use channel as an argument channel, ok := channels.ChannelFromTopic(topic) require.True(t, ok) - data := p2pfixtures.MustEncodeEvent(t, msg, channel) - // ensure the message is NOT received by any of the nodes. - require.NoError(t, node.Publish(ctx, topic, data)) + payload := messageFactory() + outgoingMessageScope, err := flownet.NewOutgoingScope( + flow.IdentifierList{unittest.IdentifierFixture()}, + channel, + payload, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, node.Publish(ctx, outgoingMessageScope)) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) p2pfixtures.SubsMustNeverReceiveAnyMessage(t, ctx, subs) cancel() diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 6f1b784fc72..b7aae10d32f 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -248,7 +248,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { // mimic that node1 is now part of the new spork while node2 remains on the old spork // by unsubscribing node1 from 'topicBeforeSpork' and subscribing it to 'topicAfterSpork' // and keeping node2 subscribed to topic 'topicBeforeSpork' - err = node1.UnSubscribe(topicBeforeSpork) + err = node1.Unsubscribe(topicBeforeSpork) require.NoError(t, err) _, err = node1.Subscribe(topicAfterSpork, topicValidator) require.NoError(t, err) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index c8a680df53e..407d03fb50d 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -176,9 +176,9 @@ func TestGossipSubMeshTracer(t *testing.T) { // all nodes except the tracerNode unsubscribe from the topic1, which triggers sending a PRUNE to the tracerNode for each unsubscription. // We expect the tracerNode to remove the otherNode1, otherNode2, and unknownNode from its mesh. - require.NoError(t, otherNode1.UnSubscribe(topic1)) - require.NoError(t, otherNode2.UnSubscribe(topic1)) - require.NoError(t, unknownNode.UnSubscribe(topic1)) + require.NoError(t, otherNode1.Unsubscribe(topic1)) + require.NoError(t, otherNode2.Unsubscribe(topic1)) + require.NoError(t, unknownNode.Unsubscribe(topic1)) assert.Eventually(t, func() bool { // eventually, the tracerNode should not have the other node in its mesh for topic1. From 5d9dfbed9e94a9ef061649a734ed0f5f84c0c499 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 16 Aug 2023 05:56:11 -0400 Subject: [PATCH 483/815] initial loader --- engine/common/synchronization/engine_test.go | 48 ++++---------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index b89d0ff4c5d..60d7aa2ac7e 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -229,6 +229,8 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { } func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load() { + load := 1000 + // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ @@ -236,48 +238,18 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load( Height: 0, } - load := 30 - - // create channel to limit goroutines - //limiter := make(chan struct{}, 10) - - // create channel to count results - results := make(chan bool, load) - - //req.Height = ss.head.Height + 1 - //ss.core.On("HandleHeight", ss.head, req.Height) - //ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - - // define custom matcher - // Define a custom matcher function that takes a Person and returns true if the Name is “John” - handleHeightMatcher := mock.MatchedBy(func(head, height int) bool { - return p.Name == “John” - }) - - // loop over goroutines to simulate load for i := 0; i < load; i++ { + ss.T().Logf("i: %d", i) // if request height is higher than local finalized, we should not respond - go func() { - - req.Height = ss.head.Height + 1 - ss.core.On("HandleHeight", ss.head, req.Height) - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - - err := ss.e.requestHandler.onSyncRequest(originID, req) - ss.Assert().NoError(err, "same height sync request should pass") - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - results <- true - }() - } - - // wait for all goroutines to finish - for i := 0; i < load; i++ { - //<-results - results <- true - ss.core.AssertExpectations(ss.T()) + req.Height = ss.head.Height + 1 + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + err := ss.e.requestHandler.onSyncRequest(originID, req) + ss.Assert().NoError(err, "same height sync request should pass") + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) } - close(results) + ss.core.AssertExpectations(ss.T()) } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and From 34fd2e7af092a04661c6330e5dea4621febb140e Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 16 Aug 2023 06:33:10 -0400 Subject: [PATCH 484/815] load test using full engine --- engine/common/synchronization/engine_test.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 60d7aa2ac7e..70d0b6b492a 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -231,21 +231,28 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load() { load := 1000 + ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) + ss.e.Start(ctx) + unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) + defer cancel() + // generate origin and request message originID := unittest.IdentifierFixture() - req := &messages.SyncRequest{ - Nonce: rand.Uint64(), - Height: 0, - } for i := 0; i < load; i++ { ss.T().Logf("i: %d", i) + + req := &messages.SyncRequest{ + Nonce: rand.Uint64(), + Height: 0, + } + // if request height is higher than local finalized, we should not respond req.Height = ss.head.Height + 1 ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - err := ss.e.requestHandler.onSyncRequest(originID, req) - ss.Assert().NoError(err, "same height sync request should pass") + + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) } From 0a486bbe33a89afea9e098c3ce051c7351f2b62d Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 16 Aug 2023 07:07:01 -0400 Subject: [PATCH 485/815] test reporting SyncRequest misbehavior --- engine/common/synchronization/engine.go | 8 +------- engine/common/synchronization/engine_test.go | 19 +++++++++---------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 4e76235f491..7b9cc36b1d5 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -484,15 +484,9 @@ func (e *Engine) validateRangeRequestForALSP(id flow.Identifier, channel channel } func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, false + return nil, true } func (e *Engine) validateSyncResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } - -func primeNumbers(foo int) { - for i := 0; i < foo; i++ { - fmt.Println(i) - } -} diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 70d0b6b492a..f5f8de8ac17 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -200,13 +200,6 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { ss.core.AssertExpectations(ss.T()) } -func BenchmarkPrimeNumbers(b *testing.B) { - foo := 5 - for i := 0; i < b.N; i++ { - primeNumbers(foo) - } -} - // TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher than the receiver's height doesn't // trigger a response, even if outside tolerance. func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { @@ -249,11 +242,17 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load( // if request height is higher than local finalized, we should not respond req.Height = ss.head.Height + 1 - ss.core.On("HandleHeight", ss.head, req.Height) - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + // assert that misbehavior is reported + ss.con.On("ReportMisbehavior", mock.Anything).Return(true) + + // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported + // also, check that response is never sent + ss.core.AssertNotCalled(ss.T(), "HandleHeight") + ss.core.AssertNotCalled(ss.T(), "WithinTolerance") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } ss.core.AssertExpectations(ss.T()) From 92eb5ae0f77e358dcd6b98c835d24eb4f560e731 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 16 Aug 2023 08:05:27 -0400 Subject: [PATCH 486/815] clean up --- engine/common/synchronization/engine.go | 22 +++++++++++----------- module/chainsync/core.go | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 7b9cc36b1d5..fee6c205c88 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -203,9 +203,9 @@ func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, eve // - IncompatibleInputTypeError if input has unexpected type // - All other errors are potential symptoms of internal state corruption or bugs (fatal). func (e *Engine) process(channel channels.Channel, originID flow.Identifier, event interface{}) error { - switch resource := event.(type) { + switch event.(type) { case *messages.BatchRequest: - report, misbehavior := e.validateBatchRequestForALSP(originID, channel, resource) + report, misbehavior := e.validateBatchRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid batch request from %x: %v", originID[:], misbehavior) @@ -214,7 +214,7 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve } return e.requestHandler.Process(channel, originID, event) case *messages.RangeRequest: - report, misbehavior := e.validateRangeRequestForALSP(originID, channel, resource) + report, misbehavior := e.validateRangeRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid range request from %x: %v", originID[:], misbehavior) @@ -224,7 +224,7 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve return e.requestHandler.Process(channel, originID, event) case *messages.SyncRequest: - report, misbehavior := e.validateSyncRequestForALSP(originID, channel, resource) + report, misbehavior := e.validateSyncRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync request from %x: %v", originID[:], misbehavior) @@ -234,7 +234,7 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve return e.requestHandler.Process(channel, originID, event) case *messages.BlockResponse: - report, misbehavior := e.validateBlockResponseForALSP(originID, channel, resource) + report, misbehavior := e.validateBlockResponseForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid block response from %x: %v", originID[:], misbehavior) @@ -244,7 +244,7 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve return e.responseMessageHandler.Process(originID, event) case *messages.SyncResponse: - report, misbehavior := e.validateSyncResponseForALSP(originID, channel, resource) + report, misbehavior := e.validateSyncResponseForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync response from %x: %v", originID[:], misbehavior) @@ -471,22 +471,22 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy } } -func (e *Engine) validateBatchRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { +func (e *Engine) validateBatchRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } -func (e *Engine) validateBlockResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { +func (e *Engine) validateBlockResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } -func (e *Engine) validateRangeRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { +func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } -func (e *Engine) validateSyncRequestForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { +func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, true } -func (e *Engine) validateSyncResponseForALSP(id flow.Identifier, channel channels.Channel, resource interface{}) (*alsp.MisbehaviorReport, bool) { +func (e *Engine) validateSyncResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } diff --git a/module/chainsync/core.go b/module/chainsync/core.go index 08514b1b513..c493df79cb9 100644 --- a/module/chainsync/core.go +++ b/module/chainsync/core.go @@ -112,7 +112,7 @@ func (c *Core) HandleBlock(header *flow.Header) bool { } // HandleHeight handles receiving a new highest finalized height from another node. -// If the height difference between local and the reported height, we do nothing. +// If the height difference between local and the reported height is outside tolerance, we do nothing. // Otherwise, we queue each missing height. func (c *Core) HandleHeight(final *flow.Header, height uint64) { log := c.log.With().Uint64("final_height", final.Height).Uint64("recv_height", height).Logger() From 03a4423e301c0081387a9dc085e7c3f77e63cc83 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 16 Aug 2023 08:09:05 -0400 Subject: [PATCH 487/815] add rpc control tracking to control message validation inspector - update builders - update unit test --- .../node_builder/access_node_builder.go | 3 +- cmd/observer/node_builder/observer_builder.go | 3 +- .../export_report.json | 4 +- follower/follower_builder.go | 3 +- .../validation_inspector_test.go | 104 ++++++++++-------- network/internal/p2pfixtures/fixtures.go | 3 +- network/netconf/flags.go | 4 +- .../control_message_validation_inspector.go | 28 +++-- network/p2p/mock/pub_sub_tracer.go | 28 +++++ network/p2p/mock/rpc_control_tracking.go | 53 +++++++++ .../p2pbuilder/gossipsub/gossipSubBuilder.go | 6 +- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 35 +++--- network/p2p/test/fixtures.go | 17 ++- network/p2p/tracer/gossipSubMeshTracer.go | 3 +- 14 files changed, 212 insertions(+), 82 deletions(-) create mode 100644 network/p2p/mock/rpc_control_tracking.go diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index d7726213994..07666cc2ed9 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1326,7 +1326,8 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri &p2p.DisallowListCacheConfig{ MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }). + }, + meshTracer). SetBasicResolver(builder.Resolver). SetSubscriptionFilter( subscription.NewRoleBasedFilter( diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 35c4f5c3a26..194bcb33f68 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -745,7 +745,8 @@ func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr &p2p.DisallowListCacheConfig{ MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }). + }, + meshTracer). SetSubscriptionFilter( subscription.NewRoleBasedFilter( subscription.UnstakedRole, builder.IdentityProvider, diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json index 4c8484e4396..07b76f87d41 100644 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ b/cmd/util/cmd/execution-state-extract/export_report.json @@ -1,6 +1,6 @@ { "EpochCounter": 0, - "PreviousStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", - "CurrentStateCommitment": "1c9f9d343cb8d4610e0b2c1eb74d6ea2f2f8aef2d666281dc22870e3efaa607b", + "PreviousStateCommitment": "8a748b8e25b19ad04cfa4f6bf0c0f9dd5d9c1daba25c78ab25320ca4e52cab0c", + "CurrentStateCommitment": "8a748b8e25b19ad04cfa4f6bf0c0f9dd5d9c1daba25c78ab25320ca4e52cab0c", "ReportSucceeded": true } \ No newline at end of file diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 27e69ce039c..90f471e1b7a 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -634,7 +634,8 @@ func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.Pr &p2p.DisallowListCacheConfig{ MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }). + }, + meshTracer). SetSubscriptionFilter( subscription.NewRoleBasedFilter( subscription.UnstakedRole, builder.IdentityProvider, diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 67d63723d21..ff46591a5f5 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/mock" @@ -27,8 +28,8 @@ import ( "github.com/onflow/flow-go/network/p2p/inspector/validation" p2pmsg "github.com/onflow/flow-go/network/p2p/message" mockp2p "github.com/onflow/flow-go/network/p2p/mock" - "github.com/onflow/flow-go/network/p2p/p2pconf" p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/utils/unittest" ) @@ -76,6 +77,12 @@ func TestValidationInspector_SafetyThreshold(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) + + messageCount := 5 + controlMessageCount := int64(2) + + meshTracer := meshTracerFixture(flowConfig, idProvider) + validationInspector, err := validation.NewControlMsgValidationInspector( logger, sporkID, @@ -84,7 +91,8 @@ func TestValidationInspector_SafetyThreshold(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -93,15 +101,13 @@ func TestValidationInspector_SafetyThreshold(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() - messageCount := 5 - controlMessageCount := int64(2) - defer distributor.AssertNotCalled(t, "Distribute", mockery.Anything) validationInspector.Start(signalerCtx) @@ -178,6 +184,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(2, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -186,7 +193,8 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -195,6 +203,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -274,6 +283,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -282,7 +292,8 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -291,6 +302,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -369,6 +381,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(4, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -377,7 +390,8 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -386,6 +400,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -500,6 +515,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -508,7 +524,8 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -517,6 +534,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -633,6 +651,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -641,7 +660,8 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -650,6 +670,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -739,6 +760,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -747,7 +769,8 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -756,6 +779,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -836,6 +860,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -844,7 +869,8 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -853,6 +879,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -928,6 +955,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -936,7 +964,8 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -945,6 +974,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -1030,6 +1060,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), sporkID, @@ -1038,7 +1069,8 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, - metrics.NewNoopCollector()) + metrics.NewNoopCollector(), + meshTracer) require.NoError(t, err) corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) victimNode, victimIdentity := p2ptest.NodeFixture( @@ -1047,6 +1079,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) @@ -1096,35 +1129,6 @@ func withExpectedNotificationDissemination(expectedNumOfTotalNotif int, f onNoti } } -// setupTest sets up common components of RPC inspector test. -func setupTest(t *testing.T, logger zerolog.Logger, role flow.Role, sporkID flow.Identifier, inspectorConfig *p2pconf.GossipSubRPCValidationInspectorConfigs, mockDistributorOpts ...mockDistributorOption) (*irrecoverable.MockSignalerContext, context.CancelFunc, *corruptlibp2p.GossipSubRouterSpammer, p2p.LibP2PNode, flow.Identity, *mockp2p.GossipSubInspectorNotificationDistributor, *validation.ControlMsgValidationInspector, *mock.IdentityProvider) { - idProvider := mock.NewIdentityProvider(t) - spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - - distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) - mockDistributorReadyDoneAware(distributor) - for _, mockDistributorOpt := range mockDistributorOpts { - mockDistributorOpt(distributor, spammer) - } - validationInspector, err := validation.NewControlMsgValidationInspector(logger, sporkID, inspectorConfig, distributor, metrics.NewNoopCollector(), metrics.NewNoopCollector(), idProvider, metrics.NewNoopCollector()) - require.NoError(t, err) - corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) - victimNode, victimIdentity := p2ptest.NodeFixture( - t, - sporkID, - t.Name(), - idProvider, - p2ptest.WithRole(role), - internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), - corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), - ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - - return signalerCtx, cancel, spammer, victimNode, victimIdentity, distributor, validationInspector, idProvider -} - // TestGossipSubSpamMitigationIntegration tests that the spam mitigation feature of GossipSub is working as expected. // The test puts toghether the spam detection (through the GossipSubInspector) and the spam mitigation (through the // scoring system) and ensures that the mitigation is triggered when the spam detection detects spam. @@ -1248,3 +1252,17 @@ func mockDistributorReadyDoneAware(d *mockp2p.GossipSubInspectorNotificationDist return ch }()).Maybe() } + +func meshTracerFixture(flowConfig *config.FlowConfig, idProvider module.IdentityProvider) *tracer.GossipSubMeshTracer { + meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ + Logger: unittest.Logger(), + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: time.Second, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + RpcSentTrackerCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: flowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + } + return tracer.NewGossipSubMeshTracer(meshTracerCfg) +} diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 7b7365d57ab..62fdc94275d 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -133,7 +133,8 @@ func CreateNode(t *testing.T, networkKey crypto.PrivateKey, sporkID flow.Identif &p2p.DisallowListCacheConfig{ MaxSize: uint32(1000), Metrics: metrics.NewNoopCollector(), - }). + }, + meshTracer). SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { return p2pdht.NewDHT(c, h, protocols.FlowDHTProtocolID(sporkID), zerolog.Nop(), metrics.NewNoopCollector()) }). diff --git a/network/netconf/flags.go b/network/netconf/flags.go index c99c97e87c0..26177846ec8 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -138,8 +138,8 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { flags.Float64(ihaveAsyncSampleSizePercentage, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IHaveAsyncInspectSampleSizePercentage, "percentage of ihave messages to sample during asynchronous validation") flags.Float64(ihaveMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IHaveInspectionMaxSampleSize, "max number of ihaves to sample when performing validation") - flags.Uint64(iwantMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.MaxSampleSize, "max number of ihaves to sample when performing validation") - flags.Float64(iwantCacheMissThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max number of ihaves to sample when performing validation") + flags.Uint(iwantMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.MaxSampleSize, "max number of iwants to sample when performing validation") + flags.Float64(iwantCacheMissThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max number of iwants to sample when performing validation") } // rpcInspectorValidationLimits utility func that adds flags for each of the validation limits for each control message type. diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index af5ce5a68fc..1f69cdcef53 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -81,10 +81,11 @@ func NewControlMsgValidationInspector( inspectMsgQueueCacheCollector module.HeroCacheMetrics, clusterPrefixedCacheCollector module.HeroCacheMetrics, idProvider module.IdentityProvider, - inspectorMetrics module.GossipSubRpcValidationInspectorMetrics) (*ControlMsgValidationInspector, error) { + inspectorMetrics module.GossipSubRpcValidationInspectorMetrics, + rpcTracker p2p.RPCControlTracking) (*ControlMsgValidationInspector, error) { lg := logger.With().Str("component", "gossip_sub_rpc_validation_inspector").Logger() - tracker, err := cache.NewClusterPrefixedMessagesReceivedTracker(logger, config.ClusterPrefixedControlMsgsReceivedCacheSize, clusterPrefixedCacheCollector, config.ClusterPrefixedControlMsgsReceivedCacheDecay) + clusterPrefixedTracker, err := cache.NewClusterPrefixedMessagesReceivedTracker(logger, config.ClusterPrefixedControlMsgsReceivedCacheSize, clusterPrefixedCacheCollector, config.ClusterPrefixedControlMsgsReceivedCacheDecay) if err != nil { return nil, fmt.Errorf("failed to create cluster prefix topics received tracker") } @@ -94,7 +95,8 @@ func NewControlMsgValidationInspector( sporkID: sporkID, config: config, distributor: distributor, - tracker: tracker, + tracker: clusterPrefixedTracker, + rpcTracker: rpcTracker, idProvider: idProvider, metrics: inspectorMetrics, rateLimiters: make(map[p2pmsg.ControlMessageType]p2p.BasicRateLimiter), @@ -132,7 +134,7 @@ func NewControlMsgValidationInspector( return c, nil } -// Inspect is called by gossipsub upon reception of an rpc from a remote node. +// Inspect is called by gossipsub upon reception of an rpc from a remote node. // It examines the provided message to ensure it adheres to the expected // format and conventions. If the message passes validation, the method returns // a nil error. If an issue is found, the method returns an error detailing @@ -214,16 +216,22 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control c.logger.Warn(). Uint("sample_size", sampleSize). Uint("max_sample_size", c.config.IWantRPCInspectionConfig.MaxSampleSize). - Str(logging.KeySuspicious, "true"). // max sample size is suspicious + Str(logging.KeySuspicious, "true"). // max sample size is suspicious Str(logging.KeyNetworkingSecurity, "true"). // zero sample size is a security hole Msg("zero or invalid sample size, using default max sample size") sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize } + numOfIWants := uint(len(iWants)) + if numOfIWants < sampleSize { + sampleSize = numOfIWants + } + swap := func(i, j uint) { iWants[i], iWants[j] = iWants[j], iWants[i] } - err := c.sampleCtrlMessages(uint(len(iWants)), sampleSize, swap) + + err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIWant, numOfIWants, sampleSize, swap) if err != nil { return fmt.Errorf("failed to sample iwant messages: %w", err) } @@ -345,7 +353,7 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer if count > validationConfig.HardThreshold { // for iHave control message topic validation we only validate a random subset of the messages // shuffle the ihave messages to perform random validation on a subset of size sampleSize - err := c.sampleCtrlMessages(totalIhaves, sampleSize, swap) + err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIHave, totalIhaves, sampleSize, swap) if err != nil { return fmt.Errorf("failed to sample ihave messages: %w", err) } @@ -371,7 +379,7 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer // to randomize async validation to avoid data race that can occur when // performing the sampling asynchronously. // for iHave control message topic validation we only validate a random subset of the messages - err := c.sampleCtrlMessages(totalIhaves, sampleSize, swap) + err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIHave, totalIhaves, sampleSize, swap) if err != nil { return fmt.Errorf("failed to sample ihave messages: %w", err) } @@ -380,10 +388,10 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer // sampleCtrlMessages performs sampling on the specified control message that will randomize // the items in the control message slice up to index sampleSize-1. -func (c *ControlMsgValidationInspector) sampleCtrlMessages(totalSize, sampleSize uint, swap func(i, j uint)) error { +func (c *ControlMsgValidationInspector) sampleCtrlMessages(ctrlMsg p2pmsg.ControlMessageType, totalSize, sampleSize uint, swap func(i, j uint)) error { err := flowrand.Samples(totalSize, sampleSize, swap) if err != nil { - return fmt.Errorf("failed to get random sample of ihave control messages: %w", err) + return fmt.Errorf("failed to get random sample of %s control messages: %w", ctrlMsg, err) } return nil } diff --git a/network/p2p/mock/pub_sub_tracer.go b/network/p2p/mock/pub_sub_tracer.go index c243118110d..26e37611bc2 100644 --- a/network/p2p/mock/pub_sub_tracer.go +++ b/network/p2p/mock/pub_sub_tracer.go @@ -64,6 +64,20 @@ func (_m *PubSubTracer) Join(topic string) { _m.Called(topic) } +// LastHighestIHaveRPCSize provides a mock function with given fields: +func (_m *PubSubTracer) LastHighestIHaveRPCSize() int64 { + ret := _m.Called() + + var r0 int64 + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + return r0 +} + // Leave provides a mock function with given fields: topic func (_m *PubSubTracer) Leave(topic string) { _m.Called(topic) @@ -130,6 +144,20 @@ func (_m *PubSubTracer) ValidateMessage(msg *pubsub.Message) { _m.Called(msg) } +// WasIHaveRPCSent provides a mock function with given fields: messageID +func (_m *PubSubTracer) WasIHaveRPCSent(messageID string) bool { + ret := _m.Called(messageID) + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(messageID) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + type mockConstructorTestingTNewPubSubTracer interface { mock.TestingT Cleanup(func()) diff --git a/network/p2p/mock/rpc_control_tracking.go b/network/p2p/mock/rpc_control_tracking.go new file mode 100644 index 00000000000..c3cacb0c45c --- /dev/null +++ b/network/p2p/mock/rpc_control_tracking.go @@ -0,0 +1,53 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import mock "github.com/stretchr/testify/mock" + +// RPCControlTracking is an autogenerated mock type for the RPCControlTracking type +type RPCControlTracking struct { + mock.Mock +} + +// LastHighestIHaveRPCSize provides a mock function with given fields: +func (_m *RPCControlTracking) LastHighestIHaveRPCSize() int64 { + ret := _m.Called() + + var r0 int64 + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + return r0 +} + +// WasIHaveRPCSent provides a mock function with given fields: messageID +func (_m *RPCControlTracking) WasIHaveRPCSent(messageID string) bool { + ret := _m.Called(messageID) + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(messageID) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +type mockConstructorTestingTNewRPCControlTracking interface { + mock.TestingT + Cleanup(func()) +} + +// NewRPCControlTracking creates a new instance of RPCControlTracking. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRPCControlTracking(t mockConstructorTestingTNewRPCControlTracking) *RPCControlTracking { + mock := &RPCControlTracking{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/p2pbuilder/gossipsub/gossipSubBuilder.go b/network/p2p/p2pbuilder/gossipsub/gossipSubBuilder.go index 3e69590dc17..a1d55ab873c 100644 --- a/network/p2p/p2pbuilder/gossipsub/gossipSubBuilder.go +++ b/network/p2p/p2pbuilder/gossipsub/gossipSubBuilder.go @@ -184,6 +184,7 @@ func NewGossipSubBuilder( sporkId flow.Identifier, idProvider module.IdentityProvider, rpcInspectorConfig *p2pconf.GossipSubRPCInspectorsConfig, + rpcTracker p2p.RPCControlTracking, ) *Builder { lg := logger.With(). Str("component", "gossipsub"). @@ -200,7 +201,7 @@ func NewGossipSubBuilder( gossipSubConfigFunc: defaultGossipSubAdapterConfig(), scoreOptionConfig: scoring.NewScoreOptionConfig(lg, idProvider), rpcInspectorConfig: rpcInspectorConfig, - rpcInspectorSuiteFactory: defaultInspectorSuite(), + rpcInspectorSuiteFactory: defaultInspectorSuite(rpcTracker), } return b @@ -230,7 +231,7 @@ func defaultGossipSubAdapterConfig() p2p.GossipSubAdapterConfigFunc { // defaultInspectorSuite returns the default inspector suite factory function. It is used to create the default inspector suite. // Inspector suite is utilized to inspect the incoming gossipsub rpc messages from different perspectives. // Note: always use the default inspector suite factory function to create the inspector suite (unless you know what you are doing). -func defaultInspectorSuite() p2p.GossipSubRpcInspectorSuiteFactoryFunc { +func defaultInspectorSuite(rpcTracker p2p.RPCControlTracking) p2p.GossipSubRpcInspectorSuiteFactoryFunc { return func( logger zerolog.Logger, sporkId flow.Identifier, @@ -264,6 +265,7 @@ func defaultInspectorSuite() p2p.GossipSubRpcInspectorSuiteFactoryFunc { clusterPrefixedCacheCollector, idProvider, gossipSubMetrics, + rpcTracker, ) if err != nil { return nil, fmt.Errorf("failed to create new control message valiadation inspector: %w", err) diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index ac3f30fd4d0..fbc56a6168c 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -84,7 +84,8 @@ func NewNodeBuilder( rCfg *p2pconf.ResourceManagerConfig, rpcInspectorCfg *p2pconf.GossipSubRPCInspectorsConfig, peerManagerConfig *p2pconfig.PeerManagerConfig, - disallowListCacheCfg *p2p.DisallowListCacheConfig) *LibP2PNodeBuilder { + disallowListCacheCfg *p2p.DisallowListCacheConfig, + rpcTracker p2p.RPCControlTracking) *LibP2PNodeBuilder { return &LibP2PNodeBuilder{ logger: logger, sporkId: sporkId, @@ -100,7 +101,8 @@ func NewNodeBuilder( networkingType, sporkId, idProvider, - rpcInspectorCfg), + rpcInspectorCfg, + rpcTracker), peerManagerConfig: peerManagerConfig, } } @@ -462,6 +464,19 @@ func DefaultNodeBuilder( connection.WithOnInterceptPeerDialFilters(append(peerFilters, connGaterCfg.InterceptPeerDialFilters...)), connection.WithOnInterceptSecuredFilters(append(peerFilters, connGaterCfg.InterceptSecuredFilters...))) + meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ + Logger: logger, + Metrics: metricsCfg.Metrics, + IDProvider: idProvider, + LoggerInterval: gossipCfg.LocalMeshLogInterval, + RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: gossipCfg.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: gossipCfg.RpcSentTrackerNumOfWorkers, + HeroCacheMetricsFactory: metricsCfg.HeroCacheFactory, + NetworkingType: flownet.PrivateNetwork, + } + meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) + builder := NewNodeBuilder( logger, metricsCfg, @@ -473,7 +488,8 @@ func DefaultNodeBuilder( rCfg, rpcInspectorCfg, peerManagerCfg, - disallowListCacheCfg). + disallowListCacheCfg, + meshTracer). SetBasicResolver(resolver). SetConnectionManager(connManager). SetConnectionGater(connGater). @@ -489,19 +505,6 @@ func DefaultNodeBuilder( builder.EnableGossipSubScoringWithOverride(p2p.PeerScoringConfigNoOverride) } - meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: logger, - Metrics: metricsCfg.Metrics, - IDProvider: idProvider, - LoggerInterval: gossipCfg.LocalMeshLogInterval, - RpcSentTrackerCacheSize: gossipCfg.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: gossipCfg.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: gossipCfg.RpcSentTrackerNumOfWorkers, - HeroCacheMetricsFactory: metricsCfg.HeroCacheFactory, - NetworkingType: flownet.PrivateNetwork, - } - meshTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) - builder.SetGossipSubTracer(meshTracer) builder.SetGossipSubScoreTracerInterval(gossipCfg.ScoreTracerInterval) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 50ecb5a568f..ca69fea8299 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -36,6 +36,7 @@ import ( "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/p2pconf" + "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/network/p2p/unicast" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/utils" @@ -70,6 +71,18 @@ func NodeFixture( return nil }) require.NotNil(t, connectionGater) + + meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ + Logger: unittest.Logger(), + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: time.Second, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + RpcSentTrackerCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultFlowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultFlowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + } + parameters := &NodeFixtureParameters{ NetworkingType: flownet.PrivateNetwork, HandlerFunc: func(network.Stream) {}, @@ -89,6 +102,7 @@ func NodeFixture( ConnGater: connectionGater, PeerManagerConfig: PeerManagerConfigFixture(), // disabled by default GossipSubRPCInspectorCfg: &defaultFlowConfig.NetworkConfig.GossipSubRPCInspectorsConfig, + PubSubTracer: tracer.NewGossipSubMeshTracer(meshTracerCfg), } for _, opt := range opts { @@ -119,7 +133,8 @@ func NodeFixture( &p2p.DisallowListCacheConfig{ MaxSize: uint32(1000), Metrics: metrics.NewNoopCollector(), - }). + }, + parameters.PubSubTracer). SetConnectionManager(connManager). SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) { return p2pdht.NewDHT(c, h, diff --git a/network/p2p/tracer/gossipSubMeshTracer.go b/network/p2p/tracer/gossipSubMeshTracer.go index f0b9c96d331..07f2c430f83 100644 --- a/network/p2p/tracer/gossipSubMeshTracer.go +++ b/network/p2p/tracer/gossipSubMeshTracer.go @@ -192,8 +192,7 @@ func (t *GossipSubMeshTracer) SendRPC(rpc *pubsub.RPC, _ peer.ID) { // WasIHaveRPCSent returns true if an iHave control message for the messageID was sent, otherwise false. func (t *GossipSubMeshTracer) WasIHaveRPCSent(messageID string) bool { - // TODO: topic string is obsolete since messageID is globally unique - return t.rpcSentTracker.WasIHaveRPCSent("", messageID) + return t.rpcSentTracker.WasIHaveRPCSent(messageID) } // LastHighestIHaveRPCSize returns the last highest RPC iHave message sent. From abfe232b9d3e61475b9456356096f4ca5855b2cc Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 16 Aug 2023 08:43:09 -0400 Subject: [PATCH 488/815] potential spam misbehavior report --- engine/common/synchronization/engine.go | 17 +++++++++++++++-- network/alsp/misbehavior.go | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index fee6c205c88..3eb671ee59c 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -483,8 +483,21 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I return nil, false } -func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { - return nil, true +func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { + //syncRequestMsg := event.(*messages.SyncRequest) + + report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) + + if err != nil { + // failing to create the misbehavior report is unlikely. If an error is encountered while + // creating the misbehavior report it indicates a bug and processing can not proceed. + e.log.Fatal(). + Err(err). + Str("originID", originID.String()). + Msg("failed to create misbehavior report") + } + + return report, true } func (e *Engine) validateSyncResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { diff --git a/network/alsp/misbehavior.go b/network/alsp/misbehavior.go index af4921cd06a..ee938331ef4 100644 --- a/network/alsp/misbehavior.go +++ b/network/alsp/misbehavior.go @@ -43,6 +43,9 @@ const ( // UnauthorizedPublishOnChannel is a misbehavior that is reported when a message not authorized to be sent via pubsub is received via pubsub. UnauthorizedPublishOnChannel network.Misbehavior = "unauthorized-pubsub-on-channel" + + // PotentialSpam is a misbehavior that is reported when a message is potentially spam. + PotentialSpam network.Misbehavior = "potential-spam" ) func AllMisbehaviorTypes() []network.Misbehavior { From 20591758ecbd73fd164f7e05fc50eccfea8459a1 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 16 Aug 2023 14:39:56 +0100 Subject: [PATCH 489/815] Tiny refactoring of p2pNode, group common operations to separate interfaces --- network/p2p/libp2pNode.go | 135 +++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 46 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 061e45a43ff..d040ff9c051 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -19,57 +19,24 @@ import ( "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) -// LibP2PNode represents a flow libp2p node. It provides the network layer with the necessary interface to -// control the underlying libp2p node. It is essentially the flow wrapper around the libp2p node, and allows -// us to define different types of libp2p nodes that can operate in different ways by overriding these methods. -// TODO: this interface is highly coupled with the current implementation of the libp2p node. We should -// -// consider refactoring it to be more generic and less coupled with the current implementation. -// https://github.com/dapperlabs/flow-go/issues/6575 -type LibP2PNode interface { - module.ReadyDoneAware - Subscriptions - // PeerConnections connection status information per peer. - PeerConnections - // PeerScore exposes the peer score API. - PeerScore - // DisallowListNotificationConsumer exposes the disallow list notification consumer API for the node so that - // it will be notified when a new disallow list update is distributed. - DisallowListNotificationConsumer - // CollectionClusterChangesConsumer is the interface for consuming the events of changes in the collection cluster. - // This is used to notify the node of changes in the collection cluster. - // LibP2PNode implements this interface and consumes the events to be notified of changes in the clustering channels. - // The clustering channels are used by the collection nodes of a cluster to communicate with each other. - // As the cluster (and hence their cluster channels) of collection nodes changes over time (per epoch) the node needs to be notified of these changes. - CollectionClusterChangesConsumer - // DisallowListOracle exposes the disallow list oracle API for external consumers to query about the disallow list. - DisallowListOracle +// Node single node management capabilities +type Node interface { // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. Stop() error - // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. - AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error - // RemovePeer closes the connection with the peer. - RemovePeer(peerID peer.ID) error - // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. - GetPeersForProtocol(pid protocol.ID) peer.IDSlice - // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. - CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) // GetIPPort returns the IP and Port the libp2p node is listening on. GetIPPort() (string, string, error) - // RoutingTable returns the node routing table - RoutingTable() *kbucket.RoutingTable - // ListPeers returns list of peer IDs for peers subscribed to the topic. - ListPeers(topic string) []peer.ID - // Subscribe subscribes the node to the given topic and returns the subscription - Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) - // UnSubscribe cancels the subscriber and closes the topic. - UnSubscribe(topic channels.Topic) error - // Publish publishes the given payload on the topic. - Publish(ctx context.Context, topic channels.Topic, data []byte) error // Host returns pointer to host object of node. Host() host.Host +} + +// PeerManagement set of node traits related to its lifecycle and metadata retrieval +type PeerManagement interface { + // SetComponentManager sets the component manager for the node. + // SetComponentManager may be called at most once. + SetComponentManager(cm *component.ComponentManager) + // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error // WithPeersProvider sets the PeersProvider for the peer manager. @@ -79,17 +46,93 @@ type LibP2PNode interface { PeerManagerComponent() component.Component // RequestPeerUpdate requests an update to the peer connections of this node using the peer manager. RequestPeerUpdate() +} + +// Routable set of node routing capabilities +type Routable interface { + // RoutingTable returns the node routing table + RoutingTable() *kbucket.RoutingTable // SetRouting sets the node's routing implementation. // SetRouting may be called at most once. SetRouting(r routing.Routing) // Routing returns node routing object. Routing() routing.Routing +} + +// PeerToPeer peers list operations +type PeerToPeer interface { + // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. + AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error + // RemovePeer closes the connection with the peer. + RemovePeer(peerID peer.ID) error + // ListPeers returns list of peer IDs for peers subscribed to the topic. + ListPeers(topic string) []peer.ID + // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. + GetPeersForProtocol(pid protocol.ID) peer.IDSlice +} + +// StreamManagement peer to peer stream management functions +type StreamManagement interface { + // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. + CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) +} + +// PubSub publish subscribe features for node +type PubSub interface { + // Subscribe subscribes the node to the given topic and returns the subscription + Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) + // UnSubscribe cancels the subscriber and closes the topic. + UnSubscribe(topic channels.Topic) error + // Publish publishes the given payload on the topic. + Publish(ctx context.Context, topic channels.Topic, data []byte) error // SetPubSub sets the node's pubsub implementation. // SetPubSub may be called at most once. SetPubSub(ps PubSubAdapter) - // SetComponentManager sets the component manager for the node. - // SetComponentManager may be called at most once. - SetComponentManager(cm *component.ComponentManager) +} + +// LibP2PNode represents a flow libp2p node. It provides the network layer with the necessary interface to +// control the underlying libp2p node. It is essentially the flow wrapper around the libp2p node, and allows +// us to define different types of libp2p nodes that can operate in different ways by overriding these methods. +// TODO: this interface is highly coupled with the current implementation of the libp2p node. We should +// +// consider refactoring it to be more generic and less coupled with the current implementation. +// https://github.com/dapperlabs/flow-go/issues/6575 +type LibP2PNode interface { + module.ReadyDoneAware + Subscriptions + // PeerConnections connection status information per peer. + PeerConnections + // PeerScore exposes the peer score API. + PeerScore + // DisallowListNotificationConsumer exposes the disallow list notification consumer API for the node so that + // it will be notified when a new disallow list update is distributed. + DisallowListNotificationConsumer + // CollectionClusterChangesConsumer is the interface for consuming the events of changes in the collection cluster. + // This is used to notify the node of changes in the collection cluster. + // LibP2PNode implements this interface and consumes the events to be notified of changes in the clustering channels. + // The clustering channels are used by the collection nodes of a cluster to communicate with each other. + // As the cluster (and hence their cluster channels) of collection nodes changes over time (per epoch) the node needs to be notified of these changes. + CollectionClusterChangesConsumer + // DisallowListOracle exposes the disallow list oracle API for external consumers to query about the disallow list. + DisallowListOracle + + // Node single node management capabilities + Node + + // PeerManagement current peer management functions + PeerManagement + + // Routable routing related features + Routable + + // PubSub publish subscribe features for node + PubSub + + // PeerToPeer peers list operations + PeerToPeer + + // StreamManagement node stream management + StreamManagement } // Subscriptions set of funcs related to current subscription info of a node. From 85f63c68646562b52de0832a59091c32fe196968 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 16 Aug 2023 09:48:12 -0400 Subject: [PATCH 490/815] distribute invalid control message notification for iwant errs - log fatal error if any error encountered while sampling --- .../control_message_validation_inspector.go | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 1f69cdcef53..fa643935ac1 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -216,7 +216,7 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control c.logger.Warn(). Uint("sample_size", sampleSize). Uint("max_sample_size", c.config.IWantRPCInspectionConfig.MaxSampleSize). - Str(logging.KeySuspicious, "true"). // max sample size is suspicious + Str(logging.KeySuspicious, "true"). // max sample size is suspicious Str(logging.KeyNetworkingSecurity, "true"). // zero sample size is a security hole Msg("zero or invalid sample size, using default max sample size") sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize @@ -233,7 +233,7 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIWant, numOfIWants, sampleSize, swap) if err != nil { - return fmt.Errorf("failed to sample iwant messages: %w", err) + c.logger.Fatal().Err(fmt.Errorf("failed to sample iwant messages: %w", err)).Msg("irrecoverable error encountered while sampling iwant control messages") } tracker := make(duplicateStrTracker) @@ -405,13 +405,17 @@ func (c *ControlMsgValidationInspector) processInspectMsgReq(req *InspectMsgRequ c.metrics.AsyncProcessingFinished(req.validationConfig.ControlMsg.String(), time.Since(start)) }() + count := c.getCtrlMsgCount(req.validationConfig.ControlMsg, req.ctrlMsg) + // iWant validation uses new sample size validation. This will be updated for all other control message types. switch req.validationConfig.ControlMsg { case p2pmsg.CtrlMsgIWant: - return c.inspectIWant(req.ctrlMsg.GetIwant()) + if err := c.inspectIWant(req.ctrlMsg.GetIwant()); err != nil { + c.logAndDistributeAsyncInspectErr(req, count, err) + } + return nil } - count := c.getCtrlMsgCount(req.validationConfig.ControlMsg, req.ctrlMsg) lg := c.logger.With(). Str("peer_id", req.Peer.String()). Str("ctrl_msg_type", string(req.validationConfig.ControlMsg)). @@ -671,3 +675,21 @@ func (c *ControlMsgValidationInspector) checkClusterPrefixHardThreshold(nodeID f } return gauge <= c.config.ClusterPrefixHardThreshold } + +// logAndDistributeErr logs the provided error and attempts to disseminate an invalid control message validation notification for the error. +func (c *ControlMsgValidationInspector) logAndDistributeAsyncInspectErr(req *InspectMsgRequest, count uint64, err error) { + lg := c.logger.With(). + Bool(logging.KeySuspicious, true). + Str("peer_id", req.Peer.String()). + Str("ctrl_msg_type", string(req.validationConfig.ControlMsg)). + Uint64("ctrl_msg_count", count).Logger() + + lg.Error().Err(err).Msg("rpc control message async inspection failed") + + err = c.distributor.Distribute(p2p.NewInvalidControlMessageNotification(req.Peer, req.validationConfig.ControlMsg, count, err)) + if err != nil { + lg.Error(). + Err(err). + Msg("failed to distribute invalid control message notification") + } +} From 30414454c2c09e06b485861dc6c2b6035f749046 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 16 Aug 2023 08:07:26 -0700 Subject: [PATCH 491/815] Apply suggestions from code review Co-authored-by: Misha --- integration/Makefile | 4 ++-- integration/tests/epochs/suite.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/Makefile b/integration/Makefile index 9216ee5d8a8..c39ba8d359f 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -39,12 +39,12 @@ consensus-tests: .PHONY: epochs-cohort1-tests epochs-cohort1-tests: # Use a higher timeout of 20m for the suite of tests which span full epochs - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort1... + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort1/... .PHONY: epochs-cohort2-tests epochs-cohort2-tests: # Use a higher timeout of 20m for the suite of tests which span full epochs - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort2... + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic -timeout 20m ./tests/epochs/cohort2/... .PHONY: ghost-tests ghost-tests: diff --git a/integration/tests/epochs/suite.go b/integration/tests/epochs/suite.go index 8e0887c3ebf..afb0d285822 100644 --- a/integration/tests/epochs/suite.go +++ b/integration/tests/epochs/suite.go @@ -4,7 +4,7 @@ // and resource-heavy, we split them into several cohorts, which can be run in parallel. // // If a new cohort is added in the future, it must be added to: -// - ci.yml, flaky-test-monitor.yml (ensure new cohort of tests is run) +// - ci.yml, flaky-test-monitor.yml, bors.toml (ensure new cohort of tests is run) // - Makefile (include new cohort in integration-test directive, etc.) package epochs From 579e951cbae8026de4a03dc043eaad2c78b307f3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 16 Aug 2023 08:12:24 -0700 Subject: [PATCH 492/815] Run FlakyTestMonitor on changed workflows/ci.yml --- .github/workflows/flaky-test-monitor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index f3fa72ee47d..b1c429b3e6f 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -8,6 +8,7 @@ on: push: paths: - '.github/workflows/flaky-test-monitor.yml' + - '.github/workflows/ci.yml' env: BIGQUERY_DATASET: production_src_flow_test_metrics From 7721590f2b1724b90820e71669734ffc9609e215 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 16 Aug 2023 08:13:26 -0700 Subject: [PATCH 493/815] cosmetic change to test flaky test monitor --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40f9077bddc..75345f60d0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name: CI - +# cosmetic change to test flaky test monitor on: push: branches: From 7579c8ef90f30e3825a63293ba1d2d4ab980e4b8 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 16 Aug 2023 08:13:39 -0700 Subject: [PATCH 494/815] Revert "cosmetic change to test flaky test monitor" This reverts commit 7721590f2b1724b90820e71669734ffc9609e215. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75345f60d0b..40f9077bddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name: CI -# cosmetic change to test flaky test monitor + on: push: branches: From 3e998426a232997e1eb3933f6b036962f02e448e Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 16 Aug 2023 11:52:17 -0400 Subject: [PATCH 495/815] sample all message IDs - remove nested for loop --- .../control_message_validation_inspector.go | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index fa643935ac1..d40b734bb5e 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -227,8 +227,17 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control sampleSize = numOfIWants } + var iWantMsgIDPool []string + // opens all iWant boxes into a sample pool to be sampled. + for _, iWant := range iWants { + if len(iWant.GetMessageIDs()) == 0 { + continue + } + iWantMsgIDPool = append(iWantMsgIDPool, iWant.GetMessageIDs()...) + } + swap := func(i, j uint) { - iWants[i], iWants[j] = iWants[j], iWants[i] + iWantMsgIDPool[i], iWantMsgIDPool[j] = iWantMsgIDPool[j], iWantMsgIDPool[i] } err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIWant, numOfIWants, sampleSize, swap) @@ -238,25 +247,20 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control tracker := make(duplicateStrTracker) cacheMisses := float64(0) - totalMessageIDS := float64(0) - for i := uint(0); i < sampleSize; i++ { - iWant := iWants[i] - for _, messageID := range iWant.GetMessageIDs() { - if tracker.isDuplicate(messageID) { - return NewDuplicateFoundErr(fmt.Errorf("duplicate message ID found: %s", messageID)) - } - if !c.rpcTracker.WasIHaveRPCSent(messageID) { - cacheMisses++ - } - tracker.set(messageID) - totalMessageIDS++ + for _, messageID := range iWantMsgIDPool[:sampleSize] { + if tracker.isDuplicate(messageID) { + return NewDuplicateFoundErr(fmt.Errorf("duplicate message ID found: %s", messageID)) + } + if !c.rpcTracker.WasIHaveRPCSent(messageID) { + cacheMisses++ } + tracker.set(messageID) } // check cache miss rate - if cacheMisses/totalMessageIDS > c.config.IWantRPCInspectionConfig.CacheMissThreshold { - return NewIWantCacheMissThresholdErr(cacheMisses, totalMessageIDS, c.config.IWantRPCInspectionConfig.CacheMissThreshold) + if cacheMisses/float64(sampleSize) > c.config.IWantRPCInspectionConfig.CacheMissThreshold { + return NewIWantCacheMissThresholdErr(cacheMisses, float64(sampleSize), c.config.IWantRPCInspectionConfig.CacheMissThreshold) } return nil From 9bdecc94d46f850e03634c793d3ae04f49a2c0ab Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 16 Aug 2023 09:43:10 -0700 Subject: [PATCH 496/815] Update network/internal/testutils/testUtil.go Co-authored-by: Misha --- network/internal/testutils/testUtil.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 8e4d457e0e9..fb07f87b7cc 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -116,9 +116,9 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // If you want to create a standalone LibP2PNode without network and middleware components, please use p2ptest.NodeFixture. // Args: // -// t: testing.T- the test object -// sporkId: flow.Identifier - the spork id to use for the nodes -// n: int - number of nodes to create +// t: testing.T- the test object +// sporkId: flow.Identifier - the spork id to use for the nodes +// n: int - number of nodes to create // // opts: []p2ptest.NodeFixtureParameterOption - options to configure the nodes // Returns: From c5870f7dc1232772536cf89aaf097f2a0b5df921 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 11:45:21 -0700 Subject: [PATCH 497/815] fixes tests --- .../validation_inspector_test.go | 15 ++++++-- .../test/gossipsub/scoring/ihave_spam_test.go | 15 ++++++-- .../test/gossipsub/scoring/scoring_test.go | 31 ++++++++++++---- .../p2p/connection/connection_gater_test.go | 36 ++++++++++++++----- network/p2p/scoring/scoring_test.go | 15 ++++++-- network/p2p/test/fixtures.go | 33 +++++++++++++---- 6 files changed, 113 insertions(+), 32 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 3dd873d0e7c..8d256bde9ba 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -1228,9 +1228,18 @@ func TestGossipSubSpamMitigationIntegration(t *testing.T) { // now we expect the detection and mitigation to kick in and the victim node to disconnect from the spammer node. // so the spammer and victim nodes should not be able to exchange messages on the topic. - p2ptest.EnsureNoPubsubExchangeBetweenGroups(t, ctx, []p2p.LibP2PNode{victimNode}, []p2p.LibP2PNode{spammer.SpammerNode}, blockTopic, 1, func() interface{} { - return unittest.ProposalFixture() - }) + p2ptest.EnsureNoPubsubExchangeBetweenGroups( + t, + ctx, + []p2p.LibP2PNode{victimNode}, + flow.IdentifierList{victimId.NodeID}, + []p2p.LibP2PNode{spammer.SpammerNode}, + flow.IdentifierList{spammer.SpammerId.NodeID}, + blockTopic, + 1, + func() interface{} { + return unittest.ProposalFixture() + }) } // mockDistributorReadyDoneAware mocks the Ready and Done methods of the distributor to return a channel that is already closed, diff --git a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go index 3342b3ac4dc..d4a1187aca0 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go @@ -302,9 +302,18 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { require.Lessf(t, spammerScore, scoring.DefaultGraylistThreshold, "sanity check failed, the score of the spammer node must be less than graylist threshold: %f, actual: %f", scoring.DefaultGraylistThreshold, spammerScore) // since the spammer score is below the gossip, graylist and publish thresholds, it should not be able to exchange messages with victim anymore. - p2ptest.EnsureNoPubsubExchangeBetweenGroups(t, ctx, []p2p.LibP2PNode{spammer.SpammerNode}, []p2p.LibP2PNode{victimNode}, blockTopic, 1, func() interface{} { - return unittest.ProposalFixture() - }) + p2ptest.EnsureNoPubsubExchangeBetweenGroups( + t, + ctx, + []p2p.LibP2PNode{spammer.SpammerNode}, + flow.IdentifierList{spammer.SpammerId.NodeID}, + []p2p.LibP2PNode{victimNode}, + flow.IdentifierList{victimIdentity.NodeID}, + blockTopic, + 1, + func() interface{} { + return unittest.ProposalFixture() + }) } // spamIHaveBrokenPromises is a test utility function that is exclusive for the TestGossipSubIHaveBrokenPromises tests. diff --git a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go index d8baf4be735..2366678576e 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go @@ -173,9 +173,18 @@ func testGossipSubInvalidMessageDeliveryScoring(t *testing.T, spamMsgFactory fun // ensure that the topic snapshot of the spammer contains a record of at least (60%) of the spam messages sent. The 60% is to account for the messages that were delivered before the score was updated, after the spammer is PRUNED, as well as to account for decay. require.True(t, blkTopicSnapshot.InvalidMessageDeliveries > 0.6*float64(totalSpamMessages), "invalid message deliveries must be greater than %f. invalid message deliveries: %f", 0.9*float64(totalSpamMessages), blkTopicSnapshot.InvalidMessageDeliveries) - p2ptest.EnsureNoPubsubExchangeBetweenGroups(t, ctx, []p2p.LibP2PNode{victimNode}, []p2p.LibP2PNode{spammer.SpammerNode}, blockTopic, 1, func() interface{} { - return unittest.ProposalFixture() - }) + p2ptest.EnsureNoPubsubExchangeBetweenGroups( + t, + ctx, + []p2p.LibP2PNode{victimNode}, + flow.IdentifierList{victimIdentity.NodeID}, + []p2p.LibP2PNode{spammer.SpammerNode}, + flow.IdentifierList{spammer.SpammerId.NodeID}, + blockTopic, + 1, + func() interface{} { + return unittest.ProposalFixture() + }) } // TestGossipSubMeshDeliveryScoring_UnderDelivery_SingleTopic tests that when a peer is under-performing in a topic mesh, its score is (slightly) penalized. @@ -500,10 +509,18 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { // now the replaying node acts maliciously and just replays the same messages again. i = -1 - p2ptest.EnsureNoPubsubMessageExchange(t, ctx, []p2p.LibP2PNode{replayingNode}, []p2p.LibP2PNode{thisNode}, blockTopic, len(proposalList), func() interface{} { - i += 1 - return proposalList[i] - }) + p2ptest.EnsureNoPubsubMessageExchange( + t, + ctx, + []p2p.LibP2PNode{replayingNode}, + []p2p.LibP2PNode{thisNode}, + flow.IdentifierList{thisId.NodeID}, + blockTopic, + len(proposalList), + func() interface{} { + i += 1 + return proposalList[i] + }) // since the last decay interval, the replaying node has not delivered anything new, so its score should be penalized for under-performing. require.Eventually(t, func() bool { diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index cd240b5293b..672947adadc 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -235,6 +235,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { count := 5 nodes := make([]p2p.LibP2PNode, 0, count) inbounds := make([]chan string, 0, count) + identities := make(flow.IdentityList, 0, count) disallowedPeerIds := unittest.NewProtectedMap[peer.ID, struct{}]() allPeerIds := make(peer.IDSlice, 0, count) @@ -266,6 +267,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { p2ptest.WithConnectionGater(connectionGater)) idProvider.On("ByPeerID", node.Host().ID()).Return(&id, true).Maybe() nodes = append(nodes, node) + identities = append(identities, &id) allPeerIds = append(allPeerIds, node.Host().ID()) inbounds = append(inbounds, inbound) } @@ -301,7 +303,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { require.False(t, disallowedPeerIds.Has(remote)) }).Return(true, control.DisconnectReason(0)) - ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:1], nodes[1:]) + ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:1], identities[:1].NodeIDs(), nodes[1:], identities[1:].NodeIDs()) ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes[1:], inbounds[1:]) } @@ -380,28 +382,44 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { // let peer manager prune the connections to the disallow-listed node. time.Sleep(1 * time.Second) // ensures no connection, unicast, or pubsub going to or coming from the disallow-listed node. - ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-1], nodes[count-1:]) + ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-1], ids[:count-1].NodeIDs(), nodes[count-1:], ids[count-1:].NodeIDs()) // now we add another node (the second last node) to the disallowed list. disallowedList.Add(ids[len(ids)-2], struct{}{}) // let peer manager prune the connections to the disallow-listed node. time.Sleep(1 * time.Second) // ensures no connection, unicast, or pubsub going to and coming from the disallow-listed nodes. - ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-2], nodes[count-2:]) + ensureCommunicationSilenceAmongGroups(t, ctx, sporkId, nodes[:count-2], ids[:count-2].NodeIDs(), nodes[count-2:], ids[count-2:].NodeIDs()) // ensures that all nodes are other non-disallow-listed nodes can exchange messages over the pubsub and unicast. ensureCommunicationOverAllProtocols(t, ctx, sporkId, nodes[:count-2], inbounds[:count-2]) } // ensureCommunicationSilenceAmongGroups ensures no connection, unicast, or pubsub going to or coming from between the two groups of nodes. -func ensureCommunicationSilenceAmongGroups(t *testing.T, ctx context.Context, sporkId flow.Identifier, groupA []p2p.LibP2PNode, groupB []p2p.LibP2PNode) { +func ensureCommunicationSilenceAmongGroups( + t *testing.T, + ctx context.Context, + sporkId flow.Identifier, + groupANodes []p2p.LibP2PNode, + groupAIdentifiers flow.IdentifierList, + groupBNodes []p2p.LibP2PNode, + groupBIdentifiers flow.IdentifierList) { // ensures no connection, unicast, or pubsub going to the disallow-listed nodes - p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, groupA, groupB) + p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, groupANodes, groupBNodes) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) - p2ptest.EnsureNoPubsubExchangeBetweenGroups(t, ctx, groupA, groupB, blockTopic, 1, func() interface{} { - return unittest.ProposalFixture() - }) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(t, ctx, groupA, groupB) + p2ptest.EnsureNoPubsubExchangeBetweenGroups( + t, + ctx, + groupANodes, + groupAIdentifiers, + groupBNodes, + groupBIdentifiers, + blockTopic, + 1, + func() interface{} { + return unittest.ProposalFixture() + }) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(t, ctx, groupANodes, groupBNodes) } // ensureCommunicationOverAllProtocols ensures that all nodes are connected to each other, and they can exchange messages over the pubsub and unicast. diff --git a/network/p2p/scoring/scoring_test.go b/network/p2p/scoring/scoring_test.go index 47e6f27cb57..9aa6644005c 100644 --- a/network/p2p/scoring/scoring_test.go +++ b/network/p2p/scoring/scoring_test.go @@ -145,7 +145,16 @@ func TestInvalidCtrlMsgScoringIntegration(t *testing.T) { } // checks no GossipSub message exchange should no longer happen between node1 and node2. - p2ptest.EnsureNoPubsubExchangeBetweenGroups(t, ctx, []p2p.LibP2PNode{node1}, []p2p.LibP2PNode{node2}, blockTopic, 1, func() interface{} { - return unittest.ProposalFixture() - }) + p2ptest.EnsureNoPubsubExchangeBetweenGroups( + t, + ctx, + []p2p.LibP2PNode{node1}, + flow.IdentifierList{id1.NodeID}, + []p2p.LibP2PNode{node2}, + flow.IdentifierList{id2.NodeID}, + blockTopic, + 1, + func() interface{} { + return unittest.ProposalFixture() + }) } diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 5be17d248eb..598bbe2693d 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -684,7 +684,15 @@ func EnsureNotConnectedBetweenGroups(t *testing.T, ctx context.Context, groupA [ // - topic: the topic to exchange messages on. // - count: the number of messages to exchange from each node. // - messageFactory: a function that creates a unique message to be published by the node. -func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p.LibP2PNode, to []p2p.LibP2PNode, topic channels.Topic, count int, messageFactory func() interface{}) { +func EnsureNoPubsubMessageExchange( + t *testing.T, + ctx context.Context, + from []p2p.LibP2PNode, + to []p2p.LibP2PNode, + toIdentifiers flow.IdentifierList, + topic channels.Topic, + count int, + messageFactory func() interface{}) { subs := make([]p2p.Subscription, len(to)) tv := validator.TopicValidator( unittest.Logger(), @@ -717,7 +725,7 @@ func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p payload := messageFactory() outgoingMessageScope, err := flownet.NewOutgoingScope( - flow.IdentifierList{unittest.IdentifierFixture()}, + toIdentifiers, channel, payload, unittest.NetworkCodec().Encode, @@ -742,16 +750,27 @@ func EnsureNoPubsubMessageExchange(t *testing.T, ctx context.Context, from []p2p // Args: // - t: *testing.T instance // - ctx: context.Context instance -// - groupA: first group of nodes- no message should be exchanged from any node of this group to the other group. -// - groupB: second group of nodes- no message should be exchanged from any node of this group to the other group. +// - groupANodes: first group of nodes- no message should be exchanged from any node of this group to the other group. +// - groupAIdentifiers: identifiers of the nodes in the first group. +// - groupBNodes: second group of nodes- no message should be exchanged from any node of this group to the other group. +// - groupBIdentifiers: identifiers of the nodes in the second group. // - topic: pubsub topic- no message should be exchanged on this topic. // - count: number of messages to be exchanged- no message should be exchanged. // - messageFactory: function to create a unique message to be published by the node. -func EnsureNoPubsubExchangeBetweenGroups(t *testing.T, ctx context.Context, groupA []p2p.LibP2PNode, groupB []p2p.LibP2PNode, topic channels.Topic, count int, messageFactory func() interface{}) { +func EnsureNoPubsubExchangeBetweenGroups( + t *testing.T, + ctx context.Context, + groupANodes []p2p.LibP2PNode, + groupAIdentifiers flow.IdentifierList, + groupBNodes []p2p.LibP2PNode, + groupBIdentifiers flow.IdentifierList, + topic channels.Topic, + count int, + messageFactory func() interface{}) { // ensure no message exchange from group A to group B - EnsureNoPubsubMessageExchange(t, ctx, groupA, groupB, topic, count, messageFactory) + EnsureNoPubsubMessageExchange(t, ctx, groupANodes, groupBNodes, groupBIdentifiers, topic, count, messageFactory) // ensure no message exchange from group B to group A - EnsureNoPubsubMessageExchange(t, ctx, groupB, groupA, topic, count, messageFactory) + EnsureNoPubsubMessageExchange(t, ctx, groupBNodes, groupANodes, groupAIdentifiers, topic, count, messageFactory) } // PeerIdSliceFixture returns a slice of random peer IDs for testing. From 2de77ca0241b24016e43178e9ecf08b5be5e5fff Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 11:54:36 -0700 Subject: [PATCH 498/815] updates mocks --- network/mocknetwork/middleware.go | 26 -------------------------- network/p2p/mock/create_node_func.go | 12 +++++++----- network/p2p/mock/lib_p2_p_node.go | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index 18cdaed21b0..a61deb80268 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -6,8 +6,6 @@ import ( datastore "github.com/ipfs/go-datastore" channels "github.com/onflow/flow-go/network/channels" - flow "github.com/onflow/flow-go/model/flow" - irrecoverable "github.com/onflow/flow-go/module/irrecoverable" mock "github.com/stretchr/testify/mock" @@ -38,30 +36,6 @@ func (_m *Middleware) Done() <-chan struct{} { return r0 } -// IsConnected provides a mock function with given fields: nodeID -func (_m *Middleware) IsConnected(nodeID flow.Identifier) (bool, error) { - ret := _m.Called(nodeID) - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(flow.Identifier) (bool, error)); ok { - return rf(nodeID) - } - if rf, ok := ret.Get(0).(func(flow.Identifier) bool); ok { - r0 = rf(nodeID) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { - r1 = rf(nodeID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // NewBlobService provides a mock function with given fields: channel, store, opts func (_m *Middleware) NewBlobService(channel channels.Channel, store datastore.Batching, opts ...network.BlobServiceOption) network.BlobService { _va := make([]interface{}, len(opts)) diff --git a/network/p2p/mock/create_node_func.go b/network/p2p/mock/create_node_func.go index 1a57772cbeb..b2db19f13ee 100644 --- a/network/p2p/mock/create_node_func.go +++ b/network/p2p/mock/create_node_func.go @@ -4,6 +4,8 @@ package mockp2p import ( host "github.com/libp2p/go-libp2p/core/host" + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" p2p "github.com/onflow/flow-go/network/p2p" @@ -16,13 +18,13 @@ type CreateNodeFunc struct { mock.Mock } -// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 -func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 host.Host, _a2 p2p.ProtocolPeerCache, _a3 p2p.PeerManager, _a4 *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - ret := _m.Called(_a0, _a1, _a2, _a3, _a4) +// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4, _a5 +func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 flow.Identifier, _a2 host.Host, _a3 p2p.ProtocolPeerCache, _a4 p2p.PeerManager, _a5 *p2p.DisallowListCacheConfig) p2p.LibP2PNode { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4, _a5) var r0 p2p.LibP2PNode - if rf, ok := ret.Get(0).(func(zerolog.Logger, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager, *p2p.DisallowListCacheConfig) p2p.LibP2PNode); ok { - r0 = rf(_a0, _a1, _a2, _a3, _a4) + if rf, ok := ret.Get(0).(func(zerolog.Logger, flow.Identifier, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager, *p2p.DisallowListCacheConfig) p2p.LibP2PNode); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4, _a5) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(p2p.LibP2PNode) diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 5813983110e..e67614d6212 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -284,13 +284,13 @@ func (_m *LibP2PNode) PeerScoreExposer() p2p.PeerScoreExposer { return r0 } -// Publish provides a mock function with given fields: ctx, topic, data -func (_m *LibP2PNode) Publish(ctx context.Context, topic channels.Topic, data []byte) error { - ret := _m.Called(ctx, topic, data) +// Publish provides a mock function with given fields: ctx, msgScope +func (_m *LibP2PNode) Publish(ctx context.Context, msgScope *flow_gonetwork.OutgoingMessageScope) error { + ret := _m.Called(ctx, msgScope) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, channels.Topic, []byte) error); ok { - r0 = rf(ctx, topic, data) + if rf, ok := ret.Get(0).(func(context.Context, *flow_gonetwork.OutgoingMessageScope) error); ok { + r0 = rf(ctx, msgScope) } else { r0 = ret.Error(0) } @@ -430,13 +430,13 @@ func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator p2p.TopicVa return r0, r1 } -// UnSubscribe provides a mock function with given fields: topic -func (_m *LibP2PNode) UnSubscribe(topic channels.Topic) error { - ret := _m.Called(topic) +// Unsubscribe provides a mock function with given fields: channel +func (_m *LibP2PNode) Unsubscribe(channel channels.Channel) error { + ret := _m.Called(channel) var r0 error - if rf, ok := ret.Get(0).(func(channels.Topic) error); ok { - r0 = rf(topic) + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) } else { r0 = ret.Error(0) } From e47a3e04fb2f675fb65a5e0a50b9c767898329d0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 12:51:23 -0700 Subject: [PATCH 499/815] increases timeout for disallowlisting test --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 09def778e51..8b0d925f642 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -278,7 +278,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "not all misbehavior reports have been processed") // ensures that the spammer is disallow-listed by the victim - p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 2*time.Second) + p2ptest.RequireEventuallyNotConnected(t, []p2p.LibP2PNode{nodes[victimIndex]}, []p2p.LibP2PNode{nodes[spammerIndex]}, 100*time.Millisecond, 5*time.Second) // despite disallow-listing spammer, ensure that (victim and honest) and (honest and spammer) are still connected. p2ptest.RequireConnectedEventually(t, []p2p.LibP2PNode{nodes[spammerIndex], nodes[honestIndex]}, 1*time.Millisecond, 100*time.Millisecond) From 43c72f5b5e3182f7904b3bb9af8323bd4286466f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 13:15:19 -0700 Subject: [PATCH 500/815] fixes all scoring tests --- network/p2p/scoring/app_score_test.go | 38 ++++++++++--- .../scoring/subscription_validator_test.go | 56 ++++++++++++++----- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 8e2a1ae1bb8..37cfd52a6b5 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -13,8 +13,10 @@ import ( "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/mock" + flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/scoring" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -93,14 +95,22 @@ func TestFullGossipSubConnectivity(t *testing.T) { // checks end-to-end message delivery works // each node sends a distinct message to all and checks that all nodes receive it. for _, node := range nodes { - proposalMsg := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) - require.NoError(t, node.Publish(ctx, blockTopic, proposalMsg)) + outgoingMessageScope, err := flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.PushBlocks, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, node.Publish(ctx, outgoingMessageScope)) // checks that the message is received by all nodes. ctx1s, cancel1s := context.WithTimeout(ctx, 5*time.Second) - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, groupOneSubs) - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, accessNodeSubs) - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, groupTwoSubs) + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, expectedReceivedData, groupOneSubs) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, expectedReceivedData, accessNodeSubs) + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, expectedReceivedData, groupTwoSubs) cancel1s() } @@ -169,7 +179,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS ) allNodes := append([]p2p.LibP2PNode{con1Node, con2Node}, accessNodeGroup...) - allIds := append([]*flow.Identity{&con1Id, &con2Id}, accessNodeIds...) + allIds := append(flow.IdentityList{&con1Id, &con2Id}, accessNodeIds...) provider := id.NewFixedIdentityProvider(allIds) idProvider.On("ByPeerID", mocktestify.Anything).Return( @@ -206,8 +216,14 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // let nodes reside on a full topology, hence no partition is caused by the topology. p2ptest.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) - proposalMsg := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) - require.NoError(t, con1Node.Publish(ctx, blockTopic, proposalMsg)) + outgoingMessageScope, err := flownet.NewOutgoingScope( + allIds.NodeIDs(), + channels.PushBlocks, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, con1Node.Publish(ctx, outgoingMessageScope)) // we check that whether within a one-second window the message is received by the other honest consensus node. // the one-second window is important because it triggers the heartbeat of the con1Node to perform a lazy pull (iHave). @@ -218,7 +234,11 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // If honest peer scoring is enabled, then con1Node and con2Node are certainly in the same mesh, and hence the message is delivered. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, con2Sub) + + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + + return p2pfixtures.HasSubReceivedMessage(t, ctx1s, expectedReceivedData, con2Sub) } // maliciousAppSpecificScore returns a malicious app specific penalty function that rewards the malicious node and diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 549006b3bde..795165ac9cc 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + flownet "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" @@ -130,8 +132,8 @@ func TestSubscriptionValidator_InvalidSubscriptions(t *testing.T) { for _, role := range flow.Roles() { peerId := p2pfixtures.PeerIdFixture(t) unauthorizedChannels := channels.Channels(). // all channels - ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role - ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. + ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role + ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. sporkID := unittest.IdentifierFixture() unauthorizedTopics := make([]string, 0, len(unauthorizedChannels)) for _, channel := range unauthorizedChannels { @@ -237,14 +239,23 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // let the subscriptions be established time.Sleep(2 * time.Second) - proposalMsg := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) - // consensus node publishes a proposal - require.NoError(t, conNode.Publish(ctx, blockTopic, proposalMsg)) + outgoingMessageScope, err := flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.PushBlocks, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, conNode.Publish(ctx, outgoingMessageScope)) // checks that the message is received by all nodes. ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, proposalMsg, []p2p.Subscription{conSub, ver1SubBlocks, ver2SubBlocks}) + + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, expectedReceivedData, []p2p.Subscription{conSub, ver1SubBlocks, ver2SubBlocks}) // now consensus node is doing something very bad! // it is subscribing to a channel that it is not supposed to subscribe to. @@ -257,9 +268,14 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // consensus node publishes another proposal, but this time, it should not reach verification node. // since upon an unauthorized subscription, verification node should have slashed consensus node on // the GossipSub scoring protocol. - proposalMsg = p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) - // publishes a message to the topic. - require.NoError(t, conNode.Publish(ctx, blockTopic, proposalMsg)) + outgoingMessageScope, err = flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.PushBlocks, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, conNode.Publish(ctx, outgoingMessageScope)) ctx5s, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() @@ -267,15 +283,25 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // moreover, a verification node publishing a message to the request chunk topic should not reach consensus node. // however, both verification nodes should receive the message. - chunkDataPackRequestMsg := p2pfixtures.MustEncodeEvent(t, &messages.ChunkDataRequest{ - ChunkID: unittest.IdentifierFixture(), - Nonce: rand.Uint64(), - }, channels.RequestChunks) - require.NoError(t, verNode1.Publish(ctx, channels.TopicFromChannel(channels.RequestChunks, sporkId), chunkDataPackRequestMsg)) + outgoingMessageScope, err = flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.RequestChunks, + &messages.ChunkDataRequest{ + ChunkID: unittest.IdentifierFixture(), + Nonce: rand.Uint64(), + }, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + require.NoError(t, verNode1.Publish(ctx, outgoingMessageScope)) ctx1s, cancel1s = context.WithTimeout(ctx, 1*time.Second) defer cancel1s() - p2pfixtures.SubsMustReceiveMessage(t, ctx1s, chunkDataPackRequestMsg, []p2p.Subscription{ver1SubChunks, ver2SubChunks}) + + expectedReceivedData, err = outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + + p2pfixtures.SubsMustReceiveMessage(t, ctx1s, expectedReceivedData, []p2p.Subscription{ver1SubChunks, ver2SubChunks}) ctx5s, cancel5s = context.WithTimeout(ctx, 5*time.Second) defer cancel5s() From fd68f3ce168b6d84622a328612fd29feaeb33049 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 13:21:45 -0700 Subject: [PATCH 501/815] fixes tracer tests --- network/p2p/tracer/gossipSubMeshTracer_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index 407d03fb50d..4384ffcf9f1 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -39,8 +39,10 @@ func TestGossipSubMeshTracer(t *testing.T) { idProvider := mockmodule.NewIdentityProvider(t) defer cancel() - topic1 := channels.TopicFromChannel(channels.PushBlocks, sporkId) - topic2 := channels.TopicFromChannel(channels.PushReceipts, sporkId) + channel1 := channels.PushBlocks + topic1 := channels.TopicFromChannel(channel1, sporkId) + channel2 := channels.PushReceipts + topic2 := channels.TopicFromChannel(channel2, sporkId) loggerCycle := atomic.NewInt32(0) warnLoggerCycle := atomic.NewInt32(0) @@ -176,9 +178,9 @@ func TestGossipSubMeshTracer(t *testing.T) { // all nodes except the tracerNode unsubscribe from the topic1, which triggers sending a PRUNE to the tracerNode for each unsubscription. // We expect the tracerNode to remove the otherNode1, otherNode2, and unknownNode from its mesh. - require.NoError(t, otherNode1.Unsubscribe(topic1)) - require.NoError(t, otherNode2.Unsubscribe(topic1)) - require.NoError(t, unknownNode.Unsubscribe(topic1)) + require.NoError(t, otherNode1.Unsubscribe(channel1)) + require.NoError(t, otherNode2.Unsubscribe(channel1)) + require.NoError(t, unknownNode.Unsubscribe(channel1)) assert.Eventually(t, func() bool { // eventually, the tracerNode should not have the other node in its mesh for topic1. From 6c65145af5420331d547527dfa128b4a04970b4c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 13:25:58 -0700 Subject: [PATCH 502/815] fixes subscription package tests --- .../subscription/subscription_filter_test.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 0c3d1d8b88c..fdb098ea012 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -14,8 +14,10 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" + flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/subscription" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -78,16 +80,26 @@ func TestFilterSubscribe(t *testing.T) { wg.Add(2) testPublish := func(wg *sync.WaitGroup, from p2p.LibP2PNode, sub p2p.Subscription) { - data := []byte("hello") - err := from.Publish(context.TODO(), badTopic, data) + outgoingMessageScope, err := flownet.NewOutgoingScope( + ids.NodeIDs(), + channels.SyncCommittee, + []byte("hello"), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + + err = from.Publish(context.TODO(), outgoingMessageScope) require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), time.Second) msg, err := sub.Next(ctx) cancel() require.NoError(t, err) - require.Equal(t, msg.Data, data) + + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() + require.NoError(t, err) + require.Equal(t, msg.Data, expectedReceivedData) ctx, cancel = context.WithTimeout(context.Background(), time.Second) _, err = unstakedSub.Next(ctx) From 2e6c88a25e9793d9fad1da1a1014af4308ea9fbd Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 16 Aug 2023 17:23:45 -0400 Subject: [PATCH 503/815] add duplicate messageID and cache miss threshold tests --- insecure/corruptlibp2p/fixtures.go | 10 +- .../rpc_inspector/metrics_inspector_test.go | 2 +- .../test/gossipsub/rpc_inspector/utils.go | 5 +- .../validation_inspector_test.go | 210 +++++++++++++++++- .../control_message_validation_inspector.go | 17 +- .../p2p/tracer/internal/rpc_sent_tracker.go | 15 +- 6 files changed, 230 insertions(+), 29 deletions(-) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index 27a8e0aa2ba..cb40c667da2 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -42,13 +42,13 @@ func WithIHave(msgCount, msgSize int, topicId string) GossipSubCtrlOption { } } -// WithIWant adds iWant control messages of the given size and number to the control message. -func WithIWant(msgCount, msgSize int) GossipSubCtrlOption { +// WithIWant adds n number of iWants each having m number of message ID's of size s. +func WithIWant(n, m int) GossipSubCtrlOption { return func(msg *pubsubpb.ControlMessage) { - iWants := make([]*pubsubpb.ControlIWant, msgCount) - for i := 0; i < msgCount; i++ { + iWants := make([]*pubsubpb.ControlIWant, n) + for i := 0; i < n; i++ { iWants[i] = &pubsubpb.ControlIWant{ - MessageIDs: GossipSubMessageIdsFixture(msgSize), + MessageIDs: GossipSubMessageIdsFixture(m), } } msg.Iwant = iWants diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go index c62cfba2f8b..4b36a2f4c88 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go @@ -71,7 +71,7 @@ func TestMetricsInspector_ObserveRPC(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, metricsInspector) + defer stopTestComponents(t, cancel, nodes, metricsInspector) // prepare to spam - generate control messages ctlMsgs := spammer.GenerateCtlMessages(controlMessageCount, corruptlibp2p.WithGraft(messageCount, channels.PushBlocks.String()), diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go index 2307c57f0ab..d10a036ab12 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go @@ -6,6 +6,7 @@ import ( "time" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p" @@ -26,7 +27,7 @@ func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContex }) } -func stopNodesAndInspector(t *testing.T, cancel context.CancelFunc, nodes []p2p.LibP2PNode, inspector p2p.GossipSubRPCInspector) { +func stopTestComponents(t *testing.T, cancel context.CancelFunc, nodes []p2p.LibP2PNode, components ...module.ReadyDoneAware) { p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) - unittest.RequireComponentsDoneBefore(t, time.Second, inspector) + unittest.RequireComponentsDoneBefore(t, time.Second, components...) } diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index ff46591a5f5..0926309ad21 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" @@ -115,7 +116,7 @@ func TestValidationInspector_SafetyThreshold(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages ctlMsgs := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(messageCount, channels.PushBlocks.String()), @@ -214,7 +215,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages graftCtlMsgs := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(messageCount, channels.PushBlocks.String())) @@ -313,7 +314,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // add an unknown topic to each of our ihave control messages, this will ensure // that whatever random sample of topic ids that are inspected cause validation @@ -411,7 +412,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // the first time we spam this message it will be processed completely so we need to ensure // all topics are valid and no duplicates exists. @@ -552,7 +553,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages graftCtlMsgsWithUnknownTopic := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(int(messageCount), unknownTopic.String())) @@ -684,7 +685,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages graftCtlMsgsDuplicateTopic := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(int(messageCount), duplicateTopic.String())) @@ -795,7 +796,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages graftCtlMsgsDuplicateTopic := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(int(messageCount), unknownClusterID.String())) @@ -892,7 +893,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // generate multiple control messages with GRAFT's for randomly generated // cluster prefixed channels, this ensures we do not encounter duplicate topic ID errors ctlMsgs := spammer.GenerateCtlMessages(int(controlMessageCount), @@ -987,7 +988,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // generate multiple control messages with GRAFT's for randomly generated // cluster prefixed channels, this ensures we do not encounter duplicate topic ID errors ctlMsgs := spammer.GenerateCtlMessages(int(controlMessageCount), @@ -1096,7 +1097,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) spammer.Start(t) - defer stopNodesAndInspector(t, cancel, nodes, validationInspector) + defer stopTestComponents(t, cancel, nodes, validationInspector) // prepare to spam - generate control messages graftCtlMsgsDuplicateTopic := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithGraft(int(messageCount), clusterIDTopic.String())) @@ -1112,6 +1113,195 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { require.Equal(t, uint64(1), invPruneNotifCount.Load()) } +// TestValidationInspector_InspectIWants_DuplicateMessageIDs ensures that expected invalid control message notification is disseminated when iWant control messages containing +// duplicate messages Ids are encountered during validation. +func TestValidationInspector_InspectIWants_DuplicateMessageIDs(t *testing.T) { + t.Parallel() + role := flow.RoleConsensus + sporkID := unittest.IdentifierFixture() + // create our RPC validation inspector + flowConfig, err := config.DefaultConfig() + require.NoError(t, err) + inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs + inspectorConfig.NumberOfWorkers = 1 + + messageCount := 2 + controlMessageCount := int64(1) + duplicateMessageIDNotifCount := atomic.NewUint64(0) + done := make(chan struct{}) + // ensure expected notifications are disseminated with expected error + inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + return func(args mockery.Arguments) { + notification, ok := args[0].(*p2p.InvCtrlMsgNotif) + require.True(t, ok) + require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, uint64(messageCount), notification.Count) + require.True(t, validation.IsDuplicateFoundErr(notification.Err)) + duplicateMessageIDNotifCount.Inc() + if duplicateMessageIDNotifCount.Load() == 2 { + close(done) + } + } + } + + idProvider := mock.NewIdentityProvider(t) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) + mockDistributorReadyDoneAware(distributor) + withExpectedNotificationDissemination(2, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := mockp2p.NewPubSubTracer(t) + validationInspector, err := validation.NewControlMsgValidationInspector( + unittest.Logger(), + sporkID, + &inspectorConfig, + distributor, + metrics.NewNoopCollector(), + metrics.NewNoopCollector(), + idProvider, + metrics.NewNoopCollector(), + meshTracer) + require.NoError(t, err) + corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) + victimNode, victimIdentity := p2ptest.NodeFixture( + t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), + ) + idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + + duplicateMessageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) + // create iWants control messages where the first 2 iwants contain a duplicate message ID "duplicate" + iWantFactory := func() []pb.ControlMessage { + ctlWithiWantDuplicateMsgIDs := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) + // add some non duplicates to our duplicate message IDs + ctlWithiWantDuplicateMsgIDs[0].Iwant[0].MessageIDs = append(duplicateMessageIDs, corruptlibp2p.GossipSubMessageIdsFixture(5)...) + ctlWithiWantDuplicateMsgIDs[0].Iwant[1].MessageIDs = append(duplicateMessageIDs, corruptlibp2p.GossipSubMessageIdsFixture(5)...) + return ctlWithiWantDuplicateMsgIDs + } + + // avoid cache miss threshold errors + meshTracer.On("WasIHaveRPCSent", mockery.AnythingOfType("string")).Return(true).Maybe() + meshTracer.On("LastHighestIHaveRPCSize").Return(int64(100)) + + validationInspector.Start(signalerCtx) + nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} + startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) + spammer.Start(t) + defer stopTestComponents(t, cancel, nodes, validationInspector) + + // spam the victim with 2 iWant control messages that contain duplicate message IDs, we expect to observe 2 invalid control message notifications disseminated + spammer.SpamControlMessage(t, victimNode, iWantFactory()) + spammer.SpamControlMessage(t, victimNode, iWantFactory()) + + unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +} + +// TestValidationInspector_InspectIWants_CacheMissThreshold ensures that expected invalid control message notification is disseminated when the number of iWant message Ids +// without a corresponding iHave message sent with the same message ID exceeds the configured cache miss threshold. +func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { + t.Parallel() + role := flow.RoleConsensus + sporkID := unittest.IdentifierFixture() + // create our RPC validation inspector + flowConfig, err := config.DefaultConfig() + require.NoError(t, err) + inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs + inspectorConfig.NumberOfWorkers = 1 + inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = .5 // set cache miss threshold to 50% + messageCount := 1 + controlMessageCount := int64(1) + cacheMissThresholdNotifCount := atomic.NewUint64(0) + done := make(chan struct{}) + // ensure expected notifications are disseminated with expected error + inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + return func(args mockery.Arguments) { + notification, ok := args[0].(*p2p.InvCtrlMsgNotif) + require.True(t, ok) + require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, uint64(messageCount), notification.Count) + require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) + cacheMissThresholdNotifCount.Inc() + if cacheMissThresholdNotifCount.Load() == 1 { + close(done) + } + } + } + + idProvider := mock.NewIdentityProvider(t) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) + mockDistributorReadyDoneAware(distributor) + withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) + validationInspector, err := validation.NewControlMsgValidationInspector( + unittest.Logger(), + sporkID, + &inspectorConfig, + distributor, + metrics.NewNoopCollector(), + metrics.NewNoopCollector(), + idProvider, + metrics.NewNoopCollector(), + meshTracer) + require.NoError(t, err) + corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) + victimNode, victimIdentity := p2ptest.NodeFixture( + t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), + ) + idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + + messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) + + // create control message with iWant that contains 5 message IDs that were not tracked + ctlWithIWants := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) + ctlWithIWants[0].Iwant[0].MessageIDs = messageIDs // the first 5 message ids will not have a corresponding iHave + + // create control message with iHave that contains only the last 4 message IDs, this will force cache misses for the other 6 message IDs + ctlWithIhaves := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIHave(messageCount, messageCount, channels.PushBlocks.String())) + ctlWithIhaves[0].Ihave[0].MessageIDs = messageIDs[6:] + + validationInspector.Start(signalerCtx) + nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} + startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) + spammer.Start(t) + meshTracer.Start(signalerCtx) + defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) + + // simulate tracking some message IDs + meshTracer.SendRPC(&pubsub.RPC{ + RPC: pb.RPC{ + Control: &ctlWithIhaves[0], + }, + }, "") + + // spam the victim with iWant message that contains message IDs that do not have a corresponding iHave + spammer.SpamControlMessage(t, victimNode, ctlWithIWants) + + unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +} + func randomClusterPrefixedTopic() channels.Topic { return channels.Topic(channels.SyncCluster(flow.ChainID(fmt.Sprintf("%d", rand.Uint64())))) } diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index d40b734bb5e..aa0a7aead81 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -211,6 +211,9 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e // - IWantCacheMissThresholdErr: if the rate of cache misses exceeds the configured allowed threshold. // - error: if any error occurs while sampling the iWants func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.ControlIWant) error { + if len(iWants) == 0 { + return nil + } sampleSize := uint(10 * c.rpcTracker.LastHighestIHaveRPCSize()) if sampleSize == 0 || sampleSize > c.config.IWantRPCInspectionConfig.MaxSampleSize { c.logger.Warn(). @@ -222,11 +225,6 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize } - numOfIWants := uint(len(iWants)) - if numOfIWants < sampleSize { - sampleSize = numOfIWants - } - var iWantMsgIDPool []string // opens all iWant boxes into a sample pool to be sampled. for _, iWant := range iWants { @@ -236,18 +234,21 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control iWantMsgIDPool = append(iWantMsgIDPool, iWant.GetMessageIDs()...) } + if sampleSize > uint(len(iWantMsgIDPool)) { + sampleSize = uint(len(iWantMsgIDPool)) + } + swap := func(i, j uint) { iWantMsgIDPool[i], iWantMsgIDPool[j] = iWantMsgIDPool[j], iWantMsgIDPool[i] } - err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIWant, numOfIWants, sampleSize, swap) + err := c.sampleCtrlMessages(p2pmsg.CtrlMsgIWant, uint(len(iWantMsgIDPool)), sampleSize, swap) if err != nil { c.logger.Fatal().Err(fmt.Errorf("failed to sample iwant messages: %w", err)).Msg("irrecoverable error encountered while sampling iwant control messages") } tracker := make(duplicateStrTracker) cacheMisses := float64(0) - for _, messageID := range iWantMsgIDPool[:sampleSize] { if tracker.isDuplicate(messageID) { return NewDuplicateFoundErr(fmt.Errorf("duplicate message ID found: %s", messageID)) @@ -464,6 +465,8 @@ func (c *ControlMsgValidationInspector) getCtrlMsgCount(ctrlMsgType p2pmsg.Contr return uint64(len(ctrlMsg.GetPrune())) case p2pmsg.CtrlMsgIHave: return uint64(len(ctrlMsg.GetIhave())) + case p2pmsg.CtrlMsgIWant: + return uint64(len(ctrlMsg.GetIwant())) default: return 0 } diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 5c2c842a48c..db3dddc350b 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -104,6 +104,7 @@ func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { if err != nil { return fmt.Errorf("failed to get track rpc work nonce: %w", err) } + if ok := t.workerPool.Submit(trackableRPC{Nonce: n, rpc: rpc}); !ok { return fmt.Errorf("failed to track RPC could not submit work to worker pool") } @@ -115,9 +116,12 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { switch { case len(work.rpc.GetControl().GetIhave()) > 0: iHave := work.rpc.GetControl().GetIhave() - t.iHaveRPCSent(iHave) - t.updateLastHighestIHaveRPCSize(int64(len(iHave))) - t.logger.Info().Int("size", len(iHave)).Msg(iHaveRPCTrackedLog) + numOfMessageIdsTracked := t.iHaveRPCSent(iHave) + t.updateLastHighestIHaveRPCSize(int64(numOfMessageIdsTracked)) + t.logger.Info(). + Int("num_of_ihaves", len(iHave)). + Int("num_of_message_ids", numOfMessageIdsTracked). + Msg(iHaveRPCTrackedLog) } return nil } @@ -135,13 +139,16 @@ func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { // iHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. // Args: // - []*pb.ControlIHave: list of iHave control messages. -func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) { +func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) int { controlMsgType := p2pmsg.CtrlMsgIHave + messageIDCount := 0 for _, iHave := range iHaves { + messageIDCount += len(iHave.GetMessageIDs()) for _, messageID := range iHave.GetMessageIDs() { t.cache.add(messageID, controlMsgType) } } + return messageIDCount } // WasIHaveRPCSent checks if an iHave control message with the provided message ID was sent. From b745ea34bca3f84fe93178a6be3d350b6a4a5776 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 16 Aug 2023 17:34:49 -0400 Subject: [PATCH 504/815] Delete export_report.json --- cmd/util/cmd/execution-state-extract/export_report.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 cmd/util/cmd/execution-state-extract/export_report.json diff --git a/cmd/util/cmd/execution-state-extract/export_report.json b/cmd/util/cmd/execution-state-extract/export_report.json deleted file mode 100644 index 07b76f87d41..00000000000 --- a/cmd/util/cmd/execution-state-extract/export_report.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "EpochCounter": 0, - "PreviousStateCommitment": "8a748b8e25b19ad04cfa4f6bf0c0f9dd5d9c1daba25c78ab25320ca4e52cab0c", - "CurrentStateCommitment": "8a748b8e25b19ad04cfa4f6bf0c0f9dd5d9c1daba25c78ab25320ca4e52cab0c", - "ReportSucceeded": true -} \ No newline at end of file From da8f4751b3bb57471d3118f83045f37251c66ace Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 15:26:39 -0700 Subject: [PATCH 505/815] moves spork id to network builder --- cmd/access/node_builder/access_node_builder.go | 1 + cmd/observer/node_builder/observer_builder.go | 1 + cmd/scaffold.go | 1 + follower/follower_builder.go | 1 + network/alsp/manager/manager_test.go | 9 +++++---- network/internal/testutils/testUtil.go | 5 ++++- network/p2p/p2pnode/libp2pNode.go | 3 ++- network/test/blob_service_test.go | 2 +- network/test/echoengine_test.go | 2 +- network/test/epochtransition_test.go | 2 +- network/test/meshengine_test.go | 2 +- 11 files changed, 19 insertions(+), 10 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 55fd48ace8e..5d33051cf96 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -802,6 +802,7 @@ func (builder *FlowAccessNodeBuilder) initNetwork(nodeID module.Local, IdentityProvider: builder.IdentityProvider, ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index a8e7abdd4b6..207e6c167be 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -493,6 +493,7 @@ func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, IdentityProvider: builder.IdentityProvider, ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 07e2d63011e..e7ba460a53d 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -465,6 +465,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( Logger: fnb.Logger, Codec: fnb.CodecFactory(), Me: fnb.Me, + SporkId: fnb.SporkID, MiddlewareFactory: func() (network.Middleware, error) { return fnb.Middleware, nil }, Topology: topology.NewFullyConnectedTopology(), SubscriptionManager: subscriptionManager, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 27e69ce039c..d03ccde45c6 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -380,6 +380,7 @@ func (builder *FollowerServiceBuilder) initNetwork(nodeID module.Local, IdentityProvider: builder.IdentityProvider, ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 8b0d925f642..9ffd384386f 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -63,7 +63,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0]) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0]) net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) require.NoError(t, err) @@ -126,7 +126,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -230,7 +230,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -313,6 +313,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t t, *ids[0], ids, + sporkId, mws[0], p2p.WithAlspConfig(managerCfgFixture(t))) victimNetwork, err := p2p.NewNetwork(networkCfg) @@ -406,7 +407,7 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index bc4da0ea972..ddfc19f186c 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -191,6 +191,7 @@ func MiddlewareFixtures(t *testing.T, identities flow.IdentityList, libP2PNodes // NetworksFixture generates the network for the given middlewares func NetworksFixture(t *testing.T, + sporkId flow.Identifier, ids flow.IdentityList, mws []network.Middleware) []network.Network { @@ -199,7 +200,7 @@ func NetworksFixture(t *testing.T, for i := 0; i < count; i++ { - params := NetworkConfigFixture(t, *ids[i], ids, mws[i]) + params := NetworkConfigFixture(t, *ids[i], ids, sporkId, mws[i]) net, err := p2p.NewNetwork(params) require.NoError(t, err) @@ -213,6 +214,7 @@ func NetworkConfigFixture( t *testing.T, myId flow.Identity, allIds flow.IdentityList, + sporkId flow.Identifier, mw network.Middleware, opts ...p2p.NetworkConfigOption) *p2p.NetworkConfig { @@ -240,6 +242,7 @@ func NetworkConfigFixture( IdentityProvider: id.NewFixedIdentityProvider(allIds), ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: sporkId, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: unittest.Logger(), SpamRecordCacheSize: defaultFlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index e2362e3fe69..18a1e89f5be 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -344,6 +344,7 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageScope) error { lg := n.logger.With(). Str("channel", msgScope.Channel().String()). + Hex("spork_id", logging.ID(msgScope.SporkId())). Interface("proto_message", msgScope.Proto()). Str("payload_type", msgScope.PayloadType()). Int("message_size", msgScope.Size()).Logger() @@ -362,7 +363,7 @@ func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageSco return fmt.Errorf("message size %d exceeds configured max message size %d", msgSize, DefaultMaxPubSubMsgSize) } - topic := channels.TopicFromChannel(msgScope.Channel(), n.sporkId) + topic := channels.TopicFromChannel(msgScope.Channel(), msgScope.SporkId()) lg = lg.With().Str("topic", topic.String()).Logger() ps, found := n.topics[topic] diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 3a401ad4b0b..f42d74ab140 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -99,7 +99,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.networks = testutils.NetworksFixture(suite.T(), ids, mws) + suite.networks = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) blobExchangeChannel := channels.Channel("blob-exchange") diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 3c544279b6f..e329a83e780 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -62,7 +62,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), suite.ids, suite.mws) + suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 46eb1ae1b79..0632f2a9a44 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -201,7 +201,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - nets := testutils.NetworksFixture(suite.T(), ids, mws) + nets := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) suite.cancels = append(suite.cancels, cancel) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index e7a5684f2f9..c2e2311a5af 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -108,7 +108,7 @@ func (suite *MeshEngineTestSuite) SetupTest() { libP2PNodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), suite.ids, suite.mws) + suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) for _, observableConnMgr := range tagObservables { From f01cf4110100d2c4e15d914410303191513b2a85 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 15:27:07 -0700 Subject: [PATCH 506/815] replaces channel with topic on outgoing message scope --- network/message_scope.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/network/message_scope.go b/network/message_scope.go index 3db13a1b2bd..18831a1f662 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -89,7 +89,7 @@ func (m IncomingMessageScope) PayloadType() string { // OutgoingMessageScope captures the context around an outgoing message that is about to be sent. type OutgoingMessageScope struct { targetIds flow.IdentifierList // the target node IDs. - channelId channels.Channel // the channel ID. + topic channels.Topic // the topic, i.e., channel-id/spork-id. payload interface{} // the payload to be sent. encoder func(interface{}) ([]byte, error) // the encoder to encode the payload. msg *message.Message // raw proto message sent on wire. @@ -103,13 +103,13 @@ type OutgoingMessageScope struct { // should have exactly one target ID, while pubsub messages should have at least one target ID). func NewOutgoingScope( targetIds flow.IdentifierList, - channelId channels.Channel, + topic channels.Topic, payload interface{}, encoder func(interface{}) ([]byte, error), protocolType message.ProtocolType) (*OutgoingMessageScope, error) { scope := &OutgoingMessageScope{ targetIds: targetIds, - channelId: channelId, + topic: topic, payload: payload, encoder: encoder, protocol: protocolType, @@ -148,8 +148,8 @@ func (o OutgoingMessageScope) PayloadType() string { return MessageType(o.payload) } -func (o OutgoingMessageScope) Channel() channels.Channel { - return o.channelId +func (o OutgoingMessageScope) Topic() channels.Topic { + return o.topic } // buildMessage builds the raw proto message to be sent on the wire. @@ -165,9 +165,14 @@ func (o OutgoingMessageScope) buildMessage() (*message.Message, error) { emTargets = append(emTargets, tempID[:]) } + channel, ok := channels.ChannelFromTopic(o.topic) + if !ok { + return nil, fmt.Errorf("could not convert topic to channel: %s", o.topic) + } + return &message.Message{ TargetIDs: emTargets, - ChannelID: o.channelId.String(), + ChannelID: channel.String(), Payload: payload, }, nil } From 2e21367d68582dc43447bc216449d2fe3d9f7e61 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 15:28:06 -0700 Subject: [PATCH 507/815] fixes errors in test fixtures --- network/p2p/test/fixtures.go | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 598bbe2693d..c5684a18480 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -583,17 +583,13 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. // let subscriptions propagate time.Sleep(1 * time.Second) - // TODO: remove this and use channel as an argument - channel, ok := channels.ChannelFromTopic(topic) - require.True(t, ok) - for _, node := range nodes { for i := 0; i < count; i++ { // creates a unique message to be published by the node payload := messageFactory() outgoingMessageScope, err := flownet.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, - channel, + topic, payload, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -630,16 +626,12 @@ func EnsurePubsubMessageExchangeFromNode(t *testing.T, ctx context.Context, send // let subscriptions propagate time.Sleep(1 * time.Second) - // TODO: remove this and use channel as an argument - channel, ok := channels.ChannelFromTopic(topic) - require.True(t, ok) - for i := 0; i < count; i++ { // creates a unique message to be published by the node payload := messageFactory() outgoingMessageScope, err := flownet.NewOutgoingScope( flow.IdentifierList{}, - channel, + topic, payload, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -719,14 +711,11 @@ func EnsureNoPubsubMessageExchange( wg.Add(1) go func() { // creates a unique message to be published by the node. - // TODO: remove this and use channel as an argument - channel, ok := channels.ChannelFromTopic(topic) - require.True(t, ok) payload := messageFactory() outgoingMessageScope, err := flownet.NewOutgoingScope( toIdentifiers, - channel, + topic, payload, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) From 9382f64b849b9712e0724d0b0fc1c85757f33d12 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 16 Aug 2023 15:54:32 -0700 Subject: [PATCH 508/815] fixes tests --- network/p2p/network.go | 20 ++- network/p2p/p2pnode/libp2pNode.go | 12 +- network/p2p/test/sporking_test.go | 109 +++++++-------- network/p2p/test/topic_validator_test.go | 161 +++++++++++++++++------ 4 files changed, 185 insertions(+), 117 deletions(-) diff --git a/network/p2p/network.go b/network/p2p/network.go index a288b92c7d7..f564aa0340d 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -40,6 +40,7 @@ var NotEjectedFilter = filter.Not(filter.Ejected) type Network struct { sync.RWMutex *component.ComponentManager + sporkId flow.Identifier identityProvider module.IdentityProvider logger zerolog.Logger codec network.Codec @@ -99,6 +100,7 @@ type NetworkConfig struct { ReceiveCache *netcache.ReceiveCache ConduitFactory network.ConduitFactory AlspCfg *alspmgr.MisbehaviorReportManagerConfig + SporkId flow.Identifier } // NetworkConfigOption is a function that can be used to override network config parmeters. @@ -167,6 +169,7 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { registerEngineRequests: make(chan *registerEngineRequest), registerBlobServiceRequests: make(chan *registerBlobServiceRequest), misbehaviorReportManager: misbehaviorMngr, + sporkId: param.SporkId, } for _, opt := range opts { @@ -428,7 +431,7 @@ func (n *Network) UnicastOnChannel(channel channels.Channel, payload interface{} msg, err := network.NewOutgoingScope( flow.IdentifierList{targetID}, - channel, + channels.TopicFromChannel(channel, n.sporkId), payload, n.codec.Encode, message.ProtocolTypeUnicast) @@ -436,14 +439,14 @@ func (n *Network) UnicastOnChannel(channel channels.Channel, payload interface{} return fmt.Errorf("could not generate outgoing message scope for unicast: %w", err) } - n.metrics.UnicastMessageSendingStarted(msg.Channel().String()) - defer n.metrics.UnicastMessageSendingCompleted(msg.Channel().String()) + n.metrics.UnicastMessageSendingStarted(channel.String()) + defer n.metrics.UnicastMessageSendingCompleted(channel.String()) err = n.mw.SendDirect(msg) if err != nil { return fmt.Errorf("failed to send message to %x: %w", targetID, err) } - n.metrics.OutboundMessageSent(msg.Size(), msg.Channel().String(), message.ProtocolTypeUnicast.String(), msg.PayloadType()) + n.metrics.OutboundMessageSent(msg.Size(), channel.String(), message.ProtocolTypeUnicast.String(), msg.PayloadType()) return nil } @@ -506,7 +509,12 @@ func (n *Network) sendOnChannel(channel channels.Channel, msg interface{}, targe Msg("sending new message on channel") // generate network message (encoding) based on list of recipients - scope, err := network.NewOutgoingScope(targetIDs, channel, msg, n.codec.Encode, message.ProtocolTypePubSub) + scope, err := network.NewOutgoingScope( + targetIDs, + channels.TopicFromChannel(channel, n.sporkId), + msg, + n.codec.Encode, + message.ProtocolTypePubSub) if err != nil { return fmt.Errorf("failed to generate outgoing message scope %s: %w", channel, err) } @@ -518,7 +526,7 @@ func (n *Network) sendOnChannel(channel channels.Channel, msg interface{}, targe return fmt.Errorf("failed to send message on channel %s: %w", channel, err) } - n.metrics.OutboundMessageSent(scope.Size(), scope.Channel().String(), message.ProtocolTypePubSub.String(), scope.PayloadType()) + n.metrics.OutboundMessageSent(scope.Size(), channel.String(), message.ProtocolTypePubSub.String(), scope.PayloadType()) return nil } diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 18a1e89f5be..ed98a658b69 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -343,8 +343,7 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { // All errors returned from this function can be considered benign. func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageScope) error { lg := n.logger.With(). - Str("channel", msgScope.Channel().String()). - Hex("spork_id", logging.ID(msgScope.SporkId())). + Str("topic", msgScope.Topic().String()). Interface("proto_message", msgScope.Proto()). Str("payload_type", msgScope.PayloadType()). Int("message_size", msgScope.Size()).Logger() @@ -363,16 +362,13 @@ func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageSco return fmt.Errorf("message size %d exceeds configured max message size %d", msgSize, DefaultMaxPubSubMsgSize) } - topic := channels.TopicFromChannel(msgScope.Channel(), msgScope.SporkId()) - lg = lg.With().Str("topic", topic.String()).Logger() - - ps, found := n.topics[topic] + ps, found := n.topics[msgScope.Topic()] if !found { - return fmt.Errorf("could not find topic (%s)", topic) + return fmt.Errorf("could not find topic (%s)", msgScope.Topic()) } err = ps.Publish(ctx, data) if err != nil { - return fmt.Errorf("could not publish to topic (%s): %w", topic, err) + return fmt.Errorf("could not publish to topic (%s): %w", msgScope.Topic(), err) } lg.Debug().Msg("published message to topic") diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index b7aae10d32f..d637114366e 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -237,54 +237,9 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { time.Sleep(time.Second) // assert that node 1 can successfully send a message to node 2 via PubSub - testOneToKMessagingSucceeds(ctx, t, node1, sub2, topicBeforeSpork) - - // new root id after spork - rootIDAfterSpork := unittest.IdentifierFixture() - - // topic after the spork - topicAfterSpork := channels.TopicFromChannel(channels.TestNetworkChannel, rootIDAfterSpork) - - // mimic that node1 is now part of the new spork while node2 remains on the old spork - // by unsubscribing node1 from 'topicBeforeSpork' and subscribing it to 'topicAfterSpork' - // and keeping node2 subscribed to topic 'topicBeforeSpork' - err = node1.Unsubscribe(topicBeforeSpork) - require.NoError(t, err) - _, err = node1.Subscribe(topicAfterSpork, topicValidator) - require.NoError(t, err) - - // assert that node 1 can no longer send a message to node 2 via PubSub - testOneToKMessagingFails(ctx, t, node1, sub2, topicAfterSpork) -} - -func testOneToOneMessagingSucceeds(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { - // create stream from node 1 to node 2 - sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) - s, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) - // assert that stream creation succeeded - require.NoError(t, err) - assert.NotNil(t, s) -} - -func testOneToOneMessagingFails(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { - // create stream from source node to destination address - sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) - _, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) - // assert that stream creation failed - assert.Error(t, err) - // assert that it failed with the expected error - assert.Regexp(t, ".*failed to negotiate security protocol.*|.*protocols not supported.*", err) -} - -func testOneToKMessagingSucceeds(ctx context.Context, - t *testing.T, - sourceNode p2p.LibP2PNode, - dstnSub p2p.Subscription, - topic channels.Topic) { - - sentMsg, err := network.NewOutgoingScope( + outgoingMessageScope, err := network.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, - channels.TestNetworkChannel, + topicBeforeSpork, &libp2pmessage.TestMessage{ Text: string("hello"), }, @@ -292,32 +247,40 @@ func testOneToKMessagingSucceeds(ctx context.Context, message.ProtocolTypePubSub) require.NoError(t, err) - sentData, err := sentMsg.Proto().Marshal() + expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() require.NoError(t, err) // send a 1-k message from source node to destination node - err = sourceNode.Publish(ctx, topic, sentData) + err = node1.Publish(ctx, outgoingMessageScope) require.NoError(t, err) // assert that the message is received by the destination node unittest.AssertReturnsBefore(t, func() { - msg, err := dstnSub.Next(ctx) + msg, err := sub2.Next(ctx) require.NoError(t, err) - assert.Equal(t, sentData, msg.Data) + assert.Equal(t, expectedReceivedData, msg.Data) }, // libp2p hearbeats every second, so at most the message should take 1 second 2*time.Second) -} -func testOneToKMessagingFails(ctx context.Context, - t *testing.T, - sourceNode p2p.LibP2PNode, - dstnSub p2p.Subscription, - topic channels.Topic) { + // new root id after spork + rootIDAfterSpork := unittest.IdentifierFixture() - sentMsg, err := network.NewOutgoingScope( - flow.IdentifierList{unittest.IdentifierFixture()}, - channels.TestNetworkChannel, + // topic after the spork + topicAfterSpork := channels.TopicFromChannel(channels.TestNetworkChannel, rootIDAfterSpork) + + // mimic that node1 is now part of the new spork while node2 remains on the old spork + // by unsubscribing node1 from 'topicBeforeSpork' and subscribing it to 'topicAfterSpork' + // and keeping node2 subscribed to topic 'topicBeforeSpork' + err = node1.Unsubscribe(channels.TestNetworkChannel) + require.NoError(t, err) + _, err = node1.Subscribe(topicAfterSpork, topicValidator) + require.NoError(t, err) + + // assert that node 1 can no longer send a message to node 2 via PubSub + outgoingMessageScope, err = network.NewOutgoingScope( + flow.IdentifierList{id2.NodeID}, + topicAfterSpork, &libp2pmessage.TestMessage{ Text: string("hello"), }, @@ -325,18 +288,34 @@ func testOneToKMessagingFails(ctx context.Context, message.ProtocolTypePubSub) require.NoError(t, err) - sentData, err := sentMsg.Proto().Marshal() - require.NoError(t, err) - // send a 1-k message from source node to destination node - err = sourceNode.Publish(ctx, topic, sentData) + err = node1.Publish(ctx, outgoingMessageScope) require.NoError(t, err) // assert that the message is never received by the destination node _ = unittest.RequireNeverReturnBefore(t, func() { - _, _ = dstnSub.Next(ctx) + _, _ = sub2.Next(ctx) }, // libp2p hearbeats every second, so at most the message should take 1 second 2*time.Second, "nodes on different sporks were able to communicate") } + +func testOneToOneMessagingSucceeds(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { + // create stream from node 1 to node 2 + sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) + s, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) + // assert that stream creation succeeded + require.NoError(t, err) + assert.NotNil(t, s) +} + +func testOneToOneMessagingFails(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { + // create stream from source node to destination address + sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) + _, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) + // assert that stream creation failed + assert.Error(t, err) + // assert that it failed with the expected error + assert.Regexp(t, ".*failed to negotiate security protocol.*|.*protocols not supported.*", err) +} diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 5a7e402b141..e3d47c7c709 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -93,10 +93,16 @@ func TestTopicValidator_Unstaked(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) - err = sn2.Publish(timedCtx, topic, data1) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + + err = sn2.Publish(timedCtx, outgoingMessageScope1) require.NoError(t, err) // sn1 should not receive message from sn2 because sn2 is unstaked @@ -146,10 +152,16 @@ func TestTopicValidator_PublicChannel(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - // create a dummy sync request to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, &messages.SyncRequest{Nonce: 0, Height: 0}, channel) - err = sn2.Publish(timedCtx, topic, data1) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + &messages.SyncRequest{Nonce: 0, Height: 0}, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + + err = sn2.Publish(timedCtx, outgoingMessageScope1) require.NoError(t, err) var wg sync.WaitGroup @@ -158,11 +170,14 @@ func TestTopicValidator_PublicChannel(t *testing.T) { timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) defer cancel1s() + expectedReceivedData, err := outgoingMessageScope1.Proto().Marshal() + require.NoError(t, err) + // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData, sub2) unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") } @@ -208,11 +223,20 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() + // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.Channel("invalid-channel")) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) - err = sn2.Publish(timedCtx, topic, data1) + // intentionally overriding the channel id to be different from the topic + outgoingMessageScope1.Proto().ChannelID = channels.PublicSyncCommittee.String() + err = sn2.Publish(timedCtx, outgoingMessageScope1) // publish fails because the channel validation fails require.Error(t, err) @@ -238,7 +262,7 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) - topic := channels.Topic("invalid-topic") + topic := channels.TopicFromChannel(channels.ConsensusCommittee, sporkId) pInfo2, err := utils.PeerAddressInfo(identity2) require.NoError(t, err) @@ -261,9 +285,19 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + + // intentionally overriding the channel id to be a valid channel id (though the topic is invalid) + // hence imitating a message that was published to the wrong topic + outgoingMessageScope1.Proto().ChannelID = channels.PushBlocks.String() - err = sn2.Publish(timedCtx, topic, data1) + err = sn2.Publish(timedCtx, outgoingMessageScope1) // publish fails because the topic conversion fails require.Error(t, err) @@ -345,34 +379,52 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 60*time.Second) defer cancel5s() - // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) // sn2 publishes the block proposal, sn1 and an1 should receive the message because // SN nodes are authorized to send block proposals - err = sn2.Publish(timedCtx, topic, data1) + // create a dummy block proposal to publish from our SN node + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + err = sn2.Publish(timedCtx, outgoingMessageScope1) + require.NoError(t, err) + + expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) timedCtx, cancel2s := context.WithTimeout(ctx, 2*time.Second) defer cancel2s() - data2 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) // the access node now publishes the block proposal message, AN are not authorized to publish block proposals // the message should be rejected by the topic validator on sn1 - err = an1.Publish(timedCtx, topic, data2) + outgoingMessageScope2, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + err = an1.Publish(timedCtx, outgoingMessageScope2) + require.NoError(t, err) + + expectedReceivedData2, err := outgoingMessageScope2.Proto().Marshal() require.NoError(t, err) // an1 receives its own message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data2, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData2, sub3) var wg sync.WaitGroup @@ -449,11 +501,17 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) + // create a dummy block proposal to publish from our SN node // sn2 publishes the block proposal on the sync committee channel - err = sn2.Publish(timedCtx, topic, data1) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + err = sn2.Publish(timedCtx, outgoingMessageScope1) require.NoError(t, err) // sn1 should not receive message from sn2 @@ -532,29 +590,46 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - // create a dummy block proposal to publish from our SN node - data1 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) // sn2 publishes the block proposal, sn1 and an1 should receive the message because // SN nodes are authorized to send block proposals - err = sn2.Publish(timedCtx, topic, data1) + // create a dummy block proposal to publish from our SN node + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + err = sn2.Publish(timedCtx, outgoingMessageScope1) + require.NoError(t, err) + + expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() require.NoError(t, err) // sn1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) // sn2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) // an1 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data1, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) // "eject" sn2 to ensure messages published by ejected nodes get rejected identity2.Ejected = true - data3 := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channel) + + outgoingMessageScope3, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + unittest.ProposalFixture(), + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) + timedCtx, cancel2s := context.WithTimeout(ctx, time.Second) defer cancel2s() - err = sn2.Publish(timedCtx, topic, data3) + err = sn2.Publish(timedCtx, outgoingMessageScope3) require.NoError(t, err) // sn1 should not receive rejected message from ejected sn2 @@ -627,19 +702,29 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() + // create a dummy sync request to publish from our LN node - data := p2pfixtures.MustEncodeEvent(t, &messages.RangeRequest{}, channel) + outgoingMessageScope1, err := network.NewOutgoingScope( + flow.IdentifierList{identity1.NodeID, identity2.NodeID}, + topic, + &messages.RangeRequest{}, + unittest.NetworkCodec().Encode, + message.ProtocolTypePubSub) + require.NoError(t, err) // ln2 publishes the sync request on the cluster channel - err = ln2.Publish(timedCtx, topic, data) + err = ln2.Publish(timedCtx, outgoingMessageScope1) + require.NoError(t, err) + + expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() require.NoError(t, err) // ln1 gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub1) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) // ln2 also gets the message (as part of the libp2p loopback of published topic messages) - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub2) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) // ln3 also gets the message - p2pfixtures.SubMustReceiveMessage(t, timedCtx, data, sub3) + p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) } From 3bab9443cb48a26efd4636be7c0f03e873e1c050 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 17 Aug 2023 05:00:21 -0400 Subject: [PATCH 509/815] misbehavior report randomizer WIP --- engine/common/synchronization/engine.go | 30 ++++++++++++++++---- engine/common/synchronization/engine_test.go | 12 ++++---- utils/rand/rand.go | 2 +- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 3eb671ee59c..3a16ca1448d 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -484,20 +484,38 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I } func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { - //syncRequestMsg := event.(*messages.SyncRequest) + // use a probabilistic approach to create a misbehavior report - create a report with a probability of 1/1000 + // this is done to avoid creating a report for every sync request received + // the probability of creating a misbehavior report is 1/1000 - report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) + // Generate a random integer between 1 and 1000 + n, err := rand.Uint32n(1001) if err != nil { - // failing to create the misbehavior report is unlikely. If an error is encountered while - // creating the misbehavior report it indicates a bug and processing can not proceed. e.log.Fatal(). Err(err). Str("originID", originID.String()). - Msg("failed to create misbehavior report") + Msg("failed to create random number") } - return report, true + if n == 835 { + // create a misbehavior report + e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") + report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) + + if err != nil { + // failing to create the misbehavior report is unlikely. If an error is encountered while + // creating the misbehavior report it indicates a bug and processing can not proceed. + e.log.Fatal(). + Err(err). + Str("originID", originID.String()). + Msg("failed to create misbehavior report") + } + return report, true + } + + // most of the time, don't report a misbehavior + return nil, false } func (e *Engine) validateSyncResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index f5f8de8ac17..dd1e57dcb24 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -222,7 +222,7 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { } func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load() { - load := 1000 + load := 10000 ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) @@ -248,14 +248,16 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load( // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported // also, check that response is never sent - ss.core.AssertNotCalled(ss.T(), "HandleHeight") - ss.core.AssertNotCalled(ss.T(), "WithinTolerance") + //ss.core.AssertNotCalled(ss.T(), "HandleHeight") + //ss.core.AssertNotCalled(ss.T(), "WithinTolerance") + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + ss.core.AssertExpectations(ss.T()) } - - ss.core.AssertExpectations(ss.T()) } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and diff --git a/utils/rand/rand.go b/utils/rand/rand.go index 9a577f7afec..e56e2e3df60 100644 --- a/utils/rand/rand.go +++ b/utils/rand/rand.go @@ -114,7 +114,7 @@ func Uint() (uint, error) { return uint(r), err } -// returns a random uint strictly less than `n`. +// Uintn returns a random uint strictly less than `n`. // `n` has to be a strictly positive integer. // // It returns an error: From 80c51b62ad3c67bf53f556f8c68d346e0e0f4d73 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 17 Aug 2023 07:12:58 -0400 Subject: [PATCH 510/815] misbehavior report randomizer - initial implementation --- engine/common/synchronization/engine.go | 16 +-- engine/common/synchronization/engine_test.go | 119 +++++++++++++++---- module/synchronization.go | 2 +- utils/rand/rand.go | 35 ++++++ 4 files changed, 137 insertions(+), 35 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 3a16ca1448d..de8ecbe3194 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -57,6 +57,7 @@ type Engine struct { participantsProvider module.IdentifierProvider requestHandler *RequestHandler // component responsible for handling requests + randomizer rand.Randomizer pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse pendingBlockResponses engine.MessageStore // message store for *message.BlockResponse @@ -107,6 +108,7 @@ func New( pollInterval: opt.PollInterval, scanInterval: opt.ScanInterval, participantsProvider: participantsProvider, + randomizer: rand.NewDefaultRandomizer(), } // register the engine with the network layer and store the conduit @@ -484,20 +486,12 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I } func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { + // Generate a random integer between 1 and 1000 + n := e.randomizer.Uint32n(uint32(1001)) + // use a probabilistic approach to create a misbehavior report - create a report with a probability of 1/1000 // this is done to avoid creating a report for every sync request received // the probability of creating a misbehavior report is 1/1000 - - // Generate a random integer between 1 and 1000 - n, err := rand.Uint32n(1001) - - if err != nil { - e.log.Fatal(). - Err(err). - Str("originID", originID.String()). - Msg("failed to create random number") - } - if n == 835 { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index dd1e57dcb24..6fe2dcfbcb7 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -4,7 +4,6 @@ import ( "context" "io" "math" - "math/rand" "testing" "time" @@ -32,6 +31,7 @@ import ( protocol "github.com/onflow/flow-go/state/protocol/mock" storerr "github.com/onflow/flow-go/storage" storage "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/rand" "github.com/onflow/flow-go/utils/unittest" ) @@ -183,17 +183,19 @@ func (ss *SyncSuite) SetupTest() { // TestOnSyncRequest_LowerThanReceiver_WithinTolerance tests that a sync request that's within tolerance of the receiver doesn't trigger // a response, even if request height is lower than receiver. func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, Height: 0, } // regardless of request height, if within tolerance, we should not respond ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(true) - err := ss.e.requestHandler.onSyncRequest(originID, req) + err = ss.e.requestHandler.onSyncRequest(originID, req) ss.Assert().NoError(err, "same height sync request should pass") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) @@ -203,10 +205,13 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { // TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher than the receiver's height doesn't // trigger a response, even if outside tolerance. func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, Height: 0, } @@ -214,29 +219,79 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { req.Height = ss.head.Height + 1 ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - err := ss.e.requestHandler.onSyncRequest(originID, req) + err = ss.e.requestHandler.onSyncRequest(originID, req) ss.Assert().NoError(err, "same height sync request should pass") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) ss.core.AssertExpectations(ss.T()) } -func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load() { - load := 10000 +func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport() { + load := 10 + mockRandomizer := new(rand.MockRandomizer) + ss.e.randomizer = mockRandomizer ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - // generate origin and request message - originID := unittest.IdentifierFixture() + for i := 0; i < load; i++ { + ss.T().Logf("i: %d", i) + + // generate origin and request message + originID := unittest.IdentifierFixture() + + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } + + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 + + ss.core.On("HandleHeight", ss.head, req.Height).Once() + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false).Once() + + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + + // expect Uint32n(1001) to be called once and return 1, not triggering a misbehavior report + mockRandomizer.On("Uint32n", uint32(1001)).Return(1).Once() + + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + + // give at least some time to process items + time.Sleep(time.Millisecond * 100) + + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) + } +} + +func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { + load := 10 + mockRandomizer := new(rand.MockRandomizer) + ss.e.randomizer = mockRandomizer + + ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) + ss.e.Start(ctx) + unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) + defer cancel() for i := 0; i < load; i++ { ss.T().Logf("i: %d", i) + // generate origin and request message + originID := unittest.IdentifierFixture() + + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + req := &messages.SyncRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, Height: 0, } @@ -244,30 +299,37 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Load( req.Height = ss.head.Height + 1 // assert that misbehavior is reported - ss.con.On("ReportMisbehavior", mock.Anything).Return(true) + ss.con.On("ReportMisbehavior", mock.Anything).Return(false) // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported // also, check that response is never sent - //ss.core.AssertNotCalled(ss.T(), "HandleHeight") - //ss.core.AssertNotCalled(ss.T(), "WithinTolerance") - ss.core.On("HandleHeight", ss.head, req.Height) - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - + ss.core.AssertNotCalled(ss.T(), "HandleHeight") + ss.core.AssertNotCalled(ss.T(), "WithinTolerance") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + // expect Uint32n(1001) to be called once and return misbehavior report because it happened to be 835 + mockRandomizer.On("Uint32n", uint32(1001)).Return(835).Once() + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + + // give at least some time to process items + time.Sleep(time.Millisecond * 100) + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) } } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and // lower than the receiver's height triggers a response. func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") // generate origin and request message originID := unittest.IdentifierFixture() req := &messages.SyncRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, Height: 0, } @@ -284,19 +346,24 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { assert.Equal(ss.T(), originID, recipientID, "should send response to original sender") }, ) - err := ss.e.requestHandler.onSyncRequest(originID, req) + err = ss.e.requestHandler.onSyncRequest(originID, req) require.NoError(ss.T(), err, "smaller height sync request should pass") ss.core.AssertExpectations(ss.T()) } func (ss *SyncSuite) TestOnSyncResponse() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + + height, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate height") // generate origin ID and response message originID := unittest.IdentifierFixture() res := &messages.SyncResponse{ - Nonce: rand.Uint64(), - Height: rand.Uint64(), + Nonce: nonce, + Height: height, } // the height should be handled @@ -306,11 +373,13 @@ func (ss *SyncSuite) TestOnSyncResponse() { } func (ss *SyncSuite) TestOnRangeRequest() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") // generate originID and range request originID := unittest.IdentifierFixture() req := &messages.RangeRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, FromHeight: 0, ToHeight: 0, } @@ -424,11 +493,13 @@ func (ss *SyncSuite) TestOnRangeRequest() { } func (ss *SyncSuite) TestOnBatchRequest() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") // generate origin ID and batch request originID := unittest.IdentifierFixture() req := &messages.BatchRequest{ - Nonce: rand.Uint64(), + Nonce: nonce, BlockIDs: nil, } @@ -501,11 +572,13 @@ func (ss *SyncSuite) TestOnBatchRequest() { } func (ss *SyncSuite) TestOnBlockResponse() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") // generate origin and block response originID := unittest.IdentifierFixture() res := &messages.BlockResponse{ - Nonce: rand.Uint64(), + Nonce: nonce, Blocks: []messages.UntrustedBlock{}, } diff --git a/module/synchronization.go b/module/synchronization.go index ec7e893699f..eda7145e891 100644 --- a/module/synchronization.go +++ b/module/synchronization.go @@ -17,7 +17,7 @@ type BlockRequester interface { // RequestHeight indicates that the given block height should be queued for retrieval. RequestHeight(height uint64) - // Manually Prune requests + // Prune manually prunes requests Prune(final *flow.Header) } diff --git a/utils/rand/rand.go b/utils/rand/rand.go index e56e2e3df60..680838232f4 100644 --- a/utils/rand/rand.go +++ b/utils/rand/rand.go @@ -15,6 +15,8 @@ import ( "crypto/rand" "encoding/binary" "fmt" + + "github.com/stretchr/testify/mock" ) // Uint64 returns a random uint64. @@ -167,3 +169,36 @@ func Samples(n uint, m uint, swap func(i, j uint)) error { } return nil } + +// Randomizer is an interface that defines a method for generating random numbers. This is useful for testing where +// we want to mock out random number generation. +type Randomizer interface { + // Uint32n returns a random uint32 strictly less than `n`. + Uint32n(n uint32) uint32 +} + +func NewDefaultRandomizer() Randomizer { + return &defaultRandomizer{} +} + +type defaultRandomizer struct{} + +// Uint32n returns a random number between 0 and n (exclusive) +func (e *defaultRandomizer) Uint32n(n uint32) uint32 { + n, err := Uint32n(n) + if err != nil { + fmt.Errorf("failed to create random number (%d): %w", n, err) + } + return n +} + +// MockRandomizer is a mock object that implements the Randomizer interface +type MockRandomizer struct { + mock.Mock +} + +// Uint32n is a mock method that returns a random number +func (m *MockRandomizer) Uint32n(n uint32) uint32 { + args := m.Called(n) + return uint32(args.Int(0)) +} From 9ab26207f8a6942196579a5401285709ba877b2e Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 17 Aug 2023 07:38:05 -0400 Subject: [PATCH 511/815] simplify adjustDecayFunc() with switch-case Co-authored-by: Khalil Claybon --- network/alsp/manager/manager.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 2af3f46c47d..fdd0e079519 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -464,18 +464,18 @@ func (m *MisbehaviorReportManager) adjustDecayFunc(cutoffCounter uint64) float64 // and forth cutoffs reduce the decay speed by 90% (1000 -> 100, 100 -> 10, 10 -> 1). // All subsequent cutoffs after the fourth cutoff use the last decay speed (1). // This is to prevent the decay speed from becoming too small and the spam record from taking too long to decay. - decaySpeeds := []float64{1000, 1000, 100, 10, 1} - - if cutoffCounter <= 0 { - // illegal state, this should never happen unless there is a bug in the code. + switch { + case cutoffCounter == 1: + return 1000 + case cutoffCounter == 2: + return 100 + case cutoffCounter == 3: + return 10 + case cutoffCounter >= 4: + return 1 + default: panic(fmt.Sprintf("illegal-state cutoff counter must be positive, it should include the current time: %d", cutoffCounter)) } - - if int(cutoffCounter) >= len(decaySpeeds) { - return decaySpeeds[len(decaySpeeds)-1] // clamp to the last value - } - - return decaySpeeds[cutoffCounter] } // WithSpamRecordsCacheFactory sets the spam record cache factory for the MisbehaviorReportManager. From f820c09590f57fbf432c4c18ff1fc36fc0b2936e Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 17 Aug 2023 07:49:49 -0400 Subject: [PATCH 512/815] flaky test fix --- network/alsp/manager/manager_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 527af12ac3f..a42519bbc37 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -1709,13 +1709,13 @@ func TestDisallowListNotification(t *testing.T) { return false } require.True(t, record.DisallowListed) // the peer should be disallow-listed. - // cuttoff counter should be incremented since the penalty is above the disallowlisting threshold. + // cutoff counter should be incremented since the penalty is above the disallow-listing threshold. require.Equal(t, uint64(1), record.CutoffCounter) // the decay should be the default decay value. require.Equal(t, model.SpamRecordFactory()(unittest.IdentifierFixture()).Decay, record.Decay) return true - }, 1*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") + }, 2*time.Second, 10*time.Millisecond, "ALSP manager did not handle the misbehavior report") } ////////////////////////////// TEST HELPERS /////////////////////////////////////////////////////////////////////////////// From e3dee97d7556659a7a4e240ac4d1f448845b36a1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 09:43:01 -0700 Subject: [PATCH 513/815] adds message space interface --- network/message_scope.go | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/network/message_scope.go b/network/message_scope.go index 18831a1f662..fe9d3acb291 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -204,3 +204,55 @@ func EventId(channel channels.Channel, payload []byte) (hash.Hash, error) { func MessageType(decodedPayload interface{}) string { return strings.TrimLeft(fmt.Sprintf("%T", decodedPayload), "*") } + +// IncomingMessageScoper defines the interface for handling incoming message scope. +type IncomingMessageScoper interface { + // OriginId returns the origin node ID. + OriginId() flow.Identifier + + // Proto returns the raw message received. + Proto() *message.Message + + // DecodedPayload returns the decoded payload of the message. + DecodedPayload() interface{} + + // Protocol returns the type of protocol used to receive the message. + Protocol() message.ProtocolType + + // Channel returns the channel of the message. + Channel() channels.Channel + + // Size returns the size of the message. + Size() int + + // TargetIDs returns the target node IDs, i.e., the intended recipients. + TargetIDs() flow.IdentifierList + + // EventID returns the hash of the payload and channel. + EventID() []byte + + // PayloadType returns the type of the decoded payload. + PayloadType() string +} + +// OutgoingMessageScoper defines the interface for handling outgoing message scope. +type OutgoingMessageScoper interface { + // TargetIds returns the target node IDs. + TargetIds() flow.IdentifierList + + // Size returns the size of the message. + Size() int + + // PayloadType returns the type of the payload to be sent. + PayloadType() string + + // Topic returns the topic, i.e., channel-id/spork-id. + Topic() channels.Topic + + // Proto returns the raw proto message sent on the wire. + Proto() *message.Message +} + +// Ensure structs implement the interfaces +var _ IncomingMessageScoper = &IncomingMessageScope{} +var _ OutgoingMessageScoper = &OutgoingMessageScope{} From 026f9e66c9ee56729e98719f211821165801482b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 09:57:57 -0700 Subject: [PATCH 514/815] moves message scopes to implementation packages --- network/message/message_scope.go | 176 ++++++++++++++++++ network/message_scope.go | 170 +---------------- network/middleware.go | 7 +- network/mocknetwork/message_validator.go | 7 +- network/mocknetwork/middleware.go | 10 +- network/mocknetwork/overlay.go | 8 +- network/p2p/dht/dht_test.go | 3 +- network/p2p/libp2pNode.go | 3 +- network/p2p/middleware/middleware.go | 8 +- network/p2p/middleware/middleware_test.go | 3 +- network/p2p/mock/lib_p2_p_node.go | 5 +- network/p2p/network.go | 8 +- network/p2p/p2pnode/libp2pNode.go | 3 +- network/p2p/scoring/app_score_test.go | 5 +- .../scoring/subscription_validator_test.go | 11 +- .../subscription/subscription_filter_test.go | 3 +- network/p2p/test/fixtures.go | 6 +- network/p2p/test/sporking_test.go | 5 +- network/p2p/test/topic_validator_test.go | 20 +- .../ratelimit/bandwidth_rate_limiter_test.go | 5 +- network/test/middleware_test.go | 40 ++-- network/test/unicast_authorization_test.go | 22 +-- network/validator.go | 4 +- network/validator/any_validator.go | 3 +- network/validator/not_validator.go | 3 +- network/validator/origin_validator.go | 3 +- network/validator/sender_validator.go | 3 +- network/validator/target_validator.go | 3 +- 28 files changed, 282 insertions(+), 265 deletions(-) create mode 100644 network/message/message_scope.go diff --git a/network/message/message_scope.go b/network/message/message_scope.go new file mode 100644 index 00000000000..aa15c0112a2 --- /dev/null +++ b/network/message/message_scope.go @@ -0,0 +1,176 @@ +package message + +import ( + "fmt" + + "github.com/onflow/flow-go/crypto/hash" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" +) + +// IncomingMessageScope captures the context around an incoming message that is received by the network layer. +type IncomingMessageScope struct { + originId flow.Identifier // the origin node ID. + targetIds flow.IdentifierList // the target node IDs (i.e., intended recipients). + eventId hash.Hash // hash of the payload and channel. + msg *Message // the raw message received. + decodedPayload interface{} // decoded payload of the message. + protocol ProtocolType // the type of protocol used to receive the message. +} + +// NewIncomingScope creates a new incoming message scope. +// All errors returned by this function are benign and should not cause the node to crash, especially that it is not +// safe to crash the node when receiving a message. +// It errors if event id (i.e., hash of the payload and channel) cannot be computed, or if it fails to +// convert the target IDs from bytes slice to a flow.IdentifierList. +func NewIncomingScope(originId flow.Identifier, protocol ProtocolType, msg *Message, decodedPayload interface{}) (*IncomingMessageScope, error) { + eventId, err := network.EventId(channels.Channel(msg.ChannelID), msg.Payload) + if err != nil { + return nil, fmt.Errorf("could not compute event id: %w", err) + } + + targetIds, err := flow.ByteSlicesToIds(msg.TargetIDs) + if err != nil { + return nil, fmt.Errorf("could not convert target ids: %w", err) + } + return &IncomingMessageScope{ + eventId: eventId, + originId: originId, + msg: msg, + decodedPayload: decodedPayload, + protocol: protocol, + targetIds: targetIds, + }, nil +} + +func (m IncomingMessageScope) OriginId() flow.Identifier { + return m.originId +} + +func (m IncomingMessageScope) Proto() *Message { + return m.msg +} + +func (m IncomingMessageScope) DecodedPayload() interface{} { + return m.decodedPayload +} + +func (m IncomingMessageScope) Protocol() ProtocolType { + return m.protocol +} + +func (m IncomingMessageScope) Channel() channels.Channel { + return channels.Channel(m.msg.ChannelID) +} + +func (m IncomingMessageScope) Size() int { + return m.msg.Size() +} + +func (m IncomingMessageScope) TargetIDs() flow.IdentifierList { + return m.targetIds +} + +func (m IncomingMessageScope) EventID() []byte { + return m.eventId[:] +} + +func (m IncomingMessageScope) PayloadType() string { + return network.MessageType(m.decodedPayload) +} + +// OutgoingMessageScope captures the context around an outgoing message that is about to be sent. +type OutgoingMessageScope struct { + targetIds flow.IdentifierList // the target node IDs. + topic channels.Topic // the topic, i.e., channel-id/spork-id. + payload interface{} // the payload to be sent. + encoder func(interface{}) ([]byte, error) // the encoder to encode the payload. + msg *Message // raw proto message sent on wire. + protocol ProtocolType // the type of protocol used to send the message. +} + +// NewOutgoingScope creates a new outgoing message scope. +// All errors returned by this function are benign and should not cause the node to crash. +// It errors if the encoder fails to encode the payload into a protobuf message, or +// if the number of target IDs does not match the protocol type (i.e., unicast messages +// should have exactly one target ID, while pubsub messages should have at least one target ID). +func NewOutgoingScope( + targetIds flow.IdentifierList, + topic channels.Topic, + payload interface{}, + encoder func(interface{}) ([]byte, error), + protocolType ProtocolType) (*OutgoingMessageScope, error) { + scope := &OutgoingMessageScope{ + targetIds: targetIds, + topic: topic, + payload: payload, + encoder: encoder, + protocol: protocolType, + } + + if protocolType == ProtocolTypeUnicast { + // for unicast messages, we should have exactly one target. + if len(targetIds) != 1 { + return nil, fmt.Errorf("expected exactly one target id for unicast message, got: %d", len(targetIds)) + } + } + if protocolType == ProtocolTypePubSub { + // for pubsub messages, we should have at least one target. + if len(targetIds) == 0 { + return nil, fmt.Errorf("expected at least one target id for pubsub message, got: %d", len(targetIds)) + } + } + + msg, err := scope.buildMessage() + if err != nil { + return nil, fmt.Errorf("could not build message: %w", err) + } + scope.msg = msg + return scope, nil +} + +func (o OutgoingMessageScope) TargetIds() flow.IdentifierList { + return o.targetIds +} + +func (o OutgoingMessageScope) Size() int { + return o.msg.Size() +} + +func (o OutgoingMessageScope) PayloadType() string { + return network.MessageType(o.payload) +} + +func (o OutgoingMessageScope) Topic() channels.Topic { + return o.topic +} + +// buildMessage builds the raw proto message to be sent on the wire. +func (o OutgoingMessageScope) buildMessage() (*Message, error) { + payload, err := o.encoder(o.payload) + if err != nil { + return nil, fmt.Errorf("could not encode payload: %w", err) + } + + emTargets := make([][]byte, 0) + for _, targetId := range o.targetIds { + tempID := targetId // avoid capturing loop variable + emTargets = append(emTargets, tempID[:]) + } + + channel, ok := channels.ChannelFromTopic(o.topic) + if !ok { + return nil, fmt.Errorf("could not convert topic to channel: %s", o.topic) + } + + return &Message{ + TargetIDs: emTargets, + ChannelID: channel.String(), + Payload: payload, + }, nil +} + +func (o OutgoingMessageScope) Proto() *Message { + return o.msg +} diff --git a/network/message_scope.go b/network/message_scope.go index fe9d3acb291..d39f7bc830f 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -15,172 +15,6 @@ const ( eventIDPackingPrefix = "libp2ppacking" ) -// IncomingMessageScope captures the context around an incoming message that is received by the network layer. -type IncomingMessageScope struct { - originId flow.Identifier // the origin node ID. - targetIds flow.IdentifierList // the target node IDs (i.e., intended recipients). - eventId hash.Hash // hash of the payload and channel. - msg *message.Message // the raw message received. - decodedPayload interface{} // decoded payload of the message. - protocol message.ProtocolType // the type of protocol used to receive the message. -} - -// NewIncomingScope creates a new incoming message scope. -// All errors returned by this function are benign and should not cause the node to crash, especially that it is not -// safe to crash the node when receiving a message. -// It errors if event id (i.e., hash of the payload and channel) cannot be computed, or if it fails to -// convert the target IDs from bytes slice to a flow.IdentifierList. -func NewIncomingScope(originId flow.Identifier, protocol message.ProtocolType, msg *message.Message, decodedPayload interface{}) (*IncomingMessageScope, error) { - eventId, err := EventId(channels.Channel(msg.ChannelID), msg.Payload) - if err != nil { - return nil, fmt.Errorf("could not compute event id: %w", err) - } - - targetIds, err := flow.ByteSlicesToIds(msg.TargetIDs) - if err != nil { - return nil, fmt.Errorf("could not convert target ids: %w", err) - } - return &IncomingMessageScope{ - eventId: eventId, - originId: originId, - msg: msg, - decodedPayload: decodedPayload, - protocol: protocol, - targetIds: targetIds, - }, nil -} - -func (m IncomingMessageScope) OriginId() flow.Identifier { - return m.originId -} - -func (m IncomingMessageScope) Proto() *message.Message { - return m.msg -} - -func (m IncomingMessageScope) DecodedPayload() interface{} { - return m.decodedPayload -} - -func (m IncomingMessageScope) Protocol() message.ProtocolType { - return m.protocol -} - -func (m IncomingMessageScope) Channel() channels.Channel { - return channels.Channel(m.msg.ChannelID) -} - -func (m IncomingMessageScope) Size() int { - return m.msg.Size() -} - -func (m IncomingMessageScope) TargetIDs() flow.IdentifierList { - return m.targetIds -} - -func (m IncomingMessageScope) EventID() []byte { - return m.eventId[:] -} - -func (m IncomingMessageScope) PayloadType() string { - return MessageType(m.decodedPayload) -} - -// OutgoingMessageScope captures the context around an outgoing message that is about to be sent. -type OutgoingMessageScope struct { - targetIds flow.IdentifierList // the target node IDs. - topic channels.Topic // the topic, i.e., channel-id/spork-id. - payload interface{} // the payload to be sent. - encoder func(interface{}) ([]byte, error) // the encoder to encode the payload. - msg *message.Message // raw proto message sent on wire. - protocol message.ProtocolType // the type of protocol used to send the message. -} - -// NewOutgoingScope creates a new outgoing message scope. -// All errors returned by this function are benign and should not cause the node to crash. -// It errors if the encoder fails to encode the payload into a protobuf message, or -// if the number of target IDs does not match the protocol type (i.e., unicast messages -// should have exactly one target ID, while pubsub messages should have at least one target ID). -func NewOutgoingScope( - targetIds flow.IdentifierList, - topic channels.Topic, - payload interface{}, - encoder func(interface{}) ([]byte, error), - protocolType message.ProtocolType) (*OutgoingMessageScope, error) { - scope := &OutgoingMessageScope{ - targetIds: targetIds, - topic: topic, - payload: payload, - encoder: encoder, - protocol: protocolType, - } - - if protocolType == message.ProtocolTypeUnicast { - // for unicast messages, we should have exactly one target. - if len(targetIds) != 1 { - return nil, fmt.Errorf("expected exactly one target id for unicast message, got: %d", len(targetIds)) - } - } - if protocolType == message.ProtocolTypePubSub { - // for pubsub messages, we should have at least one target. - if len(targetIds) == 0 { - return nil, fmt.Errorf("expected at least one target id for pubsub message, got: %d", len(targetIds)) - } - } - - msg, err := scope.buildMessage() - if err != nil { - return nil, fmt.Errorf("could not build message: %w", err) - } - scope.msg = msg - return scope, nil -} - -func (o OutgoingMessageScope) TargetIds() flow.IdentifierList { - return o.targetIds -} - -func (o OutgoingMessageScope) Size() int { - return o.msg.Size() -} - -func (o OutgoingMessageScope) PayloadType() string { - return MessageType(o.payload) -} - -func (o OutgoingMessageScope) Topic() channels.Topic { - return o.topic -} - -// buildMessage builds the raw proto message to be sent on the wire. -func (o OutgoingMessageScope) buildMessage() (*message.Message, error) { - payload, err := o.encoder(o.payload) - if err != nil { - return nil, fmt.Errorf("could not encode payload: %w", err) - } - - emTargets := make([][]byte, 0) - for _, targetId := range o.targetIds { - tempID := targetId // avoid capturing loop variable - emTargets = append(emTargets, tempID[:]) - } - - channel, ok := channels.ChannelFromTopic(o.topic) - if !ok { - return nil, fmt.Errorf("could not convert topic to channel: %s", o.topic) - } - - return &message.Message{ - TargetIDs: emTargets, - ChannelID: channel.String(), - Payload: payload, - }, nil -} - -func (o OutgoingMessageScope) Proto() *message.Message { - return o.msg -} - // EventId computes the event ID for a given channel and payload (i.e., the hash of the payload and channel). // All errors returned by this function are benign and should not cause the node to crash. // It errors if the hash function fails to hash the payload and channel. @@ -254,5 +88,5 @@ type OutgoingMessageScoper interface { } // Ensure structs implement the interfaces -var _ IncomingMessageScoper = &IncomingMessageScope{} -var _ OutgoingMessageScoper = &OutgoingMessageScope{} +var _ IncomingMessageScoper = &message.IncomingMessageScope{} +var _ OutgoingMessageScoper = &message.OutgoingMessageScope{} diff --git a/network/middleware.go b/network/middleware.go index be2c65281e7..f8bf3192d7b 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -10,6 +10,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/message" ) // Middleware represents the middleware layer, which manages the connections to @@ -32,13 +33,13 @@ type Middleware interface { // Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is // a more efficient candidate. // All errors returned from this function can be considered benign. - SendDirect(msg *OutgoingMessageScope) error + SendDirect(msg *message.OutgoingMessageScope) error // Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or // a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best // effort. // All errors returned from this function can be considered benign. - Publish(msg *OutgoingMessageScope) error + Publish(msg *message.OutgoingMessageScope) error // Subscribe subscribes the middleware to a channel. // No errors are expected during normal operation. @@ -71,7 +72,7 @@ type Overlay interface { // Identity returns the Identity associated with the given peer ID, if it exists Identity(peer.ID) (*flow.Identity, bool) - Receive(*IncomingMessageScope) error + Receive(*message.IncomingMessageScope) error } // Connection represents an interface to read from & write to a connection. diff --git a/network/mocknetwork/message_validator.go b/network/mocknetwork/message_validator.go index f2c78f75d20..c0cad7f1fef 100644 --- a/network/mocknetwork/message_validator.go +++ b/network/mocknetwork/message_validator.go @@ -3,7 +3,8 @@ package mocknetwork import ( - network "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" + mock "github.com/stretchr/testify/mock" ) @@ -13,11 +14,11 @@ type MessageValidator struct { } // Validate provides a mock function with given fields: msg -func (_m *MessageValidator) Validate(msg network.IncomingMessageScope) bool { +func (_m *MessageValidator) Validate(msg message.IncomingMessageScope) bool { ret := _m.Called(msg) var r0 bool - if rf, ok := ret.Get(0).(func(network.IncomingMessageScope) bool); ok { + if rf, ok := ret.Get(0).(func(message.IncomingMessageScope) bool); ok { r0 = rf(msg) } else { r0 = ret.Get(0).(bool) diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index a61deb80268..6f5a0fab528 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -4,7 +4,9 @@ package mocknetwork import ( datastore "github.com/ipfs/go-datastore" + channels "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/message" irrecoverable "github.com/onflow/flow-go/module/irrecoverable" @@ -86,11 +88,11 @@ func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpd } // Publish provides a mock function with given fields: msg -func (_m *Middleware) Publish(msg *network.OutgoingMessageScope) error { +func (_m *Middleware) Publish(msg *message.OutgoingMessageScope) error { ret := _m.Called(msg) var r0 error - if rf, ok := ret.Get(0).(func(*network.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(*message.OutgoingMessageScope) error); ok { r0 = rf(msg) } else { r0 = ret.Error(0) @@ -116,11 +118,11 @@ func (_m *Middleware) Ready() <-chan struct{} { } // SendDirect provides a mock function with given fields: msg -func (_m *Middleware) SendDirect(msg *network.OutgoingMessageScope) error { +func (_m *Middleware) SendDirect(msg *message.OutgoingMessageScope) error { ret := _m.Called(msg) var r0 error - if rf, ok := ret.Get(0).(func(*network.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(*message.OutgoingMessageScope) error); ok { r0 = rf(msg) } else { r0 = ret.Error(0) diff --git a/network/mocknetwork/overlay.go b/network/mocknetwork/overlay.go index e36869114c1..913ecbe66de 100644 --- a/network/mocknetwork/overlay.go +++ b/network/mocknetwork/overlay.go @@ -4,9 +4,9 @@ package mocknetwork import ( flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" + "github.com/onflow/flow-go/network/message" - network "github.com/onflow/flow-go/network" + mock "github.com/stretchr/testify/mock" peer "github.com/libp2p/go-libp2p/core/peer" ) @@ -59,11 +59,11 @@ func (_m *Overlay) Identity(_a0 peer.ID) (*flow.Identity, bool) { } // Receive provides a mock function with given fields: _a0 -func (_m *Overlay) Receive(_a0 *network.IncomingMessageScope) error { +func (_m *Overlay) Receive(_a0 *message.IncomingMessageScope) error { ret := _m.Called(_a0) var r0 error - if rf, ok := ret.Get(0).(func(*network.IncomingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(*message.IncomingMessageScope) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index a959a5a0496..3f5ccb64a18 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -15,7 +15,6 @@ import ( libp2pmsg "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/module/irrecoverable" mockmodule "github.com/onflow/flow-go/module/mock" - flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" @@ -155,7 +154,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { // hence expect count and not count - 1 messages to be received (one by each node, including the sender) ch := make(chan peer.ID, count) - messageScope, err := flownet.NewOutgoingScope( + messageScope, err := message.NewOutgoingScope( ids.NodeIDs(), channels.TestNetworkChannel, &libp2pmsg.TestMessage{}, diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 898a9c6916e..1b6764d9309 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) @@ -67,7 +68,7 @@ type LibP2PNode interface { // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. Unsubscribe(channel channels.Channel) error // Publish publishes the given payload on the topic. - Publish(ctx context.Context, msgScope *network.OutgoingMessageScope) error + Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error // Host returns pointer to host object of node. Host() host.Host // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 4d3ae590247..9be8b271a41 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -358,7 +358,7 @@ func (m *Middleware) OnAllowListNotification(notification *network.AllowListingU // - failed to send message to peer. // // All errors returned from this function can be considered benign. -func (m *Middleware) SendDirect(msg *network.OutgoingMessageScope) error { +func (m *Middleware) SendDirect(msg *message.OutgoingMessageScope) error { // since it is a unicast, we only need to get the first peer ID. peerID, err := m.idTranslator.GetPeerID(msg.TargetIds()[0]) if err != nil { @@ -714,7 +714,7 @@ func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID pe return } - scope, err := network.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) + scope, err := message.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) if err != nil { m.log.Error(). Err(err). @@ -728,7 +728,7 @@ func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID pe } // processMessage processes a message and eventually passes it to the overlay -func (m *Middleware) processMessage(scope *network.IncomingMessageScope) { +func (m *Middleware) processMessage(scope *message.IncomingMessageScope) { logger := m.log.With(). Str("channel", scope.Channel().String()). Str("type", scope.Protocol().String()). @@ -763,7 +763,7 @@ func (m *Middleware) processMessage(scope *network.IncomingMessageScope) { // - the libP2P node fails to publish the message. // // All errors returned from this function can be considered benign. -func (m *Middleware) Publish(msg *network.OutgoingMessageScope) error { +func (m *Middleware) Publish(msg *message.OutgoingMessageScope) error { return m.libP2PNode.Publish(m.ctx, msg) } diff --git a/network/p2p/middleware/middleware_test.go b/network/p2p/middleware/middleware_test.go index 9b9cc1dbc0e..840d0c3bb07 100644 --- a/network/p2p/middleware/middleware_test.go +++ b/network/p2p/middleware/middleware_test.go @@ -8,7 +8,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/messages" - "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/middleware" @@ -18,7 +17,7 @@ import ( // TestChunkDataPackMaxMessageSize tests that the max message size for a chunk data pack response is set to the large message size. func TestChunkDataPackMaxMessageSize(t *testing.T) { // creates an outgoing chunk data pack response message (imitating an EN is sending a chunk data pack response to VN). - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, channels.ProvideChunks, &messages.ChunkDataResponse{ diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index e67614d6212..b496c764bbd 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -5,6 +5,7 @@ package mockp2p import ( component "github.com/onflow/flow-go/module/component" channels "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/message" context "context" @@ -285,11 +286,11 @@ func (_m *LibP2PNode) PeerScoreExposer() p2p.PeerScoreExposer { } // Publish provides a mock function with given fields: ctx, msgScope -func (_m *LibP2PNode) Publish(ctx context.Context, msgScope *flow_gonetwork.OutgoingMessageScope) error { +func (_m *LibP2PNode) Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error { ret := _m.Called(ctx, msgScope) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *flow_gonetwork.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, *message.OutgoingMessageScope) error); ok { r0 = rf(ctx, msgScope) } else { r0 = ret.Error(0) diff --git a/network/p2p/network.go b/network/p2p/network.go index f564aa0340d..8e5b0b102f1 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -378,7 +378,7 @@ func (n *Network) Identity(pid peer.ID) (*flow.Identity, bool) { return n.identityProvider.ByPeerID(pid) } -func (n *Network) Receive(msg *network.IncomingMessageScope) error { +func (n *Network) Receive(msg *message.IncomingMessageScope) error { n.metrics.InboundMessageReceived(msg.Size(), msg.Channel().String(), msg.Protocol().String(), msg.PayloadType()) err := n.processNetworkMessage(msg) @@ -388,7 +388,7 @@ func (n *Network) Receive(msg *network.IncomingMessageScope) error { return nil } -func (n *Network) processNetworkMessage(msg *network.IncomingMessageScope) error { +func (n *Network) processNetworkMessage(msg *message.IncomingMessageScope) error { // checks the cache for deduplication and adds the message if not already present if !n.receiveCache.Add(msg.EventID()) { // drops duplicate message @@ -429,7 +429,7 @@ func (n *Network) UnicastOnChannel(channel channels.Channel, payload interface{} return nil } - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{targetID}, channels.TopicFromChannel(channel, n.sporkId), payload, @@ -509,7 +509,7 @@ func (n *Network) sendOnChannel(channel channels.Channel, msg interface{}, targe Msg("sending new message on channel") // generate network message (encoding) based on list of recipients - scope, err := network.NewOutgoingScope( + scope, err := message.NewOutgoingScope( targetIDs, channels.TopicFromChannel(channel, n.sporkId), msg, diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index ed98a658b69..7e868b6ca90 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -24,6 +24,7 @@ import ( flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2putils" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/p2pnode/internal" "github.com/onflow/flow-go/network/p2p/unicast/protocols" @@ -341,7 +342,7 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { // Publish publishes the given payload on the topic. // All errors returned from this function can be considered benign. -func (n *Node) Publish(ctx context.Context, msgScope *flownet.OutgoingMessageScope) error { +func (n *Node) Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error { lg := n.logger.With(). Str("topic", msgScope.Topic().String()). Interface("proto_message", msgScope.Proto()). diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 37cfd52a6b5..d43eeeab800 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -13,7 +13,6 @@ import ( "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/mock" - flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/message" @@ -95,7 +94,7 @@ func TestFullGossipSubConnectivity(t *testing.T) { // checks end-to-end message delivery works // each node sends a distinct message to all and checks that all nodes receive it. for _, node := range nodes { - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), channels.PushBlocks, unittest.ProposalFixture(), @@ -216,7 +215,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // let nodes reside on a full topology, hence no partition is caused by the topology. p2ptest.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( allIds.NodeIDs(), channels.PushBlocks, unittest.ProposalFixture(), diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 795165ac9cc..b8243fbdadc 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -132,8 +131,8 @@ func TestSubscriptionValidator_InvalidSubscriptions(t *testing.T) { for _, role := range flow.Roles() { peerId := p2pfixtures.PeerIdFixture(t) unauthorizedChannels := channels.Channels(). // all channels - ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role - ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. + ExcludeChannels(channels.ChannelsByRole(role)). // excluding the channels for the role + ExcludePattern(regexp.MustCompile("^(test).*")) // excluding the test channels. sporkID := unittest.IdentifierFixture() unauthorizedTopics := make([]string, 0, len(unauthorizedChannels)) for _, channel := range unauthorizedChannels { @@ -239,7 +238,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // let the subscriptions be established time.Sleep(2 * time.Second) - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), channels.PushBlocks, unittest.ProposalFixture(), @@ -268,7 +267,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // consensus node publishes another proposal, but this time, it should not reach verification node. // since upon an unauthorized subscription, verification node should have slashed consensus node on // the GossipSub scoring protocol. - outgoingMessageScope, err = flownet.NewOutgoingScope( + outgoingMessageScope, err = message.NewOutgoingScope( ids.NodeIDs(), channels.PushBlocks, unittest.ProposalFixture(), @@ -283,7 +282,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // moreover, a verification node publishing a message to the request chunk topic should not reach consensus node. // however, both verification nodes should receive the message. - outgoingMessageScope, err = flownet.NewOutgoingScope( + outgoingMessageScope, err = message.NewOutgoingScope( ids.NodeIDs(), channels.RequestChunks, &messages.ChunkDataRequest{ diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index fdb098ea012..59cc739e9e3 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -14,7 +14,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" - flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/message" @@ -81,7 +80,7 @@ func TestFilterSubscribe(t *testing.T) { testPublish := func(wg *sync.WaitGroup, from p2p.LibP2PNode, sub p2p.Subscription) { - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), channels.SyncCommittee, []byte("hello"), diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index c5684a18480..39344980e87 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -587,7 +587,7 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. for i := 0; i < count; i++ { // creates a unique message to be published by the node payload := messageFactory() - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, topic, payload, @@ -629,7 +629,7 @@ func EnsurePubsubMessageExchangeFromNode(t *testing.T, ctx context.Context, send for i := 0; i < count; i++ { // creates a unique message to be published by the node payload := messageFactory() - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( flow.IdentifierList{}, topic, payload, @@ -713,7 +713,7 @@ func EnsureNoPubsubMessageExchange( // creates a unique message to be published by the node. payload := messageFactory() - outgoingMessageScope, err := flownet.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( toIdentifiers, topic, payload, diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index d637114366e..ba5778b17b6 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -12,7 +12,6 @@ import ( "github.com/onflow/flow-go/model/flow" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" @@ -237,7 +236,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { time.Sleep(time.Second) // assert that node 1 can successfully send a message to node 2 via PubSub - outgoingMessageScope, err := network.NewOutgoingScope( + outgoingMessageScope, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, topicBeforeSpork, &libp2pmessage.TestMessage{ @@ -278,7 +277,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { require.NoError(t, err) // assert that node 1 can no longer send a message to node 2 via PubSub - outgoingMessageScope, err = network.NewOutgoingScope( + outgoingMessageScope, err = message.NewOutgoingScope( flow.IdentifierList{id2.NodeID}, topicAfterSpork, &libp2pmessage.TestMessage{ diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index e3d47c7c709..8b62e5465b3 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -94,7 +94,7 @@ func TestTopicValidator_Unstaked(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -153,7 +153,7 @@ func TestTopicValidator_PublicChannel(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, &messages.SyncRequest{Nonce: 0, Height: 0}, @@ -225,7 +225,7 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { defer cancel5s() // create a dummy block proposal to publish from our SN node - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -285,7 +285,7 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() // create a dummy block proposal to publish from our SN node - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -383,7 +383,7 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { // sn2 publishes the block proposal, sn1 and an1 should receive the message because // SN nodes are authorized to send block proposals // create a dummy block proposal to publish from our SN node - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -410,7 +410,7 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { // the access node now publishes the block proposal message, AN are not authorized to publish block proposals // the message should be rejected by the topic validator on sn1 - outgoingMessageScope2, err := network.NewOutgoingScope( + outgoingMessageScope2, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -504,7 +504,7 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { // create a dummy block proposal to publish from our SN node // sn2 publishes the block proposal on the sync committee channel - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -594,7 +594,7 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // sn2 publishes the block proposal, sn1 and an1 should receive the message because // SN nodes are authorized to send block proposals // create a dummy block proposal to publish from our SN node - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -619,7 +619,7 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // "eject" sn2 to ensure messages published by ejected nodes get rejected identity2.Ejected = true - outgoingMessageScope3, err := network.NewOutgoingScope( + outgoingMessageScope3, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, unittest.ProposalFixture(), @@ -704,7 +704,7 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { defer cancel5s() // create a dummy sync request to publish from our LN node - outgoingMessageScope1, err := network.NewOutgoingScope( + outgoingMessageScope1, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, topic, &messages.RangeRequest{}, diff --git a/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go b/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go index 0016ae49f63..40befc19b48 100644 --- a/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go +++ b/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go @@ -6,7 +6,6 @@ import ( "github.com/onflow/flow-go/model/flow" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/message" "github.com/stretchr/testify/require" @@ -37,7 +36,7 @@ func TestBandWidthRateLimiter_Allow(t *testing.T) { b[i] = byte('X') } - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, channels.TestNetworkChannel, &libp2pmessage.TestMessage{ @@ -90,7 +89,7 @@ func TestBandWidthRateLimiter_IsRateLimited(t *testing.T) { require.False(t, bandwidthRateLimiter.IsRateLimited(peerID)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, channels.TestNetworkChannel, &libp2pmessage.TestMessage{ diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 65ce4f7cca3..a853659784b 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -226,7 +226,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // needed to enable ID translation m.providers[0].SetIdentities(idList) - outMsg, err := network.NewOutgoingScope( + outMsg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -354,7 +354,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream // handling of streams. for i := 0; i < 10; i++ { - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -378,7 +378,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // eventually the rate limited node should be able to reconnect and send messages require.Eventually(m.T(), func() bool { - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -490,7 +490,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { b[i] = byte('X') } - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -528,7 +528,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // eventually the rate limited node should be able to reconnect and send messages require.Eventually(m.T(), func() bool { - msg, err = network.NewOutgoingScope( + msg, err = message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -590,7 +590,7 @@ func (m *MiddlewareTestSuite) TestPing() { lastNodeIndex := m.size - 1 expectedPayload := "TestPingContentReception" - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -604,7 +604,7 @@ func (m *MiddlewareTestSuite) TestPing() { Run(func(args mockery.Arguments) { receiveWG.Done() - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -648,7 +648,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { sendWG.Add(1) expectedPayloadText := fmt.Sprintf("hello from: %d", i) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -662,7 +662,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { Run(func(args mockery.Arguments) { receiveWG.Done() - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -711,7 +711,7 @@ func (m *MiddlewareTestSuite) TestEcho() { // message sent from first node to the last node. expectedSendMsg := "TestEcho" - sendMsg, err := network.NewOutgoingScope( + sendMsg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, testChannel, &libp2pmessage.TestMessage{ @@ -723,7 +723,7 @@ func (m *MiddlewareTestSuite) TestEcho() { // reply from last node to the first node. expectedReplyMsg := "TestEcho response" - replyMsg, err := network.NewOutgoingScope( + replyMsg, err := message.NewOutgoingScope( flow.IdentifierList{firstNode}, testChannel, &libp2pmessage.TestMessage{ @@ -739,7 +739,7 @@ func (m *MiddlewareTestSuite) TestEcho() { wg.Done() // sanity checks the message content. - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -762,7 +762,7 @@ func (m *MiddlewareTestSuite) TestEcho() { Run(func(args mockery.Arguments) { wg.Done() // sanity checks the message content. - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -805,7 +805,7 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { Text: string(payload), } - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, testChannel, event, @@ -833,7 +833,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { targetSize := uint64(middleware.DefaultMaxUnicastMsgSize) + 1000 event := unittest.ChunkDataResponseMsgFixture(unittest.IdentifierFixture(), unittest.WithApproximateSize(targetSize)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{targetNode}, channels.ProvideChunks, event, @@ -845,7 +845,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { ch := make(chan struct{}) m.ov[targetIndex].On("Receive", mockery.Anything).Return(nil).Once(). Run(func(args mockery.Arguments) { - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), channels.ProvideChunks, msg.Channel()) @@ -886,7 +886,7 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_Publish() { event := &libp2pmessage.TestMessage{ Text: string(payload), } - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, testChannel, event, @@ -921,7 +921,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { <-msgRcvd } - message1, err := network.NewOutgoingScope( + message1, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, testChannel, &libp2pmessage.TestMessage{ @@ -932,7 +932,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { require.NoError(m.T(), err) m.ov[last].On("Receive", mockery.Anything).Return(nil).Run(func(args mockery.Arguments) { - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), firstNode, msg.OriginId()) msgRcvd <- struct{}{} @@ -949,7 +949,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { assert.NoError(m.T(), err) // create and send a new message on the channel from the origin node - message2, err := network.NewOutgoingScope( + message2, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, testChannel, &libp2pmessage.TestMessage{ diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 976f702c9de..1efafc75bd5 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -157,7 +157,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -219,7 +219,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -280,7 +280,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee require.NoError(u.T(), u.receiverMW.Subscribe(channel)) require.NoError(u.T(), u.senderMW.Subscribe(channel)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, channel, &libp2pmessage.TestMessage{ @@ -343,7 +343,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -413,7 +413,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -445,7 +445,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() u.setupMiddlewaresAndProviders(slashingViolationsConsumer) expectedPayload := "hello" - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, testChannel, &libp2pmessage.TestMessage{ @@ -470,7 +470,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() Run(func(args mockery.Arguments) { close(u.waitCh) - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(u.T(), ok) require.Equal(u.T(), testChannel, msg.Channel()) // channel @@ -543,7 +543,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel payload := unittest.ProposalFixture() - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, channel, payload, @@ -600,7 +600,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu channel := channels.TestNetworkChannel - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, channel, &libp2pmessage.TestMessage{ @@ -626,7 +626,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs u.setupMiddlewaresAndProviders(slashingViolationsConsumer) channel := channels.RequestReceiptsByBlockID - msg, err := network.NewOutgoingScope( + msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, channel, &messages.EntityRequest{}, @@ -652,7 +652,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs Run(func(args mockery.Arguments) { close(u.waitCh) - msg, ok := args[0].(*network.IncomingMessageScope) + msg, ok := args[0].(*message.IncomingMessageScope) require.True(u.T(), ok) require.Equal(u.T(), channel, msg.Channel()) // channel diff --git a/network/validator.go b/network/validator.go index 0d40b9290c5..3a9167ecde9 100644 --- a/network/validator.go +++ b/network/validator.go @@ -1,8 +1,10 @@ package network +import "github.com/onflow/flow-go/network/message" + // MessageValidator validates the incoming message. Message validation happens in the middleware right before it is // delivered to the network. type MessageValidator interface { // Validate validates the message and returns true if the message is to be retained and false if it needs to be dropped - Validate(msg IncomingMessageScope) bool + Validate(msg message.IncomingMessageScope) bool } diff --git a/network/validator/any_validator.go b/network/validator/any_validator.go index 92a09fd3ef0..c02fd3ab555 100644 --- a/network/validator/any_validator.go +++ b/network/validator/any_validator.go @@ -2,6 +2,7 @@ package validator import ( "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*AnyValidator)(nil) @@ -17,7 +18,7 @@ func NewAnyValidator(validators ...network.MessageValidator) network.MessageVali } } -func (v AnyValidator) Validate(msg network.IncomingMessageScope) bool { +func (v AnyValidator) Validate(msg message.IncomingMessageScope) bool { for _, validator := range v.validators { if validator.Validate(msg) { return true diff --git a/network/validator/not_validator.go b/network/validator/not_validator.go index b439e7cae54..14ab7460349 100644 --- a/network/validator/not_validator.go +++ b/network/validator/not_validator.go @@ -2,6 +2,7 @@ package validator import ( "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*NotValidator)(nil) @@ -17,6 +18,6 @@ func NewNotValidator(validator network.MessageValidator) network.MessageValidato } } -func (n NotValidator) Validate(msg network.IncomingMessageScope) bool { +func (n NotValidator) Validate(msg message.IncomingMessageScope) bool { return !n.validator.Validate(msg) } diff --git a/network/validator/origin_validator.go b/network/validator/origin_validator.go index 857f7b58271..0963fd9b68c 100644 --- a/network/validator/origin_validator.go +++ b/network/validator/origin_validator.go @@ -3,6 +3,7 @@ package validator import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*OriginValidator)(nil) @@ -17,6 +18,6 @@ func NewOriginValidator(provider module.IdentifierProvider) network.MessageValid return &OriginValidator{provider} } -func (v OriginValidator) Validate(msg network.IncomingMessageScope) bool { +func (v OriginValidator) Validate(msg message.IncomingMessageScope) bool { return v.idProvider.Identifiers().Contains(msg.OriginId()) } diff --git a/network/validator/sender_validator.go b/network/validator/sender_validator.go index 098e1dd9c49..3db517e1c8c 100644 --- a/network/validator/sender_validator.go +++ b/network/validator/sender_validator.go @@ -3,6 +3,7 @@ package validator import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = &SenderValidator{} @@ -22,7 +23,7 @@ func ValidateSender(sender flow.Identifier) network.MessageValidator { } // Validate returns true if the message origin id is the same as the sender ID. -func (sv *SenderValidator) Validate(msg network.IncomingMessageScope) bool { +func (sv *SenderValidator) Validate(msg message.IncomingMessageScope) bool { return sv.sender == msg.OriginId() } diff --git a/network/validator/target_validator.go b/network/validator/target_validator.go index 5a9b1ab73f9..4bfab62bb1b 100644 --- a/network/validator/target_validator.go +++ b/network/validator/target_validator.go @@ -5,6 +5,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/logging" ) @@ -28,7 +29,7 @@ func ValidateTarget(log zerolog.Logger, target flow.Identifier) network.MessageV } // Validate returns true if the message is intended for the given target ID else it returns false -func (tv *TargetValidator) Validate(msg network.IncomingMessageScope) bool { +func (tv *TargetValidator) Validate(msg message.IncomingMessageScope) bool { for _, t := range msg.TargetIDs() { if tv.target == t { return true From 49d464d268677c0b89eabe4a10764c5a3acf20ad Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:31:21 -0700 Subject: [PATCH 515/815] refactors message scopes to use the interface type --- network/message/message_scope.go | 4 ++++ network/message_scope.go | 12 ++++-------- network/middleware.go | 7 +++---- network/p2p/libp2pNode.go | 3 +-- network/p2p/middleware/middleware.go | 14 +++++++++----- network/p2p/network.go | 4 ++-- network/p2p/p2pnode/libp2pNode.go | 3 +-- network/test/middleware_test.go | 12 ++++++------ network/test/unicast_authorization_test.go | 4 ++-- network/validator.go | 4 +--- network/validator/any_validator.go | 3 +-- network/validator/not_validator.go | 3 +-- network/validator/sender_validator.go | 3 +-- network/validator/target_validator.go | 3 +-- 14 files changed, 37 insertions(+), 42 deletions(-) diff --git a/network/message/message_scope.go b/network/message/message_scope.go index aa15c0112a2..340a4184ea0 100644 --- a/network/message/message_scope.go +++ b/network/message/message_scope.go @@ -9,6 +9,10 @@ import ( "github.com/onflow/flow-go/network/channels" ) +// Ensure structs implement the interfaces +var _ network.IncomingMessageScope = &IncomingMessageScope{} +var _ network.OutgoingMessageScope = &OutgoingMessageScope{} + // IncomingMessageScope captures the context around an incoming message that is received by the network layer. type IncomingMessageScope struct { originId flow.Identifier // the origin node ID. diff --git a/network/message_scope.go b/network/message_scope.go index d39f7bc830f..e09595ff110 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -39,8 +39,8 @@ func MessageType(decodedPayload interface{}) string { return strings.TrimLeft(fmt.Sprintf("%T", decodedPayload), "*") } -// IncomingMessageScoper defines the interface for handling incoming message scope. -type IncomingMessageScoper interface { +// IncomingMessageScope defines the interface for handling incoming message scope. +type IncomingMessageScope interface { // OriginId returns the origin node ID. OriginId() flow.Identifier @@ -69,8 +69,8 @@ type IncomingMessageScoper interface { PayloadType() string } -// OutgoingMessageScoper defines the interface for handling outgoing message scope. -type OutgoingMessageScoper interface { +// OutgoingMessageScope defines the interface for handling outgoing message scope. +type OutgoingMessageScope interface { // TargetIds returns the target node IDs. TargetIds() flow.IdentifierList @@ -86,7 +86,3 @@ type OutgoingMessageScoper interface { // Proto returns the raw proto message sent on the wire. Proto() *message.Message } - -// Ensure structs implement the interfaces -var _ IncomingMessageScoper = &message.IncomingMessageScope{} -var _ OutgoingMessageScoper = &message.OutgoingMessageScope{} diff --git a/network/middleware.go b/network/middleware.go index f8bf3192d7b..a7ac5820b19 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -10,7 +10,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/message" ) // Middleware represents the middleware layer, which manages the connections to @@ -33,13 +32,13 @@ type Middleware interface { // Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is // a more efficient candidate. // All errors returned from this function can be considered benign. - SendDirect(msg *message.OutgoingMessageScope) error + SendDirect(msg OutgoingMessageScope) error // Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or // a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best // effort. // All errors returned from this function can be considered benign. - Publish(msg *message.OutgoingMessageScope) error + Publish(msg OutgoingMessageScope) error // Subscribe subscribes the middleware to a channel. // No errors are expected during normal operation. @@ -72,7 +71,7 @@ type Overlay interface { // Identity returns the Identity associated with the given peer ID, if it exists Identity(peer.ID) (*flow.Identity, bool) - Receive(*message.IncomingMessageScope) error + Receive(IncomingMessageScope) error } // Connection represents an interface to read from & write to a connection. diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 1b6764d9309..0645be384e1 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -16,7 +16,6 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) @@ -68,7 +67,7 @@ type LibP2PNode interface { // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. Unsubscribe(channel channels.Channel) error // Publish publishes the given payload on the topic. - Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error + Publish(ctx context.Context, scope network.OutgoingMessageScope) error // Host returns pointer to host object of node. Host() host.Host // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 9be8b271a41..49e1a2b0835 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -358,7 +358,7 @@ func (m *Middleware) OnAllowListNotification(notification *network.AllowListingU // - failed to send message to peer. // // All errors returned from this function can be considered benign. -func (m *Middleware) SendDirect(msg *message.OutgoingMessageScope) error { +func (m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { // since it is a unicast, we only need to get the first peer ID. peerID, err := m.idTranslator.GetPeerID(msg.TargetIds()[0]) if err != nil { @@ -381,7 +381,11 @@ func (m *Middleware) SendDirect(msg *message.OutgoingMessageScope) error { // protect the underlying connection from being inadvertently pruned by the peer manager while the stream and // connection creation is being attempted, and remove it from protected list once stream created. - tag := fmt.Sprintf("%v:%v", msg.Channel(), msg.PayloadType()) + channel, ok := channels.ChannelFromTopic(msg.Topic()) + if !ok { + return fmt.Errorf("could not find channel for topic %s", msg.Topic()) + } + tag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) m.libP2PNode.Host().ConnManager().Protect(peerID, tag) defer m.libP2PNode.Host().ConnManager().Unprotect(peerID, tag) @@ -728,7 +732,7 @@ func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID pe } // processMessage processes a message and eventually passes it to the overlay -func (m *Middleware) processMessage(scope *message.IncomingMessageScope) { +func (m *Middleware) processMessage(scope network.IncomingMessageScope) { logger := m.log.With(). Str("channel", scope.Channel().String()). Str("type", scope.Protocol().String()). @@ -739,7 +743,7 @@ func (m *Middleware) processMessage(scope *message.IncomingMessageScope) { // run through all the message validators for _, v := range m.validators { // if any one fails, stop message propagation - if !v.Validate(*scope) { + if !v.Validate(scope) { logger.Debug().Msg("new message filtered by message validators") return } @@ -763,7 +767,7 @@ func (m *Middleware) processMessage(scope *message.IncomingMessageScope) { // - the libP2P node fails to publish the message. // // All errors returned from this function can be considered benign. -func (m *Middleware) Publish(msg *message.OutgoingMessageScope) error { +func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { return m.libP2PNode.Publish(m.ctx, msg) } diff --git a/network/p2p/network.go b/network/p2p/network.go index 8e5b0b102f1..a3225f59925 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -378,7 +378,7 @@ func (n *Network) Identity(pid peer.ID) (*flow.Identity, bool) { return n.identityProvider.ByPeerID(pid) } -func (n *Network) Receive(msg *message.IncomingMessageScope) error { +func (n *Network) Receive(msg network.IncomingMessageScope) error { n.metrics.InboundMessageReceived(msg.Size(), msg.Channel().String(), msg.Protocol().String(), msg.PayloadType()) err := n.processNetworkMessage(msg) @@ -388,7 +388,7 @@ func (n *Network) Receive(msg *message.IncomingMessageScope) error { return nil } -func (n *Network) processNetworkMessage(msg *message.IncomingMessageScope) error { +func (n *Network) processNetworkMessage(msg network.IncomingMessageScope) error { // checks the cache for deduplication and adds the message if not already present if !n.receiveCache.Add(msg.EventID()) { // drops duplicate message diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 7e868b6ca90..6a772217c68 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -24,7 +24,6 @@ import ( flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2putils" - "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/p2pnode/internal" "github.com/onflow/flow-go/network/p2p/unicast/protocols" @@ -342,7 +341,7 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { // Publish publishes the given payload on the topic. // All errors returned from this function can be considered benign. -func (n *Node) Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error { +func (n *Node) Publish(ctx context.Context, msgScope flownet.OutgoingMessageScope) error { lg := n.logger.With(). Str("topic", msgScope.Topic().String()). Interface("proto_message", msgScope.Proto()). diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index a853659784b..585b3418d9a 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -604,7 +604,7 @@ func (m *MiddlewareTestSuite) TestPing() { Run(func(args mockery.Arguments) { receiveWG.Done() - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -662,7 +662,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { Run(func(args mockery.Arguments) { receiveWG.Done() - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -739,7 +739,7 @@ func (m *MiddlewareTestSuite) TestEcho() { wg.Done() // sanity checks the message content. - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -762,7 +762,7 @@ func (m *MiddlewareTestSuite) TestEcho() { Run(func(args mockery.Arguments) { wg.Done() // sanity checks the message content. - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), testChannel, msg.Channel()) // channel @@ -845,7 +845,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { ch := make(chan struct{}) m.ov[targetIndex].On("Receive", mockery.Anything).Return(nil).Once(). Run(func(args mockery.Arguments) { - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), channels.ProvideChunks, msg.Channel()) @@ -932,7 +932,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { require.NoError(m.T(), err) m.ov[last].On("Receive", mockery.Anything).Return(nil).Run(func(args mockery.Arguments) { - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) require.Equal(m.T(), firstNode, msg.OriginId()) msgRcvd <- struct{}{} diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 1efafc75bd5..0dfed501f4e 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -470,7 +470,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() Run(func(args mockery.Arguments) { close(u.waitCh) - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(u.T(), ok) require.Equal(u.T(), testChannel, msg.Channel()) // channel @@ -652,7 +652,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs Run(func(args mockery.Arguments) { close(u.waitCh) - msg, ok := args[0].(*message.IncomingMessageScope) + msg, ok := args[0].(network.IncomingMessageScope) require.True(u.T(), ok) require.Equal(u.T(), channel, msg.Channel()) // channel diff --git a/network/validator.go b/network/validator.go index 3a9167ecde9..0d40b9290c5 100644 --- a/network/validator.go +++ b/network/validator.go @@ -1,10 +1,8 @@ package network -import "github.com/onflow/flow-go/network/message" - // MessageValidator validates the incoming message. Message validation happens in the middleware right before it is // delivered to the network. type MessageValidator interface { // Validate validates the message and returns true if the message is to be retained and false if it needs to be dropped - Validate(msg message.IncomingMessageScope) bool + Validate(msg IncomingMessageScope) bool } diff --git a/network/validator/any_validator.go b/network/validator/any_validator.go index c02fd3ab555..92a09fd3ef0 100644 --- a/network/validator/any_validator.go +++ b/network/validator/any_validator.go @@ -2,7 +2,6 @@ package validator import ( "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*AnyValidator)(nil) @@ -18,7 +17,7 @@ func NewAnyValidator(validators ...network.MessageValidator) network.MessageVali } } -func (v AnyValidator) Validate(msg message.IncomingMessageScope) bool { +func (v AnyValidator) Validate(msg network.IncomingMessageScope) bool { for _, validator := range v.validators { if validator.Validate(msg) { return true diff --git a/network/validator/not_validator.go b/network/validator/not_validator.go index 14ab7460349..b439e7cae54 100644 --- a/network/validator/not_validator.go +++ b/network/validator/not_validator.go @@ -2,7 +2,6 @@ package validator import ( "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*NotValidator)(nil) @@ -18,6 +17,6 @@ func NewNotValidator(validator network.MessageValidator) network.MessageValidato } } -func (n NotValidator) Validate(msg message.IncomingMessageScope) bool { +func (n NotValidator) Validate(msg network.IncomingMessageScope) bool { return !n.validator.Validate(msg) } diff --git a/network/validator/sender_validator.go b/network/validator/sender_validator.go index 3db517e1c8c..098e1dd9c49 100644 --- a/network/validator/sender_validator.go +++ b/network/validator/sender_validator.go @@ -3,7 +3,6 @@ package validator import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = &SenderValidator{} @@ -23,7 +22,7 @@ func ValidateSender(sender flow.Identifier) network.MessageValidator { } // Validate returns true if the message origin id is the same as the sender ID. -func (sv *SenderValidator) Validate(msg message.IncomingMessageScope) bool { +func (sv *SenderValidator) Validate(msg network.IncomingMessageScope) bool { return sv.sender == msg.OriginId() } diff --git a/network/validator/target_validator.go b/network/validator/target_validator.go index 4bfab62bb1b..5a9b1ab73f9 100644 --- a/network/validator/target_validator.go +++ b/network/validator/target_validator.go @@ -5,7 +5,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/logging" ) @@ -29,7 +28,7 @@ func ValidateTarget(log zerolog.Logger, target flow.Identifier) network.MessageV } // Validate returns true if the message is intended for the given target ID else it returns false -func (tv *TargetValidator) Validate(msg message.IncomingMessageScope) bool { +func (tv *TargetValidator) Validate(msg network.IncomingMessageScope) bool { for _, t := range msg.TargetIDs() { if tv.target == t { return true From 8071ab953f289e308e7cce45d5d229139c793bb8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:35:03 -0700 Subject: [PATCH 516/815] resolves import cycles --- insecure/fixtures.go | 3 ++- insecure/orchestrator/network.go | 5 ++-- network/cache/rcvcache_test.go | 14 +++++------ network/message/message_scope.go | 36 +++++++++++++++++++++++++--- network/message_scope.go | 33 ------------------------- network/p2p/middleware/middleware.go | 2 +- network/test/middleware_test.go | 6 ++--- utils/unittest/fixtures.go | 4 ++-- 8 files changed, 51 insertions(+), 52 deletions(-) diff --git a/insecure/fixtures.go b/insecure/fixtures.go index 3abe3392f8c..9e85deb3bad 100644 --- a/insecure/fixtures.go +++ b/insecure/fixtures.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" + message2 "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/unittest" ) @@ -40,7 +41,7 @@ func EgressMessageFixture(t *testing.T, codec network.Codec, protocol Protocol, // encodes event to create payload payload, err := codec.Encode(content) require.NoError(t, err) - eventIDHash, err := network.EventId(channel, payload) + eventIDHash, err := message2.EventId(channel, payload) require.NoError(t, err) eventID := flow.HashToID(eventIDHash) diff --git a/insecure/orchestrator/network.go b/insecure/orchestrator/network.go index a41a4781c2c..1c8ca95e417 100644 --- a/insecure/orchestrator/network.go +++ b/insecure/orchestrator/network.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" + message2 "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/logging" ) @@ -159,7 +160,7 @@ func (on *Network) processEgressMessage(message *insecure.EgressMessage) error { channel := channels.Channel(message.ChannelID) - egressEventIDHash, err := network.EventId(channel, message.Payload) + egressEventIDHash, err := message2.EventId(channel, message.Payload) if err != nil { return fmt.Errorf("could not create egress event ID: %w", err) } @@ -205,7 +206,7 @@ func (on *Network) processIngressMessage(message *insecure.IngressMessage) error defer on.orchestratorMutex.Unlock() channel := channels.Channel(message.ChannelID) - ingressEventIDHash, err := network.EventId(channel, message.Payload) + ingressEventIDHash, err := message2.EventId(channel, message.Payload) if err != nil { return fmt.Errorf("could not create ingress event ID: %w", err) } diff --git a/network/cache/rcvcache_test.go b/network/cache/rcvcache_test.go index 32551e1264f..28c65c52f72 100644 --- a/network/cache/rcvcache_test.go +++ b/network/cache/rcvcache_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/message" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,20 +43,20 @@ func (r *ReceiveCacheTestSuite) SetupTest() { // TestSingleElementAdd adds a single element to the cache and verifies its existence. func (r *ReceiveCacheTestSuite) TestSingleElementAdd() { - eventID, err := network.EventId(channels.Channel("0"), []byte("event-1")) + eventID, err := message.EventId(channels.Channel("0"), []byte("event-1")) require.NoError(r.T(), err) assert.True(r.Suite.T(), r.c.Add(eventID)) assert.False(r.Suite.T(), r.c.Add(eventID)) // same channel but different event should be treated as unseen - eventID2, err := network.EventId(channels.Channel("0"), []byte("event-2")) + eventID2, err := message.EventId(channels.Channel("0"), []byte("event-2")) require.NoError(r.T(), err) assert.True(r.Suite.T(), r.c.Add(eventID2)) assert.False(r.Suite.T(), r.c.Add(eventID2)) // same event but different channels should be treated as unseen - eventID3, err := network.EventId(channels.Channel("1"), []byte("event-2")) + eventID3, err := message.EventId(channels.Channel("1"), []byte("event-2")) require.NoError(r.T(), err) assert.True(r.Suite.T(), r.c.Add(eventID3)) assert.False(r.Suite.T(), r.c.Add(eventID3)) @@ -64,7 +64,7 @@ func (r *ReceiveCacheTestSuite) TestSingleElementAdd() { // TestNoneExistence evaluates the correctness of cache operation against non-existing element func (r *ReceiveCacheTestSuite) TestNoneExistence() { - eventID, err := network.EventId(channels.Channel("1"), []byte("non-existing event")) + eventID, err := message.EventId(channels.Channel("1"), []byte("non-existing event")) require.NoError(r.T(), err) // adding new event to cache should return true @@ -76,7 +76,7 @@ func (r *ReceiveCacheTestSuite) TestMultipleElementAdd() { // creates and populates slice of 10 events eventIDs := make([]hash.Hash, 0) for i := 0; i < r.size; i++ { - eventID, err := network.EventId(channels.Channel("1"), []byte(fmt.Sprintf("event-%d", i))) + eventID, err := message.EventId(channels.Channel("1"), []byte(fmt.Sprintf("event-%d", i))) require.NoError(r.T(), err) eventIDs = append(eventIDs, eventID) @@ -114,7 +114,7 @@ func (r *ReceiveCacheTestSuite) TestLRU() { eventIDs := make([]hash.Hash, 0) total := r.size + 1 for i := 0; i < total; i++ { - eventID, err := network.EventId(channels.Channel("1"), []byte(fmt.Sprintf("event-%d", i))) + eventID, err := message.EventId(channels.Channel("1"), []byte(fmt.Sprintf("event-%d", i))) require.NoError(r.T(), err) eventIDs = append(eventIDs, eventID) diff --git a/network/message/message_scope.go b/network/message/message_scope.go index 340a4184ea0..3c1076f0d79 100644 --- a/network/message/message_scope.go +++ b/network/message/message_scope.go @@ -2,6 +2,7 @@ package message import ( "fmt" + "strings" "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" @@ -13,6 +14,35 @@ import ( var _ network.IncomingMessageScope = &IncomingMessageScope{} var _ network.OutgoingMessageScope = &OutgoingMessageScope{} +const ( + // eventIDPackingPrefix is used as a salt to generate payload hash for messages. + eventIDPackingPrefix = "libp2ppacking" +) + +// EventId computes the event ID for a given channel and payload (i.e., the hash of the payload and channel). +// All errors returned by this function are benign and should not cause the node to crash. +// It errors if the hash function fails to hash the payload and channel. +func EventId(channel channels.Channel, payload []byte) (hash.Hash, error) { + // use a hash with an engine-specific salt to get the payload hash + h := hash.NewSHA3_384() + _, err := h.Write([]byte(eventIDPackingPrefix + channel)) + if err != nil { + return nil, fmt.Errorf("could not hash channel as salt: %w", err) + } + + _, err = h.Write(payload) + if err != nil { + return nil, fmt.Errorf("could not hash event: %w", err) + } + + return h.SumHash(), nil +} + +// MessageType returns the type of the message payload. +func MessageType(decodedPayload interface{}) string { + return strings.TrimLeft(fmt.Sprintf("%T", decodedPayload), "*") +} + // IncomingMessageScope captures the context around an incoming message that is received by the network layer. type IncomingMessageScope struct { originId flow.Identifier // the origin node ID. @@ -29,7 +59,7 @@ type IncomingMessageScope struct { // It errors if event id (i.e., hash of the payload and channel) cannot be computed, or if it fails to // convert the target IDs from bytes slice to a flow.IdentifierList. func NewIncomingScope(originId flow.Identifier, protocol ProtocolType, msg *Message, decodedPayload interface{}) (*IncomingMessageScope, error) { - eventId, err := network.EventId(channels.Channel(msg.ChannelID), msg.Payload) + eventId, err := EventId(channels.Channel(msg.ChannelID), msg.Payload) if err != nil { return nil, fmt.Errorf("could not compute event id: %w", err) } @@ -81,7 +111,7 @@ func (m IncomingMessageScope) EventID() []byte { } func (m IncomingMessageScope) PayloadType() string { - return network.MessageType(m.decodedPayload) + return MessageType(m.decodedPayload) } // OutgoingMessageScope captures the context around an outgoing message that is about to be sent. @@ -143,7 +173,7 @@ func (o OutgoingMessageScope) Size() int { } func (o OutgoingMessageScope) PayloadType() string { - return network.MessageType(o.payload) + return MessageType(o.payload) } func (o OutgoingMessageScope) Topic() channels.Topic { diff --git a/network/message_scope.go b/network/message_scope.go index e09595ff110..fe37a795572 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -1,44 +1,11 @@ package network import ( - "fmt" - "strings" - - "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" ) -const ( - // eventIDPackingPrefix is used as a salt to generate payload hash for messages. - eventIDPackingPrefix = "libp2ppacking" -) - -// EventId computes the event ID for a given channel and payload (i.e., the hash of the payload and channel). -// All errors returned by this function are benign and should not cause the node to crash. -// It errors if the hash function fails to hash the payload and channel. -func EventId(channel channels.Channel, payload []byte) (hash.Hash, error) { - // use a hash with an engine-specific salt to get the payload hash - h := hash.NewSHA3_384() - _, err := h.Write([]byte(eventIDPackingPrefix + channel)) - if err != nil { - return nil, fmt.Errorf("could not hash channel as salt: %w", err) - } - - _, err = h.Write(payload) - if err != nil { - return nil, fmt.Errorf("could not hash event: %w", err) - } - - return h.SumHash(), nil -} - -// MessageType returns the type of the message payload. -func MessageType(decodedPayload interface{}) string { - return strings.TrimLeft(fmt.Sprintf("%T", decodedPayload), "*") -} - // IncomingMessageScope defines the interface for handling incoming message scope. type IncomingMessageScope interface { // OriginId returns the origin node ID. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 49e1a2b0835..904f99aaa54 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -559,7 +559,7 @@ func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { remotePeer, role, msg.Size(), - network.MessageType(msg.Payload), + message.MessageType(msg.Payload), channels.Topic(msg.ChannelID)) { return } diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 585b3418d9a..38e5d331aef 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -748,7 +748,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol require.Equal(m.T(), expectedSendMsg, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload // event id - eventId, err := network.EventId(msg.Channel(), msg.Proto().Payload) + eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) require.NoError(m.T(), err) require.True(m.T(), bytes.Equal(eventId, msg.EventID())) @@ -771,7 +771,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol require.Equal(m.T(), expectedReplyMsg, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload // event id - eventId, err := network.EventId(msg.Channel(), msg.Proto().Payload) + eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) require.NoError(m.T(), err) require.True(m.T(), bytes.Equal(eventId, msg.EventID())) }) @@ -853,7 +853,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { require.Equal(m.T(), targetNode, msg.TargetIDs()[0]) require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) - eventId, err := network.EventId(msg.Channel(), msg.Proto().Payload) + eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) require.NoError(m.T(), err) require.True(m.T(), bytes.Equal(eventId, msg.EventID())) close(ch) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index e69a263ce6d..288494a2f99 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -15,6 +15,7 @@ import ( "github.com/onflow/cadence" sdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/network/message" hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/crypto" @@ -40,7 +41,6 @@ import ( "github.com/onflow/flow-go/module/mempool/entity" "github.com/onflow/flow-go/module/signature" "github.com/onflow/flow-go/module/updatable_configs" - "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p/keyutils" "github.com/onflow/flow-go/state/protocol" @@ -2422,7 +2422,7 @@ func GetFlowProtocolEventID( ) flow.Identifier { payload, err := NetworkCodec().Encode(event) require.NoError(t, err) - eventIDHash, err := network.EventId(channel, payload) + eventIDHash, err := message.EventId(channel, payload) require.NoError(t, err) return flow.HashToID(eventIDHash) } From fb8820ddca21377ef356cf67272d21595ac04c21 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:36:46 -0700 Subject: [PATCH 517/815] fixes import ailiases --- insecure/fixtures.go | 4 ++-- insecure/orchestrator/network.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/insecure/fixtures.go b/insecure/fixtures.go index 9e85deb3bad..6292a7ac316 100644 --- a/insecure/fixtures.go +++ b/insecure/fixtures.go @@ -11,7 +11,7 @@ import ( "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" - message2 "github.com/onflow/flow-go/network/message" + flownetmsg "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/unittest" ) @@ -41,7 +41,7 @@ func EgressMessageFixture(t *testing.T, codec network.Codec, protocol Protocol, // encodes event to create payload payload, err := codec.Encode(content) require.NoError(t, err) - eventIDHash, err := message2.EventId(channel, payload) + eventIDHash, err := flownetmsg.EventId(channel, payload) require.NoError(t, err) eventID := flow.HashToID(eventIDHash) diff --git a/insecure/orchestrator/network.go b/insecure/orchestrator/network.go index 1c8ca95e417..11e24874e79 100644 --- a/insecure/orchestrator/network.go +++ b/insecure/orchestrator/network.go @@ -13,7 +13,7 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" - message2 "github.com/onflow/flow-go/network/message" + flownetmsg "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/utils/logging" ) @@ -160,7 +160,7 @@ func (on *Network) processEgressMessage(message *insecure.EgressMessage) error { channel := channels.Channel(message.ChannelID) - egressEventIDHash, err := message2.EventId(channel, message.Payload) + egressEventIDHash, err := flownetmsg.EventId(channel, message.Payload) if err != nil { return fmt.Errorf("could not create egress event ID: %w", err) } @@ -206,7 +206,7 @@ func (on *Network) processIngressMessage(message *insecure.IngressMessage) error defer on.orchestratorMutex.Unlock() channel := channels.Channel(message.ChannelID) - ingressEventIDHash, err := message2.EventId(channel, message.Payload) + ingressEventIDHash, err := flownetmsg.EventId(channel, message.Payload) if err != nil { return fmt.Errorf("could not create ingress event ID: %w", err) } From 7591e33a6235c7d9a91ae3c11e1bd3c914063ac9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:38:35 -0700 Subject: [PATCH 518/815] fixes improt cycles --- network/message/message_scope.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/network/message/message_scope.go b/network/message/message_scope.go index 3c1076f0d79..fa4b0d667ef 100644 --- a/network/message/message_scope.go +++ b/network/message/message_scope.go @@ -6,14 +6,9 @@ import ( "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" ) -// Ensure structs implement the interfaces -var _ network.IncomingMessageScope = &IncomingMessageScope{} -var _ network.OutgoingMessageScope = &OutgoingMessageScope{} - const ( // eventIDPackingPrefix is used as a salt to generate payload hash for messages. eventIDPackingPrefix = "libp2ppacking" From c820722d5f3d68187720407b11c60edd1ef24875 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:41:00 -0700 Subject: [PATCH 519/815] re-generates mocks --- network/mocknetwork/incoming_message_scope.go | 168 ++++++++++++++++++ network/mocknetwork/message_validator.go | 7 +- network/mocknetwork/middleware.go | 10 +- network/mocknetwork/outgoing_message_scope.go | 106 +++++++++++ network/mocknetwork/overlay.go | 8 +- network/p2p/mock/lib_p2_p_node.go | 11 +- 6 files changed, 290 insertions(+), 20 deletions(-) create mode 100644 network/mocknetwork/incoming_message_scope.go create mode 100644 network/mocknetwork/outgoing_message_scope.go diff --git a/network/mocknetwork/incoming_message_scope.go b/network/mocknetwork/incoming_message_scope.go new file mode 100644 index 00000000000..543c9da9796 --- /dev/null +++ b/network/mocknetwork/incoming_message_scope.go @@ -0,0 +1,168 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + flow "github.com/onflow/flow-go/model/flow" + channels "github.com/onflow/flow-go/network/channels" + + message "github.com/onflow/flow-go/network/message" + + mock "github.com/stretchr/testify/mock" +) + +// IncomingMessageScope is an autogenerated mock type for the IncomingMessageScope type +type IncomingMessageScope struct { + mock.Mock +} + +// Channel provides a mock function with given fields: +func (_m *IncomingMessageScope) Channel() channels.Channel { + ret := _m.Called() + + var r0 channels.Channel + if rf, ok := ret.Get(0).(func() channels.Channel); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(channels.Channel) + } + + return r0 +} + +// DecodedPayload provides a mock function with given fields: +func (_m *IncomingMessageScope) DecodedPayload() interface{} { + ret := _m.Called() + + var r0 interface{} + if rf, ok := ret.Get(0).(func() interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} + +// EventID provides a mock function with given fields: +func (_m *IncomingMessageScope) EventID() []byte { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// OriginId provides a mock function with given fields: +func (_m *IncomingMessageScope) OriginId() flow.Identifier { + ret := _m.Called() + + var r0 flow.Identifier + if rf, ok := ret.Get(0).(func() flow.Identifier); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(flow.Identifier) + } + } + + return r0 +} + +// PayloadType provides a mock function with given fields: +func (_m *IncomingMessageScope) PayloadType() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Proto provides a mock function with given fields: +func (_m *IncomingMessageScope) Proto() *message.Message { + ret := _m.Called() + + var r0 *message.Message + if rf, ok := ret.Get(0).(func() *message.Message); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*message.Message) + } + } + + return r0 +} + +// Protocol provides a mock function with given fields: +func (_m *IncomingMessageScope) Protocol() message.ProtocolType { + ret := _m.Called() + + var r0 message.ProtocolType + if rf, ok := ret.Get(0).(func() message.ProtocolType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(message.ProtocolType) + } + + return r0 +} + +// Size provides a mock function with given fields: +func (_m *IncomingMessageScope) Size() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// TargetIDs provides a mock function with given fields: +func (_m *IncomingMessageScope) TargetIDs() flow.IdentifierList { + ret := _m.Called() + + var r0 flow.IdentifierList + if rf, ok := ret.Get(0).(func() flow.IdentifierList); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(flow.IdentifierList) + } + } + + return r0 +} + +type mockConstructorTestingTNewIncomingMessageScope interface { + mock.TestingT + Cleanup(func()) +} + +// NewIncomingMessageScope creates a new instance of IncomingMessageScope. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewIncomingMessageScope(t mockConstructorTestingTNewIncomingMessageScope) *IncomingMessageScope { + mock := &IncomingMessageScope{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/message_validator.go b/network/mocknetwork/message_validator.go index c0cad7f1fef..f2c78f75d20 100644 --- a/network/mocknetwork/message_validator.go +++ b/network/mocknetwork/message_validator.go @@ -3,8 +3,7 @@ package mocknetwork import ( - "github.com/onflow/flow-go/network/message" - + network "github.com/onflow/flow-go/network" mock "github.com/stretchr/testify/mock" ) @@ -14,11 +13,11 @@ type MessageValidator struct { } // Validate provides a mock function with given fields: msg -func (_m *MessageValidator) Validate(msg message.IncomingMessageScope) bool { +func (_m *MessageValidator) Validate(msg network.IncomingMessageScope) bool { ret := _m.Called(msg) var r0 bool - if rf, ok := ret.Get(0).(func(message.IncomingMessageScope) bool); ok { + if rf, ok := ret.Get(0).(func(network.IncomingMessageScope) bool); ok { r0 = rf(msg) } else { r0 = ret.Get(0).(bool) diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index 6f5a0fab528..2b0f7409df4 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -4,9 +4,7 @@ package mocknetwork import ( datastore "github.com/ipfs/go-datastore" - channels "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/message" irrecoverable "github.com/onflow/flow-go/module/irrecoverable" @@ -88,11 +86,11 @@ func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpd } // Publish provides a mock function with given fields: msg -func (_m *Middleware) Publish(msg *message.OutgoingMessageScope) error { +func (_m *Middleware) Publish(msg network.OutgoingMessageScope) error { ret := _m.Called(msg) var r0 error - if rf, ok := ret.Get(0).(func(*message.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(network.OutgoingMessageScope) error); ok { r0 = rf(msg) } else { r0 = ret.Error(0) @@ -118,11 +116,11 @@ func (_m *Middleware) Ready() <-chan struct{} { } // SendDirect provides a mock function with given fields: msg -func (_m *Middleware) SendDirect(msg *message.OutgoingMessageScope) error { +func (_m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { ret := _m.Called(msg) var r0 error - if rf, ok := ret.Get(0).(func(*message.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(network.OutgoingMessageScope) error); ok { r0 = rf(msg) } else { r0 = ret.Error(0) diff --git a/network/mocknetwork/outgoing_message_scope.go b/network/mocknetwork/outgoing_message_scope.go new file mode 100644 index 00000000000..f630c94e01a --- /dev/null +++ b/network/mocknetwork/outgoing_message_scope.go @@ -0,0 +1,106 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + flow "github.com/onflow/flow-go/model/flow" + channels "github.com/onflow/flow-go/network/channels" + + message "github.com/onflow/flow-go/network/message" + + mock "github.com/stretchr/testify/mock" +) + +// OutgoingMessageScope is an autogenerated mock type for the OutgoingMessageScope type +type OutgoingMessageScope struct { + mock.Mock +} + +// PayloadType provides a mock function with given fields: +func (_m *OutgoingMessageScope) PayloadType() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Proto provides a mock function with given fields: +func (_m *OutgoingMessageScope) Proto() *message.Message { + ret := _m.Called() + + var r0 *message.Message + if rf, ok := ret.Get(0).(func() *message.Message); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*message.Message) + } + } + + return r0 +} + +// Size provides a mock function with given fields: +func (_m *OutgoingMessageScope) Size() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// TargetIds provides a mock function with given fields: +func (_m *OutgoingMessageScope) TargetIds() flow.IdentifierList { + ret := _m.Called() + + var r0 flow.IdentifierList + if rf, ok := ret.Get(0).(func() flow.IdentifierList); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(flow.IdentifierList) + } + } + + return r0 +} + +// Topic provides a mock function with given fields: +func (_m *OutgoingMessageScope) Topic() channels.Topic { + ret := _m.Called() + + var r0 channels.Topic + if rf, ok := ret.Get(0).(func() channels.Topic); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(channels.Topic) + } + + return r0 +} + +type mockConstructorTestingTNewOutgoingMessageScope interface { + mock.TestingT + Cleanup(func()) +} + +// NewOutgoingMessageScope creates a new instance of OutgoingMessageScope. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewOutgoingMessageScope(t mockConstructorTestingTNewOutgoingMessageScope) *OutgoingMessageScope { + mock := &OutgoingMessageScope{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/overlay.go b/network/mocknetwork/overlay.go index 913ecbe66de..7eab360e012 100644 --- a/network/mocknetwork/overlay.go +++ b/network/mocknetwork/overlay.go @@ -4,10 +4,10 @@ package mocknetwork import ( flow "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/network/message" - mock "github.com/stretchr/testify/mock" + network "github.com/onflow/flow-go/network" + peer "github.com/libp2p/go-libp2p/core/peer" ) @@ -59,11 +59,11 @@ func (_m *Overlay) Identity(_a0 peer.ID) (*flow.Identity, bool) { } // Receive provides a mock function with given fields: _a0 -func (_m *Overlay) Receive(_a0 *message.IncomingMessageScope) error { +func (_m *Overlay) Receive(_a0 network.IncomingMessageScope) error { ret := _m.Called(_a0) var r0 error - if rf, ok := ret.Get(0).(func(*message.IncomingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(network.IncomingMessageScope) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index b496c764bbd..008ad125a96 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -5,7 +5,6 @@ package mockp2p import ( component "github.com/onflow/flow-go/module/component" channels "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/message" context "context" @@ -285,13 +284,13 @@ func (_m *LibP2PNode) PeerScoreExposer() p2p.PeerScoreExposer { return r0 } -// Publish provides a mock function with given fields: ctx, msgScope -func (_m *LibP2PNode) Publish(ctx context.Context, msgScope *message.OutgoingMessageScope) error { - ret := _m.Called(ctx, msgScope) +// Publish provides a mock function with given fields: ctx, scope +func (_m *LibP2PNode) Publish(ctx context.Context, scope flow_gonetwork.OutgoingMessageScope) error { + ret := _m.Called(ctx, scope) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *message.OutgoingMessageScope) error); ok { - r0 = rf(ctx, msgScope) + if rf, ok := ret.Get(0).(func(context.Context, flow_gonetwork.OutgoingMessageScope) error); ok { + r0 = rf(ctx, scope) } else { r0 = ret.Error(0) } From ff4122bb21e97962d68fcffeaa7e62c703f8fad5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 12:41:55 -0700 Subject: [PATCH 520/815] fixes build --- network/validator/origin_validator.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/validator/origin_validator.go b/network/validator/origin_validator.go index 0963fd9b68c..857f7b58271 100644 --- a/network/validator/origin_validator.go +++ b/network/validator/origin_validator.go @@ -3,7 +3,6 @@ package validator import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/message" ) var _ network.MessageValidator = (*OriginValidator)(nil) @@ -18,6 +17,6 @@ func NewOriginValidator(provider module.IdentifierProvider) network.MessageValid return &OriginValidator{provider} } -func (v OriginValidator) Validate(msg message.IncomingMessageScope) bool { +func (v OriginValidator) Validate(msg network.IncomingMessageScope) bool { return v.idProvider.Identifiers().Contains(msg.OriginId()) } From 996ac1f2578cfd673d6c53540aa0b986b652191d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 13:22:36 -0700 Subject: [PATCH 521/815] fixes tests --- network/p2p/test/topic_validator_test.go | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 8b62e5465b3..0245985dc27 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -262,7 +262,7 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) - topic := channels.TopicFromChannel(channels.ConsensusCommittee, sporkId) + topic := channels.Topic("invalid-topic") pInfo2, err := utils.PeerAddressInfo(identity2) require.NoError(t, err) @@ -284,24 +284,32 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) defer cancel5s() - // create a dummy block proposal to publish from our SN node - outgoingMessageScope1, err := message.NewOutgoingScope( + + // invalid topic is malformed, hence it cannot be used to create a message scope, as it faces an error. + // Hence, we create a dummy block proposal message scope to publish on a legit topic, and then override + // the topic in the next step to a malformed topic. + dummyMessageScope, err := message.NewOutgoingScope( flow.IdentifierList{identity1.NodeID, identity2.NodeID}, - topic, + channels.TopicFromChannel(channels.PushBlocks, sporkId), unittest.ProposalFixture(), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) require.NoError(t, err) - // intentionally overriding the channel id to be a valid channel id (though the topic is invalid) - // hence imitating a message that was published to the wrong topic - outgoingMessageScope1.Proto().ChannelID = channels.PushBlocks.String() + // overrides the topic to be an invalid topic + corruptOutgoingMessageScope := mocknetwork.NewOutgoingMessageScope(t) + corruptOutgoingMessageScope.On("TargetIds").Return(dummyMessageScope.TargetIds()).Maybe() + corruptOutgoingMessageScope.On("Topic").Return(topic).Maybe() + corruptOutgoingMessageScope.On("Proto").Return(dummyMessageScope.Proto()).Maybe() + corruptOutgoingMessageScope.On("PayloadType").Return(dummyMessageScope.PayloadType()).Maybe() + corruptOutgoingMessageScope.On("Size").Return(dummyMessageScope.Size()).Maybe() - err = sn2.Publish(timedCtx, outgoingMessageScope1) + // create a dummy block proposal to publish from our SN node + err = sn2.Publish(timedCtx, corruptOutgoingMessageScope) // publish fails because the topic conversion fails require.Error(t, err) - + fmt.Println(err) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), "could not convert topic to channel") } From dc8a3b594b6f23572fe804c593e07d110e0635e9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 13:50:34 -0700 Subject: [PATCH 522/815] fixes tests --- network/test/middleware_test.go | 48 ++++++++++----------- network/test/unicast_authorization_test.go | 49 +++++++++++----------- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 38e5d331aef..90ef9709627 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -42,8 +42,6 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -const testChannel = channels.TestNetworkChannel - // libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSUb mesh, see: // https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go // One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSubD such calls, @@ -172,7 +170,7 @@ func (m *MiddlewareTestSuite) SetupTest() { mw.SetOverlay(m.ov[i]) mw.Start(m.mwCtx) unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, mw) - require.NoError(m.T(), mw.Subscribe(testChannel)) + require.NoError(m.T(), mw.Subscribe(channels.TestNetworkChannel)) } } @@ -228,7 +226,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { outMsg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: "TestUpdateNodeAddresses", }, @@ -317,7 +315,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { calls := atomic.NewUint64(0) ch := make(chan struct{}) - overlay.On("Receive", mockery.AnythingOfType("*network.IncomingMessageScope")).Return(nil).Run(func(args mockery.Arguments) { + overlay.On("Receive", mockery.AnythingOfType("*message.IncomingMessageScope")).Return(nil).Run(func(args mockery.Arguments) { calls.Inc() if calls.Load() >= 5 { close(ch) @@ -335,7 +333,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { newMw.Start(irrecoverableCtx) unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) - require.NoError(m.T(), newMw.Subscribe(testChannel)) + require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) // needed to enable ID translation m.providers[0].SetIdentities(idList) @@ -356,7 +354,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { for i := 0; i < 10; i++ { msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: fmt.Sprintf("hello-%d", i), }, @@ -380,7 +378,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.Eventually(m.T(), func() bool { msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: "hello", }, @@ -477,7 +475,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { newMw.Start(irrecoverableCtx) unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) - require.NoError(m.T(), newMw.Subscribe(testChannel)) + require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) idList := flow.IdentityList(append(m.ids, newId)) @@ -492,7 +490,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: string(b), }, @@ -530,7 +528,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { require.Eventually(m.T(), func() bool { msg, err = message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: "", }, @@ -592,7 +590,7 @@ func (m *MiddlewareTestSuite) TestPing() { expectedPayload := "TestPingContentReception" msg, err := message.NewOutgoingScope( flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: expectedPayload, }, @@ -607,7 +605,7 @@ func (m *MiddlewareTestSuite) TestPing() { msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) - require.Equal(m.T(), testChannel, msg.Channel()) // channel + require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel require.Equal(m.T(), m.ids[firstNodeIndex].NodeID, msg.OriginId()) // sender id require.Equal(m.T(), m.ids[lastNodeIndex].NodeID, msg.TargetIDs()[0]) // target id require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol @@ -650,7 +648,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { expectedPayloadText := fmt.Sprintf("hello from: %d", i) msg, err := message.NewOutgoingScope( flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: expectedPayloadText, }, @@ -665,7 +663,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) - require.Equal(m.T(), testChannel, msg.Channel()) // channel + require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel require.Equal(m.T(), m.ids[firstNodeIndex].NodeID, msg.OriginId()) // sender id require.Equal(m.T(), m.ids[lastNodeIndex].NodeID, msg.TargetIDs()[0]) // target id require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol @@ -713,7 +711,7 @@ func (m *MiddlewareTestSuite) TestEcho() { expectedSendMsg := "TestEcho" sendMsg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: expectedSendMsg, }, @@ -725,7 +723,7 @@ func (m *MiddlewareTestSuite) TestEcho() { expectedReplyMsg := "TestEcho response" replyMsg, err := message.NewOutgoingScope( flow.IdentifierList{firstNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: expectedReplyMsg, }, @@ -742,7 +740,7 @@ func (m *MiddlewareTestSuite) TestEcho() { msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) - require.Equal(m.T(), testChannel, msg.Channel()) // channel + require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel require.Equal(m.T(), m.ids[first].NodeID, msg.OriginId()) // sender id require.Equal(m.T(), lastNode, msg.TargetIDs()[0]) // target id require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol @@ -765,7 +763,7 @@ func (m *MiddlewareTestSuite) TestEcho() { msg, ok := args[0].(network.IncomingMessageScope) require.True(m.T(), ok) - require.Equal(m.T(), testChannel, msg.Channel()) // channel + require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel require.Equal(m.T(), m.ids[last].NodeID, msg.OriginId()) // sender id require.Equal(m.T(), firstNode, msg.TargetIDs()[0]) // target id require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol @@ -807,7 +805,7 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { msg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), event, unittest.NetworkCodec().Encode, message.ProtocolTypeUnicast) @@ -835,7 +833,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { msg, err := message.NewOutgoingScope( flow.IdentifierList{targetNode}, - channels.ProvideChunks, + channels.TopicFromChannel(channels.ProvideChunks, m.sporkId), event, unittest.NetworkCodec().Encode, message.ProtocolTypeUnicast) @@ -888,7 +886,7 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_Publish() { } msg, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), event, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -923,7 +921,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { message1, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: string("hello1"), }, @@ -945,13 +943,13 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { unittest.RequireReturnsBefore(m.T(), msgRcvdFun, 2*time.Second, "message not received") // now unsubscribe the target node from the channel - err = m.mws[last].Unsubscribe(testChannel) + err = m.mws[last].Unsubscribe(channels.TestNetworkChannel) assert.NoError(m.T(), err) // create and send a new message on the channel from the origin node message2, err := message.NewOutgoingScope( flow.IdentifierList{lastNode}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), &libp2pmessage.TestMessage{ Text: string("hello2"), }, diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 0dfed501f4e..b2915fc1bd8 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -48,7 +48,8 @@ type UnicastAuthorizationTestSuite struct { // providers id providers generated at beginning of a test run providers []*unittest.UpdatableIDProvider // cancel is the cancel func from the context that was used to start the middlewares in a test run - cancel context.CancelFunc + cancel context.CancelFunc + sporkId flow.Identifier // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing //violation's consumer before making mock assertions and cleaning up resources waitCh chan struct{} @@ -73,9 +74,9 @@ func (u *UnicastAuthorizationTestSuite) TearDownTest() { // setupMiddlewaresAndProviders will setup 2 middlewares that will be used as a sender and receiver in each suite test. func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingViolationsConsumer network.ViolationsConsumer) { - sporkId := unittest.IdentifierFixture() - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), sporkId, 2) - cfg := testutils.MiddlewareConfigFixture(u.T(), sporkId) + u.sporkId = unittest.IdentifierFixture() + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) + cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) mws, providers := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg, slashingViolationsConsumer) require.Len(u.T(), ids, 2) require.Len(u.T(), providers, 2) @@ -154,12 +155,12 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() u.startMiddlewares(overlay) - require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) + require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), &libp2pmessage.TestMessage{ Text: string("hello"), }, @@ -216,12 +217,12 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { u.startMiddlewares(overlay) - require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) + require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), &libp2pmessage.TestMessage{ Text: string("hello"), }, @@ -282,7 +283,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - channel, + channels.TopicFromChannel(channels.ConsensusCommittee, u.sporkId), &libp2pmessage.TestMessage{ Text: string("hello"), }, @@ -340,12 +341,12 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( u.startMiddlewares(overlay) - require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) + require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), &libp2pmessage.TestMessage{ Text: "hello", }, @@ -410,12 +411,12 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() u.startMiddlewares(overlay) - require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) + require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), &libp2pmessage.TestMessage{ Text: "hello", }, @@ -447,7 +448,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() expectedPayload := "hello" msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - testChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), &libp2pmessage.TestMessage{ Text: expectedPayload, }, @@ -473,7 +474,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() msg, ok := args[0].(network.IncomingMessageScope) require.True(u.T(), ok) - require.Equal(u.T(), testChannel, msg.Channel()) // channel + require.Equal(u.T(), channels.TestNetworkChannel, msg.Channel()) // channel require.Equal(u.T(), u.senderID.NodeID, msg.OriginId()) // sender id require.Equal(u.T(), u.receiverID.NodeID, msg.TargetIDs()[0]) // target id require.Equal(u.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol @@ -482,8 +483,8 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() u.startMiddlewares(overlay) - require.NoError(u.T(), u.receiverMW.Subscribe(testChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(testChannel)) + require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) // send message via unicast err = u.senderMW.SendDirect(msg) @@ -545,7 +546,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - channel, + channels.TopicFromChannel(channel, u.sporkId), payload, unittest.NetworkCodec().Encode, message.ProtocolTypeUnicast) @@ -602,7 +603,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - channel, + channels.TopicFromChannel(channel, u.sporkId), &libp2pmessage.TestMessage{ Text: "TestUnicastAuthorization_ReceiverHasNoSubscription", }, @@ -628,7 +629,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs msg, err := message.NewOutgoingScope( flow.IdentifierList{u.receiverID.NodeID}, - channel, + channels.TopicFromChannel(channel, u.sporkId), &messages.EntityRequest{}, unittest.NetworkCodec().Encode, message.ProtocolTypeUnicast) From 67f60536ab5622306cf994c940b0cf1bf0c5ecfb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 14:41:41 -0700 Subject: [PATCH 523/815] fixes tests --- network/p2p/dht/dht_test.go | 2 +- network/p2p/middleware/middleware_test.go | 2 +- network/p2p/scoring/app_score_test.go | 4 ++-- network/p2p/scoring/subscription_validator_test.go | 6 +++--- network/p2p/subscription/subscription_filter_test.go | 2 +- .../p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go | 6 ++++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 3f5ccb64a18..3bbbf25079c 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -156,7 +156,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { messageScope, err := message.NewOutgoingScope( ids.NodeIDs(), - channels.TestNetworkChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, sporkId), &libp2pmsg.TestMessage{}, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) diff --git a/network/p2p/middleware/middleware_test.go b/network/p2p/middleware/middleware_test.go index 840d0c3bb07..b1708701418 100644 --- a/network/p2p/middleware/middleware_test.go +++ b/network/p2p/middleware/middleware_test.go @@ -19,7 +19,7 @@ func TestChunkDataPackMaxMessageSize(t *testing.T) { // creates an outgoing chunk data pack response message (imitating an EN is sending a chunk data pack response to VN). msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, - channels.ProvideChunks, + channels.TopicFromChannel(channels.ProvideChunks, unittest.IdentifierFixture()), &messages.ChunkDataResponse{ ChunkDataPack: *unittest.ChunkDataPackFixture(unittest.IdentifierFixture()), Nonce: rand.Uint64(), diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index d43eeeab800..43cbba7f966 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -96,7 +96,7 @@ func TestFullGossipSubConnectivity(t *testing.T) { for _, node := range nodes { outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), - channels.PushBlocks, + channels.TopicFromChannel(channels.PushBlocks, sporkId), unittest.ProposalFixture(), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -217,7 +217,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS outgoingMessageScope, err := message.NewOutgoingScope( allIds.NodeIDs(), - channels.PushBlocks, + channels.TopicFromChannel(channels.PushBlocks, sporkId), unittest.ProposalFixture(), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index b8243fbdadc..2bda9d78a1b 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -240,7 +240,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), - channels.PushBlocks, + channels.TopicFromChannel(channels.PushBlocks, sporkId), unittest.ProposalFixture(), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -269,7 +269,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // the GossipSub scoring protocol. outgoingMessageScope, err = message.NewOutgoingScope( ids.NodeIDs(), - channels.PushBlocks, + channels.TopicFromChannel(channels.PushBlocks, sporkId), unittest.ProposalFixture(), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) @@ -284,7 +284,7 @@ func TestSubscriptionValidator_Integration(t *testing.T) { // however, both verification nodes should receive the message. outgoingMessageScope, err = message.NewOutgoingScope( ids.NodeIDs(), - channels.RequestChunks, + channels.TopicFromChannel(channels.RequestChunks, sporkId), &messages.ChunkDataRequest{ ChunkID: unittest.IdentifierFixture(), Nonce: rand.Uint64(), diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 59cc739e9e3..ec43a039ab1 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -82,7 +82,7 @@ func TestFilterSubscribe(t *testing.T) { outgoingMessageScope, err := message.NewOutgoingScope( ids.NodeIDs(), - channels.SyncCommittee, + channels.TopicFromChannel(channels.SyncCommittee, sporkId), []byte("hello"), unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) diff --git a/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go b/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go index 40befc19b48..1bcb28a46cc 100644 --- a/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go +++ b/network/p2p/unicast/ratelimit/bandwidth_rate_limiter_test.go @@ -36,9 +36,10 @@ func TestBandWidthRateLimiter_Allow(t *testing.T) { b[i] = byte('X') } + sporkId := unittest.IdentifierFixture() msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, - channels.TestNetworkChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, sporkId), &libp2pmessage.TestMessage{ Text: string(b), }, @@ -89,9 +90,10 @@ func TestBandWidthRateLimiter_IsRateLimited(t *testing.T) { require.False(t, bandwidthRateLimiter.IsRateLimited(peerID)) + sporkId := unittest.IdentifierFixture() msg, err := message.NewOutgoingScope( flow.IdentifierList{unittest.IdentifierFixture()}, - channels.TestNetworkChannel, + channels.TopicFromChannel(channels.TestNetworkChannel, sporkId), &libp2pmessage.TestMessage{ Text: string(b), }, From a5eabe199d85bd3caa3a248dd514cd30d460f59b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 17 Aug 2023 14:47:04 -0700 Subject: [PATCH 524/815] fixes tests --- .../test/gossipsub/scoring/scoring_test.go | 2 +- network/p2p/test/fixtures.go | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go index 2366678576e..11248d109da 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go @@ -483,7 +483,7 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { proposalList[i] = unittest.ProposalFixture() } i := -1 - p2ptest.EnsurePubsubMessageExchangeFromNode(t, ctx, replayingNode, thisNode, blockTopic, len(proposalList), func() interface{} { + p2ptest.EnsurePubsubMessageExchangeFromNode(t, ctx, replayingNode, thisNode, thisId.NodeID, blockTopic, len(proposalList), func() interface{} { i += 1 return proposalList[i] }) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 39344980e87..f124ac4d17b 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -612,15 +612,24 @@ func EnsurePubsubMessageExchange(t *testing.T, ctx context.Context, nodes []p2p. // // - ctx: the context- the test will fail if the context expires. // - sender: the node that sends the message to the other node. -// - receiver: the node that receives the message from the other node. +// - receiverNode: the node that receives the message from the other node. +// - receiverIdentifier: the identifier of the receiver node. // - topic: the topic to exchange messages on. // - count: the number of messages to exchange from `sender` to `receiver`. // - messageFactory: a function that creates a unique message to be published by the node. -func EnsurePubsubMessageExchangeFromNode(t *testing.T, ctx context.Context, sender p2p.LibP2PNode, receiver p2p.LibP2PNode, topic channels.Topic, count int, messageFactory func() interface{}) { +func EnsurePubsubMessageExchangeFromNode( + t *testing.T, + ctx context.Context, + sender p2p.LibP2PNode, + receiverNode p2p.LibP2PNode, + receiverIdentifier flow.Identifier, + topic channels.Topic, + count int, + messageFactory func() interface{}) { _, err := sender.Subscribe(topic, validator.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) require.NoError(t, err) - toSub, err := receiver.Subscribe(topic, validator.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) + toSub, err := receiverNode.Subscribe(topic, validator.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) require.NoError(t, err) // let subscriptions propagate @@ -630,7 +639,7 @@ func EnsurePubsubMessageExchangeFromNode(t *testing.T, ctx context.Context, send // creates a unique message to be published by the node payload := messageFactory() outgoingMessageScope, err := message.NewOutgoingScope( - flow.IdentifierList{}, + flow.IdentifierList{receiverIdentifier}, topic, payload, unittest.NetworkCodec().Encode, From 045ae4088379220adb701a59ee44623c506fd5f7 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 04:07:57 -0400 Subject: [PATCH 525/815] removed mini load tests --- engine/common/synchronization/engine_test.go | 128 ++++++++----------- 1 file changed, 50 insertions(+), 78 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 6fe2dcfbcb7..8a8695858b9 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -202,32 +202,10 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { ss.core.AssertExpectations(ss.T()) } -// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher than the receiver's height doesn't -// trigger a response, even if outside tolerance. -func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { - nonce, err := rand.Uint64() - require.NoError(ss.T(), err, "should generate nonce") - - // generate origin and request message - originID := unittest.IdentifierFixture() - req := &messages.SyncRequest{ - Nonce: nonce, - Height: 0, - } - - // if request height is higher than local finalized, we should not respond - req.Height = ss.head.Height + 1 - ss.core.On("HandleHeight", ss.head, req.Height) - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - err = ss.e.requestHandler.onSyncRequest(originID, req) - ss.Assert().NoError(err, "same height sync request should pass") - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - - ss.core.AssertExpectations(ss.T()) -} - +// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport tests that a sync request that's higher +// than the receiver's height doesn't trigger a response, even if outside tolerance and does not generate ALSP +// spamming misbehavior report (simulating the most likely probability). func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport() { - load := 10 mockRandomizer := new(rand.MockRandomizer) ss.e.randomizer = mockRandomizer @@ -236,43 +214,41 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMis unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - for i := 0; i < load; i++ { - ss.T().Logf("i: %d", i) - - // generate origin and request message - originID := unittest.IdentifierFixture() + // generate origin and request message + originID := unittest.IdentifierFixture() - nonce, err := rand.Uint64() - require.NoError(ss.T(), err, "should generate nonce") + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") - req := &messages.SyncRequest{ - Nonce: nonce, - Height: 0, - } + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } - // if request height is higher than local finalized, we should not respond - req.Height = ss.head.Height + 1 + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 - ss.core.On("HandleHeight", ss.head, req.Height).Once() - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false).Once() + ss.core.On("HandleHeight", ss.head, req.Height).Once() + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false).Once() - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - // expect Uint32n(1001) to be called once and return 1, not triggering a misbehavior report - mockRandomizer.On("Uint32n", uint32(1001)).Return(1).Once() + // expect Uint32n(1001) to be called once and return 1, not triggering a misbehavior report + mockRandomizer.On("Uint32n", uint32(1001)).Return(1).Once() - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) - // give at least some time to process items - time.Sleep(time.Millisecond * 100) + // give at least some time to process items + time.Sleep(time.Millisecond * 100) - ss.core.AssertExpectations(ss.T()) - ss.con.AssertExpectations(ss.T()) - } + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) } +// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport tests that a sync request that's higher +// than the receiver's height doesn't trigger a response, even if outside tolerance and generates ALSP +// spamming misbehavior report (simulating the unlikely probability). func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { - load := 10 mockRandomizer := new(rand.MockRandomizer) ss.e.randomizer = mockRandomizer @@ -281,43 +257,39 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Misbe unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - for i := 0; i < load; i++ { - ss.T().Logf("i: %d", i) - - // generate origin and request message - originID := unittest.IdentifierFixture() + // generate origin and request message + originID := unittest.IdentifierFixture() - nonce, err := rand.Uint64() - require.NoError(ss.T(), err, "should generate nonce") + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") - req := &messages.SyncRequest{ - Nonce: nonce, - Height: 0, - } + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } - // if request height is higher than local finalized, we should not respond - req.Height = ss.head.Height + 1 + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 - // assert that misbehavior is reported - ss.con.On("ReportMisbehavior", mock.Anything).Return(false) + // assert that misbehavior is reported + ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Once() - // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported - // also, check that response is never sent - ss.core.AssertNotCalled(ss.T(), "HandleHeight") - ss.core.AssertNotCalled(ss.T(), "WithinTolerance") - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported + // also, check that response is never sent + ss.core.AssertNotCalled(ss.T(), "HandleHeight") + ss.core.AssertNotCalled(ss.T(), "WithinTolerance") + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - // expect Uint32n(1001) to be called once and return misbehavior report because it happened to be 835 - mockRandomizer.On("Uint32n", uint32(1001)).Return(835).Once() + // expect Uint32n(1001) to be called once and return misbehavior report because it happened to be 835 + mockRandomizer.On("Uint32n", uint32(1001)).Return(835).Once() - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) - // give at least some time to process items - time.Sleep(time.Millisecond * 100) + // give at least some time to process items + time.Sleep(time.Millisecond * 100) - ss.core.AssertExpectations(ss.T()) - ss.con.AssertExpectations(ss.T()) - } + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and From 70074227d5eba9d035c07d4f525dbce13c7fb1cc Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 05:27:17 -0400 Subject: [PATCH 526/815] load test WIP --- engine/common/synchronization/engine_test.go | 75 ++++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 8a8695858b9..bbefaa79f6f 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/suite" mockconsensus "github.com/onflow/flow-go/engine/consensus/mock" + "github.com/onflow/flow-go/model/chainsync" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/model/messages" @@ -195,17 +196,37 @@ func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_WithinTolerance() { // regardless of request height, if within tolerance, we should not respond ss.core.On("HandleHeight", ss.head, req.Height) ss.core.On("WithinTolerance", ss.head, req.Height).Return(true) - err = ss.e.requestHandler.onSyncRequest(originID, req) - ss.Assert().NoError(err, "same height sync request should pass") + ss.Assert().NoError(ss.e.requestHandler.onSyncRequest(originID, req)) ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + ss.core.AssertExpectations(ss.T()) +} + +// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance tests that a sync request that's higher +// than the receiver's height doesn't trigger a response, even if outside tolerance. +func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } + + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + ss.Assert().NoError(ss.e.requestHandler.onSyncRequest(originID, req)) + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) ss.core.AssertExpectations(ss.T()) } -// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport tests that a sync request that's higher +// TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport tests that a sync request that's higher // than the receiver's height doesn't trigger a response, even if outside tolerance and does not generate ALSP // spamming misbehavior report (simulating the most likely probability). -func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport() { +func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport() { mockRandomizer := new(rand.MockRandomizer) ss.e.randomizer = mockRandomizer @@ -245,10 +266,10 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_NoMis ss.con.AssertExpectations(ss.T()) } -// TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport tests that a sync request that's higher +// TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport tests that a sync request that's higher // than the receiver's height doesn't trigger a response, even if outside tolerance and generates ALSP // spamming misbehavior report (simulating the unlikely probability). -func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { +func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { mockRandomizer := new(rand.MockRandomizer) ss.e.randomizer = mockRandomizer @@ -292,6 +313,48 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance_Misbe ss.con.AssertExpectations(ss.T()) } +func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load() { + ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) + ss.e.Start(ctx) + unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) + defer cancel() + + load := 1000 + for i := 0; i < load; i++ { + ss.T().Log("load iteration", i) + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } + + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 + + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + + // maybe function calls that might or might not occur over the course of the load test + ss.core.On("ScanPending", ss.head).Return([]chainsync.Range{}, []chainsync.Batch{}).Maybe() + ss.con.On("Multicast", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe() + + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + } + + // give at least some time to process items + time.Sleep(time.Millisecond * 100) + + // check function call expectations at the end of the load test; otherwise, load test would take much longer + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) +} + // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and // lower than the receiver's height triggers a response. func (ss *SyncSuite) TestOnSyncRequest_LowerThanReceiver_OutsideTolerance() { From 7837dfb80271b9f8bdfef66f200a4200a0d474a9 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 05:43:54 -0400 Subject: [PATCH 527/815] lint fix - Randomizer returns error --- engine/common/synchronization/engine.go | 11 ++++++++++- utils/rand/rand.go | 15 ++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index de8ecbe3194..02f1e90ad51 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -487,7 +487,16 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { // Generate a random integer between 1 and 1000 - n := e.randomizer.Uint32n(uint32(1001)) + n, err := e.randomizer.Uint32n(uint32(1001)) + + if err != nil { + // failing to generate a random number is unlikely. If an error is encountered while + // generating a random number it indicates a bug and processing can not proceed. + e.log.Fatal(). + Err(err). + Str("originID", originID.String()). + Msg("failed to generate random number") + } // use a probabilistic approach to create a misbehavior report - create a report with a probability of 1/1000 // this is done to avoid creating a report for every sync request received diff --git a/utils/rand/rand.go b/utils/rand/rand.go index 680838232f4..df11838dabb 100644 --- a/utils/rand/rand.go +++ b/utils/rand/rand.go @@ -174,7 +174,7 @@ func Samples(n uint, m uint, swap func(i, j uint)) error { // we want to mock out random number generation. type Randomizer interface { // Uint32n returns a random uint32 strictly less than `n`. - Uint32n(n uint32) uint32 + Uint32n(n uint32) (uint32, error) } func NewDefaultRandomizer() Randomizer { @@ -184,12 +184,13 @@ func NewDefaultRandomizer() Randomizer { type defaultRandomizer struct{} // Uint32n returns a random number between 0 and n (exclusive) -func (e *defaultRandomizer) Uint32n(n uint32) uint32 { +func (e *defaultRandomizer) Uint32n(n uint32) (uint32, error) { n, err := Uint32n(n) if err != nil { - fmt.Errorf("failed to create random number (%d): %w", n, err) + e := fmt.Errorf("failed to create random number (%d): %w", n, err) + return 0, e } - return n + return n, nil } // MockRandomizer is a mock object that implements the Randomizer interface @@ -197,8 +198,8 @@ type MockRandomizer struct { mock.Mock } -// Uint32n is a mock method that returns a random number -func (m *MockRandomizer) Uint32n(n uint32) uint32 { +// Uint32n is a mock method that allows tests to specify what random number to return +func (m *MockRandomizer) Uint32n(n uint32) (uint32, error) { args := m.Called(n) - return uint32(args.Int(0)) + return uint32(args.Int(0)), nil } From b4b08498ed8a9fe8405bbed70f49a20ca655b360 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 06:15:06 -0400 Subject: [PATCH 528/815] load test implemented --- engine/common/synchronization/engine_test.go | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index bbefaa79f6f..06e3c50b16c 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -313,13 +313,18 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.con.AssertExpectations(ss.T()) } +// TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load load tests that a sync request that's higher +// than the receiver's height doesn't trigger a response, even if outside tolerance. It checks that an ALSP +// spamming misbehavior report was generated and that the number of misbehavior reports is within a reasonable range. func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load() { ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - load := 1000 + misbehaviorReported := 0 + load := 5000 + for i := 0; i < load; i++ { ss.T().Log("load iteration", i) nonce, err := rand.Uint64() @@ -342,17 +347,29 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // maybe function calls that might or might not occur over the course of the load test ss.core.On("ScanPending", ss.head).Return([]chainsync.Range{}, []chainsync.Batch{}).Maybe() ss.con.On("Multicast", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() - ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe() + + // count misbehavior reports over the course of a load test + ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe().Run( + func(args mock.Arguments) { + misbehaviorReported++ + }, + ) require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } // give at least some time to process items - time.Sleep(time.Millisecond * 100) + //time.Sleep(time.Millisecond * 100) // check function call expectations at the end of the load test; otherwise, load test would take much longer ss.core.AssertExpectations(ss.T()) ss.con.AssertExpectations(ss.T()) + + // check that correct range of misbehavior reports were generated (between 1-2 reports per 1000 requests) + // since we're using a random method to generate misbehavior reports, we can't guarantee the exact number, so we + // check that it's within a larger range, but that at least 1 misbehavior report was generated + assert.GreaterOrEqual(ss.T(), misbehaviorReported, 1) + assert.LessOrEqual(ss.T(), misbehaviorReported, 8) // too many reports would indicate a bug } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and From 6db0d8d5640d7db34add1ae9756bf9f8241b00d7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 18 Aug 2023 10:06:06 -0700 Subject: [PATCH 529/815] skims off the spork id from libp2p node --- insecure/corruptlibp2p/p2p_node.go | 4 +--- network/p2p/builder.go | 2 +- network/p2p/libp2pNode.go | 2 +- network/p2p/p2pbuilder/libp2pNodeBuilder.go | 5 ++--- network/p2p/p2pnode/libp2pNode.go | 10 +++++----- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/insecure/corruptlibp2p/p2p_node.go b/insecure/corruptlibp2p/p2p_node.go index 02ce4e3a3f3..b3fbd0cb36d 100644 --- a/insecure/corruptlibp2p/p2p_node.go +++ b/insecure/corruptlibp2p/p2p_node.go @@ -8,7 +8,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/codec/cbor" @@ -54,11 +53,10 @@ func (n *CorruptP2PNode) Subscribe(topic channels.Topic, _ p2p.TopicValidatorFun // NewCorruptLibP2PNode returns corrupted libP2PNode that will subscribe to topics using the AcceptAllTopicValidator. func NewCorruptLibP2PNode( logger zerolog.Logger, - sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - node := p2pnode.NewNode(logger, sporkId, host, pCache, peerManager, disallowListCacheCfg) + node := p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) return &CorruptP2PNode{Node: node, logger: logger, codec: cbor.NewCodec()} } diff --git a/network/p2p/builder.go b/network/p2p/builder.go index b530f4cc49c..b856f931a29 100644 --- a/network/p2p/builder.go +++ b/network/p2p/builder.go @@ -23,7 +23,7 @@ import ( ) type GossipSubFactoryFunc func(context.Context, zerolog.Logger, host.Host, PubSubAdapterConfig, CollectionClusterChangesConsumer) (PubSubAdapter, error) -type CreateNodeFunc func(zerolog.Logger, flow.Identifier, host.Host, ProtocolPeerCache, PeerManager, *DisallowListCacheConfig) LibP2PNode +type CreateNodeFunc func(zerolog.Logger, host.Host, ProtocolPeerCache, PeerManager, *DisallowListCacheConfig) LibP2PNode type GossipSubAdapterConfigFunc func(*BasePubSubAdapterConfig) PubSubAdapterConfig // GossipSubBuilder provides a builder pattern for creating a GossipSub pubsub system. diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 0645be384e1..68f907a80b4 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -65,7 +65,7 @@ type LibP2PNode interface { // Subscribe subscribes the node to the given topic and returns the subscription Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. - Unsubscribe(channel channels.Channel) error + Unsubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. Publish(ctx context.Context, scope network.OutgoingMessageScope) error // Host returns pointer to host object of node. diff --git a/network/p2p/p2pbuilder/libp2pNodeBuilder.go b/network/p2p/p2pbuilder/libp2pNodeBuilder.go index 42e22a2fb02..7ded51080bc 100644 --- a/network/p2p/p2pbuilder/libp2pNodeBuilder.go +++ b/network/p2p/p2pbuilder/libp2pNodeBuilder.go @@ -301,7 +301,7 @@ func (builder *LibP2PNodeBuilder) Build() (p2p.LibP2PNode, error) { } } - node := builder.createNode(builder.logger, builder.sporkId, h, pCache, peerManager, builder.disallowListCacheCfg) + node := builder.createNode(builder.logger, h, pCache, peerManager, builder.disallowListCacheCfg) if builder.connGater != nil { builder.connGater.SetDisallowListOracle(node) @@ -414,12 +414,11 @@ func defaultLibP2POptions(address string, key fcrypto.PrivateKey) ([]config.Opti // DefaultCreateNodeFunc returns new libP2P node. func DefaultCreateNodeFunc(logger zerolog.Logger, - sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, disallowListCacheCfg *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - return p2pnode.NewNode(logger, sporkId, host, pCache, peerManager, disallowListCacheCfg) + return p2pnode.NewNode(logger, host, pCache, peerManager, disallowListCacheCfg) } // DefaultNodeBuilder returns a node builder. diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 6a772217c68..8a19c715c56 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -66,13 +66,11 @@ type Node struct { // Cache of temporary disallow-listed peers, when a peer is disallow-listed, the connections to that peer // are closed and further connections are not allowed till the peer is removed from the disallow-list. disallowListedCache p2p.DisallowListCache - sporkId flow.Identifier } // NewNode creates a new libp2p node and sets its parameters. func NewNode( logger zerolog.Logger, - sporkId flow.Identifier, host host.Host, pCache p2p.ProtocolPeerCache, peerManager p2p.PeerManager, @@ -81,7 +79,6 @@ func NewNode( return &Node{ host: host, logger: logger.With().Str("component", "libp2p-node").Logger(), - sporkId: sporkId, topics: make(map[channels.Topic]p2p.Topic), subs: make(map[channels.Topic]p2p.Subscription), pCache: pCache, @@ -281,9 +278,12 @@ func (n *Node) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidator } // Unsubscribe cancels the subscriber and closes the topic. +// Args: +// topic: topic to unsubscribe from. +// Returns: +// error: error if any, which means unsubscribe failed. // All errors returned from this function can be considered benign. -func (n *Node) Unsubscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, n.sporkId) +func (n *Node) Unsubscribe(topic channels.Topic) error { err := n.unsubscribeTopic(topic) if err != nil { return fmt.Errorf("failed to unsubscribe from topic: %w", err) From 14c893987a4d604c8740b5ae1a5247b9b12b7d58 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 18 Aug 2023 10:34:17 -0700 Subject: [PATCH 530/815] lint and test fix --- cmd/access/node_builder/access_node_builder.go | 2 +- cmd/observer/node_builder/observer_builder.go | 2 +- cmd/scaffold.go | 2 +- follower/follower_builder.go | 2 +- network/internal/testutils/testUtil.go | 2 +- network/message_scope.go | 6 ++++-- network/p2p/dht/dht_test.go | 2 +- network/p2p/libp2pNode.go | 2 +- network/p2p/middleware/middleware.go | 15 ++++++++------- network/p2p/p2pnode/libp2pNode.go | 18 +++++++++--------- 10 files changed, 28 insertions(+), 25 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 5d33051cf96..e87f1e6327d 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1405,7 +1405,7 @@ func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, Libp2pNode: libp2pNode, FlowId: nodeID, BitSwapMetrics: builder.Metrics.Bitswap, - RootBlockID: builder.SporkID, + SporkId: builder.SporkID, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 207e6c167be..1e294e70d01 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -1032,7 +1032,7 @@ func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, Libp2pNode: libp2pNode, FlowId: nodeID, BitSwapMetrics: builder.Metrics.Bitswap, - RootBlockID: builder.SporkID, + SporkId: builder.SporkID, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), diff --git a/cmd/scaffold.go b/cmd/scaffold.go index e7ba460a53d..61d9680f524 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -440,7 +440,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( Libp2pNode: fnb.LibP2PNode, FlowId: fnb.Me.NodeID(), BitSwapMetrics: fnb.Metrics.Bitswap, - RootBlockID: fnb.SporkID, + SporkId: fnb.SporkID, UnicastMessageTimeout: fnb.FlowConfig.NetworkConfig.UnicastMessageTimeout, IdTranslator: fnb.IDTranslator, Codec: fnb.CodecFactory(), diff --git a/follower/follower_builder.go b/follower/follower_builder.go index d03ccde45c6..4b561916fe4 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -759,7 +759,7 @@ func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, Libp2pNode: libp2pNode, FlowId: nodeID, BitSwapMetrics: builder.Metrics.Bitswap, - RootBlockID: builder.SporkID, + SporkId: builder.SporkID, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index ddfc19f186c..e08482e6acd 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -154,7 +154,7 @@ func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware. return &middleware.Config{ Logger: unittest.Logger(), BitSwapMetrics: metrics.NewNoopCollector(), - RootBlockID: sporkId, + SporkId: sporkId, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, Codec: unittest.NetworkCodec(), } diff --git a/network/message_scope.go b/network/message_scope.go index fe37a795572..4e4ded4b9cc 100644 --- a/network/message_scope.go +++ b/network/message_scope.go @@ -6,7 +6,8 @@ import ( "github.com/onflow/flow-go/network/message" ) -// IncomingMessageScope defines the interface for handling incoming message scope. +// IncomingMessageScope defines the interface for incoming message scopes, i.e., self-contained messages that have been +// received on the wire and are ready to be processed. type IncomingMessageScope interface { // OriginId returns the origin node ID. OriginId() flow.Identifier @@ -36,7 +37,8 @@ type IncomingMessageScope interface { PayloadType() string } -// OutgoingMessageScope defines the interface for handling outgoing message scope. +// OutgoingMessageScope defines the interface for building outgoing message scopes, i.e., self-contained messages +// that are ready to be sent on the wire. type OutgoingMessageScope interface { // TargetIds returns the target node IDs. TargetIds() flow.IdentifierList diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 3bbbf25079c..ae306f2effd 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -163,7 +163,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { require.NoError(t, err) logger := unittest.Logger() - topic := channels.TopicFromChannel(channels.TestNetworkChannel, sporkId) + topic := channels.Topic("/flow/" + unittest.IdentifierFixture().String()) topicValidator := flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter()) for _, n := range nodes { s, err := n.Subscribe(topic, topicValidator) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 68f907a80b4..8426bd18ba6 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -67,7 +67,7 @@ type LibP2PNode interface { // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. Unsubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. - Publish(ctx context.Context, scope network.OutgoingMessageScope) error + Publish(ctx context.Context, messageScope network.OutgoingMessageScope) error // Host returns pointer to host object of node. Host() host.Host // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 904f99aaa54..90a82fc9cb6 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -88,7 +88,7 @@ type Middleware struct { libP2PNode p2p.LibP2PNode preferredUnicasts []protocols.ProtocolName bitswapMetrics module.BitswapMetrics - rootBlockID flow.Identifier + sporkId flow.Identifier validators []network.MessageValidator peerManagerFilters []p2p.PeerFilter unicastMessageTimeout time.Duration @@ -135,7 +135,7 @@ type Config struct { Libp2pNode p2p.LibP2PNode FlowId flow.Identifier // This node's Flow ID BitSwapMetrics module.BitswapMetrics - RootBlockID flow.Identifier + SporkId flow.Identifier UnicastMessageTimeout time.Duration IdTranslator p2p.IDTranslator Codec network.Codec @@ -165,7 +165,7 @@ func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { log: cfg.Logger, libP2PNode: cfg.Libp2pNode, bitswapMetrics: cfg.BitSwapMetrics, - rootBlockID: cfg.RootBlockID, + sporkId: cfg.SporkId, validators: DefaultValidators(cfg.Logger, cfg.FlowId), unicastMessageTimeout: cfg.UnicastMessageTimeout, idTranslator: cfg.IdTranslator, @@ -512,7 +512,7 @@ func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { } channel := channels.Channel(msg.ChannelID) - topic := channels.TopicFromChannel(channel, m.rootBlockID) + topic := channels.TopicFromChannel(channel, m.sporkId) // ignore messages if node does not have subscription to topic if !m.libP2PNode.HasSubscription(topic) { @@ -577,8 +577,7 @@ func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { // Subscribe subscribes the middleware to a channel. // No errors are expected during normal operation. func (m *Middleware) Subscribe(channel channels.Channel) error { - - topic := channels.TopicFromChannel(channel, m.rootBlockID) + topic := channels.TopicFromChannel(channel, m.sporkId) var peerFilter p2p.PeerFilter var validators []validator.PubSubMessageValidator @@ -628,7 +627,8 @@ func (m *Middleware) processPubSubMessages(msg *message.Message, peerID peer.ID) // // All errors returned from this function can be considered benign. func (m *Middleware) Unsubscribe(channel channels.Channel) error { - return m.libP2PNode.Unsubscribe(channel) + topic := channels.TopicFromChannel(channel, m.sporkId) + return m.libP2PNode.Unsubscribe(topic) } // processUnicastStreamMessage will decode, perform authorized sender validation and process a message @@ -767,6 +767,7 @@ func (m *Middleware) processMessage(scope network.IncomingMessageScope) { // - the libP2P node fails to publish the message. // // All errors returned from this function can be considered benign. +// TODO: publish has made ready to be removed from middleware, and instead the libp2pNode.Publish should be used directly. func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { return m.libP2PNode.Publish(m.ctx, msg) } diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 8a19c715c56..9fcbcc9a466 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -341,16 +341,16 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { // Publish publishes the given payload on the topic. // All errors returned from this function can be considered benign. -func (n *Node) Publish(ctx context.Context, msgScope flownet.OutgoingMessageScope) error { +func (n *Node) Publish(ctx context.Context, messageScope flownet.OutgoingMessageScope) error { lg := n.logger.With(). - Str("topic", msgScope.Topic().String()). - Interface("proto_message", msgScope.Proto()). - Str("payload_type", msgScope.PayloadType()). - Int("message_size", msgScope.Size()).Logger() + Str("topic", messageScope.Topic().String()). + Interface("proto_message", messageScope.Proto()). + Str("payload_type", messageScope.PayloadType()). + Int("message_size", messageScope.Size()).Logger() lg.Debug().Msg("received message to publish") // convert the message to bytes to be put on the wire. - data, err := msgScope.Proto().Marshal() + data, err := messageScope.Proto().Marshal() if err != nil { return fmt.Errorf("failed to marshal the message: %w", err) } @@ -362,13 +362,13 @@ func (n *Node) Publish(ctx context.Context, msgScope flownet.OutgoingMessageScop return fmt.Errorf("message size %d exceeds configured max message size %d", msgSize, DefaultMaxPubSubMsgSize) } - ps, found := n.topics[msgScope.Topic()] + ps, found := n.topics[messageScope.Topic()] if !found { - return fmt.Errorf("could not find topic (%s)", msgScope.Topic()) + return fmt.Errorf("could not find topic (%s)", messageScope.Topic()) } err = ps.Publish(ctx, data) if err != nil { - return fmt.Errorf("could not publish to topic (%s): %w", msgScope.Topic(), err) + return fmt.Errorf("could not publish to topic (%s): %w", messageScope.Topic(), err) } lg.Debug().Msg("published message to topic") From 4d296ee7c4f8dad495a352dbc41e7f4561464337 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 18 Aug 2023 10:43:46 -0700 Subject: [PATCH 531/815] fixes build errors --- network/p2p/dht/dht_test.go | 6 ++--- network/p2p/test/sporking_test.go | 24 ++++++++----------- .../p2p/tracer/gossipSubMeshTracer_test.go | 6 ++--- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index ae306f2effd..534cca7eaa6 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -121,6 +121,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { // N4 N5 N4-----N5 sporkId := unittest.IdentifierFixture() + topic := channels.TopicFromChannel(channels.TestNetworkChannel, sporkId) idProvider := mockmodule.NewIdentityProvider(t) // create one node running the DHT Server (mimicking the staked AN) dhtServerNodes, serverIDs := p2ptest.NodesFixture(t, sporkId, "dht_test", 1, idProvider, p2ptest.WithDHTOptions(dht.AsServer())) @@ -156,14 +157,13 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { messageScope, err := message.NewOutgoingScope( ids.NodeIDs(), - channels.TopicFromChannel(channels.TestNetworkChannel, sporkId), + topic, &libp2pmsg.TestMessage{}, unittest.NetworkCodec().Encode, message.ProtocolTypePubSub) require.NoError(t, err) logger := unittest.Logger() - topic := channels.Topic("/flow/" + unittest.IdentifierFixture().String()) topicValidator := flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter()) for _, n := range nodes { s, err := n.Subscribe(topic, topicValidator) @@ -218,6 +218,6 @@ loop: // Step 6: unsubscribes all nodes from the topic for _, n := range nodes { - assert.NoError(t, n.Unsubscribe(channels.TestNetworkChannel)) + assert.NoError(t, n.Unsubscribe(topic)) } } diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index ba5778b17b6..41a0379b2ac 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -86,10 +86,12 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { require.NoError(t, err) // create stream from node 1 to node 2 - testOneToOneMessagingSucceeds(t, node1, peerInfo2) + node1.Host().Peerstore().AddAddrs(peerInfo2.ID, peerInfo2.Addrs, peerstore.AddressTTL) + s, err := node1.CreateStream(context.Background(), peerInfo2.ID) + require.NoError(t, err) + assert.NotNil(t, s) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain - // stop node 2 and start it again with a different networking key but on the same IP and port p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) @@ -150,8 +152,11 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { idProvider.SetIdentities(flow.IdentityList{&id1, &id2}) p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) - // create stream from node 2 to node 1 - testOneToOneMessagingSucceeds(t, node2, peerInfo1) + // create stream from node 1 to node 2 + node2.Host().Peerstore().AddAddrs(peerInfo1.ID, peerInfo1.Addrs, peerstore.AddressTTL) + s, err := node2.CreateStream(context.Background(), peerInfo1.ID) + require.NoError(t, err) + assert.NotNil(t, s) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different libp2p protocol id to listen for @@ -271,7 +276,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { // mimic that node1 is now part of the new spork while node2 remains on the old spork // by unsubscribing node1 from 'topicBeforeSpork' and subscribing it to 'topicAfterSpork' // and keeping node2 subscribed to topic 'topicBeforeSpork' - err = node1.Unsubscribe(channels.TestNetworkChannel) + err = node1.Unsubscribe(topicBeforeSpork) require.NoError(t, err) _, err = node1.Subscribe(topicAfterSpork, topicValidator) require.NoError(t, err) @@ -300,15 +305,6 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { "nodes on different sporks were able to communicate") } -func testOneToOneMessagingSucceeds(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { - // create stream from node 1 to node 2 - sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) - s, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) - // assert that stream creation succeeded - require.NoError(t, err) - assert.NotNil(t, s) -} - func testOneToOneMessagingFails(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { // create stream from source node to destination address sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index 4384ffcf9f1..6e7bc77e0f6 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -178,9 +178,9 @@ func TestGossipSubMeshTracer(t *testing.T) { // all nodes except the tracerNode unsubscribe from the topic1, which triggers sending a PRUNE to the tracerNode for each unsubscription. // We expect the tracerNode to remove the otherNode1, otherNode2, and unknownNode from its mesh. - require.NoError(t, otherNode1.Unsubscribe(channel1)) - require.NoError(t, otherNode2.Unsubscribe(channel1)) - require.NoError(t, unknownNode.Unsubscribe(channel1)) + require.NoError(t, otherNode1.Unsubscribe(topic1)) + require.NoError(t, otherNode2.Unsubscribe(topic1)) + require.NoError(t, unknownNode.Unsubscribe(topic1)) assert.Eventually(t, func() bool { // eventually, the tracerNode should not have the other node in its mesh for topic1. From d306a89fe6b9cf95eb88c1d704d9e751f55ea714 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 18 Aug 2023 10:50:40 -0700 Subject: [PATCH 532/815] updates mocks --- network/p2p/mock/create_node_func.go | 12 +++++------- network/p2p/mock/lib_p2_p_node.go | 18 +++++++++--------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/network/p2p/mock/create_node_func.go b/network/p2p/mock/create_node_func.go index b2db19f13ee..1a57772cbeb 100644 --- a/network/p2p/mock/create_node_func.go +++ b/network/p2p/mock/create_node_func.go @@ -4,8 +4,6 @@ package mockp2p import ( host "github.com/libp2p/go-libp2p/core/host" - flow "github.com/onflow/flow-go/model/flow" - mock "github.com/stretchr/testify/mock" p2p "github.com/onflow/flow-go/network/p2p" @@ -18,13 +16,13 @@ type CreateNodeFunc struct { mock.Mock } -// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4, _a5 -func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 flow.Identifier, _a2 host.Host, _a3 p2p.ProtocolPeerCache, _a4 p2p.PeerManager, _a5 *p2p.DisallowListCacheConfig) p2p.LibP2PNode { - ret := _m.Called(_a0, _a1, _a2, _a3, _a4, _a5) +// Execute provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 +func (_m *CreateNodeFunc) Execute(_a0 zerolog.Logger, _a1 host.Host, _a2 p2p.ProtocolPeerCache, _a3 p2p.PeerManager, _a4 *p2p.DisallowListCacheConfig) p2p.LibP2PNode { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4) var r0 p2p.LibP2PNode - if rf, ok := ret.Get(0).(func(zerolog.Logger, flow.Identifier, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager, *p2p.DisallowListCacheConfig) p2p.LibP2PNode); ok { - r0 = rf(_a0, _a1, _a2, _a3, _a4, _a5) + if rf, ok := ret.Get(0).(func(zerolog.Logger, host.Host, p2p.ProtocolPeerCache, p2p.PeerManager, *p2p.DisallowListCacheConfig) p2p.LibP2PNode); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(p2p.LibP2PNode) diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 008ad125a96..2fcdc336ad7 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -284,13 +284,13 @@ func (_m *LibP2PNode) PeerScoreExposer() p2p.PeerScoreExposer { return r0 } -// Publish provides a mock function with given fields: ctx, scope -func (_m *LibP2PNode) Publish(ctx context.Context, scope flow_gonetwork.OutgoingMessageScope) error { - ret := _m.Called(ctx, scope) +// Publish provides a mock function with given fields: ctx, messageScope +func (_m *LibP2PNode) Publish(ctx context.Context, messageScope flow_gonetwork.OutgoingMessageScope) error { + ret := _m.Called(ctx, messageScope) var r0 error if rf, ok := ret.Get(0).(func(context.Context, flow_gonetwork.OutgoingMessageScope) error); ok { - r0 = rf(ctx, scope) + r0 = rf(ctx, messageScope) } else { r0 = ret.Error(0) } @@ -430,13 +430,13 @@ func (_m *LibP2PNode) Subscribe(topic channels.Topic, topicValidator p2p.TopicVa return r0, r1 } -// Unsubscribe provides a mock function with given fields: channel -func (_m *LibP2PNode) Unsubscribe(channel channels.Channel) error { - ret := _m.Called(channel) +// Unsubscribe provides a mock function with given fields: topic +func (_m *LibP2PNode) Unsubscribe(topic channels.Topic) error { + ret := _m.Called(topic) var r0 error - if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { - r0 = rf(channel) + if rf, ok := ret.Get(0).(func(channels.Topic) error); ok { + r0 = rf(topic) } else { r0 = ret.Error(0) } From c3fc4957cbdfbaa34c4a04c385029a9e4f90f515 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 17:24:03 -0400 Subject: [PATCH 533/815] probability factor WIP --- engine/common/synchronization/alsp.go | 5 +++++ engine/common/synchronization/engine.go | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 engine/common/synchronization/alsp.go diff --git a/engine/common/synchronization/alsp.go b/engine/common/synchronization/alsp.go new file mode 100644 index 00000000000..4aabc497cd6 --- /dev/null +++ b/engine/common/synchronization/alsp.go @@ -0,0 +1,5 @@ +package synchronization + +type Alsp struct { + syncRequestProbabilityFactor float32 +} diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 02f1e90ad51..7953d09f854 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -58,6 +58,7 @@ type Engine struct { requestHandler *RequestHandler // component responsible for handling requests randomizer rand.Randomizer + alsp *Alsp pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse pendingBlockResponses engine.MessageStore // message store for *message.BlockResponse @@ -488,6 +489,7 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { // Generate a random integer between 1 and 1000 n, err := e.randomizer.Uint32n(uint32(1001)) + e.alsp.syncRequestProbabilityFactor if err != nil { // failing to generate a random number is unlikely. If an error is encountered while From b107e35e28eb19f70aa6f0764c04e35bf1cadab8 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 18 Aug 2023 17:53:21 -0400 Subject: [PATCH 534/815] lint fix --- network/alsp/manager/manager_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 1b6eb07ad16..e8509312dfe 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -293,6 +293,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // handling of repeated reported misbehavior and disallow listing. func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integration(t *testing.T) { cfg := managerCfgFixture(t) + sporkId := unittest.IdentifierFixture() fastDecay := false fastDecayFunc := func(record model.ProtocolSpamRecord) float64 { t.Logf("decayFuc called with record: %+v", record) @@ -319,9 +320,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio alspmgr.WithDecayFunc(fastDecayFunc), } - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, 3, + ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t), mocknetwork.NewViolationsConsumer(t)) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) From e648a4a94e4c3a78393cf162bae670ced129b8d9 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Thu, 10 Aug 2023 22:02:42 -0700 Subject: [PATCH 535/815] store chunk data pack in a separate database --- cmd/execution_builder.go | 15 ++++++++++++++- cmd/execution_config.go | 2 ++ engine/execution/state/state.go | 14 ++++++-------- storage/badger/chunkDataPacks.go | 16 ++++++++++++++++ storage/chunkDataPacks.go | 2 ++ storage/mock/chunk_data_packs.go | 14 ++++++++++++++ 6 files changed, 54 insertions(+), 9 deletions(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 871ab90b8f6..142d5c0afba 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -13,6 +13,7 @@ import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" + badgerDB "github.com/dgraph-io/badger/v2" "github.com/ipfs/go-cid" badger "github.com/ipfs/go-ds-badger2" "github.com/onflow/flow-core-contracts/lib/go/templates" @@ -627,6 +628,14 @@ func (exeNode *ExecutionNode) LoadExecutionDataGetter(node *NodeConfig) error { return nil } +func openChunkDataPackDB(dbPath string) (*badgerDB.DB, error) { + db, err := badgerDB.Open(badgerDB.LSMOnlyOptions(dbPath)) + if err != nil { + return nil, fmt.Errorf("could not open chunk data pack badger db at path %v: %w", dbPath, err) + } + return db, nil +} + func (exeNode *ExecutionNode) LoadExecutionState( node *NodeConfig, ) ( @@ -634,7 +643,11 @@ func (exeNode *ExecutionNode) LoadExecutionState( error, ) { - chunkDataPacks := storage.NewChunkDataPacks(node.Metrics.Cache, node.DB, node.Storage.Collections, exeNode.exeConf.chunkDataPackCacheSize) + chunkDataPackDB, err := openChunkDataPackDB(exeNode.exeConf.chunkDataPackDir) + if err != nil { + return nil, err + } + chunkDataPacks := storage.NewChunkDataPacks(node.Metrics.Cache, chunkDataPackDB, node.Storage.Collections, exeNode.exeConf.chunkDataPackCacheSize) // Needed for gRPC server, make sure to assign to main scoped vars exeNode.events = storage.NewEvents(node.Metrics.Cache, node.DB) diff --git a/cmd/execution_config.go b/cmd/execution_config.go index 1d7de8e5c8f..646860e238a 100644 --- a/cmd/execution_config.go +++ b/cmd/execution_config.go @@ -32,6 +32,7 @@ type ExecutionConfig struct { transactionResultsCacheSize uint checkpointDistance uint checkpointsToKeep uint + chunkDataPackDir string chunkDataPackCacheSize uint chunkDataPackRequestsCacheSize uint32 requestInterval time.Duration @@ -80,6 +81,7 @@ func (exeConf *ExecutionConfig) SetupFlags(flags *pflag.FlagSet) { flags.BoolVar(&exeConf.computationConfig.ExtensiveTracing, "extensive-tracing", false, "adds high-overhead tracing to execution") flags.BoolVar(&exeConf.computationConfig.CadenceTracing, "cadence-tracing", false, "enables cadence runtime level tracing") flags.IntVar(&exeConf.computationConfig.MaxConcurrency, "computer-max-concurrency", 1, "set to greater than 1 to enable concurrent transaction execution") + flags.StringVar(&exeConf.chunkDataPackDir, "chunk-data-pack-dir", filepath.Join(homedir, ".flow", "chunk_data_packs"), "directory to use for storing chunk data packs") flags.UintVar(&exeConf.chunkDataPackCacheSize, "chdp-cache", storage.DefaultCacheSize, "cache size for chunk data packs") flags.Uint32Var(&exeConf.chunkDataPackRequestsCacheSize, "chdp-request-queue", mempool.DefaultChunkDataPackRequestQueueSize, "queue size for chunk data pack requests") flags.DurationVar(&exeConf.requestInterval, "request-interval", 60*time.Second, "the interval between requests for the requester engine") diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 0f754520493..007dee16894 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -286,6 +286,11 @@ func (s *state) SaveExecutionResults( header := result.ExecutableBlock.Block.Header blockID := header.ID() + err := s.chunkDataPacks.StoreMul(result.AllChunkDataPacks()) + if err != nil { + return fmt.Errorf("can not store multile chunk data pack: %w", err) + } + // Write Batch is BadgerDB feature designed for handling lots of writes // in efficient and automatic manner, hence pushing all the updates we can // as tightly as possible to let Badger manage it. @@ -293,14 +298,7 @@ func (s *state) SaveExecutionResults( // but it's the closest thing to atomicity we could have batch := badgerstorage.NewBatch(s.db) - for _, chunkDataPack := range result.AllChunkDataPacks() { - err := s.chunkDataPacks.BatchStore(chunkDataPack, batch) - if err != nil { - return fmt.Errorf("cannot store chunk data pack: %w", err) - } - } - - err := s.events.BatchStore(blockID, []flow.EventsList{result.AllEvents()}, batch) + err = s.events.BatchStore(blockID, []flow.EventsList{result.AllEvents()}, batch) if err != nil { return fmt.Errorf("cannot store events: %w", err) } diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index 2a5c733f60e..ec0d81fa6a7 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -79,6 +79,22 @@ func (ch *ChunkDataPacks) BatchStore(c *flow.ChunkDataPack, batch storage.BatchS return operation.BatchInsertChunkDataPack(sc)(writeBatch) } +func (ch *ChunkDataPacks) StoreMul(cs []*flow.ChunkDataPack) error { + batch := NewBatch(ch.db) + for _, c := range cs { + err := ch.BatchStore(c, batch) + if err != nil { + return fmt.Errorf("cannot store chunk data pack: %w", err) + } + } + + err := batch.Flush() + if err != nil { + return fmt.Errorf("cannot flush batch: %w", err) + } + return nil +} + // BatchRemove removes ChunkDataPack c keyed by its ChunkID in provided batch // No errors are expected during normal operation, even if no entries are matched. // If Badger unexpectedly fails to process the request, the error is wrapped in a generic error and returned. diff --git a/storage/chunkDataPacks.go b/storage/chunkDataPacks.go index a6055b8b9b3..5308b81ee40 100644 --- a/storage/chunkDataPacks.go +++ b/storage/chunkDataPacks.go @@ -10,6 +10,8 @@ type ChunkDataPacks interface { // Store inserts the chunk header, keyed by chunk ID. Store(c *flow.ChunkDataPack) error + StoreMul(c []*flow.ChunkDataPack) error + // BatchStore inserts the chunk header, keyed by chunk ID into a given batch BatchStore(c *flow.ChunkDataPack, batch BatchStorage) error diff --git a/storage/mock/chunk_data_packs.go b/storage/mock/chunk_data_packs.go index 66205d7c099..604e7f8543b 100644 --- a/storage/mock/chunk_data_packs.go +++ b/storage/mock/chunk_data_packs.go @@ -82,6 +82,20 @@ func (_m *ChunkDataPacks) Store(c *flow.ChunkDataPack) error { return r0 } +// StoreMul provides a mock function with given fields: c +func (_m *ChunkDataPacks) StoreMul(c []*flow.ChunkDataPack) error { + ret := _m.Called(c) + + var r0 error + if rf, ok := ret.Get(0).(func([]*flow.ChunkDataPack) error); ok { + r0 = rf(c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + type mockConstructorTestingTNewChunkDataPacks interface { mock.TestingT Cleanup(func()) From ccef0d4b463a9b5a4f6d34256fdbdfb2d229a4a2 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 11 Aug 2023 10:37:42 -0700 Subject: [PATCH 536/815] skip flakey test --- module/mempool/queue/queue_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/module/mempool/queue/queue_test.go b/module/mempool/queue/queue_test.go index 90fce14156b..388e697e335 100644 --- a/module/mempool/queue/queue_test.go +++ b/module/mempool/queue/queue_test.go @@ -292,6 +292,7 @@ func TestQueue(t *testing.T) { //}) t.Run("String()", func(t *testing.T) { + t.Skip("flakey, because String() will iterate through a map, which is not determinsitic") // a <- c <- d <- f queue := NewQueue(a) stored, _ := queue.TryAdd(c) From 189340178aafc4a38ba40c7a3055bb398d602d2f Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 11 Aug 2023 11:12:47 -0700 Subject: [PATCH 537/815] fix flag --- integration/testnet/network.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 8b9522d7ba6..e87fce1da6f 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -75,6 +75,8 @@ const ( DefaultExecutionRootDir = "/data/exedb" // DefaultExecutionDataServiceDir for the execution data service blobstore. DefaultExecutionDataServiceDir = "/data/execution_data" + // DefaultChunkDataPackDir for the chunk data packs + DefaultChunkDataPackDir = "/data/chunk_data_pack" // DefaultProfilerDir is the default directory for the profiler DefaultProfilerDir = "/data/profiler" @@ -861,6 +863,7 @@ func (net *FlowNetwork) AddNode(t *testing.T, bootstrapDir string, nodeConf Cont nodeContainer.AddFlag("triedir", DefaultExecutionRootDir) nodeContainer.AddFlag("execution-data-dir", DefaultExecutionDataServiceDir) + nodeContainer.AddFlag("chunk-data-pack-dir", DefaultChunkDataPackDir) case flow.RoleAccess: nodeContainer.exposePort(GRPCPort, testingdock.RandomPort(t)) From 577c706908220639772df0dd5a08524988e8e25f Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 11 Aug 2023 12:50:42 -0700 Subject: [PATCH 538/815] add test case --- storage/badger/chunk_data_pack_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/storage/badger/chunk_data_pack_test.go b/storage/badger/chunk_data_pack_test.go index e1733d4e10f..dbde884aeda 100644 --- a/storage/badger/chunk_data_pack_test.go +++ b/storage/badger/chunk_data_pack_test.go @@ -79,6 +79,25 @@ func TestChunkDataPacks_MissingItem(t *testing.T) { }) } +// TestChunkDataPacks_StoreTwice evaluates that storing the same chunk data pack twice +// does not result in an error. +func TestChunkDataPacks_StoreTwice(t *testing.T) { + WithChunkDataPacks(t, 2, func(t *testing.T, chunkDataPacks []*flow.ChunkDataPack, chunkDataPackStore *badgerstorage.ChunkDataPacks, db *badger.DB) { + transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) + collections := badgerstorage.NewCollections(db, transactions) + store := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) + require.NoError(t, store.StoreMul(chunkDataPacks)) + + for _, c := range chunkDataPacks { + c2, err := store.ByChunkID(c.ChunkID) + require.NoError(t, err) + require.Equal(t, c, c2) + } + + require.NoError(t, store.StoreMul(chunkDataPacks)) + }) +} + // WithChunkDataPacks is a test helper that generates specified number of chunk data packs, store them using the storeFunc, and // then evaluates whether they are successfully retrieved from storage. func WithChunkDataPacks(t *testing.T, chunks int, storeFunc func(*testing.T, []*flow.ChunkDataPack, *badgerstorage.ChunkDataPacks, *badger.DB)) { From a3ff57b22e40e902a554063d0a8fd666ba2a3d2b Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Mon, 14 Aug 2023 09:38:10 -0700 Subject: [PATCH 539/815] address review comments --- cmd/execution_builder.go | 23 ++++++++++++++++++++--- cmd/util/cmd/truncate-database/cmd.go | 13 +++++++++++-- integration/localnet/builder/bootstrap.go | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 142d5c0afba..7d13939a041 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -83,6 +83,7 @@ import ( storageerr "github.com/onflow/flow-go/storage" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/procedure" + sutil "github.com/onflow/flow-go/storage/util" ) const ( @@ -628,8 +629,24 @@ func (exeNode *ExecutionNode) LoadExecutionDataGetter(node *NodeConfig) error { return nil } -func openChunkDataPackDB(dbPath string) (*badgerDB.DB, error) { - db, err := badgerDB.Open(badgerDB.LSMOnlyOptions(dbPath)) +func openChunkDataPackDB(dbPath string, logger zerolog.Logger) (*badgerDB.DB, error) { + log := sutil.NewLogger(logger) + + opts := badgerDB. + DefaultOptions(dbPath). + WithKeepL0InMemory(true). + WithLogger(log). + + // the ValueLogFileSize option specifies how big the value of a + // key-value pair is allowed to be saved into badger. + // exceeding this limit, will fail with an error like this: + // could not store data: Value with size exceeded 1073741824 limit + // Maximum value size is 10G, needed by execution node + // TODO: finding a better max value for each node type + WithValueLogFileSize(256 << 23). + WithValueLogMaxEntries(100000) // Default is 1000000 + + db, err := badgerDB.Open(opts) if err != nil { return nil, fmt.Errorf("could not open chunk data pack badger db at path %v: %w", dbPath, err) } @@ -643,7 +660,7 @@ func (exeNode *ExecutionNode) LoadExecutionState( error, ) { - chunkDataPackDB, err := openChunkDataPackDB(exeNode.exeConf.chunkDataPackDir) + chunkDataPackDB, err := openChunkDataPackDB(exeNode.exeConf.chunkDataPackDir, node.Logger) if err != nil { return nil, err } diff --git a/cmd/util/cmd/truncate-database/cmd.go b/cmd/util/cmd/truncate-database/cmd.go index d18cbe610c4..e509aa39f32 100644 --- a/cmd/util/cmd/truncate-database/cmd.go +++ b/cmd/util/cmd/truncate-database/cmd.go @@ -9,7 +9,8 @@ import ( ) var ( - flagDatadir string + flagDatadir string + flagChunkDataPackDir string ) var Cmd = &cobra.Command{ @@ -24,14 +25,22 @@ func init() { "directory that stores the protocol state") _ = Cmd.MarkFlagRequired("datadir") + Cmd.Flags().StringVar(&flagChunkDataPackDir, "chunk-data-pack-dir", "", + "directory that stores the chunk data pack") + _ = Cmd.MarkFlagRequired("chunk-data-pack-dir") } func run(*cobra.Command, []string) { - log.Info().Msg("Opening database with truncate") + log.Info().Msg("Opening protocol database with truncate") db := common.InitStorageWithTruncate(flagDatadir, true) defer db.Close() + log.Info().Msg("ProtocolDB Truncated") + + chunkdb := common.InitStorageWithTruncate(flagChunkDataPackDir, true) + defer chunkdb.Close() + log.Info().Msg("Truncated") } diff --git a/integration/localnet/builder/bootstrap.go b/integration/localnet/builder/bootstrap.go index 2a42963461f..1aeee814b1d 100644 --- a/integration/localnet/builder/bootstrap.go +++ b/integration/localnet/builder/bootstrap.go @@ -395,6 +395,7 @@ func prepareExecutionService(container testnet.ContainerConfig, i int, n int) Se fmt.Sprintf("--cadence-tracing=%t", cadenceTracing), fmt.Sprintf("--extensive-tracing=%t", extesiveTracing), "--execution-data-dir=/data/execution-data", + "--chunk-data-pack-dir=/data/chunk-data-pack", ) service.Volumes = append(service.Volumes, From f8614b380700ed60464bce48fb0ffffeddddbc17 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Mon, 14 Aug 2023 09:42:33 -0700 Subject: [PATCH 540/815] add comment --- engine/execution/state/state.go | 4 ++-- storage/badger/chunkDataPacks.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 007dee16894..94d89b83b74 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -288,11 +288,11 @@ func (s *state) SaveExecutionResults( err := s.chunkDataPacks.StoreMul(result.AllChunkDataPacks()) if err != nil { - return fmt.Errorf("can not store multile chunk data pack: %w", err) + return fmt.Errorf("can not store multiple chunk data pack: %w", err) } // Write Batch is BadgerDB feature designed for handling lots of writes - // in efficient and automatic manner, hence pushing all the updates we can + // in efficient and atomic manner, hence pushing all the updates we can // as tightly as possible to let Badger manage it. // Note, that it does not guarantee atomicity as transactions has size limit, // but it's the closest thing to atomicity we could have diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index ec0d81fa6a7..bd5444cf991 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -79,6 +79,8 @@ func (ch *ChunkDataPacks) BatchStore(c *flow.ChunkDataPack, batch storage.BatchS return operation.BatchInsertChunkDataPack(sc)(writeBatch) } +// StoreMul stores multiple ChunkDataPacks cs keyed by their ChunkIDs in provided batch. +// No errors are expected during normal operation, but it may return generic error func (ch *ChunkDataPacks) StoreMul(cs []*flow.ChunkDataPack) error { batch := NewBatch(ch.db) for _, c := range cs { From 8fdd55427649614a59542e0038d840f53ba4c07a Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Tue, 15 Aug 2023 12:28:56 -0700 Subject: [PATCH 541/815] remove chunkdatapack'Store method --- engine/execution/state/state.go | 2 +- storage/badger/chunkDataPacks.go | 13 ++----------- storage/badger/chunk_data_pack_test.go | 4 ++-- storage/chunkDataPacks.go | 7 +++---- storage/mock/chunk_data_packs.go | 22 ++++------------------ 5 files changed, 12 insertions(+), 36 deletions(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 94d89b83b74..00ace8ecb0b 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -286,7 +286,7 @@ func (s *state) SaveExecutionResults( header := result.ExecutableBlock.Block.Header blockID := header.ID() - err := s.chunkDataPacks.StoreMul(result.AllChunkDataPacks()) + err := s.chunkDataPacks.Store(result.AllChunkDataPacks()) if err != nil { return fmt.Errorf("can not store multiple chunk data pack: %w", err) } diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index bd5444cf991..ad0ed7926f7 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -48,15 +48,6 @@ func NewChunkDataPacks(collector module.CacheMetrics, db *badger.DB, collections return &ch } -func (ch *ChunkDataPacks) Store(c *flow.ChunkDataPack) error { - sc := toStoredChunkDataPack(c) - err := operation.RetryOnConflictTx(ch.db, transaction.Update, ch.byChunkIDCache.PutTx(sc.ChunkID, sc)) - if err != nil { - return fmt.Errorf("could not store chunk datapack: %w", err) - } - return nil -} - func (ch *ChunkDataPacks) Remove(chunkID flow.Identifier) error { err := operation.RetryOnConflict(ch.db.Update, operation.RemoveChunkDataPack(chunkID)) if err != nil { @@ -79,9 +70,9 @@ func (ch *ChunkDataPacks) BatchStore(c *flow.ChunkDataPack, batch storage.BatchS return operation.BatchInsertChunkDataPack(sc)(writeBatch) } -// StoreMul stores multiple ChunkDataPacks cs keyed by their ChunkIDs in provided batch. +// Store stores multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch. // No errors are expected during normal operation, but it may return generic error -func (ch *ChunkDataPacks) StoreMul(cs []*flow.ChunkDataPack) error { +func (ch *ChunkDataPacks) Store(cs []*flow.ChunkDataPack) error { batch := NewBatch(ch.db) for _, c := range cs { err := ch.BatchStore(c, batch) diff --git a/storage/badger/chunk_data_pack_test.go b/storage/badger/chunk_data_pack_test.go index dbde884aeda..cd6146b26e6 100644 --- a/storage/badger/chunk_data_pack_test.go +++ b/storage/badger/chunk_data_pack_test.go @@ -86,7 +86,7 @@ func TestChunkDataPacks_StoreTwice(t *testing.T) { transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) collections := badgerstorage.NewCollections(db, transactions) store := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) - require.NoError(t, store.StoreMul(chunkDataPacks)) + require.NoError(t, store.Store(chunkDataPacks)) for _, c := range chunkDataPacks { c2, err := store.ByChunkID(c.ChunkID) @@ -94,7 +94,7 @@ func TestChunkDataPacks_StoreTwice(t *testing.T) { require.Equal(t, c, c2) } - require.NoError(t, store.StoreMul(chunkDataPacks)) + require.NoError(t, store.Store(chunkDataPacks)) }) } diff --git a/storage/chunkDataPacks.go b/storage/chunkDataPacks.go index 5308b81ee40..ddcd0f69da9 100644 --- a/storage/chunkDataPacks.go +++ b/storage/chunkDataPacks.go @@ -7,10 +7,9 @@ import ( // ChunkDataPacks represents persistent storage for chunk data packs. type ChunkDataPacks interface { - // Store inserts the chunk header, keyed by chunk ID. - Store(c *flow.ChunkDataPack) error - - StoreMul(c []*flow.ChunkDataPack) error + // Store stores multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch. + // No errors are expected during normal operation, but it may return generic error + Store(cs []*flow.ChunkDataPack) error // BatchStore inserts the chunk header, keyed by chunk ID into a given batch BatchStore(c *flow.ChunkDataPack, batch BatchStorage) error diff --git a/storage/mock/chunk_data_packs.go b/storage/mock/chunk_data_packs.go index 604e7f8543b..081424ee1b0 100644 --- a/storage/mock/chunk_data_packs.go +++ b/storage/mock/chunk_data_packs.go @@ -68,27 +68,13 @@ func (_m *ChunkDataPacks) ByChunkID(chunkID flow.Identifier) (*flow.ChunkDataPac return r0, r1 } -// Store provides a mock function with given fields: c -func (_m *ChunkDataPacks) Store(c *flow.ChunkDataPack) error { - ret := _m.Called(c) - - var r0 error - if rf, ok := ret.Get(0).(func(*flow.ChunkDataPack) error); ok { - r0 = rf(c) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// StoreMul provides a mock function with given fields: c -func (_m *ChunkDataPacks) StoreMul(c []*flow.ChunkDataPack) error { - ret := _m.Called(c) +// Store provides a mock function with given fields: cs +func (_m *ChunkDataPacks) Store(cs []*flow.ChunkDataPack) error { + ret := _m.Called(cs) var r0 error if rf, ok := ret.Get(0).(func([]*flow.ChunkDataPack) error); ok { - r0 = rf(c) + r0 = rf(cs) } else { r0 = ret.Error(0) } From 4c494b56f3396b7a4853d3516126a41d484de8f9 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Wed, 16 Aug 2023 11:34:39 -0700 Subject: [PATCH 542/815] rollback stored chunk data pack when fails to store execution result --- cmd/execution_builder.go | 6 ++++ engine/execution/state/state.go | 12 ++++++++ storage/badger/chunkDataPacks.go | 21 +++++++++---- storage/badger/chunk_data_pack_test.go | 41 ++++++++++++++++++-------- storage/chunkDataPacks.go | 4 +++ storage/mock/chunk_data_packs.go | 14 +++++++++ 6 files changed, 79 insertions(+), 19 deletions(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 7d13939a041..0c60c2cec0b 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -664,6 +664,12 @@ func (exeNode *ExecutionNode) LoadExecutionState( if err != nil { return nil, err } + exeNode.builder.ShutdownFunc(func() error { + if err := chunkDataPackDB.Close(); err != nil { + return fmt.Errorf("error closing chunk data pack database: %w", err) + } + return nil + }) chunkDataPacks := storage.NewChunkDataPacks(node.Metrics.Cache, chunkDataPackDB, node.Storage.Collections, exeNode.exeConf.chunkDataPackCacheSize) // Needed for gRPC server, make sure to assign to main scoped vars diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 00ace8ecb0b..abd45352457 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -298,6 +298,18 @@ func (s *state) SaveExecutionResults( // but it's the closest thing to atomicity we could have batch := badgerstorage.NewBatch(s.db) + defer func() { + // Rollback if an error occurs during batch operations + if err != nil { + chunks := result.AllChunkDataPacks() + chunkIDs := make([]flow.Identifier, 0, len(chunks)) + for _, chunk := range chunks { + chunkIDs = append(chunkIDs, chunk.ID()) + } + s.chunkDataPacks.Remove(chunkIDs) + } + }() + err = s.events.BatchStore(blockID, []flow.EventsList{result.AllEvents()}, batch) if err != nil { return fmt.Errorf("cannot store events: %w", err) diff --git a/storage/badger/chunkDataPacks.go b/storage/badger/chunkDataPacks.go index ad0ed7926f7..4b17fc96a3e 100644 --- a/storage/badger/chunkDataPacks.go +++ b/storage/badger/chunkDataPacks.go @@ -34,7 +34,7 @@ func NewChunkDataPacks(collector module.CacheMetrics, db *badger.DB, collections } } - cache := newCache[flow.Identifier, *badgermodel.StoredChunkDataPack](collector, metrics.ResourceChunkDataPack, + cache := newCache(collector, metrics.ResourceChunkDataPack, withLimit[flow.Identifier, *badgermodel.StoredChunkDataPack](byChunkIDCacheSize), withStore(store), withRetrieve(retrieve), @@ -48,13 +48,22 @@ func NewChunkDataPacks(collector module.CacheMetrics, db *badger.DB, collections return &ch } -func (ch *ChunkDataPacks) Remove(chunkID flow.Identifier) error { - err := operation.RetryOnConflict(ch.db.Update, operation.RemoveChunkDataPack(chunkID)) +// Remove removes multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch. +// No errors are expected during normal operation, even if no entries are matched. +func (ch *ChunkDataPacks) Remove(chunkIDs []flow.Identifier) error { + batch := NewBatch(ch.db) + + for _, c := range chunkIDs { + err := ch.BatchRemove(c, batch) + if err != nil { + return fmt.Errorf("cannot remove chunk data pack: %w", err) + } + } + + err := batch.Flush() if err != nil { - return fmt.Errorf("could not remove chunk datapack: %w", err) + return fmt.Errorf("cannot flush batch to remove chunk data pack: %w", err) } - // TODO Integrate cache removal in a similar way as storage/retrieval is - ch.byChunkIDCache.Remove(chunkID) return nil } diff --git a/storage/badger/chunk_data_pack_test.go b/storage/badger/chunk_data_pack_test.go index cd6146b26e6..0a98e9d170d 100644 --- a/storage/badger/chunk_data_pack_test.go +++ b/storage/badger/chunk_data_pack_test.go @@ -22,24 +22,39 @@ import ( // It also evaluates that re-inserting is idempotent. func TestChunkDataPacks_Store(t *testing.T) { WithChunkDataPacks(t, 100, func(t *testing.T, chunkDataPacks []*flow.ChunkDataPack, chunkDataPackStore *badgerstorage.ChunkDataPacks, _ *badger.DB) { - wg := sync.WaitGroup{} - wg.Add(len(chunkDataPacks)) - for _, chunkDataPack := range chunkDataPacks { - go func(cdp flow.ChunkDataPack) { - err := chunkDataPackStore.Store(&cdp) - require.NoError(t, err) - - wg.Done() - }(*chunkDataPack) - } + require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) + require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) + }) +} - unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "could not store chunk data packs on time") +func TestChunkDataPack_Remove(t *testing.T) { + unittest.RunWithBadgerDB(t, func(db *badger.DB) { + transactions := badgerstorage.NewTransactions(&metrics.NoopCollector{}, db) + collections := badgerstorage.NewCollections(db, transactions) + // keep the cache size at 1 to make sure that entries are written and read from storage itself. + chunkDataPackStore := badgerstorage.NewChunkDataPacks(&metrics.NoopCollector{}, db, collections, 1) - // re-insert - should be idempotent + chunkDataPacks := unittest.ChunkDataPacksFixture(10) for _, chunkDataPack := range chunkDataPacks { - err := chunkDataPackStore.Store(chunkDataPack) + // stores collection in Collections storage (which ChunkDataPacks store uses internally) + err := collections.Store(chunkDataPack.Collection) require.NoError(t, err) } + + chunkIDs := make([]flow.Identifier, 0, len(chunkDataPacks)) + for _, chunk := range chunkDataPacks { + chunkIDs = append(chunkIDs, chunk.ID()) + } + + require.NoError(t, chunkDataPackStore.Store(chunkDataPacks)) + require.NoError(t, chunkDataPackStore.Remove(chunkIDs)) + + // verify it has been removed + _, err := chunkDataPackStore.ByChunkID(chunkIDs[0]) + assert.True(t, errors.Is(err, storage.ErrNotFound)) + + // Removing again should not error + require.NoError(t, chunkDataPackStore.Remove(chunkIDs)) }) } diff --git a/storage/chunkDataPacks.go b/storage/chunkDataPacks.go index ddcd0f69da9..d9f49735d90 100644 --- a/storage/chunkDataPacks.go +++ b/storage/chunkDataPacks.go @@ -11,6 +11,10 @@ type ChunkDataPacks interface { // No errors are expected during normal operation, but it may return generic error Store(cs []*flow.ChunkDataPack) error + // Remove removes multiple ChunkDataPacks cs keyed by their ChunkIDs in a batch. + // No errors are expected during normal operation, but it may return generic error + Remove(cs []flow.Identifier) error + // BatchStore inserts the chunk header, keyed by chunk ID into a given batch BatchStore(c *flow.ChunkDataPack, batch BatchStorage) error diff --git a/storage/mock/chunk_data_packs.go b/storage/mock/chunk_data_packs.go index 081424ee1b0..6082d72beff 100644 --- a/storage/mock/chunk_data_packs.go +++ b/storage/mock/chunk_data_packs.go @@ -68,6 +68,20 @@ func (_m *ChunkDataPacks) ByChunkID(chunkID flow.Identifier) (*flow.ChunkDataPac return r0, r1 } +// Remove provides a mock function with given fields: cs +func (_m *ChunkDataPacks) Remove(cs []flow.Identifier) error { + ret := _m.Called(cs) + + var r0 error + if rf, ok := ret.Get(0).(func([]flow.Identifier) error); ok { + r0 = rf(cs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Store provides a mock function with given fields: cs func (_m *ChunkDataPacks) Store(cs []*flow.ChunkDataPack) error { ret := _m.Called(cs) From b4b9af9e7aca4896ec007ca6700005c41268e5bb Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Wed, 16 Aug 2023 17:19:02 -0700 Subject: [PATCH 543/815] refactor save execution result --- engine/execution/state/state.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index abd45352457..0d01716cbb5 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -283,6 +283,20 @@ func (s *state) SaveExecutionResults( trace.EXEStateSaveExecutionResults) defer span.End() + header := result.ExecutableBlock.Block.Header + + //outside batch because it requires read access + err := s.UpdateHighestExecutedBlockIfHigher(childCtx, header) + if err != nil { + return fmt.Errorf("cannot update highest executed block: %w", err) + } + return nil +} + +func (s *state) saveExecutionResults( + ctx context.Context, + result *execution.ComputationResult, +) error { header := result.ExecutableBlock.Block.Header blockID := header.ID() @@ -357,11 +371,6 @@ func (s *state) SaveExecutionResults( return fmt.Errorf("batch flush error: %w", err) } - //outside batch because it requires read access - err = s.UpdateHighestExecutedBlockIfHigher(childCtx, header) - if err != nil { - return fmt.Errorf("cannot update highest executed block: %w", err) - } return nil } From e4842dc61757ed9d47c2fe35a1cb314539a92c74 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Wed, 16 Aug 2023 17:25:52 -0700 Subject: [PATCH 544/815] ignore the error from rollback chunk data pack --- engine/execution/state/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 0d01716cbb5..f72a907782e 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -320,7 +320,7 @@ func (s *state) saveExecutionResults( for _, chunk := range chunks { chunkIDs = append(chunkIDs, chunk.ID()) } - s.chunkDataPacks.Remove(chunkIDs) + _ = s.chunkDataPacks.Remove(chunkIDs) } }() From b826100b8eb52cccf838b474d4f496f13a9c6165 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Thu, 17 Aug 2023 09:26:22 -0700 Subject: [PATCH 545/815] fix refactor --- engine/execution/state/state.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index f72a907782e..427425d94e9 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -283,10 +283,13 @@ func (s *state) SaveExecutionResults( trace.EXEStateSaveExecutionResults) defer span.End() - header := result.ExecutableBlock.Block.Header + err := s.saveExecutionResults(ctx, result) + if err != nil { + return fmt.Errorf("could not save execution results: %w", err) + } //outside batch because it requires read access - err := s.UpdateHighestExecutedBlockIfHigher(childCtx, header) + err = s.UpdateHighestExecutedBlockIfHigher(childCtx, result.ExecutableBlock.Block.Header) if err != nil { return fmt.Errorf("cannot update highest executed block: %w", err) } From 21bd1f417055cf20d6557e3fd6a32031ccc30875 Mon Sep 17 00:00:00 2001 From: Leo Zhang Date: Thu, 17 Aug 2023 09:37:51 -0700 Subject: [PATCH 546/815] Apply suggestions from code review Co-authored-by: Faye Amacker <33205765+fxamacker@users.noreply.github.com> --- engine/execution/state/state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 427425d94e9..5da53e16cd7 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -299,11 +299,11 @@ func (s *state) SaveExecutionResults( func (s *state) saveExecutionResults( ctx context.Context, result *execution.ComputationResult, -) error { +) (err error) { header := result.ExecutableBlock.Block.Header blockID := header.ID() - err := s.chunkDataPacks.Store(result.AllChunkDataPacks()) + err = s.chunkDataPacks.Store(result.AllChunkDataPacks()) if err != nil { return fmt.Errorf("can not store multiple chunk data pack: %w", err) } From fc076f452a1a11d9ffc65ab15b52e78fd4bd33ac Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 18 Aug 2023 15:03:41 -0700 Subject: [PATCH 547/815] skip flakey test --- module/mempool/queue/queue_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mempool/queue/queue_test.go b/module/mempool/queue/queue_test.go index 388e697e335..c7b5d4c04d6 100644 --- a/module/mempool/queue/queue_test.go +++ b/module/mempool/queue/queue_test.go @@ -292,7 +292,7 @@ func TestQueue(t *testing.T) { //}) t.Run("String()", func(t *testing.T) { - t.Skip("flakey, because String() will iterate through a map, which is not determinsitic") + unittest.SkipUnless(t, unittest.TEST_FLAKY, "flakey, because String() will iterate through a map, which is not determinsitic") // a <- c <- d <- f queue := NewQueue(a) stored, _ := queue.TryAdd(c) From eb73addd51e0de2834e7a84d32dfe41d714b92f5 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 18 Aug 2023 15:07:05 -0700 Subject: [PATCH 548/815] skip flakey test --- module/mempool/queue/queue_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mempool/queue/queue_test.go b/module/mempool/queue/queue_test.go index c7b5d4c04d6..22176134c6c 100644 --- a/module/mempool/queue/queue_test.go +++ b/module/mempool/queue/queue_test.go @@ -292,7 +292,7 @@ func TestQueue(t *testing.T) { //}) t.Run("String()", func(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "flakey, because String() will iterate through a map, which is not determinsitic") + unittest.SkipUnless(t, unittest.TEST_FLAKY, "flakey, because String will iterate through a map, which is not determinsitic") // a <- c <- d <- f queue := NewQueue(a) stored, _ := queue.TryAdd(c) From f217595d504c9d4a3a3072aaf68d4847c59672f4 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 21 Aug 2023 07:07:42 -0400 Subject: [PATCH 549/815] using synchronization.Alsp probability factor --- engine/common/synchronization/alsp.go | 7 +++++++ engine/common/synchronization/engine.go | 9 ++++----- engine/common/synchronization/engine_test.go | 3 +++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/engine/common/synchronization/alsp.go b/engine/common/synchronization/alsp.go index 4aabc497cd6..3aa86dcdca2 100644 --- a/engine/common/synchronization/alsp.go +++ b/engine/common/synchronization/alsp.go @@ -3,3 +3,10 @@ package synchronization type Alsp struct { syncRequestProbabilityFactor float32 } + +func NewAlsp() *Alsp { + return &Alsp{ + // create misbehavior report 1/1000 times + syncRequestProbabilityFactor: 0.001, + } +} diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 7953d09f854..15cf3db688f 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -110,6 +110,7 @@ func New( scanInterval: opt.ScanInterval, participantsProvider: participantsProvider, randomizer: rand.NewDefaultRandomizer(), + alsp: NewAlsp(), } // register the engine with the network layer and store the conduit @@ -489,7 +490,6 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { // Generate a random integer between 1 and 1000 n, err := e.randomizer.Uint32n(uint32(1001)) - e.alsp.syncRequestProbabilityFactor if err != nil { // failing to generate a random number is unlikely. If an error is encountered while @@ -500,10 +500,9 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f Msg("failed to generate random number") } - // use a probabilistic approach to create a misbehavior report - create a report with a probability of 1/1000 - // this is done to avoid creating a report for every sync request received - // the probability of creating a misbehavior report is 1/1000 - if n == 835 { + // to avoid creating a misbehavior report for every sync request received, use a probabilistic approach. + // Create a report with a probability of alsp.syncRequestProbabilityFactor + if float32(n) < e.alsp.syncRequestProbabilityFactor*1000 { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 06e3c50b16c..b7345bbb31b 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -301,6 +301,9 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.core.AssertNotCalled(ss.T(), "WithinTolerance") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + // force creating misbehavior report by setting syncRequestProbabilityFactor to 1.0 (i.e. report misbehavior 100% of the time) + ss.e.alsp.syncRequestProbabilityFactor = 1.0 + // expect Uint32n(1001) to be called once and return misbehavior report because it happened to be 835 mockRandomizer.On("Uint32n", uint32(1001)).Return(835).Once() From 33aa3f92f70e9d9aabc2e1fd3b1c734dc448f1a5 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 21 Aug 2023 07:41:40 -0400 Subject: [PATCH 550/815] remove Randomizer interface --- engine/common/synchronization/engine.go | 4 +-- engine/common/synchronization/engine_test.go | 12 +------ utils/rand/rand.go | 36 -------------------- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 15cf3db688f..5a38d94a7a6 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -57,7 +57,6 @@ type Engine struct { participantsProvider module.IdentifierProvider requestHandler *RequestHandler // component responsible for handling requests - randomizer rand.Randomizer alsp *Alsp pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse @@ -109,7 +108,6 @@ func New( pollInterval: opt.PollInterval, scanInterval: opt.ScanInterval, participantsProvider: participantsProvider, - randomizer: rand.NewDefaultRandomizer(), alsp: NewAlsp(), } @@ -489,7 +487,7 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { // Generate a random integer between 1 and 1000 - n, err := e.randomizer.Uint32n(uint32(1001)) + n, err := rand.Uint32n(1001) if err != nil { // failing to generate a random number is unlikely. If an error is encountered while diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index b7345bbb31b..8a35ad0071f 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -227,9 +227,6 @@ func (ss *SyncSuite) TestOnSyncRequest_HigherThanReceiver_OutsideTolerance() { // than the receiver's height doesn't trigger a response, even if outside tolerance and does not generate ALSP // spamming misbehavior report (simulating the most likely probability). func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_NoMisbehaviorReport() { - mockRandomizer := new(rand.MockRandomizer) - ss.e.randomizer = mockRandomizer - ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) @@ -254,8 +251,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - // expect Uint32n(1001) to be called once and return 1, not triggering a misbehavior report - mockRandomizer.On("Uint32n", uint32(1001)).Return(1).Once() + ss.e.alsp.syncRequestProbabilityFactor = 0.0 // force not creating misbehavior report require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) @@ -270,9 +266,6 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // than the receiver's height doesn't trigger a response, even if outside tolerance and generates ALSP // spamming misbehavior report (simulating the unlikely probability). func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { - mockRandomizer := new(rand.MockRandomizer) - ss.e.randomizer = mockRandomizer - ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) @@ -304,9 +297,6 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // force creating misbehavior report by setting syncRequestProbabilityFactor to 1.0 (i.e. report misbehavior 100% of the time) ss.e.alsp.syncRequestProbabilityFactor = 1.0 - // expect Uint32n(1001) to be called once and return misbehavior report because it happened to be 835 - mockRandomizer.On("Uint32n", uint32(1001)).Return(835).Once() - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) // give at least some time to process items diff --git a/utils/rand/rand.go b/utils/rand/rand.go index df11838dabb..e56e2e3df60 100644 --- a/utils/rand/rand.go +++ b/utils/rand/rand.go @@ -15,8 +15,6 @@ import ( "crypto/rand" "encoding/binary" "fmt" - - "github.com/stretchr/testify/mock" ) // Uint64 returns a random uint64. @@ -169,37 +167,3 @@ func Samples(n uint, m uint, swap func(i, j uint)) error { } return nil } - -// Randomizer is an interface that defines a method for generating random numbers. This is useful for testing where -// we want to mock out random number generation. -type Randomizer interface { - // Uint32n returns a random uint32 strictly less than `n`. - Uint32n(n uint32) (uint32, error) -} - -func NewDefaultRandomizer() Randomizer { - return &defaultRandomizer{} -} - -type defaultRandomizer struct{} - -// Uint32n returns a random number between 0 and n (exclusive) -func (e *defaultRandomizer) Uint32n(n uint32) (uint32, error) { - n, err := Uint32n(n) - if err != nil { - e := fmt.Errorf("failed to create random number (%d): %w", n, err) - return 0, e - } - return n, nil -} - -// MockRandomizer is a mock object that implements the Randomizer interface -type MockRandomizer struct { - mock.Mock -} - -// Uint32n is a mock method that allows tests to specify what random number to return -func (m *MockRandomizer) Uint32n(n uint32) (uint32, error) { - args := m.Called(n) - return uint32(args.Int(0)), nil -} From a4ae34397108a70180ba7acc48b0a3c551934784 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 21 Aug 2023 14:11:23 +0100 Subject: [PATCH 551/815] Remove mock file from gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 024b0d9417e..1be2e18a99f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ /cmd/testclient/testclient /cmd/util/util /cmd/bootstrap/bootstrap -/engine/access/rpc/backend/mock_communicator.go # crypto relic folder crypto/relic/ From a6e33aa58afc77f550be9de8fcba4a46af095566 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 21 Aug 2023 15:41:42 +0100 Subject: [PATCH 552/815] Move node communicator interface out of backend file --- engine/access/rpc/backend/backend.go | 8 -------- engine/access/rpc/backend/node_communicator.go | 10 ++++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index e532be24ad5..8dfbf993b8f 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -91,14 +91,6 @@ type Config struct { CircuitBreakerConfig connection.CircuitBreakerConfig // the configuration for circuit breaker } -type Communicator interface { - CallAvailableNode( - nodes flow.IdentityList, - call NodeAction, - shouldTerminateOnError ErrorTerminator, - ) error -} - type Params struct { State protocol.State CollectionRPC accessproto.AccessAPIClient diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index d75432b0b29..b3a73e5b545 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -20,6 +20,16 @@ type NodeAction func(node *flow.Identity) error // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. type ErrorTerminator func(node *flow.Identity, err error) bool +type Communicator interface { + CallAvailableNode( + nodes flow.IdentityList, + call NodeAction, + shouldTerminateOnError ErrorTerminator, + ) error +} + +var _ Communicator = (*NodeCommunicator)(nil) + // NodeCommunicator is responsible for calling available nodes in the backend. type NodeCommunicator struct { nodeSelectorFactory NodeSelectorFactory From 6b95d409b4f2a65044e7ee40a1d696bbf88077fb Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 21 Aug 2023 16:32:43 +0100 Subject: [PATCH 553/815] Remove irrelevant code --- module/metrics.go | 12 ------------ module/metrics/access.go | 2 -- 2 files changed, 14 deletions(-) diff --git a/module/metrics.go b/module/metrics.go index e5bc5b6fecd..b1bfbad81ae 100644 --- a/module/metrics.go +++ b/module/metrics.go @@ -646,8 +646,6 @@ type AccessMetrics interface { TransactionMetrics BackendScriptsMetrics - //TrnasactionResultMetrics - // UpdateExecutionReceiptMaxHeight is called whenever we store an execution receipt from a block from a newer height UpdateExecutionReceiptMaxHeight(height uint64) @@ -655,16 +653,6 @@ type AccessMetrics interface { UpdateLastFullBlockHeight(height uint64) } -type TrnasactionResultMetrics interface { - OnKeyHitSuccess() - OnKeyHitFailure() - - OnKeyAddSuccess() - OnKeyEviction() - - OnKeyAddFailureDueToFullCache() -} - type ExecutionResultStats struct { ComputationUsed uint64 MemoryUsed uint64 diff --git a/module/metrics/access.go b/module/metrics/access.go index 56e24b70da2..effb24c4326 100644 --- a/module/metrics/access.go +++ b/module/metrics/access.go @@ -32,8 +32,6 @@ type AccessCollector struct { module.TransactionMetrics module.BackendScriptsMetrics - //module.TransactionResultMetrics - connectionReused prometheus.Counter connectionsInPool *prometheus.GaugeVec connectionAdded prometheus.Counter From ca46f5c0211aaf63010fee8847ba73c086bca526 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Mon, 21 Aug 2023 18:02:18 +0200 Subject: [PATCH 554/815] Pass zerolog by value --- cmd/util/cmd/checkpoint-list-tries/cmd.go | 2 +- .../execution_state_extract.go | 5 +-- .../migrations/account_based_migration.go | 6 ++-- ledger/complete/checkpoint_benchmark_test.go | 6 ++-- ledger/complete/wal/checkpoint_v5_test.go | 4 +-- .../complete/wal/checkpoint_v6_leaf_reader.go | 4 +-- ledger/complete/wal/checkpoint_v6_reader.go | 18 +++++----- ledger/complete/wal/checkpoint_v6_test.go | 34 +++++++++---------- ledger/complete/wal/checkpoint_v6_writer.go | 20 +++++------ ledger/complete/wal/checkpointer.go | 22 ++++++------ ledger/complete/wal/checkpointer_test.go | 6 ++-- .../wal/checkpointer_versioning_test.go | 6 ++-- ledger/complete/wal/syncrename.go | 2 +- ledger/complete/wal/syncrename_test.go | 2 +- module/util/log.go | 2 +- module/util/log_test.go | 4 +-- 16 files changed, 72 insertions(+), 71 deletions(-) diff --git a/cmd/util/cmd/checkpoint-list-tries/cmd.go b/cmd/util/cmd/checkpoint-list-tries/cmd.go index 26e5ca01c8b..830075bc5c8 100644 --- a/cmd/util/cmd/checkpoint-list-tries/cmd.go +++ b/cmd/util/cmd/checkpoint-list-tries/cmd.go @@ -29,7 +29,7 @@ func init() { func run(*cobra.Command, []string) { log.Info().Msgf("loading checkpoint %v", flagCheckpoint) - tries, err := wal.LoadCheckpoint(flagCheckpoint, &log.Logger) + tries, err := wal.LoadCheckpoint(flagCheckpoint, log.Logger) if err != nil { log.Fatal().Err(err).Msg("error while loading checkpoint") } diff --git a/cmd/util/cmd/execution-state-extract/execution_state_extract.go b/cmd/util/cmd/execution-state-extract/execution_state_extract.go index dbcb82d53e5..fe5bdf93471 100644 --- a/cmd/util/cmd/execution-state-extract/execution_state_extract.go +++ b/cmd/util/cmd/execution-state-extract/execution_state_extract.go @@ -85,7 +85,7 @@ func extractExecutionState( var migrations []ledger.Migration newState := ledger.State(targetHash) - // migrate the trie if there migrations + // migrate the trie if there are migrations newTrie, err := led.MigrateAt( newState, migrations, @@ -97,7 +97,8 @@ func extractExecutionState( } // create reporter - reporter := reporters.NewExportReporter(log, + reporter := reporters.NewExportReporter( + log, func() flow.StateCommitment { return targetHash }, ) diff --git a/cmd/util/ledger/migrations/account_based_migration.go b/cmd/util/ledger/migrations/account_based_migration.go index 0172e04737f..e7fff3b74db 100644 --- a/cmd/util/ledger/migrations/account_based_migration.go +++ b/cmd/util/ledger/migrations/account_based_migration.go @@ -70,7 +70,7 @@ func MigrateByAccount(migrator AccountMigrator, allPayloads []ledger.Payload, nW log.Info().Msgf("start grouping for a total of %v payloads", len(allPayloads)) var err error - logGrouping := util.LogProgress("grouping payload", len(allPayloads), &log.Logger) + logGrouping := util.LogProgress("grouping payload", len(allPayloads), log.Logger) for i, payload := range allPayloads { groups, err = PayloadGrouping(groups, payload) if err != nil { @@ -107,7 +107,7 @@ func MigrateGroupSequentially( ) ( []ledger.Payload, error) { - logAccount := util.LogProgress("processing account group", len(payloadsByAccount), &log.Logger) + logAccount := util.LogProgress("processing account group", len(payloadsByAccount), log.Logger) i := 0 migrated := make([]ledger.Payload, 0) @@ -170,7 +170,7 @@ func MigrateGroupConcurrently( } // read job results - logAccount := util.LogProgress("processing account group", len(payloadsByAccount), &log.Logger) + logAccount := util.LogProgress("processing account group", len(payloadsByAccount), log.Logger) migrated := make([]ledger.Payload, 0) diff --git a/ledger/complete/checkpoint_benchmark_test.go b/ledger/complete/checkpoint_benchmark_test.go index 177804be5a7..5d5ae9f726c 100644 --- a/ledger/complete/checkpoint_benchmark_test.go +++ b/ledger/complete/checkpoint_benchmark_test.go @@ -58,7 +58,7 @@ func benchmarkStoreCheckpoint(b *testing.B, version int, concurrent bool) { }() // Load checkpoint - tries, err := wal.LoadCheckpoint(*checkpointFile, &log) + tries, err := wal.LoadCheckpoint(*checkpointFile, log) if err != nil { b.Fatalf("cannot load checkpoint: %s", err) } @@ -69,7 +69,7 @@ func benchmarkStoreCheckpoint(b *testing.B, version int, concurrent bool) { // Serialize checkpoint V5. switch version { case 5: - err = wal.StoreCheckpointV5(outputDir, fileName, &log, tries...) + err = wal.StoreCheckpointV5(outputDir, fileName, log, tries...) case 6: if concurrent { err = wal.StoreCheckpointV6Concurrently(tries, outputDir, fileName, &log) @@ -102,7 +102,7 @@ func BenchmarkLoadCheckpoint(b *testing.B) { b.ResetTimer() // Load checkpoint - _, err = wal.LoadCheckpoint(*checkpointFile, &log) + _, err = wal.LoadCheckpoint(*checkpointFile, log) b.StopTimer() elapsed := time.Since(start) diff --git a/ledger/complete/wal/checkpoint_v5_test.go b/ledger/complete/wal/checkpoint_v5_test.go index 9721a50d04e..4422d3376c0 100644 --- a/ledger/complete/wal/checkpoint_v5_test.go +++ b/ledger/complete/wal/checkpoint_v5_test.go @@ -15,12 +15,12 @@ func TestCopyCheckpointFileV5(t *testing.T) { tries := createSimpleTrie(t) fileName := "checkpoint" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV5(dir, fileName, &logger, tries...), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV5(dir, fileName, logger, tries...), "fail to store checkpoint") to := filepath.Join(dir, "newfolder") newPaths, err := CopyCheckpointFile(fileName, dir, to) require.NoError(t, err) log.Info().Msgf("copied to :%v", newPaths) - decoded, err := LoadCheckpoint(filepath.Join(to, fileName), &logger) + decoded, err := LoadCheckpoint(filepath.Join(to, fileName), logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) }) diff --git a/ledger/complete/wal/checkpoint_v6_leaf_reader.go b/ledger/complete/wal/checkpoint_v6_leaf_reader.go index 77dbc0716b5..169bf895cbd 100644 --- a/ledger/complete/wal/checkpoint_v6_leaf_reader.go +++ b/ledger/complete/wal/checkpoint_v6_leaf_reader.go @@ -29,7 +29,7 @@ func nodeToLeaf(leaf *node.Node) *LeafNode { // OpenAndReadLeafNodesFromCheckpointV6 takes a channel for pushing the leaf nodes that are read from // the given checkpoint file specified by dir and fileName. // It returns when finish reading the checkpoint file and the input channel can be closed. -func OpenAndReadLeafNodesFromCheckpointV6(allLeafNodesCh chan<- *LeafNode, dir string, fileName string, logger *zerolog.Logger) (errToReturn error) { +func OpenAndReadLeafNodesFromCheckpointV6(allLeafNodesCh chan<- *LeafNode, dir string, fileName string, logger zerolog.Logger) (errToReturn error) { // we are the only sender of the channel, closing it after done defer func() { close(allLeafNodesCh) @@ -68,7 +68,7 @@ func OpenAndReadLeafNodesFromCheckpointV6(allLeafNodesCh chan<- *LeafNode, dir s return nil } -func readCheckpointSubTrieLeafNodes(leafNodesCh chan<- *LeafNode, dir string, fileName string, index int, checksum uint32, logger *zerolog.Logger) error { +func readCheckpointSubTrieLeafNodes(leafNodesCh chan<- *LeafNode, dir string, fileName string, index int, checksum uint32, logger zerolog.Logger) error { return processCheckpointSubTrie(dir, fileName, index, checksum, logger, func(reader *Crc32Reader, nodesCount uint64) error { scratch := make([]byte, 1024*4) // must not be less than 1024 diff --git a/ledger/complete/wal/checkpoint_v6_reader.go b/ledger/complete/wal/checkpoint_v6_reader.go index 98a9b2f4b77..63791e0f46c 100644 --- a/ledger/complete/wal/checkpoint_v6_reader.go +++ b/ledger/complete/wal/checkpoint_v6_reader.go @@ -31,7 +31,7 @@ var ErrEOFNotReached = errors.New("expect to reach EOF, but actually didn't") // it returns (nil, os.ErrNotExist) if a certain file is missing, use (os.IsNotExist to check) // it returns (nil, ErrEOFNotReached) if a certain part file is malformed // it returns (nil, err) if running into any exception -func readCheckpointV6(headerFile *os.File, logger *zerolog.Logger) ([]*trie.MTrie, error) { +func readCheckpointV6(headerFile *os.File, logger zerolog.Logger) ([]*trie.MTrie, error) { // the full path of header file headerPath := headerFile.Name() dir, fileName := filepath.Split(headerPath) @@ -53,7 +53,7 @@ func readCheckpointV6(headerFile *os.File, logger *zerolog.Logger) ([]*trie.MTri // TODO making number of goroutine configable for reading subtries, which can help us // test the code on machines that don't have as much RAM as EN by using fewer goroutines. - subtrieNodes, err := readSubTriesConcurrently(dir, fileName, subtrieChecksums, &lg) + subtrieNodes, err := readSubTriesConcurrently(dir, fileName, subtrieChecksums, lg) if err != nil { return nil, fmt.Errorf("could not read subtrie from dir: %w", err) } @@ -61,7 +61,7 @@ func readCheckpointV6(headerFile *os.File, logger *zerolog.Logger) ([]*trie.MTri lg.Info().Uint32("topsum", topTrieChecksum). Msg("finish reading all v6 subtrie files, start reading top level tries") - tries, err := readTopLevelTries(dir, fileName, subtrieNodes, topTrieChecksum, &lg) + tries, err := readTopLevelTries(dir, fileName, subtrieNodes, topTrieChecksum, lg) if err != nil { return nil, fmt.Errorf("could not read top level nodes or tries: %w", err) } @@ -83,7 +83,7 @@ func readCheckpointV6(headerFile *os.File, logger *zerolog.Logger) ([]*trie.MTri } // OpenAndReadCheckpointV6 open the checkpoint file and read it with readCheckpointV6 -func OpenAndReadCheckpointV6(dir string, fileName string, logger *zerolog.Logger) ( +func OpenAndReadCheckpointV6(dir string, fileName string, logger zerolog.Logger) ( tries []*trie.MTrie, errToReturn error, ) { @@ -127,7 +127,7 @@ func filePathPattern(dir string, fileName string) string { // readCheckpointHeader takes a file path and returns subtrieChecksums and topTrieChecksum // any error returned are exceptions -func readCheckpointHeader(filepath string, logger *zerolog.Logger) ( +func readCheckpointHeader(filepath string, logger zerolog.Logger) ( checksumsOfSubtries []uint32, checksumOfTopTrie uint32, errToReturn error, @@ -278,7 +278,7 @@ type resultReadSubTrie struct { Err error } -func readSubTriesConcurrently(dir string, fileName string, subtrieChecksums []uint32, logger *zerolog.Logger) ([][]*node.Node, error) { +func readSubTriesConcurrently(dir string, fileName string, subtrieChecksums []uint32, logger zerolog.Logger) ([][]*node.Node, error) { numOfSubTries := len(subtrieChecksums) jobs := make(chan jobReadSubtrie, numOfSubTries) @@ -325,7 +325,7 @@ func readSubTriesConcurrently(dir string, fileName string, subtrieChecksums []ui return nodesGroups, nil } -func readCheckpointSubTrie(dir string, fileName string, index int, checksum uint32, logger *zerolog.Logger) ( +func readCheckpointSubTrie(dir string, fileName string, index int, checksum uint32, logger zerolog.Logger) ( []*node.Node, error, ) { @@ -372,7 +372,7 @@ func processCheckpointSubTrie( fileName string, index int, checksum uint32, - logger *zerolog.Logger, + logger zerolog.Logger, processNode func(*Crc32Reader, uint64) error, ) ( errToReturn error, @@ -498,7 +498,7 @@ func readSubTriesFooter(f *os.File) (uint64, uint32, error) { // 5. node count // 6. trie count // 7. checksum -func readTopLevelTries(dir string, fileName string, subtrieNodes [][]*node.Node, topTrieChecksum uint32, logger *zerolog.Logger) ( +func readTopLevelTries(dir string, fileName string, subtrieNodes [][]*node.Node, topTrieChecksum uint32, logger zerolog.Logger) ( rootTries []*trie.MTrie, errToReturn error, ) { diff --git a/ledger/complete/wal/checkpoint_v6_test.go b/ledger/complete/wal/checkpoint_v6_test.go index fb98777e0ec..78a52624bbc 100644 --- a/ledger/complete/wal/checkpoint_v6_test.go +++ b/ledger/complete/wal/checkpoint_v6_test.go @@ -170,7 +170,7 @@ func TestEncodeSubTrie(t *testing.T) { for index, roots := range subtrieRoots { unittest.RunWithTempDir(t, func(dir string) { uniqueIndices, nodeCount, checksum, err := storeCheckpointSubTrie( - index, roots, estimatedSubtrieNodeCount, dir, file, &logger) + index, roots, estimatedSubtrieNodeCount, dir, file, logger) require.NoError(t, err) // subtrie roots might have duplciates, that why we group the them, @@ -205,7 +205,7 @@ func TestEncodeSubTrie(t *testing.T) { uniqueIndices, nodeCount, checksum) // all the nodes - nodes, err := readCheckpointSubTrie(dir, file, index, checksum, &logger) + nodes, err := readCheckpointSubTrie(dir, file, index, checksum, logger) require.NoError(t, err) for _, root := range roots { @@ -263,7 +263,7 @@ func TestWriteAndReadCheckpointV6EmptyTrie(t *testing.T) { fileName := "checkpoint-empty-trie" logger := unittest.Logger() require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") - decoded, err := OpenAndReadCheckpointV6(dir, fileName, &logger) + decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) }) @@ -275,7 +275,7 @@ func TestWriteAndReadCheckpointV6SimpleTrie(t *testing.T) { fileName := "checkpoint" logger := unittest.Logger() require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") - decoded, err := OpenAndReadCheckpointV6(dir, fileName, &logger) + decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) }) @@ -287,7 +287,7 @@ func TestWriteAndReadCheckpointV6MultipleTries(t *testing.T) { fileName := "checkpoint-multi-file" logger := unittest.Logger() require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") - decoded, err := OpenAndReadCheckpointV6(dir, fileName, &logger) + decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) }) @@ -322,7 +322,7 @@ func TestWriteAndReadCheckpointV6LeafEmptyTrie(t *testing.T) { bufSize := 10 leafNodesCh := make(chan *LeafNode, bufSize) go func() { - err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, &logger) + err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) }() for range leafNodesCh { @@ -340,7 +340,7 @@ func TestWriteAndReadCheckpointV6LeafSimpleTrie(t *testing.T) { bufSize := 1 leafNodesCh := make(chan *LeafNode, bufSize) go func() { - err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, &logger) + err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) }() resultPayloads := make([]ledger.Payload, 0) @@ -363,7 +363,7 @@ func TestWriteAndReadCheckpointV6LeafMultipleTries(t *testing.T) { bufSize := 5 leafNodesCh := make(chan *LeafNode, bufSize) go func() { - err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, &logger) + err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) }() resultPayloads := make([]ledger.Payload, 0) @@ -422,7 +422,7 @@ func compareFiles(file1, file2 string) error { return nil } -func storeCheckpointV5(tries []*trie.MTrie, dir string, fileName string, logger *zerolog.Logger) error { +func storeCheckpointV5(tries []*trie.MTrie, dir string, fileName string, logger zerolog.Logger) error { return StoreCheckpointV5(dir, fileName, logger, tries...) } @@ -432,8 +432,8 @@ func TestWriteAndReadCheckpointV5(t *testing.T) { fileName := "checkpoint1" logger := unittest.Logger() - require.NoErrorf(t, storeCheckpointV5(tries, dir, fileName, &logger), "fail to store checkpoint") - decoded, err := LoadCheckpoint(filepath.Join(dir, fileName), &logger) + require.NoErrorf(t, storeCheckpointV5(tries, dir, fileName, logger), "fail to store checkpoint") + decoded, err := LoadCheckpoint(filepath.Join(dir, fileName), logger) require.NoErrorf(t, err, "fail to load checkpoint") requireTriesEqual(t, tries, decoded) }) @@ -448,12 +448,12 @@ func TestWriteAndReadCheckpointV6ThenBackToV5(t *testing.T) { // store tries into v6 then read back, then store into v5 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", &logger), "fail to store checkpoint") - decoded, err := OpenAndReadCheckpointV6(dir, "checkpoint-v6", &logger) + decoded, err := OpenAndReadCheckpointV6(dir, "checkpoint-v6", logger) require.NoErrorf(t, err, "fail to read checkpoint %v/checkpoint-v6", dir) - require.NoErrorf(t, storeCheckpointV5(decoded, dir, "checkpoint-v6-v5", &logger), "fail to store checkpoint") + require.NoErrorf(t, storeCheckpointV5(decoded, dir, "checkpoint-v6-v5", logger), "fail to store checkpoint") // store tries directly into v5 checkpoint - require.NoErrorf(t, storeCheckpointV5(tries, dir, "checkpoint-v5", &logger), "fail to store checkpoint") + require.NoErrorf(t, storeCheckpointV5(tries, dir, "checkpoint-v5", logger), "fail to store checkpoint") // compare the two v5 checkpoint files should be identical require.NoError(t, compareFiles( @@ -511,7 +511,7 @@ func TestAllPartFileExist(t *testing.T) { err = os.Remove(fileToDelete) require.NoError(t, err, "fail to remove part file") - _, err = OpenAndReadCheckpointV6(dir, fileName, &logger) + _, err = OpenAndReadCheckpointV6(dir, fileName, logger) require.ErrorIs(t, err, os.ErrNotExist, "wrong error type returned") } }) @@ -541,7 +541,7 @@ func TestAllPartFileExistLeafReader(t *testing.T) { bufSize := 10 leafNodesCh := make(chan *LeafNode, bufSize) - err = OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, &logger) + err = OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) require.ErrorIs(t, err, os.ErrNotExist, "wrong error type returned") } }) @@ -585,7 +585,7 @@ func TestCopyCheckpointFileV6(t *testing.T) { newPaths, err := CopyCheckpointFile(fileName, dir, to) require.NoError(t, err) log.Info().Msgf("copied to :%v", newPaths) - decoded, err := OpenAndReadCheckpointV6(to, fileName, &logger) + decoded, err := OpenAndReadCheckpointV6(to, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) }) diff --git a/ledger/complete/wal/checkpoint_v6_writer.go b/ledger/complete/wal/checkpoint_v6_writer.go index 7b138a61085..ea2021f2156 100644 --- a/ledger/complete/wal/checkpoint_v6_writer.go +++ b/ledger/complete/wal/checkpoint_v6_writer.go @@ -103,7 +103,7 @@ func storeCheckpointV6( subTrieRootAndTopLevelTrieCount(tries), outputDir, outputFile, - &lg, + lg, nWorker, ) if err != nil { @@ -113,12 +113,12 @@ func storeCheckpointV6( lg.Info().Msgf("subtrie have been stored. sub trie node count: %v", subTriesNodeCount) topTrieChecksum, err := storeTopLevelNodesAndTrieRoots( - tries, subTrieRootIndices, subTriesNodeCount, outputDir, outputFile, &lg) + tries, subTrieRootIndices, subTriesNodeCount, outputDir, outputFile, lg) if err != nil { return fmt.Errorf("could not store top level tries: %w", err) } - err = storeCheckpointHeader(subTrieChecksums, topTrieChecksum, outputDir, outputFile, &lg) + err = storeCheckpointHeader(subTrieChecksums, topTrieChecksum, outputDir, outputFile, lg) if err != nil { return fmt.Errorf("could not store checkpoint header: %w", err) } @@ -136,7 +136,7 @@ func storeCheckpointHeader( topTrieChecksum uint32, outputDir string, outputFile string, - logger *zerolog.Logger, + logger zerolog.Logger, ) ( errToReturn error, ) { @@ -207,7 +207,7 @@ func storeTopLevelNodesAndTrieRoots( subTriesNodeCount uint64, outputDir string, outputFile string, - logger *zerolog.Logger, + logger zerolog.Logger, ) ( checksumOfTopTriePartFile uint32, errToReturn error, @@ -319,7 +319,7 @@ func storeSubTrieConcurrently( subAndTopNodeCount int, // useful for preallocating memory for the node indices map to be returned outputDir string, outputFile string, - logger *zerolog.Logger, + logger zerolog.Logger, nWorker uint, ) ( map[*node.Node]uint64, // node indices @@ -399,13 +399,13 @@ func storeSubTrieConcurrently( return results, nodeCounter, checksums, nil } -func createWriterForTopTries(dir string, file string, logger *zerolog.Logger) (io.WriteCloser, error) { +func createWriterForTopTries(dir string, file string, logger zerolog.Logger) (io.WriteCloser, error) { _, topTriesFileName := filePathTopTries(dir, file) return createClosableWriter(dir, topTriesFileName, logger) } -func createWriterForSubtrie(dir string, file string, logger *zerolog.Logger, index int) (io.WriteCloser, error) { +func createWriterForSubtrie(dir string, file string, logger zerolog.Logger, index int) (io.WriteCloser, error) { _, subTriesFileName, err := filePathSubTries(dir, file, index) if err != nil { return nil, err @@ -414,7 +414,7 @@ func createWriterForSubtrie(dir string, file string, logger *zerolog.Logger, ind return createClosableWriter(dir, subTriesFileName, logger) } -func createClosableWriter(dir string, fileName string, logger *zerolog.Logger) (io.WriteCloser, error) { +func createClosableWriter(dir string, fileName string, logger zerolog.Logger) (io.WriteCloser, error) { fullPath := path.Join(dir, fileName) if utilsio.FileExists(fullPath) { return nil, fmt.Errorf("checkpoint part file %v already exists", fullPath) @@ -447,7 +447,7 @@ func storeCheckpointSubTrie( estimatedSubtrieNodeCount int, // for estimate the amount of memory to be preallocated outputDir string, outputFile string, - logger *zerolog.Logger, + logger zerolog.Logger, ) ( rootNodesOfAllSubtries map[*node.Node]uint64, // the stored position of each unique root node totalSubtrieNodeCount uint64, diff --git a/ledger/complete/wal/checkpointer.go b/ledger/complete/wal/checkpointer.go index 6b9239f1c22..5e58ff95235 100644 --- a/ledger/complete/wal/checkpointer.go +++ b/ledger/complete/wal/checkpointer.go @@ -267,7 +267,7 @@ func NumberToFilename(n int) string { } func (c *Checkpointer) CheckpointWriter(to int) (io.WriteCloser, error) { - return CreateCheckpointWriterForFile(c.dir, NumberToFilename(to), &c.wal.log) + return CreateCheckpointWriterForFile(c.dir, NumberToFilename(to), c.wal.log) } func (c *Checkpointer) Dir() string { @@ -275,7 +275,7 @@ func (c *Checkpointer) Dir() string { } // CreateCheckpointWriterForFile returns a file writer that will write to a temporary file and then move it to the checkpoint folder by renaming it. -func CreateCheckpointWriterForFile(dir, filename string, logger *zerolog.Logger) (io.WriteCloser, error) { +func CreateCheckpointWriterForFile(dir, filename string, logger zerolog.Logger) (io.WriteCloser, error) { fullname := path.Join(dir, filename) @@ -312,7 +312,7 @@ func CreateCheckpointWriterForFile(dir, filename string, logger *zerolog.Logger) // as for each node, the children have been previously encountered. // TODO: evaluate alternatives to CRC32 since checkpoint file is many GB in size. // TODO: add concurrency if the performance gains are enough to offset complexity. -func StoreCheckpointV5(dir string, fileName string, logger *zerolog.Logger, tries ...*trie.MTrie) ( +func StoreCheckpointV5(dir string, fileName string, logger zerolog.Logger, tries ...*trie.MTrie) ( // error // Note, the above code, which didn't define the name "err" for the returned error, would be wrong, // beause err needs to be defined in order to be updated by the defer function @@ -428,7 +428,7 @@ func StoreCheckpointV5(dir string, fileName string, logger *zerolog.Logger, trie // Index 0 is a special case with nil node. traversedSubtrieNodes[nil] = 0 - logging := logProgress(fmt.Sprintf("storing %v-th sub trie roots", i), estimatedSubtrieNodeCount, &log.Logger) + logging := logProgress(fmt.Sprintf("storing %v-th sub trie roots", i), estimatedSubtrieNodeCount, log.Logger) for _, root := range subTrieRoot { // Empty trie is always added to forest as starting point and // empty trie's root is nil. It remains in the forest until evicted @@ -516,7 +516,7 @@ func StoreCheckpointV5(dir string, fileName string, logger *zerolog.Logger, trie return nil } -func logProgress(msg string, estimatedSubtrieNodeCount int, logger *zerolog.Logger) func(nodeCounter uint64) { +func logProgress(msg string, estimatedSubtrieNodeCount int, logger zerolog.Logger) func(nodeCounter uint64) { lg := util.LogProgress(msg, estimatedSubtrieNodeCount, logger) return func(index uint64) { lg(int(index)) @@ -601,12 +601,12 @@ func getNodesAtLevel(root *node.Node, level uint) []*node.Node { func (c *Checkpointer) LoadCheckpoint(checkpoint int) ([]*trie.MTrie, error) { filepath := path.Join(c.dir, NumberToFilename(checkpoint)) - return LoadCheckpoint(filepath, &c.wal.log) + return LoadCheckpoint(filepath, c.wal.log) } func (c *Checkpointer) LoadRootCheckpoint() ([]*trie.MTrie, error) { filepath := path.Join(c.dir, bootstrap.FilenameWALRootCheckpoint) - return LoadCheckpoint(filepath, &c.wal.log) + return LoadCheckpoint(filepath, c.wal.log) } func (c *Checkpointer) HasRootCheckpoint() (bool, error) { @@ -628,7 +628,7 @@ func (c *Checkpointer) RemoveCheckpoint(checkpoint int) error { return deleteCheckpointFiles(c.dir, name) } -func LoadCheckpoint(filepath string, logger *zerolog.Logger) ( +func LoadCheckpoint(filepath string, logger zerolog.Logger) ( tries []*trie.MTrie, errToReturn error) { file, err := os.Open(filepath) @@ -648,7 +648,7 @@ func LoadCheckpoint(filepath string, logger *zerolog.Logger) ( return readCheckpoint(file, logger) } -func readCheckpoint(f *os.File, logger *zerolog.Logger) ([]*trie.MTrie, error) { +func readCheckpoint(f *os.File, logger zerolog.Logger) ([]*trie.MTrie, error) { // Read header: magic (2 bytes) + version (2 bytes) header := make([]byte, headerSize) @@ -888,7 +888,7 @@ func readCheckpointV4(f *os.File) ([]*trie.MTrie, error) { // readCheckpointV5 decodes checkpoint file (version 5) and returns a list of tries. // Checkpoint file header (magic and version) are verified by the caller. -func readCheckpointV5(f *os.File, logger *zerolog.Logger) ([]*trie.MTrie, error) { +func readCheckpointV5(f *os.File, logger zerolog.Logger) ([]*trie.MTrie, error) { logger.Info().Msgf("reading v5 checkpoint file") // Scratch buffer is used as temporary buffer that reader can read into. @@ -1006,7 +1006,7 @@ func readCheckpointV5(f *os.File, logger *zerolog.Logger) ([]*trie.MTrie, error) // causes two checkpoint files to be cached for each checkpointing, eventually // caching hundreds of GB. // CAUTION: no-op when GOOS != linux. -func evictFileFromLinuxPageCache(f *os.File, fsync bool, logger *zerolog.Logger) error { +func evictFileFromLinuxPageCache(f *os.File, fsync bool, logger zerolog.Logger) error { err := fadviseNoLinuxPageCache(f.Fd(), fsync) if err != nil { return err diff --git a/ledger/complete/wal/checkpointer_test.go b/ledger/complete/wal/checkpointer_test.go index 40ec3ff5925..a0a828748d3 100644 --- a/ledger/complete/wal/checkpointer_test.go +++ b/ledger/complete/wal/checkpointer_test.go @@ -531,12 +531,12 @@ func Test_StoringLoadingCheckpoints(t *testing.T) { fullpath := path.Join(dir, "temp-checkpoint") - err = realWAL.StoreCheckpointV5(dir, "temp-checkpoint", &logger, updatedTrie) + err = realWAL.StoreCheckpointV5(dir, "temp-checkpoint", logger, updatedTrie) require.NoError(t, err) t.Run("works without data modification", func(t *testing.T) { logger := unittest.Logger() - tries, err := realWAL.LoadCheckpoint(fullpath, &logger) + tries, err := realWAL.LoadCheckpoint(fullpath, logger) require.NoError(t, err) require.Equal(t, 1, len(tries)) require.Equal(t, updatedTrie, tries[0]) @@ -554,7 +554,7 @@ func Test_StoringLoadingCheckpoints(t *testing.T) { require.NoError(t, err) logger := unittest.Logger() - tries, err := realWAL.LoadCheckpoint(fullpath, &logger) + tries, err := realWAL.LoadCheckpoint(fullpath, logger) require.Error(t, err) require.Nil(t, tries) require.Contains(t, err.Error(), "checksum") diff --git a/ledger/complete/wal/checkpointer_versioning_test.go b/ledger/complete/wal/checkpointer_versioning_test.go index 58c85a3d2dc..af2d6ab4acd 100644 --- a/ledger/complete/wal/checkpointer_versioning_test.go +++ b/ledger/complete/wal/checkpointer_versioning_test.go @@ -20,7 +20,7 @@ func TestLoadCheckpointV1(t *testing.T) { } logger := zerolog.Nop() - tries, err := LoadCheckpoint("test_data/checkpoint.v1", &logger) + tries, err := LoadCheckpoint("test_data/checkpoint.v1", logger) require.NoError(t, err) require.Equal(t, len(expectedRootHash), len(tries)) @@ -40,7 +40,7 @@ func TestLoadCheckpointV3(t *testing.T) { } logger := zerolog.Nop() - tries, err := LoadCheckpoint("test_data/checkpoint.v3", &logger) + tries, err := LoadCheckpoint("test_data/checkpoint.v3", logger) require.NoError(t, err) require.Equal(t, len(expectedRootHash), len(tries)) @@ -60,7 +60,7 @@ func TestLoadCheckpointV4(t *testing.T) { } logger := zerolog.Nop() - tries, err := LoadCheckpoint("test_data/checkpoint.v4", &logger) + tries, err := LoadCheckpoint("test_data/checkpoint.v4", logger) require.NoError(t, err) require.Equal(t, len(expectedRootHash), len(tries)) diff --git a/ledger/complete/wal/syncrename.go b/ledger/complete/wal/syncrename.go index 140d4534006..28a0e47cfea 100644 --- a/ledger/complete/wal/syncrename.go +++ b/ledger/complete/wal/syncrename.go @@ -21,7 +21,7 @@ type WriterSeekerCloser interface { // to target one as the last step. This help avoid situation when writing is // interrupted and unusable file but with target name exists. type SyncOnCloseRenameFile struct { - logger *zerolog.Logger + logger zerolog.Logger file *os.File targetName string savedError error // savedError is the first error returned from Write. Close() renames temp file to target file only if savedError is nil. diff --git a/ledger/complete/wal/syncrename_test.go b/ledger/complete/wal/syncrename_test.go index 406905a631b..c8ee860f487 100644 --- a/ledger/complete/wal/syncrename_test.go +++ b/ledger/complete/wal/syncrename_test.go @@ -34,7 +34,7 @@ func Test_RenameHappensAfterClosing(t *testing.T) { file: file, targetName: fullFileName, Writer: writer, - logger: &logger, + logger: logger, } sampleBytes := []byte{2, 1, 3, 7} diff --git a/module/util/log.go b/module/util/log.go index 45807b9757d..13060d9f017 100644 --- a/module/util/log.go +++ b/module/util/log.go @@ -8,7 +8,7 @@ import ( // it prints the progress from 0% to 100% to indicate the index from 0 to (total - 1) has been // processed. // useful to report the progress of processing the index from 0 to (total - 1) -func LogProgress(msg string, total int, logger *zerolog.Logger) func(currentIndex int) { +func LogProgress(msg string, total int, logger zerolog.Logger) func(currentIndex int) { logThreshold := float64(0) return func(currentIndex int) { percentage := float64(100) diff --git a/module/util/log_test.go b/module/util/log_test.go index 9d1d4851dcd..76f74b2a016 100644 --- a/module/util/log_test.go +++ b/module/util/log_test.go @@ -12,7 +12,7 @@ func TestLogProgress40(t *testing.T) { buf := bytes.NewBufferString("") lg := zerolog.New(buf) total := 40 - logger := LogProgress("test", total, &lg) + logger := LogProgress("test", total, lg) for i := 0; i < total; i++ { logger(i) } @@ -37,7 +37,7 @@ func TestLogProgress1000(t *testing.T) { for total := 11; total < 1000; total++ { buf := bytes.NewBufferString("") lg := zerolog.New(buf) - logger := LogProgress("test", total, &lg) + logger := LogProgress("test", total, lg) for i := 0; i < total; i++ { logger(i) } From 7e04afd7dee4de0ba24dc17ef192f9a8c8aaa451 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 11:51:52 -0700 Subject: [PATCH 555/815] changes signature of start and stop nodes --- insecure/corruptlibp2p/spam_test.go | 4 +- .../test/gossipsub/rpc_inspector/utils.go | 4 +- .../validation_inspector_test.go | 4 +- .../test/gossipsub/scoring/ihave_spam_test.go | 8 ++-- .../test/gossipsub/scoring/scoring_test.go | 16 ++++---- network/p2p/connection/connManager_test.go | 4 +- .../p2p/connection/connection_gater_test.go | 20 +++++----- .../peerManager_integration_test.go | 4 +- network/p2p/dht/dht_test.go | 8 ++-- network/p2p/p2pnode/disallow_listing_test.go | 4 +- network/p2p/p2pnode/libp2pNode_test.go | 32 +++++++-------- network/p2p/p2pnode/libp2pStream_test.go | 40 +++++++++---------- network/p2p/scoring/app_score_test.go | 8 ++-- network/p2p/scoring/scoring_test.go | 5 +-- .../scoring/subscription_validator_test.go | 4 +- .../subscription/subscription_filter_test.go | 4 +- network/p2p/test/fixtures.go | 34 +++++++++++----- network/p2p/test/sporking_test.go | 32 +++++++-------- network/p2p/test/topic_validator_test.go | 32 +++++++-------- .../p2p/tracer/gossipSubMeshTracer_test.go | 4 +- .../p2p/tracer/gossipSubScoreTracer_test.go | 4 +- 21 files changed, 143 insertions(+), 132 deletions(-) diff --git a/insecure/corruptlibp2p/spam_test.go b/insecure/corruptlibp2p/spam_test.go index 2886b598c66..1c48d9feca5 100644 --- a/insecure/corruptlibp2p/spam_test.go +++ b/insecure/corruptlibp2p/spam_test.go @@ -62,8 +62,8 @@ func TestSpam_IHave(t *testing.T) { signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) defer cancel() nodes := []p2p.LibP2PNode{gsrSpammer.SpammerNode, victimNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 5*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) gsrSpammer.Start(t) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go index 2307c57f0ab..1f111642ab1 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go @@ -15,7 +15,7 @@ import ( // StartNodesAndEnsureConnected starts the victim and spammer node and ensures they are both connected. func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode, sporkID flow.Identifier) { - p2ptest.StartNodes(t, ctx, nodes, 5*time.Second) + p2ptest.StartNodes(t, ctx, nodes) // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a prior connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. @@ -27,6 +27,6 @@ func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContex } func stopNodesAndInspector(t *testing.T, cancel context.CancelFunc, nodes []p2p.LibP2PNode, inspector p2p.GossipSubRPCInspector) { - p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) + p2ptest.StopNodes(t, nodes, cancel) unittest.RequireComponentsDoneBefore(t, time.Second, inspector) } diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 3dd873d0e7c..8fe78d0f6df 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -1188,8 +1188,8 @@ func TestGossipSubSpamMitigationIntegration(t *testing.T) { // starting the nodes. nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) spammer.Start(t) // wait for the nodes to discover each other diff --git a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go index 3342b3ac4dc..d9ba2cfcf24 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go @@ -82,8 +82,8 @@ func TestGossipSubIHaveBrokenPromises_Below_Threshold(t *testing.T) { ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -198,8 +198,8 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) diff --git a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go index d8baf4be735..910063110e9 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go @@ -121,8 +121,8 @@ func testGossipSubInvalidMessageDeliveryScoring(t *testing.T, spamMsgFactory fun ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -223,8 +223,8 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_SingleTopic(t *testing.T) { ids := flow.IdentityList{&underPerformerId, &thisId} nodes := []p2p.LibP2PNode{underPerformerNode, thisNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -329,8 +329,8 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_TwoTopics(t *testing.T) { ids := flow.IdentityList{&underPerformerId, &thisId} nodes := []p2p.LibP2PNode{underPerformerNode, thisNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) @@ -438,8 +438,8 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { ids := flow.IdentityList{&replayingId, &thisId} nodes := []p2p.LibP2PNode{replayingNode, thisNode} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2ptest.TryConnectionAndEnsureConnected(t, ctx, nodes) diff --git a/network/p2p/connection/connManager_test.go b/network/p2p/connection/connManager_test.go index 39016a09fc3..6d10faa810c 100644 --- a/network/p2p/connection/connManager_test.go +++ b/network/p2p/connection/connManager_test.go @@ -130,8 +130,8 @@ func TestConnectionManager_Watermarking(t *testing.T) { nodes := append(otherNodes, thisNode) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // connect this node to all other nodes. for _, otherNode := range otherNodes { diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index cd240b5293b..4bd2314e509 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -64,8 +64,8 @@ func TestConnectionGating(t *testing.T) { nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2pfixtures.AddNodesToEachOthersPeerStore(t, nodes, ids) @@ -161,8 +161,8 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2pfixtures.AddNodesToEachOthersPeerStore(t, nodes, ids) @@ -206,8 +206,8 @@ func TestConnectionGating_ResourceAllocation_DisAllowListing(t *testing.T) { nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2pfixtures.AddNodesToEachOthersPeerStore(t, nodes, ids) @@ -287,8 +287,8 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { disallowedPeerIds.Add(nodes[0].Host().ID(), struct{}{}) // starts the nodes - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // Checks that only an allowed REMOTE node can establish an upgradable connection. connectionGater.On("InterceptUpgraded", mock.Anything).Run(func(args mock.Arguments) { @@ -367,8 +367,8 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { inbounds = append(inbounds, inbound) } - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) diff --git a/network/p2p/connection/peerManager_integration_test.go b/network/p2p/connection/peerManager_integration_test.go index 684acfbdb8a..28b6e6927c9 100644 --- a/network/p2p/connection/peerManager_integration_test.go +++ b/network/p2p/connection/peerManager_integration_test.go @@ -36,8 +36,8 @@ func TestPeerManager_Integration(t *testing.T) { idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_peer_manager", count, idProvider) idProvider.SetIdentities(identities) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) thisNode := nodes[0] topologyPeers := identities[1:] diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 5ea0ee70e6a..8f1f8cd942e 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -45,8 +45,8 @@ func TestFindPeerWithDHT(t *testing.T) { nodes := append(dhtServerNodes, dhtClientNodes...) idProvider.SetIdentities(append(serverIDs, clientIDs...)) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) getDhtServerAddr := func(i uint) peer.AddrInfo { return peer.AddrInfo{ID: dhtServerNodes[i].Host().ID(), Addrs: dhtServerNodes[i].Host().Addrs()} @@ -137,8 +137,8 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { idProvider.On("ByPeerID", node.Host().ID()).Return(&ids[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // Step 2: Connect all nodes running a DHT client to the node running the DHT server // This has to be done before subscribing to any topic, otherwise the node gives up on advertising diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index 566b2d6ec5e..44c3f00f8e3 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -66,8 +66,8 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { nodes := []p2p.LibP2PNode{node1, node2, node3} ids := flow.IdentityList{&identity1, &identity2, &identity3} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 519a0579163..536c3ed19cb 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -116,8 +116,8 @@ func TestAddPeers(t *testing.T) { idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_add_peers", count, idProvider) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // add the remaining nodes to the first node as its set of peers for _, identity := range identities[1:] { @@ -141,8 +141,8 @@ func TestRemovePeers(t *testing.T) { peerInfos, errs := utils.PeerInfosFromIDs(identities) assert.Len(t, errs, 0) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // add nodes two and three to the first node as its peers for _, pInfo := range peerInfos[1:] { @@ -180,8 +180,8 @@ func TestConnGater(t *testing.T) { }))) idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity1, true).Maybe() - p2ptest.StartNode(t, signalerCtx, node1, 100*time.Millisecond) - defer p2ptest.StopNode(t, node1, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node1) + defer p2ptest.StopNode(t, node1, cancel) node1Info, err := utils.PeerAddressInfo(identity1) assert.NoError(t, err) @@ -201,8 +201,8 @@ func TestConnGater(t *testing.T) { true).Maybe() - p2ptest.StartNode(t, signalerCtx, node2, 100*time.Millisecond) - defer p2ptest.StopNode(t, node2, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node2) + defer p2ptest.StopNode(t, node2, cancel) node2Info, err := utils.PeerAddressInfo(identity2) assert.NoError(t, err) @@ -233,8 +233,8 @@ func TestNode_HasSubscription(t *testing.T) { sporkID := unittest.IdentifierFixture() node, _ := p2ptest.NodeFixture(t, sporkID, "test_has_subscription", idProvider) - p2ptest.StartNode(t, signalerCtx, node, 100*time.Millisecond) - defer p2ptest.StopNode(t, node, cancel, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx, node) + defer p2ptest.StopNode(t, node, cancel) logger := unittest.Logger() @@ -271,8 +271,8 @@ func TestCreateStream_SinglePairwiseConnection(t *testing.T) { p2ptest.WithDefaultResourceManager()) idProvider.SetIdentities(ids) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) ctxWithTimeout, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() @@ -363,8 +363,8 @@ func TestCreateStream_SinglePeerDial(t *testing.T) { idProvider.On("ByPeerID", sender.Host().ID()).Return(&id1, true).Maybe() idProvider.On("ByPeerID", receiver.Host().ID()).Return(&id2, true).Maybe() - p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}, 100*time.Millisecond) - defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}) + defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel) var wg sync.WaitGroup wg.Add(2) @@ -422,8 +422,8 @@ func TestCreateStream_InboundConnResourceLimit(t *testing.T) { idProvider.On("ByPeerID", sender.Host().ID()).Return(&id1, true).Maybe() idProvider.On("ByPeerID", receiver.Host().ID()).Return(&id2, true).Maybe() - p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}, 100*time.Millisecond) - defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}) + defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel) p2ptest.LetNodesDiscoverEachOther(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}, flow.IdentityList{&id1, &id2}) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 224a913aa8f..16808d227e0 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -52,8 +52,8 @@ func TestStreamClosing(t *testing.T) { p2ptest.WithDefaultStreamHandler(handler)) idProvider.SetIdentities(identities) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) require.NoError(t, err) @@ -159,8 +159,8 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol idProvider, p2ptest.WithPreferredUnicasts(unicasts)) idProvider.SetIdentities(identities) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) id2 := identities[1] @@ -225,8 +225,8 @@ func TestCreateStream_FallBack(t *testing.T) { idProvider.On("ByPeerID", node.Host().ID()).Return(&identities[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // Assert that there is no outbound stream to the target yet (neither default nor preferred) defaultProtocolId := protocols.FlowProtocolID(sporkId) @@ -287,8 +287,8 @@ func TestCreateStreamIsConcurrencySafe(t *testing.T) { nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_create_stream_is_concurrency_safe", 2, idProvider) require.Len(t, identities, 2) idProvider.SetIdentities(flow.IdentityList{identities[0], identities[1]}) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) require.NoError(t, err) @@ -342,12 +342,12 @@ func TestNoBackoffWhenCreatingStream(t *testing.T) { node1 := nodes[0] node2 := nodes[1] idProvider.SetIdentities(flow.IdentityList{identities[0], identities[1]}) - p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1) + p2ptest.StartNode(t, signalerCtx2, node2) // stop node 2 immediately - p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) - defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StopNode(t, node2, cancel2) + defer p2ptest.StopNode(t, node1, cancel1) id2 := identities[1] pInfo, err := utils.PeerAddressInfo(*id2) @@ -427,8 +427,8 @@ func testUnicastOverStream(t *testing.T, opts ...p2ptest.NodeFixtureParameterOpt idProvider.On("ByPeerID", node.Host().ID()).Return(ids[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) @@ -476,8 +476,8 @@ func TestUnicastOverStream_Fallback(t *testing.T) { idProvider.On("ByPeerID", node.Host().ID()).Return(ids[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) p2pfixtures.EnsureMessageExchangeOverUnicast(t, ctx, nodes, []chan string{inbound1, inbound2}, p2pfixtures.LongStringMessageFactoryFixture(t)) @@ -498,8 +498,8 @@ func TestCreateStreamTimeoutWithUnresponsiveNode(t *testing.T) { ) require.Len(t, identities, 1) idProvider.SetIdentities(identities) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // create a silent node which never replies listener, silentNodeId := p2pfixtures.SilentNodeFixture(t) @@ -540,8 +540,8 @@ func TestCreateStreamIsConcurrent(t *testing.T) { ) require.Len(t, goodNodeIds, 2) idProvider.SetIdentities(goodNodeIds) - p2ptest.StartNodes(t, signalerCtx, goodNodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, goodNodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, goodNodes) + defer p2ptest.StopNodes(t, goodNodes, cancel) goodNodeInfo1, err := utils.PeerAddressInfo(*goodNodeIds[1]) require.NoError(t, err) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 8e2a1ae1bb8..8a165b4cec7 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -57,8 +57,8 @@ func TestFullGossipSubConnectivity(t *testing.T) { _, ok := provider.ByPeerID(peerId) return ok }) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) @@ -181,8 +181,8 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS return ok }).Maybe() - p2ptest.StartNodes(t, signalerCtx, allNodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, allNodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, allNodes) + defer p2ptest.StopNodes(t, allNodes, cancel) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) diff --git a/network/p2p/scoring/scoring_test.go b/network/p2p/scoring/scoring_test.go index 47e6f27cb57..d71f6dd4556 100644 --- a/network/p2p/scoring/scoring_test.go +++ b/network/p2p/scoring/scoring_test.go @@ -5,7 +5,6 @@ import ( "fmt" "math/rand" "testing" - "time" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" @@ -124,8 +123,8 @@ func TestInvalidCtrlMsgScoringIntegration(t *testing.T) { _, ok := provider.ByPeerID(peerId) return ok }) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 2*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) diff --git a/network/p2p/scoring/subscription_validator_test.go b/network/p2p/scoring/subscription_validator_test.go index 549006b3bde..7527b11c37b 100644 --- a/network/p2p/scoring/subscription_validator_test.go +++ b/network/p2p/scoring/subscription_validator_test.go @@ -207,8 +207,8 @@ func TestSubscriptionValidator_Integration(t *testing.T) { return ok }) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 0c3d1d8b88c..58dd211347d 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -122,8 +122,8 @@ func TestCanSubscribe(t *testing.T) { flow.IdentityList{identity}, p2pfixtures.WithSubscriptionFilter(subscriptionFilter(identity, flow.IdentityList{identity}))) - p2ptest.StartNode(t, signalerCtx, collectionNode, 100*time.Millisecond) - defer p2ptest.StopNode(t, collectionNode, cancel, 1*time.Second) + p2ptest.StartNode(t, signalerCtx, collectionNode) + defer p2ptest.StopNode(t, collectionNode, cancel) logger := unittest.Logger() topicValidator := flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter()) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 50ecb5a568f..07dd2588f97 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -416,7 +416,7 @@ func NodesFixture(t *testing.T, sporkID flow.Identifier, dhtPrefix string, count // StartNodes start all nodes in the input slice using the provided context, timing out if nodes are // not all Ready() before duration expires -func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode, timeout time.Duration) { +func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode) { rdas := make([]module.ReadyDoneAware, 0, len(nodes)) for _, node := range nodes { node.Start(ctx) @@ -428,30 +428,42 @@ func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.Lib rdas = append(rdas, peerManager) } } - unittest.RequireComponentsReadyBefore(t, timeout, rdas...) + unittest.RequireComponentsReadyBefore(t, 2*time.Second, rdas...) } // StartNode start a single node using the provided context, timing out if nodes are not all Ready() -// before duration expires -func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PNode, timeout time.Duration) { +// before duration expires, (i.e., 2 seconds). +// Args: +// - t: testing.T- the test object. +// - ctx: context to use. +// - node: node to start. +func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PNode) { node.Start(ctx) - unittest.RequireComponentsReadyBefore(t, timeout, node) + unittest.RequireComponentsReadyBefore(t, 2*time.Second, node) } // StopNodes stops all nodes in the input slice using the provided cancel func, timing out if nodes are -// not all Done() before duration expires -func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { +// not all Done() before duration expires (i.e., 2 seconds). +// Args: +// - t: testing.T- the test object. +// - nodes: nodes to stop. +// - cancel: cancel func, the function first cancels the context and then waits for the nodes to be done. +func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc) { cancel() for _, node := range nodes { - unittest.RequireComponentsDoneBefore(t, timeout, node) + unittest.RequireComponentsDoneBefore(t, 2*time.Second, node) } } // StopNode stops a single node using the provided cancel func, timing out if nodes are not all Done() -// before duration expires -func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc, timeout time.Duration) { +// before duration expires, (i.e., 2 seconds). +// Args: +// - t: testing.T- the test object. +// - node: node to stop. +// - cancel: cancel func, the function first cancels the context and then waits for the nodes to be done. +func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc) { cancel() - unittest.RequireComponentsDoneBefore(t, timeout, node) + unittest.RequireComponentsDoneBefore(t, 2*time.Second, node) } // StreamHandlerFixture returns a stream handler that writes the received message to the given channel. diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 6f1b784fc72..e01a0531e71 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -65,8 +65,8 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { ) idProvider.SetIdentities(flow.IdentityList{&id1}) - p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1) + defer p2ptest.StopNode(t, node1, cancel1) t.Logf(" %s node started on %s", id1.NodeID.String(), id1.Address) t.Logf("libp2p ID for %s: %s", id1.NodeID.String(), node1.Host().ID()) @@ -81,7 +81,7 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { ) idProvider.SetIdentities(flow.IdentityList{&id1, &id2}) - p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2) peerInfo2, err := utils.PeerAddressInfo(id2) require.NoError(t, err) @@ -92,7 +92,7 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different networking key but on the same IP and port - p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) + p2ptest.StopNode(t, node2, cancel2) // start node2 with the same name, ip and port but with the new key node2keyNew := p2pfixtures.NetworkingKeyFixtures(t) @@ -106,8 +106,8 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { ) idProvider.SetIdentities(flow.IdentityList{&id1, &id2New}) - p2ptest.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) - defer p2ptest.StopNode(t, node2, cancel2a, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2a, node2) + defer p2ptest.StopNode(t, node2, cancel2a) // make sure the node2 indeed came up on the old ip and port assert.Equal(t, id2New.Address, id2.Address) @@ -139,8 +139,8 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { // create and start node 1 on localhost and random port node1, id1 := p2ptest.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention", idProvider) - p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1) + defer p2ptest.StopNode(t, node1, cancel1) peerInfo1, err := utils.PeerAddressInfo(id1) require.NoError(t, err) @@ -149,14 +149,14 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { node2, id2 := p2ptest.NodeFixture(t, sporkId1, "test_one_to_one_crosstalk_prevention", idProvider) idProvider.SetIdentities(flow.IdentityList{&id1, &id2}) - p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2) // create stream from node 2 to node 1 testOneToOneMessagingSucceeds(t, node2, peerInfo1) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different libp2p protocol id to listen for - p2ptest.StopNode(t, node2, cancel2, time.Second) + p2ptest.StopNode(t, node2, cancel2) // start node2 with the same address and root key but different root block id node2, id2New := p2ptest.NodeFixture(t, @@ -167,8 +167,8 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { ) idProvider.SetIdentities(flow.IdentityList{&id1, &id2New}) - p2ptest.StartNode(t, signalerCtx2a, node2, 100*time.Millisecond) - defer p2ptest.StopNode(t, node2, cancel2a, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2a, node2) + defer p2ptest.StopNode(t, node2, cancel2a) // make sure the node2 indeed came up on the old ip and port assert.Equal(t, id2New.Address, id2.Address) @@ -201,8 +201,8 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { idProvider, ) - p2ptest.StartNode(t, signalerCtx1, node1, 100*time.Millisecond) - defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx1, node1) + defer p2ptest.StopNode(t, node1, cancel1) idProvider.SetIdentities(flow.IdentityList{&id1}) // create and start node 2 on localhost and random port with the same root block ID node2, id2 := p2ptest.NodeFixture(t, @@ -211,8 +211,8 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { idProvider, ) - p2ptest.StartNode(t, signalerCtx2, node2, 100*time.Millisecond) - defer p2ptest.StopNode(t, node2, cancel2, 100*time.Millisecond) + p2ptest.StartNode(t, signalerCtx2, node2) + defer p2ptest.StopNode(t, node2, cancel2) pInfo2, err := utils.PeerAddressInfo(id2) require.NoError(t, err) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 5a7e402b141..3a1c2ac7277 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -48,8 +48,8 @@ func TestTopicValidator_Unstaked(t *testing.T) { idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -121,8 +121,8 @@ func TestTopicValidator_PublicChannel(t *testing.T) { idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // unauthenticated messages should not be dropped on public channels channel := channels.PublicSyncCommittee @@ -182,8 +182,8 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -235,8 +235,8 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) topic := channels.Topic("invalid-topic") @@ -288,8 +288,8 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() idProvider.On("ByPeerID", an1.Host().ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2, an1} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -404,8 +404,8 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) // try to publish BlockProposal on invalid SyncCommittee channel channel := channels.SyncCommittee @@ -482,8 +482,8 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() idProvider.On("ByPeerID", an1.Host().ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2, an1} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) channel := channels.ConsensusCommittee topic := channels.TopicFromChannel(channel, sporkId) @@ -580,8 +580,8 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { idProvider.On("ByPeerID", ln2.Host().ID()).Return(&identity2, true).Maybe() idProvider.On("ByPeerID", ln3.Host().ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{ln1, ln2, ln3} - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) channel := channels.SyncCluster(flow.Testnet) topic := channels.TopicFromChannel(channel, sporkId) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index c8a680df53e..b0cf703681a 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -114,8 +114,8 @@ func TestGossipSubMeshTracer(t *testing.T) { nodes := []p2p.LibP2PNode{tracerNode, otherNode1, otherNode2, unknownNode} ids := flow.IdentityList{&tracerId, &otherId1, &otherId2, &unknownId} - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) diff --git a/network/p2p/tracer/gossipSubScoreTracer_test.go b/network/p2p/tracer/gossipSubScoreTracer_test.go index 2a3ea623eb0..b03a53012a9 100644 --- a/network/p2p/tracer/gossipSubScoreTracer_test.go +++ b/network/p2p/tracer/gossipSubScoreTracer_test.go @@ -145,8 +145,8 @@ func TestGossipSubScoreTracer(t *testing.T) { ids := flow.IdentityList{&tracerId, &consensusId, &accessId} // 5. Starts the nodes and lets them discover each other. - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) - defer p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) + defer p2ptest.StopNodes(t, nodes, cancel) p2ptest.LetNodesDiscoverEachOther(t, ctx, nodes, ids) From 5ff6b101b649f59882616ffc8ebb099ef7613d9f Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 21 Aug 2023 22:07:03 +0100 Subject: [PATCH 556/815] Fix imports order according to convention --- engine/access/rpc/backend/backend_block_headers.go | 5 +++-- .../access/rpc/backend/backend_transactions_test.go | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index 9b51201d067..ac4116224d4 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -3,12 +3,13 @@ package backend import ( "context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type backendBlockHeaders struct { diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index c577886912c..26bd3c7ffbf 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -5,6 +5,13 @@ import ( "fmt" "github.com/dgraph-io/badger/v2" + "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + acc "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -13,12 +20,6 @@ import ( "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" - "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { From ae9a88bf05e981c4311e5663041ebe67b7e767c9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 14:42:48 -0700 Subject: [PATCH 557/815] encapsulates timeouts as package variables --- network/p2p/test/fixtures.go | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 07dd2588f97..fb74b426e80 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -44,6 +44,21 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) +const ( + // libp2pNodeStartupTimeout is the timeout for starting a libp2p node in tests. Note that the + // timeout has been selected to be large enough to allow for the node to start up on a CI even when + // the test is run in parallel with other tests. Hence, no further increase of the timeout is + // expected to be necessary. Any failure to start a node within this timeout is likely to be + // caused by a bug in the code. + libp2pNodeStartupTimeout = 5 * time.Second + // libp2pNodeStartupTimeout is the timeout for starting a libp2p node in tests. Note that the + // timeout has been selected to be large enough to allow for the node to start up on a CI even when + // the test is run in parallel with other tests. Hence, no further increase of the timeout is + // expected to be necessary. Any failure to start a node within this timeout is likely to be + // caused by a bug in the code. + libp2pNodeShutdownTimeout = 5 * time.Second +) + // NetworkingKeyFixtures is a test helper that generates a ECDSA flow key pair. func NetworkingKeyFixtures(t *testing.T) crypto.PrivateKey { seed := unittest.SeedFixture(48) @@ -428,7 +443,10 @@ func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.Lib rdas = append(rdas, peerManager) } } - unittest.RequireComponentsReadyBefore(t, 2*time.Second, rdas...) + for _, r := range rdas { + // Any failure to start a node within this timeout is likely to be caused by a bug in the code. + unittest.RequireComponentsReadyBefore(t, libp2pNodeStartupTimeout, r) + } } // StartNode start a single node using the provided context, timing out if nodes are not all Ready() @@ -439,11 +457,12 @@ func StartNodes(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.Lib // - node: node to start. func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PNode) { node.Start(ctx) - unittest.RequireComponentsReadyBefore(t, 2*time.Second, node) + // Any failure to start a node within this timeout is likely to be caused by a bug in the code. + unittest.RequireComponentsReadyBefore(t, libp2pNodeStartupTimeout, node) } // StopNodes stops all nodes in the input slice using the provided cancel func, timing out if nodes are -// not all Done() before duration expires (i.e., 2 seconds). +// not all Done() before duration expires (i.e., 5 seconds). // Args: // - t: testing.T- the test object. // - nodes: nodes to stop. @@ -451,7 +470,8 @@ func StartNode(t *testing.T, ctx irrecoverable.SignalerContext, node p2p.LibP2PN func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc) { cancel() for _, node := range nodes { - unittest.RequireComponentsDoneBefore(t, 2*time.Second, node) + // Any failure to start a node within this timeout is likely to be caused by a bug in the code. + unittest.RequireComponentsDoneBefore(t, libp2pNodeShutdownTimeout, node) } } @@ -463,7 +483,8 @@ func StopNodes(t *testing.T, nodes []p2p.LibP2PNode, cancel context.CancelFunc) // - cancel: cancel func, the function first cancels the context and then waits for the nodes to be done. func StopNode(t *testing.T, node p2p.LibP2PNode, cancel context.CancelFunc) { cancel() - unittest.RequireComponentsDoneBefore(t, 2*time.Second, node) + // Any failure to start a node within this timeout is likely to be caused by a bug in the code. + unittest.RequireComponentsDoneBefore(t, libp2pNodeShutdownTimeout, node) } // StreamHandlerFixture returns a stream handler that writes the received message to the given channel. From e2e3aa0844deacf3b3ebe57a905dfbb6e2faf588 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 14:43:07 -0700 Subject: [PATCH 558/815] refactors the test based on mesh tracer --- network/p2p/scoring/app_score_test.go | 89 ++++++++++++++++++++------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 8a165b4cec7..1bf381b1193 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -9,15 +9,18 @@ import ( mocktestify "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/scoring" p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/network/p2p/tracer" flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/unittest" ) @@ -125,7 +128,6 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // This test proves that if access nodes are NOT pushed to the edge of network, a malicious majority of them can // partition the network by disconnecting honest nodes from each other even when the network topology is a complete graph (i.e., full topology). func TestNetworkPartitionWithNoHonestPeerScoringInFullTopology(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "to be fixed later") total := 100 for i := 0; i < total; i++ { // false means no honest peer scoring. @@ -139,8 +141,8 @@ func TestNetworkPartitionWithNoHonestPeerScoringInFullTopology(t *testing.T) { // testGossipSubMessageDeliveryUnderNetworkPartition tests that whether two honest nodes can exchange messages on GossipSub // when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. // If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. -// A true return value means that the two honest nodes can exchange messages. -// A false return value means that the two honest nodes cannot exchange messages within the given timeout. +// A true return value means that the two honest nodes have each other in each others' mesh. +// A false return value means that the two honest nodes do not have each other in each others' mesh (at least one of them is partitioned). func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) @@ -152,8 +154,24 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS if honestPeerScoring { opts = append(opts, p2ptest.EnablePeerScoringWithOverride(p2p.PeerScoringConfigNoOverride)) } - con1Node, con1Id := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, opts...) - con2Node, con2Id := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, opts...) + + defaultConfig, err := config.DefaultConfig() + require.NoError(t, err) + meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ + Logger: unittest.Logger(), + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: time.Second, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + RpcSentTrackerCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: defaultConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: defaultConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + } + + con1NodeTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) // mesh tracer for con1 + con2NodeTracer := tracer.NewGossipSubMeshTracer(meshTracerCfg) // mesh tracer for con2 + con1Node, con1Id := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, append(opts, p2ptest.WithGossipSubTracer(con1NodeTracer))...) + con2Node, con2Id := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, append(opts, p2ptest.WithGossipSubTracer(con2NodeTracer))...) // create > 2 * 12 malicious access nodes // 12 is the maximum size of default GossipSub mesh. @@ -186,19 +204,17 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS blockTopic := channels.TopicFromChannel(channels.PushBlocks, sporkId) - logger := unittest.Logger() - // all nodes subscribe to block topic (common topic among all roles) - _, err := con1Node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) + _, err = con1Node.Subscribe(blockTopic, flowpubsub.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) require.NoError(t, err) - con2Sub, err := con2Node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) + _, err = con2Node.Subscribe(blockTopic, flowpubsub.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) require.NoError(t, err) // access node group accessNodeSubs := make([]p2p.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { - sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) + sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) require.NoError(t, err) accessNodeSubs[i] = sub } @@ -206,19 +222,46 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // let nodes reside on a full topology, hence no partition is caused by the topology. p2ptest.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) - proposalMsg := p2pfixtures.MustEncodeEvent(t, unittest.ProposalFixture(), channels.PushBlocks) - require.NoError(t, con1Node.Publish(ctx, blockTopic, proposalMsg)) - - // we check that whether within a one-second window the message is received by the other honest consensus node. - // the one-second window is important because it triggers the heartbeat of the con1Node to perform a lazy pull (iHave). - // And con1Node may randomly choose con2Node as the peer to perform the lazy pull. - // However, under a network partition con2Node is not in the mesh of con1Node, and hence is deprived of the eager push from con1Node. - // - // If no honest peer scoring is enabled, then con1Node and con2Node are less-likely to be in the same mesh, and hence the message is not delivered. - // If honest peer scoring is enabled, then con1Node and con2Node are certainly in the same mesh, and hence the message is delivered. - ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) - defer cancel1s() - return p2pfixtures.HasSubReceivedMessage(t, ctx1s, proposalMsg, con2Sub) + // checks whether con1 and con2 are in the same mesh + tick := time.Second // Set the tick duration as needed + timeout := 5 * time.Second // Set the timeout duration as needed + + ticker := time.NewTicker(tick) + defer ticker.Stop() + timeoutCh := time.After(timeout) + + con1HasCon2 := false // denotes whether con1 has con2 in its mesh + con2HasCon1 := false // denotes whether con2 has con1 in its mesh + for { + select { + case <-ticker.C: + con1BlockTopicPeers := con1NodeTracer.GetMeshPeers(blockTopic.String()) + for _, p := range con1BlockTopicPeers { + if p == con2Node.Host().ID() { + t.Log("con1 has con2 in its mesh") + con2HasCon1 = true + break // con1 has con2 in its mesh, break out of the current loop + } + } + + con2BlockTopicPeers := con2NodeTracer.GetMeshPeers(blockTopic.String()) + for _, p := range con2BlockTopicPeers { + if p == con1Node.Host().ID() { + t.Log("con2 has con1 in its mesh") + con1HasCon2 = true + break // con2 has con1 in its mesh, break out of the current loop + } + } + + if con2HasCon1 && con1HasCon2 { + return true + } + + case <-timeoutCh: + t.Log("timed out waiting for con1 to have con2 in its mesh") + return false + } + } } // maliciousAppSpecificScore returns a malicious app specific penalty function that rewards the malicious node and From 411be7ab1b3d3910ee6ef43b189c791957b1810e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 15:04:16 -0700 Subject: [PATCH 559/815] revises comments --- network/p2p/scoring/app_score_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 1bf381b1193..1545b306120 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -116,10 +116,10 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // Note: if this test is ever flaky, this means a bug in our scoring system. Please escalate to the team instead of skipping. total := 10 for i := 0; i < total; i++ { - if !testGossipSubMessageDeliveryUnderNetworkPartition(t, true) { + if !testGossipConnectivityUnderNetworkPartition(t, true) { // even one failure should not happen, as it means that malicious majority can partition the network // with our peer scoring parameters. - require.Fail(t, "honest nodes could not exchange message on GossipSub") + require.Fail(t, "honest nodes are not on each others' topic mesh on GossipSub") } } } @@ -131,19 +131,19 @@ func TestNetworkPartitionWithNoHonestPeerScoringInFullTopology(t *testing.T) { total := 100 for i := 0; i < total; i++ { // false means no honest peer scoring. - if !testGossipSubMessageDeliveryUnderNetworkPartition(t, false) { + if !testGossipConnectivityUnderNetworkPartition(t, false) { return // partition is successful } } require.Fail(t, "expected at least one network partition") } -// testGossipSubMessageDeliveryUnderNetworkPartition tests that whether two honest nodes can exchange messages on GossipSub +// testGossipConnectivityUnderNetworkPartition tests that whether two honest nodes are in each others topic mesh on GossipSub // when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. // If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. // A true return value means that the two honest nodes have each other in each others' mesh. // A false return value means that the two honest nodes do not have each other in each others' mesh (at least one of them is partitioned). -func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { +func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) sporkId := unittest.IdentifierFixture() @@ -175,8 +175,7 @@ func testGossipSubMessageDeliveryUnderNetworkPartition(t *testing.T, honestPeerS // create > 2 * 12 malicious access nodes // 12 is the maximum size of default GossipSub mesh. - // We want to make sure that it is unlikely for honest nodes to be in the same mesh (hence messages from - // one honest node to the other is routed through the malicious nodes). + // We want to make sure that it is unlikely for honest nodes to be in the same mesh without peer scoring. accessNodeGroup, accessNodeIds := p2ptest.NodesFixture(t, sporkId, t.Name(), 30, idProvider, p2ptest.WithRole(flow.RoleAccess), From d6ab0005926fc14e1166a8c5c9cfaf9026e4ebab Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 15:39:32 -0700 Subject: [PATCH 560/815] removes retrial --- network/p2p/scoring/app_score_test.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 1545b306120..2f77147b744 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -114,14 +114,12 @@ func TestFullGossipSubConnectivity(t *testing.T) { // the network of honest nodes. func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testing.T) { // Note: if this test is ever flaky, this means a bug in our scoring system. Please escalate to the team instead of skipping. - total := 10 - for i := 0; i < total; i++ { - if !testGossipConnectivityUnderNetworkPartition(t, true) { - // even one failure should not happen, as it means that malicious majority can partition the network - // with our peer scoring parameters. - require.Fail(t, "honest nodes are not on each others' topic mesh on GossipSub") - } + if !testGossipConnectivityUnderNetworkPartition(t, true) { + // even one failure should not happen, as it means that malicious majority can partition the network + // with our peer scoring parameters. + require.Fail(t, "honest nodes are not on each others' topic mesh on GossipSub") } + } // TestNetworkPartitionWithNoHonestPeerScoringInFullTopology is part one of testing pushing access nodes to the edges of the network. From 2ac3173e98a86be4d06c1eceba088115ca6216bd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 15:51:15 -0700 Subject: [PATCH 561/815] reduces retrial --- network/p2p/scoring/app_score_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 2f77147b744..eb14edbb70b 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -126,7 +126,7 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // This test proves that if access nodes are NOT pushed to the edge of network, a malicious majority of them can // partition the network by disconnecting honest nodes from each other even when the network topology is a complete graph (i.e., full topology). func TestNetworkPartitionWithNoHonestPeerScoringInFullTopology(t *testing.T) { - total := 100 + total := 50 for i := 0; i < total; i++ { // false means no honest peer scoring. if !testGossipConnectivityUnderNetworkPartition(t, false) { From a0ebb632a5648a3b3712ed77abed966f9184c922 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 16:30:19 -0700 Subject: [PATCH 562/815] removes debug log --- network/p2p/scoring/app_score_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index eb14edbb70b..6bb89d9aa21 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -235,7 +235,6 @@ func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring con1BlockTopicPeers := con1NodeTracer.GetMeshPeers(blockTopic.String()) for _, p := range con1BlockTopicPeers { if p == con2Node.Host().ID() { - t.Log("con1 has con2 in its mesh") con2HasCon1 = true break // con1 has con2 in its mesh, break out of the current loop } @@ -244,7 +243,6 @@ func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring con2BlockTopicPeers := con2NodeTracer.GetMeshPeers(blockTopic.String()) for _, p := range con2BlockTopicPeers { if p == con1Node.Host().ID() { - t.Log("con2 has con1 in its mesh") con1HasCon2 = true break // con2 has con1 in its mesh, break out of the current loop } From fe29da36467fe27dca1f5a1287c9f29c5e8f198a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 21 Aug 2023 16:31:10 -0700 Subject: [PATCH 563/815] ads a log message --- network/p2p/scoring/app_score_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 6bb89d9aa21..97b9310a595 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -212,7 +212,7 @@ func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring accessNodeSubs := make([]p2p.Subscription, len(accessNodeGroup)) for i, node := range accessNodeGroup { sub, err := node.Subscribe(blockTopic, flowpubsub.TopicValidator(unittest.Logger(), unittest.AllowAllPeerFilter())) - require.NoError(t, err) + require.NoError(t, err, "access node %d failed to subscribe to block topic", i) accessNodeSubs[i] = sub } From 500d415f9818e6517d02f7ffabf64eacefa57723 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 22 Aug 2023 05:31:54 -0400 Subject: [PATCH 564/815] load test refactoring - WIP --- engine/common/synchronization/engine_test.go | 102 +++++++++++-------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 8a35ad0071f..2fbfb2c9f2d 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -2,6 +2,7 @@ package synchronization import ( "context" + "fmt" "io" "math" "testing" @@ -315,54 +316,75 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - misbehaviorReported := 0 load := 5000 - for i := 0; i < load; i++ { - ss.T().Log("load iteration", i) - nonce, err := rand.Uint64() - require.NoError(ss.T(), err, "should generate nonce") - - // generate origin and request message - originID := unittest.IdentifierFixture() - req := &messages.SyncRequest{ - Nonce: nonce, - Height: 0, - } - - // if request height is higher than local finalized, we should not respond - req.Height = ss.head.Height + 1 - - ss.core.On("HandleHeight", ss.head, req.Height) - ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + type loadGroup struct { + syncRequestProbabilityFactor float32 + expectedMisbehaviorsLower int + expectedMisbehaviorsUpper int + } - // maybe function calls that might or might not occur over the course of the load test - ss.core.On("ScanPending", ss.head).Return([]chainsync.Range{}, []chainsync.Batch{}).Maybe() - ss.con.On("Multicast", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + loadGroups := []loadGroup{} + loadGroups = append(loadGroups, loadGroup{0.001, 1, 9}) - // count misbehavior reports over the course of a load test - ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe().Run( - func(args mock.Arguments) { - misbehaviorReported++ - }, - ) - - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + for _, group := range loadGroups { + } - // give at least some time to process items - //time.Sleep(time.Millisecond * 100) + for h := 0; h < 1; h++ { + + // reset misbehavior report counter for each subtest + misbehaviorReported := 0 + + syncRequestProbabilityFactor := float32(0.001) + expectedMisbehaviorsLower := 1 + expectedMisbehaviorsUpper := 9 + + ss.T().Run(fmt.Sprintf("load test; pfactor=%f lower=%d upper=%d", syncRequestProbabilityFactor, expectedMisbehaviorsLower, expectedMisbehaviorsUpper), func(t *testing.T) { + for i := 0; i < load; i++ { + ss.T().Log("load iteration", i) + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") + + // generate origin and request message + originID := unittest.IdentifierFixture() + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } + + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 + + ss.core.On("HandleHeight", ss.head, req.Height) + ss.core.On("WithinTolerance", ss.head, req.Height).Return(false) + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + + // maybe function calls that might or might not occur over the course of the load test + ss.core.On("ScanPending", ss.head).Return([]chainsync.Range{}, []chainsync.Batch{}).Maybe() + ss.con.On("Multicast", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + + // count misbehavior reports over the course of a load test + ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe().Run( + func(args mock.Arguments) { + misbehaviorReported++ + }, + ) + ss.e.alsp.syncRequestProbabilityFactor = syncRequestProbabilityFactor + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + } - // check function call expectations at the end of the load test; otherwise, load test would take much longer - ss.core.AssertExpectations(ss.T()) - ss.con.AssertExpectations(ss.T()) + // check function call expectations at the end of the load test; otherwise, load test would take much longer + ss.core.AssertExpectations(ss.T()) + ss.con.AssertExpectations(ss.T()) - // check that correct range of misbehavior reports were generated (between 1-2 reports per 1000 requests) - // since we're using a random method to generate misbehavior reports, we can't guarantee the exact number, so we - // check that it's within a larger range, but that at least 1 misbehavior report was generated - assert.GreaterOrEqual(ss.T(), misbehaviorReported, 1) - assert.LessOrEqual(ss.T(), misbehaviorReported, 8) // too many reports would indicate a bug + // check that correct range of misbehavior reports were generated (between 1-2 reports per 1000 requests) + // since we're using a random method to generate misbehavior reports, we can't guarantee the exact number, so we + // check that it's within a larger range, but that at least 1 misbehavior report was generated + assert.GreaterOrEqual(ss.T(), misbehaviorReported, expectedMisbehaviorsLower) + assert.LessOrEqual(ss.T(), misbehaviorReported, expectedMisbehaviorsUpper) // too many reports would indicate a bug + }) + } } // TestOnSyncRequest_LowerThanReceiver_OutsideTolerance tests that a sync request that's outside tolerance and From 82aa009ef00f1c873b2ded9627b13aa5a1cd0538 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 22 Aug 2023 06:35:45 -0400 Subject: [PATCH 565/815] load test refactor, more generic for different loads --- engine/common/synchronization/engine.go | 2 +- engine/common/synchronization/engine_test.go | 33 +++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 5a38d94a7a6..9b4031638aa 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -500,7 +500,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f // to avoid creating a misbehavior report for every sync request received, use a probabilistic approach. // Create a report with a probability of alsp.syncRequestProbabilityFactor - if float32(n) < e.alsp.syncRequestProbabilityFactor*1000 { + if float32(n) < e.alsp.syncRequestProbabilityFactor*1001 { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 2fbfb2c9f2d..1085fb463e7 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -316,7 +316,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - load := 5000 + load := 1000 type loadGroup struct { syncRequestProbabilityFactor float32 @@ -324,23 +324,18 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance expectedMisbehaviorsUpper int } - loadGroups := []loadGroup{} - loadGroups = append(loadGroups, loadGroup{0.001, 1, 9}) + loadGroups := make([]loadGroup, 5) - for _, group := range loadGroups { - - } - - for h := 0; h < 1; h++ { + loadGroups = append(loadGroups, loadGroup{0.1, 75, 140}) + loadGroups = append(loadGroups, loadGroup{0.001, 0, 7}) + loadGroups = append(loadGroups, loadGroup{0.5, 450, 550}) + loadGroups = append(loadGroups, loadGroup{0.9, 850, 950}) - // reset misbehavior report counter for each subtest - misbehaviorReported := 0 + // reset misbehavior report counter for each subtest + misbehaviorReported := 0 - syncRequestProbabilityFactor := float32(0.001) - expectedMisbehaviorsLower := 1 - expectedMisbehaviorsUpper := 9 - - ss.T().Run(fmt.Sprintf("load test; pfactor=%f lower=%d upper=%d", syncRequestProbabilityFactor, expectedMisbehaviorsLower, expectedMisbehaviorsUpper), func(t *testing.T) { + for _, loadGroup := range loadGroups { + ss.T().Run(fmt.Sprintf("load test; pfactor=%f lower=%d upper=%d", loadGroup.syncRequestProbabilityFactor, loadGroup.expectedMisbehaviorsLower, loadGroup.expectedMisbehaviorsUpper), func(t *testing.T) { for i := 0; i < load; i++ { ss.T().Log("load iteration", i) nonce, err := rand.Uint64() @@ -370,7 +365,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance misbehaviorReported++ }, ) - ss.e.alsp.syncRequestProbabilityFactor = syncRequestProbabilityFactor + ss.e.alsp.syncRequestProbabilityFactor = loadGroup.syncRequestProbabilityFactor require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } @@ -381,8 +376,10 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // check that correct range of misbehavior reports were generated (between 1-2 reports per 1000 requests) // since we're using a random method to generate misbehavior reports, we can't guarantee the exact number, so we // check that it's within a larger range, but that at least 1 misbehavior report was generated - assert.GreaterOrEqual(ss.T(), misbehaviorReported, expectedMisbehaviorsLower) - assert.LessOrEqual(ss.T(), misbehaviorReported, expectedMisbehaviorsUpper) // too many reports would indicate a bug + assert.GreaterOrEqual(ss.T(), misbehaviorReported, loadGroup.expectedMisbehaviorsLower) + assert.LessOrEqual(ss.T(), misbehaviorReported, loadGroup.expectedMisbehaviorsUpper) // too many reports would indicate a bug + + misbehaviorReported = 0 // reset counter for next subtest }) } } From 759570e91cf6d16c5e50732b79b4d88a6c167422 Mon Sep 17 00:00:00 2001 From: Misha Date: Tue, 22 Aug 2023 06:51:14 -0400 Subject: [PATCH 566/815] const probabilityFactorMultiplier --- engine/common/synchronization/alsp.go | 4 ++++ engine/common/synchronization/engine.go | 6 +++--- engine/common/synchronization/engine_test.go | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/engine/common/synchronization/alsp.go b/engine/common/synchronization/alsp.go index 3aa86dcdca2..39664a58691 100644 --- a/engine/common/synchronization/alsp.go +++ b/engine/common/synchronization/alsp.go @@ -1,5 +1,9 @@ package synchronization +// probabilityFactorMultiplier is used to convert probability factor to an integer as well as a maximum value - 1 +// random number that can be generated by the random number generator. +const probabilityFactorMultiplier = 1001 + type Alsp struct { syncRequestProbabilityFactor float32 } diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 9b4031638aa..d48345606dd 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -486,8 +486,8 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I } func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { - // Generate a random integer between 1 and 1000 - n, err := rand.Uint32n(1001) + // Generate a random integer between 1 and probabilityFactorMultiplier (exclusive) + n, err := rand.Uint32n(probabilityFactorMultiplier) if err != nil { // failing to generate a random number is unlikely. If an error is encountered while @@ -500,7 +500,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f // to avoid creating a misbehavior report for every sync request received, use a probabilistic approach. // Create a report with a probability of alsp.syncRequestProbabilityFactor - if float32(n) < e.alsp.syncRequestProbabilityFactor*1001 { + if float32(n) < e.alsp.syncRequestProbabilityFactor*probabilityFactorMultiplier { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 1085fb463e7..b4f227e5c20 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -324,7 +324,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance expectedMisbehaviorsUpper int } - loadGroups := make([]loadGroup, 5) + loadGroups := []loadGroup{} loadGroups = append(loadGroups, loadGroup{0.1, 75, 140}) loadGroups = append(loadGroups, loadGroup{0.001, 0, 7}) From 56e03073d70469707b5b5ec8178a10e21d3834ad Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 22 Aug 2023 12:52:14 +0100 Subject: [PATCH 567/815] Bring mock generation command to conventional way --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b09174f2dee..46b87640022 100644 --- a/Makefile +++ b/Makefile @@ -194,7 +194,7 @@ generate-mocks: install-mock-generators mockery --name 'API' --dir="./engine/protocol" --case=underscore --output="./engine/protocol/mock" --outpkg="mock" mockery --name '.*' --dir="./engine/access/state_stream" --case=underscore --output="./engine/access/state_stream/mock" --outpkg="mock" mockery --name 'ConnectionFactory' --dir="./engine/access/rpc/connection" --case=underscore --output="./engine/access/rpc/connection/mock" --outpkg="mock" - mockery --name 'Communicator' --structname 'NodeCommunicatorMock' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend" --inpackage + mockery --name 'Communicator' --dir="./engine/access/rpc/backend" --case=underscore --output="./engine/access/rpc/backend/mock" --outpkg="mock" mockery --name '.*' --dir=model/fingerprint --case=underscore --output="./model/fingerprint/mock" --outpkg="mock" mockery --name 'ExecForkActor' --structname 'ExecForkActorMock' --dir=module/mempool/consensus/mock/ --case=underscore --output="./module/mempool/consensus/mock/" --outpkg="mock" From b69632bb3b5424dad3734cf3b1591f6b0edd1933 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 22 Aug 2023 12:56:11 +0100 Subject: [PATCH 568/815] Refactor communicator interface to reduce dependency between packages --- .../integration_unsecure_grpc_server_test.go | 19 ++++++++++--------- engine/access/rest_api_test.go | 15 ++++++++------- engine/access/rpc/backend/backend_test.go | 4 ++-- .../access/rpc/backend/node_communicator.go | 8 ++++---- engine/access/secure_grpcr_test.go | 17 +++++++++-------- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index 9ad3e7f54df..cf9ec1fa744 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -7,6 +7,16 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "github.com/onflow/flow-go/engine" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -27,15 +37,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - executiondataproto "github.com/onflow/flow/protobuf/go/flow/executiondata" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) // SameGRPCPortTestSuite verifies both AccessAPI and ExecutionDataAPI client continue to work when configured diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 5164bba6106..1b891832249 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -11,6 +11,14 @@ import ( "time" "github.com/antihax/optional" + restclient "github.com/onflow/flow/openapi/go-client-generated" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/credentials" + accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest/request" "github.com/onflow/flow-go/engine/access/rest/routes" @@ -27,13 +35,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - restclient "github.com/onflow/flow/openapi/go-client-generated" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/credentials" ) // RestAPITestSuite tests that the Access node serves the REST API defined via the OpenApi spec accurately diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 8c38a34920e..1a4989dac79 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -54,7 +54,7 @@ type Suite struct { archiveClient *access.AccessAPIClient connectionFactory *backendmock.ConnectionFactory - communicator *NodeCommunicatorMock + communicator *backendmock.Communicator chainID flow.ChainID } @@ -85,7 +85,7 @@ func (suite *Suite) SetupTest() { suite.historicalAccessClient = new(access.AccessAPIClient) suite.connectionFactory = new(backendmock.ConnectionFactory) - suite.communicator = new(NodeCommunicatorMock) + suite.communicator = new(backendmock.Communicator) } func (suite *Suite) TestPing() { diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index b3a73e5b545..448b0c7b267 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -23,8 +23,8 @@ type ErrorTerminator func(node *flow.Identity, err error) bool type Communicator interface { CallAvailableNode( nodes flow.IdentityList, - call NodeAction, - shouldTerminateOnError ErrorTerminator, + call func(node *flow.Identity) error, + shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error } @@ -49,8 +49,8 @@ func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { // If the maximum failed request count is reached, it returns the accumulated errors. func (b *NodeCommunicator) CallAvailableNode( nodes flow.IdentityList, - call NodeAction, - shouldTerminateOnError ErrorTerminator, + call func(id *flow.Identity) error, + shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error { var errs *multierror.Error nodeSelector, err := b.nodeSelectorFactory.SelectNodes(nodes) diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index d8d4f71b732..d5f64352cf6 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -7,6 +7,15 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "github.com/onflow/flow-go/crypto" accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc" @@ -21,14 +30,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) // SecureGRPCTestSuite tests that Access node provides a secure GRPC server From fff2cf2db368ae019a563f1b82b0313cdb07ae26 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 22 Aug 2023 13:00:49 +0100 Subject: [PATCH 569/815] Rename bnd variable in access_node_builder --- cmd/access/node_builder/access_node_builder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 68d32a9efa4..2e607b1ab63 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1094,7 +1094,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { ), } - bnd := backend.New(backend.Params{ + nodeBackend := backend.New(backend.Params{ State: node.State, CollectionRPC: builder.CollectionRPC, HistoricalAccessNodes: builder.HistoricalAccessRPCs, @@ -1127,8 +1127,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.AccessMetrics, builder.rpcMetricsEnabled, builder.Me, - bnd, - bnd, + nodeBackend, + nodeBackend, builder.secureGrpcServer, builder.unsecureGrpcServer, ) From a59b7630db01d5c32cb5a53390f95963094228af Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 22 Aug 2023 13:19:30 +0100 Subject: [PATCH 570/815] Refine mock call expectations with type arguments in backend transactions tests --- engine/access/rpc/backend/backend_transactions_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 26bd3c7ffbf..9d7e12f5024 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -199,7 +199,7 @@ func (suite *Suite) TestGetTransactionResultFromCache() { } suite.historicalAccessClient. - On("GetTransactionResult", mock.Anything, mock.Anything). + On("GetTransactionResult", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*access.GetTransactionRequest")). Return(&transactionResultResponse, nil).Once() backend := New(Params{ @@ -238,7 +238,7 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. - On("GetTransactionResult", mock.Anything, mock.Anything). + On("GetTransactionResult", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*access.GetTransactionRequest")). Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() backend := New(Params{ @@ -281,7 +281,7 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { func (suite *Suite) TestGetTransactionResultUnknownFromCache() { suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. - On("GetTransactionResult", mock.Anything, mock.Anything). + On("GetTransactionResult", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*access.GetTransactionRequest")). Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() backend := New(Params{ From 3ff2eaf39951fa4c8263f4c1dd32878641a065d6 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 10:46:00 -0400 Subject: [PATCH 571/815] update WithIWant godoc and var names --- insecure/corruptlibp2p/fixtures.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index cb40c667da2..9b37a046435 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -42,13 +42,22 @@ func WithIHave(msgCount, msgSize int, topicId string) GossipSubCtrlOption { } } -// WithIWant adds n number of iWants each having m number of message ID's of size s. -func WithIWant(n, m int) GossipSubCtrlOption { +// WithIWant adds iWant control messages of the given size and number to the control message. +// The message IDs are generated randomly. +// Args: +// +// msgCount: number of iWant messages to add. +// msgIdsPerIWant: number of message IDs to add to each iWant message. +// +// Returns: +// A GossipSubCtrlOption that adds iWant messages to the control message. +// Example: WithIWant(2, 3) will add 2 iWant messages, each with 3 message IDs. +func WithIWant(iWantCount int, msgIdsPerIWant int) GossipSubCtrlOption { return func(msg *pubsubpb.ControlMessage) { - iWants := make([]*pubsubpb.ControlIWant, n) - for i := 0; i < n; i++ { + iWants := make([]*pubsubpb.ControlIWant, iWantCount) + for i := 0; i < iWantCount; i++ { iWants[i] = &pubsubpb.ControlIWant{ - MessageIDs: GossipSubMessageIdsFixture(m), + MessageIDs: GossipSubMessageIdsFixture(msgIdsPerIWant), } } msg.Iwant = iWants From eccb7a1945c19d13395d223f04fcd3b216a1d731 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 12:32:58 -0400 Subject: [PATCH 572/815] allow a threshold of duplicate message ids --- config/default-config.yml | 3 +- .../validation_inspector_test.go | 86 +++++++++++++++++++ network/netconf/flags.go | 8 +- .../control_message_validation_inspector.go | 16 ++-- network/p2p/inspector/validation/errors.go | 34 ++++++-- .../p2p/p2pconf/gossipsub_rpc_inspectors.go | 2 + 6 files changed, 134 insertions(+), 15 deletions(-) diff --git a/config/default-config.yml b/config/default-config.yml index 84c3c0837b1..13a17d8750a 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -111,7 +111,8 @@ network-config: gossipsub-rpc-iwant-max-sample-size: 1_000_000 # The allowed threshold of iWant messages receievd without a corresponding tracked iHave message that was sent gossipsub-rpc-iwant-cache-miss-threshold: .5 - + # The max allowed duplicate message IDs in a single iWant control message + gossipsub-rpc-iwant-duplicate-message-id-threshold: .15 # RPC metrics observer inspector configs # The number of metrics inspector pool workers gossipsub-rpc-metrics-inspector-workers: 1 diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 0926309ad21..a3cb5e869b4 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -1302,6 +1302,92 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") } +// TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold ensures that expected invalid control message notification is disseminated when the number +// of duplicate message Ids in a single iWant control message exceeds the configured duplicate message ID threshold. +func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) { + t.Parallel() + role := flow.RoleConsensus + sporkID := unittest.IdentifierFixture() + // create our RPC validation inspector + flowConfig, err := config.DefaultConfig() + require.NoError(t, err) + inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs + inspectorConfig.NumberOfWorkers = 1 + inspectorConfig.IWantRPCInspectionConfig.DuplicateMsgIDThreshold = .5 // set duplicate message id threshold to 50% + inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = 2 // avoid throwing cache miss threshold error + messageCount := 1 + controlMessageCount := int64(1) + done := make(chan struct{}) + // ensure expected notifications are disseminated with expected error + inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + return func(args mockery.Arguments) { + defer close(done) + notification, ok := args[0].(*p2p.InvCtrlMsgNotif) + require.True(t, ok) + require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, uint64(messageCount), notification.Count) + require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) + } + } + + idProvider := mock.NewIdentityProvider(t) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) + mockDistributorReadyDoneAware(distributor) + withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) + validationInspector, err := validation.NewControlMsgValidationInspector( + unittest.Logger(), + sporkID, + &inspectorConfig, + distributor, + metrics.NewNoopCollector(), + metrics.NewNoopCollector(), + idProvider, + metrics.NewNoopCollector(), + meshTracer) + require.NoError(t, err) + corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) + victimNode, victimIdentity := p2ptest.NodeFixture( + t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), + ) + idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + + messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) + + // create control message with iWant that contains 5 message IDs that were not tracked + ctlMsg := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) + ctlMsg[0].Iwant[0].MessageIDs = messageIDs + // set first 6 message ids to duplicate id > 50% duplicate message ID threshold + for i := 0; i <= 6; i++ { + ctlMsg[0].Iwant[0].MessageIDs[i] = messageIDs[0] + } + + validationInspector.Start(signalerCtx) + nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} + startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) + spammer.Start(t) + meshTracer.Start(signalerCtx) + defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) + + // spam the victim with iWant message that contains 60% duplicate message ids more than the configured 50% allowed threshold + spammer.SpamControlMessage(t, victimNode, ctlMsg) + + unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +} + func randomClusterPrefixedTopic() channels.Topic { return channels.Topic(channels.SyncCluster(flow.ChainID(fmt.Sprintf("%d", rand.Uint64())))) } diff --git a/network/netconf/flags.go b/network/netconf/flags.go index 26177846ec8..18bf88e4423 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -59,8 +59,9 @@ const ( metricsInspectorNumberOfWorkers = "gossipsub-rpc-metrics-inspector-workers" metricsInspectorCacheSize = "gossipsub-rpc-metrics-inspector-cache-size" - iwantMaxSampleSize = "gossipsub-rpc-iwant-max-sample-size" - iwantCacheMissThreshold = "gossipsub-rpc-iwant-cache-miss-threshold" + iwantMaxSampleSize = "gossipsub-rpc-iwant-max-sample-size" + iwantCacheMissThreshold = "gossipsub-rpc-iwant-cache-miss-threshold" + iwantDuplicateMsgIDThreshold = "gossipsub-rpc-iwant-duplicate-message-id-threshold" alspDisabled = "alsp-disable-penalty" alspSpamRecordCacheSize = "alsp-spam-record-cache-size" @@ -76,7 +77,7 @@ func AllFlagNames() []string { scoreTracerInterval, gossipSubRPCInspectorNotificationCacheSize, validationInspectorNumberOfWorkers, validationInspectorInspectMessageQueueCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheSize, validationInspectorClusterPrefixedTopicsReceivedCacheDecay, validationInspectorClusterPrefixHardThreshold, ihaveSyncSampleSizePercentage, ihaveAsyncSampleSizePercentage, ihaveMaxSampleSize, metricsInspectorNumberOfWorkers, metricsInspectorCacheSize, alspDisabled, alspSpamRecordCacheSize, alspSpamRecordQueueSize, alspHearBeatInterval, - iwantMaxSampleSize, iwantCacheMissThreshold, + iwantMaxSampleSize, iwantCacheMissThreshold, iwantDuplicateMsgIDThreshold, } } @@ -140,6 +141,7 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { flags.Uint(iwantMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.MaxSampleSize, "max number of iwants to sample when performing validation") flags.Float64(iwantCacheMissThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max number of iwants to sample when performing validation") + flags.Float64(iwantDuplicateMsgIDThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max allowed duplicate message IDs in a single iWant control message") } // rpcInspectorValidationLimits utility func that adds flags for each of the validation limits for each control message type. diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index aa0a7aead81..c90da188d4e 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -248,10 +248,11 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control } tracker := make(duplicateStrTracker) - cacheMisses := float64(0) + cacheMisses := 0 + duplicates := 0 for _, messageID := range iWantMsgIDPool[:sampleSize] { if tracker.isDuplicate(messageID) { - return NewDuplicateFoundErr(fmt.Errorf("duplicate message ID found: %s", messageID)) + duplicates++ } if !c.rpcTracker.WasIHaveRPCSent(messageID) { cacheMisses++ @@ -259,9 +260,14 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control tracker.set(messageID) } - // check cache miss rate - if cacheMisses/float64(sampleSize) > c.config.IWantRPCInspectionConfig.CacheMissThreshold { - return NewIWantCacheMissThresholdErr(cacheMisses, float64(sampleSize), c.config.IWantRPCInspectionConfig.CacheMissThreshold) + // check cache miss threshold + if float64(cacheMisses)/float64(sampleSize) > c.config.IWantRPCInspectionConfig.CacheMissThreshold { + return NewIWantCacheMissThresholdErr(cacheMisses, sampleSize, c.config.IWantRPCInspectionConfig.CacheMissThreshold) + } + + // check duplicate allowed threshold + if float64(duplicates)/float64(sampleSize) > c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold { + return NewIWantDuplicateMsgIDThresholdErr(duplicates, sampleSize, c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold) } return nil diff --git a/network/p2p/inspector/validation/errors.go b/network/p2p/inspector/validation/errors.go index 0b5606d56ea..3695af1d8e2 100644 --- a/network/p2p/inspector/validation/errors.go +++ b/network/p2p/inspector/validation/errors.go @@ -8,20 +8,42 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) +// IWantDuplicateMsgIDThresholdErr indicates that the amount of duplicate message ids exceeds the allowed threshold. +type IWantDuplicateMsgIDThresholdErr struct { + duplicates int + sampleSize uint + threshold float64 +} + +func (e IWantDuplicateMsgIDThresholdErr) Error() string { + return fmt.Sprintf("%d/%d iWant duplicate message ids exceeds the allowed threshold: %f", e.duplicates, e.sampleSize, e.threshold) +} + +// NewIWantDuplicateMsgIDThresholdErr returns a new IWantDuplicateMsgIDThresholdErr. +func NewIWantDuplicateMsgIDThresholdErr(duplicates int, sampleSize uint, threshold float64) IWantDuplicateMsgIDThresholdErr { + return IWantDuplicateMsgIDThresholdErr{duplicates, sampleSize, threshold} +} + +// IsIWantDuplicateMsgIDThresholdErr returns true if an error is IWantDuplicateMsgIDThresholdErr +func IsIWantDuplicateMsgIDThresholdErr(err error) bool { + var e IWantDuplicateMsgIDThresholdErr + return errors.As(err, &e) +} + // IWantCacheMissThresholdErr indicates that the amount of cache misses exceeds the allowed threshold. type IWantCacheMissThresholdErr struct { - misses float64 - totalMessageIDS float64 - threshold float64 + misses int + sampleSize uint + threshold float64 } func (e IWantCacheMissThresholdErr) Error() string { - return fmt.Sprintf("%f/%f iWant cache misses exceeds the allowed threshold: %f", e.misses, e.totalMessageIDS, e.threshold) + return fmt.Sprintf("%d/%d iWant cache misses exceeds the allowed threshold: %f", e.misses, e.sampleSize, e.threshold) } // NewIWantCacheMissThresholdErr returns a new IWantCacheMissThresholdErr. -func NewIWantCacheMissThresholdErr(misses, totalMessageIDS, threshold float64) IWantCacheMissThresholdErr { - return IWantCacheMissThresholdErr{misses, totalMessageIDS, threshold} +func NewIWantCacheMissThresholdErr(misses int, sampleSize uint, threshold float64) IWantCacheMissThresholdErr { + return IWantCacheMissThresholdErr{misses, sampleSize, threshold} } // IsIWantCacheMissThresholdErr returns true if an error is IWantCacheMissThresholdErr diff --git a/network/p2p/p2pconf/gossipsub_rpc_inspectors.go b/network/p2p/p2pconf/gossipsub_rpc_inspectors.go index 0c42e2b0587..67f75af75db 100644 --- a/network/p2p/p2pconf/gossipsub_rpc_inspectors.go +++ b/network/p2p/p2pconf/gossipsub_rpc_inspectors.go @@ -54,6 +54,8 @@ type IWantRPCInspectionConfig struct { MaxSampleSize uint `validate:"gt=0" mapstructure:"gossipsub-rpc-iwant-max-sample-size"` // CacheMissThreshold the threshold of missing corresponding iHave messages for iWant messages received before an invalid control message notification is disseminated. CacheMissThreshold float64 `validate:"gt=0" mapstructure:"gossipsub-rpc-iwant-cache-miss-threshold"` + // DuplicateMsgIDThreshold maximum allowed duplicate message IDs in a single iWant control message. + DuplicateMsgIDThreshold float64 `validate:"gt=0" mapstructure:"gossipsub-rpc-iwant-duplicate-message-id-threshold"` } // half of the sample From 730b46e1f2f6454bd4ab3a7c96ec56c25b3300ac Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 22 Aug 2023 17:42:41 +0100 Subject: [PATCH 573/815] Add backend transaction tests annotations --- .../rpc/backend/backend_transactions_test.go | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 9d7e12f5024..3b1464a3e13 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -22,7 +22,7 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { +func (suite *Suite) withPreConfiguredState(f func(snap protocol.Snapshot)) { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) util.RunWithFullProtocolState(suite.T(), rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) { @@ -54,8 +54,9 @@ func (suite *Suite) WithPreConfiguredState(f func(snap protocol.Snapshot)) { } +// TestGetTransactionResultReturnsUnknown returns unknown result when tx not found func (suite *Suite) TestGetTransactionResultReturnsUnknown() { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + suite.withPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -88,8 +89,9 @@ func (suite *Suite) TestGetTransactionResultReturnsUnknown() { }) } +// TestGetTransactionResultReturnsTransactionError returns error from transaction storage func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + suite.withPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -128,8 +130,9 @@ func (suite *Suite) TestGetTransactionResultReturnsTransactionError() { }) } +// TestGetTransactionResultReturnsValidTransactionResultFromHistoricNode tests lookup in historic nodes func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHistoricNode() { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { + suite.withPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -174,8 +177,8 @@ func (suite *Suite) TestGetTransactionResultReturnsValidTransactionResultFromHis }) } -func (suite *Suite) WithGetTransactionCachingTestSetup(f func(b *flow.Block, t *flow.Transaction)) { - suite.WithPreConfiguredState(func(snap protocol.Snapshot) { +func (suite *Suite) withGetTransactionCachingTestSetup(f func(b *flow.Block, t *flow.Transaction)) { + suite.withPreConfiguredState(func(snap protocol.Snapshot) { block := unittest.BlockFixture() tbody := unittest.TransactionBodyFixture() tx := unittest.TransactionFixture() @@ -191,8 +194,9 @@ func (suite *Suite) WithGetTransactionCachingTestSetup(f func(b *flow.Block, t * }) } +// TestGetTransactionResultFromCache get historic transaction result from cache func (suite *Suite) TestGetTransactionResultFromCache() { - suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { + suite.withGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { transactionResultResponse := access.TransactionResultResponse{ Status: entities.TransactionStatus_EXECUTED, StatusCode: uint32(entities.TransactionStatus_EXECUTED), @@ -234,8 +238,9 @@ func (suite *Suite) TestGetTransactionResultFromCache() { }) } +// TestGetTransactionResultCacheNonExistent tests caches non existing result func (suite *Suite) TestGetTransactionResultCacheNonExistent() { - suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { + suite.withGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. On("GetTransactionResult", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*access.GetTransactionRequest")). @@ -278,8 +283,9 @@ func (suite *Suite) TestGetTransactionResultCacheNonExistent() { }) } +// TestGetTransactionResultUnknownFromCache retrive unknown result from cache func (suite *Suite) TestGetTransactionResultUnknownFromCache() { - suite.WithGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { + suite.withGetTransactionCachingTestSetup(func(block *flow.Block, tx *flow.Transaction) { suite.historicalAccessClient. On("GetTransactionResult", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*access.GetTransactionRequest")). Return(nil, status.Errorf(codes.NotFound, "no known transaction with ID %s", tx.ID())).Once() From 38d5caf567e2fdffae28da82f5cb749b747a0d59 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 12:44:58 -0400 Subject: [PATCH 574/815] update threshold comparisons --- .../control_message_validation_inspector.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index c90da188d4e..6d1d4641e06 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -249,27 +249,26 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control tracker := make(duplicateStrTracker) cacheMisses := 0 + numOfAllowedCacheMisses := float64(sampleSize) * c.config.IWantRPCInspectionConfig.CacheMissThreshold duplicates := 0 + numOfAllowedDuplicates := float64(sampleSize) * c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold for _, messageID := range iWantMsgIDPool[:sampleSize] { + // check duplicate allowed threshold if tracker.isDuplicate(messageID) { duplicates++ + if float64(duplicates) > numOfAllowedDuplicates { + return NewIWantDuplicateMsgIDThresholdErr(duplicates, sampleSize, c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold) + } } + // check cache miss threshold if !c.rpcTracker.WasIHaveRPCSent(messageID) { cacheMisses++ + if float64(cacheMisses) > numOfAllowedCacheMisses { + return NewIWantCacheMissThresholdErr(cacheMisses, sampleSize, c.config.IWantRPCInspectionConfig.CacheMissThreshold) + } } tracker.set(messageID) } - - // check cache miss threshold - if float64(cacheMisses)/float64(sampleSize) > c.config.IWantRPCInspectionConfig.CacheMissThreshold { - return NewIWantCacheMissThresholdErr(cacheMisses, sampleSize, c.config.IWantRPCInspectionConfig.CacheMissThreshold) - } - - // check duplicate allowed threshold - if float64(duplicates)/float64(sampleSize) > c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold { - return NewIWantDuplicateMsgIDThresholdErr(duplicates, sampleSize, c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold) - } - return nil } From 704a20d8d5fa4204ee2c1cfbc4521d0febca6fa3 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 12:45:05 -0400 Subject: [PATCH 575/815] Update network/p2p/inspector/validation/control_message_validation_inspector.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- .../validation/control_message_validation_inspector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index aa0a7aead81..6cb9052cc07 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -209,7 +209,7 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e // Returns: // - DuplicateFoundErr: if there are any duplicate message ids found in across any of the iWants. // - IWantCacheMissThresholdErr: if the rate of cache misses exceeds the configured allowed threshold. -// - error: if any error occurs while sampling the iWants +// - error: if any error occurs while sampling the iWants, all returned errors are benign and should not cause the node to crash. func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.ControlIWant) error { if len(iWants) == 0 { return nil From 26a7159730373827ec2e928009878a299ec0113f Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 12:45:56 -0400 Subject: [PATCH 576/815] Update network/p2p/inspector/validation/errors.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/p2p/inspector/validation/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/errors.go b/network/p2p/inspector/validation/errors.go index 0b5606d56ea..fd56d48636b 100644 --- a/network/p2p/inspector/validation/errors.go +++ b/network/p2p/inspector/validation/errors.go @@ -8,7 +8,7 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" ) -// IWantCacheMissThresholdErr indicates that the amount of cache misses exceeds the allowed threshold. +// IWantCacheMissThresholdErr indicates that the amount of cache misses exceeds the allowed threshold, it indicates that the libp2p node has received an RPC with the majority of iWants that has never been advertised originally by the current node with an iHave. type IWantCacheMissThresholdErr struct { misses float64 totalMessageIDS float64 From c815a126577a0f91a4c7547ea5e3b3db2a97ae76 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 12:46:42 -0400 Subject: [PATCH 577/815] Update network/p2p/inspector/validation/errors.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/p2p/inspector/validation/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/errors.go b/network/p2p/inspector/validation/errors.go index fd56d48636b..e3bff2b5edb 100644 --- a/network/p2p/inspector/validation/errors.go +++ b/network/p2p/inspector/validation/errors.go @@ -10,7 +10,7 @@ import ( // IWantCacheMissThresholdErr indicates that the amount of cache misses exceeds the allowed threshold, it indicates that the libp2p node has received an RPC with the majority of iWants that has never been advertised originally by the current node with an iHave. type IWantCacheMissThresholdErr struct { - misses float64 + cacheMissCount float64 // total iwant cache misses totalMessageIDS float64 threshold float64 } From d9c7970098f3756a6149cd6b6d7044ddccb18a09 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 13:09:40 -0400 Subject: [PATCH 578/815] add round trip tests for new errors --- .../p2p/inspector/validation/errors_test.go | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/network/p2p/inspector/validation/errors_test.go b/network/p2p/inspector/validation/errors_test.go index bb57af601c0..41fe365188b 100644 --- a/network/p2p/inspector/validation/errors_test.go +++ b/network/p2p/inspector/validation/errors_test.go @@ -80,3 +80,35 @@ func TestErrDuplicateTopicRoundTrip(t *testing.T) { dummyErr := fmt.Errorf("dummy error") assert.False(t, IsDuplicateFoundErr(dummyErr), "IsDuplicateFoundErr should return false for non-DuplicateFoundErr error") } + +// TestIWantCacheMissThresholdErrRoundTrip ensures correct error formatting for IWantCacheMissThresholdErr. +func TestIWantCacheMissThresholdErrRoundTrip(t *testing.T) { + err := NewIWantCacheMissThresholdErr(5, 10, .4) + + // tests the error message formatting. + expectedErrMsg := "5/10 iWant cache misses exceeds the allowed threshold: 0.400000" + assert.Equal(t, expectedErrMsg, err.Error(), "the error message should be correctly formatted") + + // tests the IsIWantCacheMissThresholdErr function. + assert.True(t, IsIWantCacheMissThresholdErr(err), "IsIWantCacheMissThresholdErr should return true for IWantCacheMissThresholdErr error") + + // test IsIWantCacheMissThresholdErr with a different error type. + dummyErr := fmt.Errorf("dummy error") + assert.False(t, IsIWantCacheMissThresholdErr(dummyErr), "IsIWantCacheMissThresholdErr should return false for non-IWantCacheMissThresholdErr error") +} + +// TestIWantDuplicateMsgIDThresholdErrRoundTrip ensures correct error formatting for IWantDuplicateMsgIDThresholdErr. +func TestIWantDuplicateMsgIDThresholdErrRoundTrip(t *testing.T) { + err := NewIWantDuplicateMsgIDThresholdErr(5, 10, .4) + + // tests the error message formatting. + expectedErrMsg := "5/10 iWant duplicate message ids exceeds the allowed threshold: 0.400000" + assert.Equal(t, expectedErrMsg, err.Error(), "the error message should be correctly formatted") + + // tests the IsIWantDuplicateMsgIDThresholdErr function. + assert.True(t, IsIWantDuplicateMsgIDThresholdErr(err), "IsIWantDuplicateMsgIDThresholdErr should return true for IWantDuplicateMsgIDThresholdErr error") + + // test IsDuplicateFoundErr with a different error type. + dummyErr := fmt.Errorf("dummy error") + assert.False(t, IsIWantDuplicateMsgIDThresholdErr(dummyErr), "IsIWantDuplicateMsgIDThresholdErr should return false for non-IWantDuplicateMsgIDThresholdErr error") +} From bb87e90d2d932bb3053fe5aebdcce9e693491874 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 13:13:35 -0400 Subject: [PATCH 579/815] add happy path tests for util dup str tracker --- network/p2p/inspector/validation/utils_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 network/p2p/inspector/validation/utils_test.go diff --git a/network/p2p/inspector/validation/utils_test.go b/network/p2p/inspector/validation/utils_test.go new file mode 100644 index 00000000000..14015dcc621 --- /dev/null +++ b/network/p2p/inspector/validation/utils_test.go @@ -0,0 +1,16 @@ +package validation + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestDuplicateStrTracker ensures duplicateStrTracker performs simple set and returns expected result for duplicate str. +func TestDuplicateStrTracker(t *testing.T) { + tracker := make(duplicateStrTracker) + s := "hello world" + require.False(t, tracker.isDuplicate(s)) + tracker.set(s) + require.True(t, tracker.isDuplicate(s)) +} From e3f7fb89404951b8fcdc5d364cf72d943f48cfe1 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 13:16:09 -0400 Subject: [PATCH 580/815] Update network/p2p/tracer/internal/rpc_sent_tracker.go Co-authored-by: Yahya Hassanzadeh, Ph.D. --- network/p2p/tracer/internal/rpc_sent_tracker.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index db3dddc350b..0e6381db814 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -117,10 +117,11 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { case len(work.rpc.GetControl().GetIhave()) > 0: iHave := work.rpc.GetControl().GetIhave() numOfMessageIdsTracked := t.iHaveRPCSent(iHave) - t.updateLastHighestIHaveRPCSize(int64(numOfMessageIdsTracked)) - t.logger.Info(). + lastHighestIHaveCount := t.updateLastHighestIHaveRPCSize(int64(numOfMessageIdsTracked)) + t.logger.Debug(). Int("num_of_ihaves", len(iHave)). Int("num_of_message_ids", numOfMessageIdsTracked). + Int("last_highest_ihave_count", lastHighestIHaveCount). Msg(iHaveRPCTrackedLog) } return nil From 7721c00f37529994876508a43f1f81eff8c28956 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 13:41:44 -0400 Subject: [PATCH 581/815] distribute notif only for expected errors --- .../control_message_validation_inspector.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 1bfc551c6ae..768c9c36711 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -421,7 +421,18 @@ func (c *ControlMsgValidationInspector) processInspectMsgReq(req *InspectMsgRequ switch req.validationConfig.ControlMsg { case p2pmsg.CtrlMsgIWant: if err := c.inspectIWant(req.ctrlMsg.GetIwant()); err != nil { - c.logAndDistributeAsyncInspectErr(req, count, err) + if IsIWantCacheMissThresholdErr(err) || IsIWantDuplicateMsgIDThresholdErr(err) { + c.logAndDistributeAsyncInspectErr(req, count, err) + return nil + } + c.logger. + Error(). + Err(err). + Bool(logging.KeySuspicious, true). + Str("peer_id", req.Peer.String()). + Str("ctrl_msg_type", p2pmsg.CtrlMsgIWant.String()). + Uint64("ctrl_msg_count", count). + Msg("unexpected error encountered while performing iwant validation") } return nil } From f8558f2989fde225d1a4d39c67bcedf56620d80c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 10:43:55 -0700 Subject: [PATCH 582/815] removes redundant test --- network/p2p/scoring/app_score_test.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 97b9310a595..1cebe6f710c 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -122,20 +122,6 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi } -// TestNetworkPartitionWithNoHonestPeerScoringInFullTopology is part one of testing pushing access nodes to the edges of the network. -// This test proves that if access nodes are NOT pushed to the edge of network, a malicious majority of them can -// partition the network by disconnecting honest nodes from each other even when the network topology is a complete graph (i.e., full topology). -func TestNetworkPartitionWithNoHonestPeerScoringInFullTopology(t *testing.T) { - total := 50 - for i := 0; i < total; i++ { - // false means no honest peer scoring. - if !testGossipConnectivityUnderNetworkPartition(t, false) { - return // partition is successful - } - } - require.Fail(t, "expected at least one network partition") -} - // testGossipConnectivityUnderNetworkPartition tests that whether two honest nodes are in each others topic mesh on GossipSub // when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. // If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. From 48b8fa1847308033c03933b3b9811a8334f2e104 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 22 Aug 2023 13:44:26 -0400 Subject: [PATCH 583/815] Update rpc_sent_tracker.go --- network/p2p/tracer/internal/rpc_sent_tracker.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/p2p/tracer/internal/rpc_sent_tracker.go b/network/p2p/tracer/internal/rpc_sent_tracker.go index 0e6381db814..f2c8183ed8f 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker.go @@ -121,13 +121,13 @@ func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { t.logger.Debug(). Int("num_of_ihaves", len(iHave)). Int("num_of_message_ids", numOfMessageIdsTracked). - Int("last_highest_ihave_count", lastHighestIHaveCount). + Int64("last_highest_ihave_count", lastHighestIHaveCount). Msg(iHaveRPCTrackedLog) } return nil } -func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { +func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) int64 { t.Lock() defer t.Unlock() if t.lastSize < size || time.Since(t.lastUpdate) > t.lastHighestIHaveRPCSizeResetInterval { @@ -135,6 +135,7 @@ func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) { t.lastSize = size t.lastUpdate = time.Now() } + return t.lastSize } // iHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. From ac3680188180e322bdb395683eecd4734f985791 Mon Sep 17 00:00:00 2001 From: Kay-Zee Date: Tue, 22 Aug 2023 11:00:33 -0700 Subject: [PATCH 584/815] Change queue.String() to iterate through children instead of queue existence map --- module/mempool/queue/queue.go | 25 ++++++++++++++++++------- module/mempool/queue/queue_test.go | 7 +++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/module/mempool/queue/queue.go b/module/mempool/queue/queue.go index 23f5cf3cf62..13b5969b787 100644 --- a/module/mempool/queue/queue.go +++ b/module/mempool/queue/queue.go @@ -171,16 +171,27 @@ func (q *Queue) Dismount() (Blockify, []*Queue) { } func (q *Queue) String() string { - var builder strings.Builder + builder := new(strings.Builder) builder.WriteString(fmt.Sprintf("Header: %v\n", q.Head.Item.ID())) builder.WriteString(fmt.Sprintf("Highest: %v\n", q.Highest.Item.ID())) builder.WriteString(fmt.Sprintf("Size: %v, Height: %v\n", q.Size(), q.Height())) - for _, node := range q.Nodes { - builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: %v)\n", - node.Item.Height(), - node.Item.ID(), - len(node.Children), + builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: %v)\n", + q.Head.Item.Height(), + q.Head.Item.ID(), + len(q.Head.Children), + )) + printChildren(builder, q.Head, "") + return builder.String() +} + +func printChildren(builder *strings.Builder, n *Node, prefix string) { + for _, child := range n.Children { + builder.WriteString(fmt.Sprintf("%s Node(height: %v): %v (children: %v)\n", + prefix+"|-", + child.Item.Height(), + child.Item.ID(), + len(child.Children), )) + printChildren(builder, child, prefix+" ") } - return builder.String() } diff --git a/module/mempool/queue/queue_test.go b/module/mempool/queue/queue_test.go index 22176134c6c..b1b69205806 100644 --- a/module/mempool/queue/queue_test.go +++ b/module/mempool/queue/queue_test.go @@ -292,7 +292,6 @@ func TestQueue(t *testing.T) { //}) t.Run("String()", func(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "flakey, because String will iterate through a map, which is not determinsitic") // a <- c <- d <- f queue := NewQueue(a) stored, _ := queue.TryAdd(c) @@ -306,9 +305,9 @@ func TestQueue(t *testing.T) { builder.WriteString(fmt.Sprintf("Highest: %v\n", f.ID())) builder.WriteString("Size: 4, Height: 3\n") builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: 1)\n", a.Height(), a.ID())) - builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: 1)\n", c.Height(), c.ID())) - builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: 1)\n", d.Height(), d.ID())) - builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: 0)\n", f.Height(), f.ID())) + builder.WriteString(fmt.Sprintf("|- Node(height: %v): %v (children: 1)\n", c.Height(), c.ID())) + builder.WriteString(fmt.Sprintf(" |- Node(height: %v): %v (children: 1)\n", d.Height(), d.ID())) + builder.WriteString(fmt.Sprintf(" |- Node(height: %v): %v (children: 0)\n", f.Height(), f.ID())) require.Equal(t, builder.String(), queue.String()) }) } From ab52153c80c036b7bb13129212a3b66d190a995d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 11:07:15 -0700 Subject: [PATCH 585/815] separates bft tests into sub-packages and sub-ci jobs --- .github/workflows/ci.yml | 4 +++- .github/workflows/flaky-test-monitor.yml | 8 ++++++-- bors.toml | 4 +++- integration/Makefile | 13 +++++++++---- .../dummy_orchestrator.go | 2 +- .../{passthrough => framework}/passthrough_test.go | 2 +- .../tests/bft/{passthrough => framework}/suite.go | 2 +- .../{ => gossipsub}/topicvalidator/orchestrator.go | 0 .../bft/{ => gossipsub}/topicvalidator/suite.go | 0 .../topicvalidator/topic_validator_test.go | 0 .../blocklist/admin_command_blocklist_test.go | 0 .../{admin => protocol}/blocklist/orchestrator.go | 0 .../bft/{admin => protocol}/blocklist/suite.go | 0 .../tests/bft/{ => protocol}/wintermute/suite.go | 0 .../{ => protocol}/wintermute/wintermute_test.go | 0 15 files changed, 24 insertions(+), 11 deletions(-) rename integration/tests/bft/{passthrough => framework}/dummy_orchestrator.go (99%) rename integration/tests/bft/{passthrough => framework}/passthrough_test.go (99%) rename integration/tests/bft/{passthrough => framework}/suite.go (99%) rename integration/tests/bft/{ => gossipsub}/topicvalidator/orchestrator.go (100%) rename integration/tests/bft/{ => gossipsub}/topicvalidator/suite.go (100%) rename integration/tests/bft/{ => gossipsub}/topicvalidator/topic_validator_test.go (100%) rename integration/tests/bft/{admin => protocol}/blocklist/admin_command_blocklist_test.go (100%) rename integration/tests/bft/{admin => protocol}/blocklist/orchestrator.go (100%) rename integration/tests/bft/{admin => protocol}/blocklist/suite.go (100%) rename integration/tests/bft/{ => protocol}/wintermute/suite.go (100%) rename integration/tests/bft/{ => protocol}/wintermute/wintermute_test.go (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be813cbb920..2d510a8d196 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,7 +192,9 @@ jobs: matrix: make: - make -C integration access-tests - - make -C integration bft-tests + - make -C integration bft-framework-tests + - make -C integration bft-protocol-tests + - make -C integration bft-gossipsub-tests - make -C integration collection-tests - make -C integration consensus-tests - make -C integration epochs-cohort1-tests diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index b1c429b3e6f..ec0852485e2 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -129,8 +129,12 @@ jobs: include: - target: access-tests test_category: integration-access - - target: bft-tests - test_category: integration-bft + - target: bft-protocol-tests + test_category: integration-bft-protocol + - target: bft-framework-tests + test_category: integration-bft-framework + - target: bft-gossipsub-tests + test_category: integration-bft-gossipsub - target: collection-tests test_category: integration-collection - target: consensus-tests diff --git a/bors.toml b/bors.toml index 9f059e1c0b2..4366b20e275 100644 --- a/bors.toml +++ b/bors.toml @@ -17,7 +17,9 @@ status = [ "Unit Tests (utils)", "Unit Tests (others)", "Integration Tests (make -C integration access-tests)", - "Integration Tests (make -C integration bft-tests)", + "Integration Tests (make -C integration bft-framework-tests)", + "Integration Tests (make -C integration bft-gossipsub-tests)", + "Integration Tests (make -C integration bft-protocol-tests)", "Integration Tests (make -C integration collection-tests)", "Integration Tests (make -C integration consensus-tests)", "Integration Tests (make -C integration epochs-cohort1-tests)", diff --git a/integration/Makefile b/integration/Makefile index c39ba8d359f..3a28df6c202 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -72,9 +72,14 @@ network-tests: go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/network/... # BFT tests need to be run sequentially (-p 1) due to interference between different Docker networks when tests are run in parallel -.PHONY: bft-tests -bft-tests: - go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/... -p 1 - +.PHONY: bft-framework-tests +bft-framework-tests: + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/framework/... -p 1 +.PHONY: bft-protocol-tests +bft-protocol-tests: + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/protocol/... -p 1 +.PHONY: bft-gossipsub-tests +bft-gossipsub-tests: + go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/gossipsub/... -p 1 ############################################################################################ diff --git a/integration/tests/bft/passthrough/dummy_orchestrator.go b/integration/tests/bft/framework/dummy_orchestrator.go similarity index 99% rename from integration/tests/bft/passthrough/dummy_orchestrator.go rename to integration/tests/bft/framework/dummy_orchestrator.go index 36143988c7d..9873a3d1689 100644 --- a/integration/tests/bft/passthrough/dummy_orchestrator.go +++ b/integration/tests/bft/framework/dummy_orchestrator.go @@ -1,4 +1,4 @@ -package passthrough +package framework import ( "errors" diff --git a/integration/tests/bft/passthrough/passthrough_test.go b/integration/tests/bft/framework/passthrough_test.go similarity index 99% rename from integration/tests/bft/passthrough/passthrough_test.go rename to integration/tests/bft/framework/passthrough_test.go index eaf04b3923c..f9a436de782 100644 --- a/integration/tests/bft/passthrough/passthrough_test.go +++ b/integration/tests/bft/framework/passthrough_test.go @@ -1,4 +1,4 @@ -package passthrough +package framework import ( "testing" diff --git a/integration/tests/bft/passthrough/suite.go b/integration/tests/bft/framework/suite.go similarity index 99% rename from integration/tests/bft/passthrough/suite.go rename to integration/tests/bft/framework/suite.go index d735489de8b..330099f4d17 100644 --- a/integration/tests/bft/passthrough/suite.go +++ b/integration/tests/bft/framework/suite.go @@ -1,4 +1,4 @@ -package passthrough +package framework import ( "github.com/rs/zerolog" diff --git a/integration/tests/bft/topicvalidator/orchestrator.go b/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go similarity index 100% rename from integration/tests/bft/topicvalidator/orchestrator.go rename to integration/tests/bft/gossipsub/topicvalidator/orchestrator.go diff --git a/integration/tests/bft/topicvalidator/suite.go b/integration/tests/bft/gossipsub/topicvalidator/suite.go similarity index 100% rename from integration/tests/bft/topicvalidator/suite.go rename to integration/tests/bft/gossipsub/topicvalidator/suite.go diff --git a/integration/tests/bft/topicvalidator/topic_validator_test.go b/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go similarity index 100% rename from integration/tests/bft/topicvalidator/topic_validator_test.go rename to integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go diff --git a/integration/tests/bft/admin/blocklist/admin_command_blocklist_test.go b/integration/tests/bft/protocol/blocklist/admin_command_blocklist_test.go similarity index 100% rename from integration/tests/bft/admin/blocklist/admin_command_blocklist_test.go rename to integration/tests/bft/protocol/blocklist/admin_command_blocklist_test.go diff --git a/integration/tests/bft/admin/blocklist/orchestrator.go b/integration/tests/bft/protocol/blocklist/orchestrator.go similarity index 100% rename from integration/tests/bft/admin/blocklist/orchestrator.go rename to integration/tests/bft/protocol/blocklist/orchestrator.go diff --git a/integration/tests/bft/admin/blocklist/suite.go b/integration/tests/bft/protocol/blocklist/suite.go similarity index 100% rename from integration/tests/bft/admin/blocklist/suite.go rename to integration/tests/bft/protocol/blocklist/suite.go diff --git a/integration/tests/bft/wintermute/suite.go b/integration/tests/bft/protocol/wintermute/suite.go similarity index 100% rename from integration/tests/bft/wintermute/suite.go rename to integration/tests/bft/protocol/wintermute/suite.go diff --git a/integration/tests/bft/wintermute/wintermute_test.go b/integration/tests/bft/protocol/wintermute/wintermute_test.go similarity index 100% rename from integration/tests/bft/wintermute/wintermute_test.go rename to integration/tests/bft/protocol/wintermute/wintermute_test.go From df7c137d7a71ce83d99a656dcee2e31ae80c59fc Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 12:09:02 -0700 Subject: [PATCH 586/815] adds bft test ci command --- integration/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration/Makefile b/integration/Makefile index 3a28df6c202..8869fd8f56f 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -82,4 +82,6 @@ bft-protocol-tests: bft-gossipsub-tests: go test $(if $(VERBOSE),-v,) $(RACE_FLAG) $(if $(JSON_OUTPUT),-json,) $(if $(NUM_RUNS),-count $(NUM_RUNS),) -tags relic ./tests/bft/gossipsub/... -p 1 +.PHONY: bft-tests +bft-tests: bft-framework-tests bft-protocol-tests bft-gossipsub-tests ############################################################################################ From cfb85bdb3c63f3871e703c2d36b7dc9d699c08c7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 12:54:06 -0700 Subject: [PATCH 587/815] adds test for node config filter --- integration/testnet/node_config.go | 9 +++-- integration/testnet/node_config_test.go | 47 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 integration/testnet/node_config_test.go diff --git a/integration/testnet/node_config.go b/integration/testnet/node_config.go index e8b28fded58..076cd11bc4e 100644 --- a/integration/testnet/node_config.go +++ b/integration/testnet/node_config.go @@ -32,12 +32,15 @@ type NodeConfig struct { func (n NodeConfigs) Filter(filters ...NodeConfigFilter) NodeConfigs { nodeConfigs := make(NodeConfigs, 0) for _, config := range n { - filter := false + passedAllFilters := true for _, f := range filters { - filter = f(config) + if !f(config) { + passedAllFilters = false + break + } } - if filter { + if passedAllFilters { nodeConfigs = append(nodeConfigs, config) } } diff --git a/integration/testnet/node_config_test.go b/integration/testnet/node_config_test.go new file mode 100644 index 00000000000..44d5ed993c2 --- /dev/null +++ b/integration/testnet/node_config_test.go @@ -0,0 +1,47 @@ +package testnet_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/onflow/flow-go/integration/testnet" + "github.com/onflow/flow-go/model/flow" +) + +func TestFilter(t *testing.T) { + t.Run("filters by role", func(t *testing.T) { + configs := testnet.NewNodeConfigSet(5, flow.RoleAccess) + + filters := configs.Filter(func(n testnet.NodeConfig) bool { return n.Role == flow.RoleAccess }) + + for _, config := range filters { + assert.Equal(t, flow.RoleAccess, config.Role) + } + }) + + t.Run("filters by multiple conditions", func(t *testing.T) { + configs := testnet.NodeConfigs{ + testnet.NewNodeConfig(flow.RoleAccess, testnet.WithDebugImage(true)), + testnet.NewNodeConfig(flow.RoleExecution, testnet.WithDebugImage(true)), + } + + filters := configs.Filter( + func(n testnet.NodeConfig) bool { return n.Role == flow.RoleAccess }, + func(n testnet.NodeConfig) bool { + return n.Debug + }, + ) + + assert.Len(t, filters, 1) // should exclude execution node + assert.True(t, filters[0].Debug) + }) + + t.Run("no matching filters", func(t *testing.T) { + configs := testnet.NewNodeConfigSet(5, flow.RoleConsensus) + + filters := configs.Filter(func(n testnet.NodeConfig) bool { return n.Role == flow.RoleAccess }) + + assert.Len(t, filters, 0) + }) +} From 1f42d81e7c90947fab2c254f3e1aad7f3f1b7bae Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 12:54:31 -0700 Subject: [PATCH 588/815] removes redundant nodes --- integration/tests/bft/base_suite.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/integration/tests/bft/base_suite.go b/integration/tests/bft/base_suite.go index 2ce0d7f6fdc..19d3feeafe0 100644 --- a/integration/tests/bft/base_suite.go +++ b/integration/tests/bft/base_suite.go @@ -32,16 +32,16 @@ type BaseSuite struct { // Ghost returns a client to interact with the Ghost node on testnet. func (b *BaseSuite) Ghost() *client.GhostClient { - client, err := b.Net.ContainerByID(b.GhostID).GhostClient() + c, err := b.Net.ContainerByID(b.GhostID).GhostClient() require.NoError(b.T(), err, "could not get ghost client") - return client + return c } // AccessClient returns a client to interact with the access node api on testnet. func (b *BaseSuite) AccessClient() *testnet.Client { - client, err := b.Net.ContainerByName(testnet.PrimaryAN).TestnetClient() + c, err := b.Net.ContainerByName(testnet.PrimaryAN).TestnetClient() require.NoError(b.T(), err, "could not get access client") - return client + return c } // SetupSuite sets up node configs to run a bare minimum Flow network to function correctly. @@ -51,11 +51,10 @@ func (b *BaseSuite) SetupSuite() { // setup access nodes b.NodeConfigs = append(b.NodeConfigs, testnet.NewNodeConfig(flow.RoleAccess, testnet.WithLogLevel(zerolog.FatalLevel)), - testnet.NewNodeConfig(flow.RoleAccess, testnet.WithLogLevel(zerolog.FatalLevel)), ) // setup consensus nodes - for _, nodeID := range unittest.IdentifierListFixture(4) { + for _, nodeID := range unittest.IdentifierListFixture(3) { nodeConfig := testnet.NewNodeConfig(flow.RoleConsensus, testnet.WithID(nodeID), testnet.WithLogLevel(zerolog.FatalLevel), @@ -69,7 +68,6 @@ func (b *BaseSuite) SetupSuite() { // setup verification nodes b.NodeConfigs = append(b.NodeConfigs, testnet.NewNodeConfig(flow.RoleVerification, testnet.WithLogLevel(zerolog.FatalLevel)), - testnet.NewNodeConfig(flow.RoleVerification, testnet.WithLogLevel(zerolog.FatalLevel)), ) // setup execution nodes From 69d5000c7b6e27d236c1d18289201ea0bf0f417d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 12:57:04 -0700 Subject: [PATCH 589/815] reduces the number of messages sent on topic validation test --- .../tests/bft/gossipsub/topicvalidator/orchestrator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go b/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go index fac5f2a672a..d7ed9bfcf8d 100644 --- a/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go +++ b/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go @@ -21,10 +21,10 @@ const ( // The numOfAuthorizedEvents allows us to wait for a certain number of authorized messages to be received, this should // give the network enough time to process the unauthorized messages. This ensures us that the unauthorized messages // were indeed dropped and not unprocessed. - numOfAuthorizedEvents = 100 + numOfAuthorizedEvents = 5 // numOfUnauthorizedEvents the number of unauthorized events per type to send by the test orchestrator. - numOfUnauthorizedEvents = 10 + numOfUnauthorizedEvents = 5 ) // Orchestrator represents an insecure.AttackOrchestrator track incoming unauthorized messages and authorized messages received by victim nodes. From 804909189ef44378744b929bed6b3e6804090f89 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:18:57 -0700 Subject: [PATCH 590/815] changes block list to disallow list --- integration/tests/bft/protocol/README.md | 0 .../admin_command_disallowlisting_test.go} | 51 +++++++++++-------- .../orchestrator.go | 0 .../{blocklist => disallowlisting}/suite.go | 0 4 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 integration/tests/bft/protocol/README.md rename integration/tests/bft/protocol/{blocklist/admin_command_blocklist_test.go => disallowlisting/admin_command_disallowlisting_test.go} (50%) rename integration/tests/bft/protocol/{blocklist => disallowlisting}/orchestrator.go (100%) rename integration/tests/bft/protocol/{blocklist => disallowlisting}/suite.go (100%) diff --git a/integration/tests/bft/protocol/README.md b/integration/tests/bft/protocol/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration/tests/bft/protocol/blocklist/admin_command_blocklist_test.go b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go similarity index 50% rename from integration/tests/bft/protocol/blocklist/admin_command_blocklist_test.go rename to integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go index d0245e40e19..64497f82ae0 100644 --- a/integration/tests/bft/protocol/blocklist/admin_command_blocklist_test.go +++ b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go @@ -1,4 +1,4 @@ -package blocklist +package disallowlisting import ( "fmt" @@ -11,39 +11,44 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -type AdminCommandBlockListTestSuite struct { +type AdminCommandDisallowListTestSuite struct { Suite } -func TestAdminCommandBlockList(t *testing.T) { - suite.Run(t, new(AdminCommandBlockListTestSuite)) +func TestAdminCommandDisallowList(t *testing.T) { + suite.Run(t, new(AdminCommandDisallowListTestSuite)) } -// TestAdminCommandBlockList ensures that the blocklist admin command works as expected. When a node is blocked via the admin blocklist command +// TestAdminCommandDisallowList ensures that the disallow-list admin command works as expected. When a node is blocked via the admin disallow-list command // the libp2p connection to that node should be pruned immediately and the connection gater should start to block incoming connection requests. This test // sets up 2 corrupt nodes a sender and receiver, the sender will send messages before and after being blocked by the receiver node via -// the blocklist admin command. The receiver node is expected to receive messages like normal before blocking the sender, after blocking the sender -// it should not receive any messages. The reason this test is conducted via two corrupt nodes is to empower the test logic to command one (corrupt) node to send a message and to examine the other (corrupt) node to check whether it has received the message. -func (a *AdminCommandBlockListTestSuite) TestAdminCommandBlockList() { +// the disallow-list admin command. The receiver node is expected to receive messages like normal before disallow-listing the sender, after disallow-listing the sender +// it should not receive any messages. The reason this test is conducted via two corrupt nodes is to empower the test logic to command one (corrupt) node to send a message and +// to examine the other (corrupt) node to check whether it has received the message. +func (a *AdminCommandDisallowListTestSuite) TestAdminCommandDisallowList() { // send some authorized messages indicating the network is working as expected a.Orchestrator.sendAuthorizedMsgs(a.T()) unittest.RequireReturnsBefore(a.T(), a.Orchestrator.authorizedEventsReceivedWg.Wait, 5*time.Second, "could not receive authorized messages on time") // messages with correct message signatures are expected to always pass libp2p signature verification and be delivered to the victim EN. - require.Equal(a.T(), int64(numOfAuthorizedEvents), a.Orchestrator.authorizedEventsReceived.Load(), fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, a.Orchestrator.expectedBlockedEventsReceived.Load())) - - // after blocking node a.senderVN we should not receive any messages from that node. - // This is an asynchronous process with a number of sub processes involved including not limited to ; - // - submitting request to admin server for node to be blocked - // - node block list must update - // - peer manager needs to prune the connection - // - connection gater will start blocking incoming connections - //We wait for 500 milliseconds to reduce the small chance of a race condition between the time a node is blocked + require.Equal( + a.T(), + int64(numOfAuthorizedEvents), + a.Orchestrator.authorizedEventsReceived.Load(), + fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, a.Orchestrator.expectedBlockedEventsReceived.Load())) + + // after disallow-listing node (a.senderVN) we should not receive any messages from that node. + // This is an asynchronous process with a number of sub processes involved including not limited to; + // - submitting request to admin server for node to be disallow-listed. + // - node disallow-list must update. + // - peer manager needs to prune the connection (takes at least 1 second). + // - connection gater will start blocking incoming connections (takes at least 1 second). + // We wait for 5 seconds to reduce the small chance of a race condition between the time a node is blocked // and the time the blocked node sends the first unauthorized message. - a.blockNode(a.senderVN) - time.Sleep(500 * time.Millisecond) + a.disallowListNode(a.senderVN) + time.Sleep(5 * time.Second) // send unauthorized messages and sleep for 3 seconds to allow all requests to be processed - // in normal situations if the node is not block listed, these messages would be considered + // in normal situations if the node is not disallow-listed, these messages would be considered // legit and hence would be delivered to the recipients. a.Orchestrator.sendExpectedBlockedMsgs(a.T()) // The sleep is unavoidable for the following reasons these messages are sent between 2 running libp2p nodes we don't have any hooks in between @@ -52,5 +57,9 @@ func (a *AdminCommandBlockListTestSuite) TestAdminCommandBlockList() { time.Sleep(3 * time.Second) // messages sent after the node is block listed are considered unauthorized, we don't expect to receive any of them. - require.Equal(a.T(), int64(0), a.Orchestrator.expectedBlockedEventsReceived.Load(), fmt.Sprintf("expected to not receive any unauthorized messages instead got: %d", a.Orchestrator.expectedBlockedEventsReceived.Load())) + require.Equal( + a.T(), + int64(0), + a.Orchestrator.expectedBlockedEventsReceived.Load(), + fmt.Sprintf("expected to not receive any unauthorized messages instead got: %d", a.Orchestrator.expectedBlockedEventsReceived.Load())) } diff --git a/integration/tests/bft/protocol/blocklist/orchestrator.go b/integration/tests/bft/protocol/disallowlisting/orchestrator.go similarity index 100% rename from integration/tests/bft/protocol/blocklist/orchestrator.go rename to integration/tests/bft/protocol/disallowlisting/orchestrator.go diff --git a/integration/tests/bft/protocol/blocklist/suite.go b/integration/tests/bft/protocol/disallowlisting/suite.go similarity index 100% rename from integration/tests/bft/protocol/blocklist/suite.go rename to integration/tests/bft/protocol/disallowlisting/suite.go From f5829949579ec88e675e0591600a9b56231cb726 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:19:11 -0700 Subject: [PATCH 591/815] reduces the load of tests --- .../protocol/disallowlisting/orchestrator.go | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/integration/tests/bft/protocol/disallowlisting/orchestrator.go b/integration/tests/bft/protocol/disallowlisting/orchestrator.go index fe4039960ec..8ff2a6612f9 100644 --- a/integration/tests/bft/protocol/disallowlisting/orchestrator.go +++ b/integration/tests/bft/protocol/disallowlisting/orchestrator.go @@ -1,4 +1,4 @@ -package blocklist +package disallowlisting import ( "sync" @@ -16,14 +16,15 @@ import ( ) const ( - // numOfAuthorizedEvents number of events to send before blocking the sender node via the block list command. - numOfAuthorizedEvents = 10 + // numOfAuthorizedEvents number of events to send before disallow-listing the sender node via the disallow-list command. + numOfAuthorizedEvents = 5 - // numOfUnauthorizedEvents number of events to send after blocking the sender node via the block list command. - numOfUnauthorizedEvents = 10 + // numOfUnauthorizedEvents number of events to send after disallow-listing the sender node via the disallow-list command. + numOfUnauthorizedEvents = 5 ) -// Orchestrator represents a simple `insecure.AttackOrchestrator` that tracks messages received before and after the senderVN is blocked by the receiverEN via the admin blocklist command. +// Orchestrator represents a simple `insecure.AttackOrchestrator` that tracks messages received before and after the +// senderVN is disallow-listed by the receiverEN via the admin disallow-list command. type Orchestrator struct { *bft.BaseOrchestrator sync.Mutex @@ -60,11 +61,11 @@ func NewOrchestrator(t *testing.T, logger zerolog.Logger, senderVN, receiverEN f return orchestrator } -// trackIngressEvents callback that will track authorized messages that are expected to be received by the receiverEN before we block the sender. -// It also tracks unauthorized messages received if any that are expected to be blocked after the senderVN is blocked via the admin blocklist command. +// trackIngressEvents callback that will track authorized messages that are expected to be received by the receiverEN before we disallow-list the sender. +// It also tracks unauthorized messages received if any that are expected to be blocked after the senderVN is disallow-listed via the admin disallow-list command. func (a *Orchestrator) trackIngressEvents(event *insecure.IngressEvent) error { - // Track any unauthorized events that are received, these events are sent after the admin blocklist command - // is used to block the sender node. + // Track any unauthorized events that are received, these events are sent after the admin disallow-list command + // is used to disallow-list the sender node. if _, ok := a.expectedBlockedEvents[event.FlowProtocolEventID]; ok { if event.OriginID == a.senderVN { a.expectedBlockedEventsReceived.Inc() @@ -72,7 +73,7 @@ func (a *Orchestrator) trackIngressEvents(event *insecure.IngressEvent) error { } } - // track all authorized events sent before the sender node is blocked. + // track all authorized events sent before the sender node is disallow-listed. if _, ok := a.authorizedEvents[event.FlowProtocolEventID]; ok { // ensure event received intact no changes have been made to the underlying message //a.assertEventsEqual(expectedEvent, event) @@ -84,7 +85,7 @@ func (a *Orchestrator) trackIngressEvents(event *insecure.IngressEvent) error { } // sendAuthorizedMsgs publishes a number of authorized messages from the senderVN. Authorized messages are messages -// that are sent before the senderVN is blocked. +// that are sent before the senderVN is disallow-listed. func (a *Orchestrator) sendAuthorizedMsgs(t *testing.T) { for i := 0; i < numOfAuthorizedEvents; i++ { event := bft.RequestChunkDataPackEgressFixture(a.T, a.senderVN, a.receiverEN, insecure.Protocol_PUBLISH) @@ -96,7 +97,7 @@ func (a *Orchestrator) sendAuthorizedMsgs(t *testing.T) { } // sendExpectedBlockedMsgs publishes a number of unauthorized messages. Unauthorized messages are messages that are sent -// after the senderVN is blocked via the admin blocklist command. These messages are not expected to be received. +// after the senderVN is blocked via the admin disallow-listed command. These messages are not expected to be received. func (a *Orchestrator) sendExpectedBlockedMsgs(t *testing.T) { for i := 0; i < numOfUnauthorizedEvents; i++ { event := bft.RequestChunkDataPackEgressFixture(a.T, a.senderVN, a.receiverEN, insecure.Protocol_PUBLISH) From f66d6f55c0a8558190bd67e894ea965e7916b525 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:19:34 -0700 Subject: [PATCH 592/815] adds readme --- integration/tests/bft/protocol/README.md | 22 +++++++++++++++++++ .../bft/protocol/disallowlisting/suite.go | 6 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/integration/tests/bft/protocol/README.md b/integration/tests/bft/protocol/README.md index e69de29bb2d..9bea5f714ad 100644 --- a/integration/tests/bft/protocol/README.md +++ b/integration/tests/bft/protocol/README.md @@ -0,0 +1,22 @@ +# Protocol BFT Tests +This package contains BFT tests concerning the core Flow protocol. These tests are run as part of the integration test suite. + + +## Admin Command Disallow List Test +The `AdminCommandDisallowListTestSuite` in the `disallowlisting` package is designed to test the functionality of the disallow-list admin command within a network context. +It ensures that connections to a blocked node are immediately pruned and incoming connection requests are blocked. +The test simulates the setup of two corrupt nodes (a sender and a receiver) and examines the behavior of the network before and after the sender node is disallowed. +It includes steps to send authorized messages to verify normal behavior, implement disallow-listing, send unauthorized messages, and validate the expectation that no unauthorized messages are received. +Various timing controls are put in place to handle asynchronous processes and potential race conditions. +The entire suite assures that the disallow-listing command behaves as intended, safeguarding network integrity. + +## Wintermute Attack Test +The `WintermuteTestSuite` in the `wintermute` package is focused on validating a specific attack scenario within the network, termed the "wintermute attack." +This attack involves an orchestrator corrupting an execution result and then leveraging corrupted verification nodes to verify it. +The suite includes a constant timeout to define the attack window and a detailed test sequence. +The `TestWintermuteAttack` method carries out the attack process. +It first waits for an execution result to be corrupted and identifies the corresponding victim block. +It ensures that the corrupted execution nodes generate the correct result for the victim block and then waits for a specific number of approvals from corrupted verification nodes for each chunk of the corrupted result. +Further, the test waits for a block height equal to the victim block height to be sealed and verifies that the original victim block is correctly identified. +Additional methods and logging information assist in detailing and controlling the flow of the attack. +The entire suite is instrumental in evaluating the system's behavior under this specific attack condition and ensuring that the expected actions and responses are observed. \ No newline at end of file diff --git a/integration/tests/bft/protocol/disallowlisting/suite.go b/integration/tests/bft/protocol/disallowlisting/suite.go index 48c3547f8b4..94a7c747e94 100644 --- a/integration/tests/bft/protocol/disallowlisting/suite.go +++ b/integration/tests/bft/protocol/disallowlisting/suite.go @@ -1,4 +1,4 @@ -package blocklist +package disallowlisting import ( "context" @@ -54,8 +54,8 @@ func (s *Suite) SetupSuite() { ) } -// blockNode submit request to our EN admin server to block sender VN. -func (s *Suite) blockNode(nodeID flow.Identifier) { +// disallowListNode submit request to our EN admin server to block sender VN. +func (s *Suite) disallowListNode(nodeID flow.Identifier) { serverAddr := fmt.Sprintf("localhost:%s", s.Net.ContainerByID(s.receiverEN).Port(testnet.AdminPort)) adminClient := client.NewAdminClient(serverAddr) From 5273868f0a80c0623b53b25e583ac826d13b8872 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:29:16 -0700 Subject: [PATCH 593/815] improves topic validator test --- integration/tests/bft/gossipsub/README.md | 15 ++++++++++++++ .../bft/gossipsub/topicvalidator/suite.go | 2 -- .../topicvalidator/topic_validator_test.go | 20 ++++++++++++++++--- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 integration/tests/bft/gossipsub/README.md diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md new file mode 100644 index 00000000000..07bb78a2077 --- /dev/null +++ b/integration/tests/bft/gossipsub/README.md @@ -0,0 +1,15 @@ +# GossipSub BFT Tests + +## Topic Validator Test +The `TopicValidatorTestSuite` in the `topicvalidator` package is specifically designed to test the functionality of the +libp2p topic validator within a network scenario. +This suite includes an end-to-end test to verify the topic validator's behavior in different situations, +focusing on both unauthorized and authorized message handling. +The method `TestTopicValidatorE2E` is a comprehensive test that mimics an environment with a corrupted byzantine attacker node +attempting to send unauthorized messages to a victim node. +These messages should be dropped by the topic validator, as they fail the message authorization validation. +The test simultaneously sends authorized messages to the victim node, ensuring that they are processed correctly, +demonstrating the validator's correct operation. +The test confirms two main aspects: +1. Unauthorized messages must be dropped, and the victim node should not receive any of them. +2. Authorized messages should be correctly delivered and processed by the victim node. diff --git a/integration/tests/bft/gossipsub/topicvalidator/suite.go b/integration/tests/bft/gossipsub/topicvalidator/suite.go index 3493d299611..b48d4c57bc1 100644 --- a/integration/tests/bft/gossipsub/topicvalidator/suite.go +++ b/integration/tests/bft/gossipsub/topicvalidator/suite.go @@ -38,8 +38,6 @@ func (s *Suite) SetupSuite() { return n.Role != flow.RoleExecution && n.Role != flow.RoleVerification && n.Role != flow.RoleAccess }) - s.NodeConfigs = append(s.NodeConfigs, testnet.NewNodeConfig(flow.RoleAccess, testnet.WithLogLevel(zerolog.FatalLevel))) - // create corrupt access node s.attackerANID = unittest.IdentifierFixture() s.NodeConfigs = append(s.NodeConfigs, testnet.NewNodeConfig(flow.RoleAccess, diff --git a/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go b/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go index b0fab269309..323baf0f5b4 100644 --- a/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go +++ b/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go @@ -28,12 +28,26 @@ func TestTopicValidator(t *testing.T) { func (s *TopicValidatorTestSuite) TestTopicValidatorE2E() { s.Orchestrator.sendUnauthorizedMsgs(s.T()) s.Orchestrator.sendAuthorizedMsgs(s.T()) - unittest.RequireReturnsBefore(s.T(), s.Orchestrator.authorizedEventReceivedWg.Wait, 5*time.Second, "could not send authorized messages on time") + unittest.RequireReturnsBefore( + s.T(), + s.Orchestrator.authorizedEventReceivedWg.Wait, + 5*time.Second, + "could not send authorized messages on time") // Victim nodes are configured with the topic validator enabled, therefore they should not have // received any of the unauthorized messages. - require.Equal(s.T(), 0, len(s.Orchestrator.unauthorizedEventsReceived), fmt.Sprintf("expected to not receive any unauthorized messages instead got: %d", len(s.Orchestrator.unauthorizedEventsReceived))) + require.Equal( + s.T(), + 0, + len(s.Orchestrator.unauthorizedEventsReceived), + fmt.Sprintf("expected to not receive any unauthorized messages instead got: %d", len(s.Orchestrator.unauthorizedEventsReceived))) // Victim nodes should receive all the authorized events sent. - require.Equal(s.T(), numOfAuthorizedEvents, len(s.Orchestrator.authorizedEventsReceived), fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, len(s.Orchestrator.unauthorizedEventsReceived))) + require.Eventually(s.T(), func() bool { + if len(s.Orchestrator.authorizedEventsReceived) == numOfAuthorizedEvents { + return true + } + return false + }, 5*time.Second, 500*time.Millisecond, + fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, len(s.Orchestrator.unauthorizedEventsReceived))) } From ed252b8db89a1fcbc147d16bdb4c210e18f82a89 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:34:53 -0700 Subject: [PATCH 594/815] improves signature requirements test --- integration/tests/bft/gossipsub/README.md | 8 ++++++++ .../bft/gossipsub/signature/requirement/orchestrator.go | 4 ++-- .../signature/requirement/signature_requirement_test.go | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 07bb78a2077..493bf79e0e4 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -13,3 +13,11 @@ demonstrating the validator's correct operation. The test confirms two main aspects: 1. Unauthorized messages must be dropped, and the victim node should not receive any of them. 2. Authorized messages should be correctly delivered and processed by the victim node. + +## Signature Requirement Test +The `TestGossipSubSignatureRequirement` method sets up a test environment consisting of three corrupted nodes:two attackers and one victim. +One attacker is configured without message signing, intending to send unsigned messages that should be rejected by the victim. +The other attacker sends valid signed messages that should be received by the victim. +The test is broken down into the following main parts: +1. **Unauthorized Messages Testing**: The victim node should not receive any messages sent without correct signatures from the unauthorized attacker. The test checks for zero unauthorized messages received by the victim. +2. **Authorized Messages Testing**: Messages sent by the authorized attacker, with the correct signature, must pass the libp2p signature verification process and be delivered to the victim. The test checks for all authorized messages received by the victim within a certain time frame. diff --git a/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go b/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go index 6a7faab3dae..c498f4f2f22 100644 --- a/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go +++ b/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go @@ -21,10 +21,10 @@ const ( // The numOfAuthorizedEvents allows us to wait for a certain number of authorized messages to be received, this should // give the network enough time to process the unauthorized messages. This ensures us that the unauthorized messages // were indeed dropped and not unprocessed. - numOfAuthorizedEvents = 50 + numOfAuthorizedEvents = 5 // numOfUnauthorizedEvents the number of unauthorized events to send by the test orchestrator. - numOfUnauthorizedEvents = 10 + numOfUnauthorizedEvents = 5 ) // Orchestrator represents a simple `insecure.AttackOrchestrator` that tracks any unsigned messages received by victim nodes as well as the typically expected messages. diff --git a/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go b/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go index ee5a417446c..d9d5453b33d 100644 --- a/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go +++ b/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go @@ -35,5 +35,10 @@ func (s *GossipSubSignatureRequirementTestSuite) TestGossipSubSignatureRequireme require.Equal(s.T(), int64(0), s.Orchestrator.unauthorizedEventsReceived.Load(), fmt.Sprintf("expected to not receive any unauthorized messages instead got: %d", s.Orchestrator.unauthorizedEventsReceived.Load())) // messages with correct message signatures are expected to always pass libp2p signature verification and be delivered to the victim EN. - require.Equal(s.T(), int64(numOfAuthorizedEvents), s.Orchestrator.authorizedEventsReceived.Load(), fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, s.Orchestrator.unauthorizedEventsReceived.Load())) + require.Eventually(s.T(), func() bool { + if s.Orchestrator.authorizedEventsReceived.Load() == int64(numOfAuthorizedEvents) { + return true + } + return false + }, 5*time.Second, 500*time.Millisecond, fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, s.Orchestrator.unauthorizedEventsReceived.Load())) } From 4c8a158c9105dbd0f05fc6fe3770bb7f664fd0ee Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:40:23 -0700 Subject: [PATCH 595/815] extends the readme for gossipsub package --- integration/tests/bft/gossipsub/README.md | 9 +++++++++ .../admin_command_disallowlisting_test.go | 1 + 2 files changed, 10 insertions(+) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 493bf79e0e4..91a7a6ed038 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -1,4 +1,5 @@ # GossipSub BFT Tests +GossipSub BFT tests are designed to test the behavior of the GossipSub protocol in a network environment with Byzantine nodes. ## Topic Validator Test The `TopicValidatorTestSuite` in the `topicvalidator` package is specifically designed to test the functionality of the @@ -21,3 +22,11 @@ The other attacker sends valid signed messages that should be received by the vi The test is broken down into the following main parts: 1. **Unauthorized Messages Testing**: The victim node should not receive any messages sent without correct signatures from the unauthorized attacker. The test checks for zero unauthorized messages received by the victim. 2. **Authorized Messages Testing**: Messages sent by the authorized attacker, with the correct signature, must pass the libp2p signature verification process and be delivered to the victim. The test checks for all authorized messages received by the victim within a certain time frame. + +## RPC Inspector False Positive Test +The `GossipsubRPCInspectorFalsePositiveNotificationsTestSuite` test within the `rpc_inspector` package test suite aims to ensure that the underlying libp2p libraries related to gossip sub RPC control message inspection do not trigger false positives during their validation processes. +Here's a breakdown of the `TestGossipsubRPCInspectorFalsePositiveNotifications` method: +1. **Configuration and Context Setup**: A specific duration for loading and intervals is defined, and a context with a timeout is created for the test scenario. +2. **Simulating Network Activity**: The method triggers a "loader loop" with a specific number of test accounts and intervals, intending to create artificial network activity. It does this by submitting transactions to create Flow accounts, waiting for them to be sealed. +3. **State Commitments**: The method waits for 20 state commitment changes, ensuring that the simulated network load behaves as expected. +4. **Verification of Control Messages**: After simulating network activity, the method checks to ensure that no node in the network has disseminated an invalid control message notification. This is done by collecting metrics from the network containers and verifying that no false notifications are detected. diff --git a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go index 64497f82ae0..8da4083e0ed 100644 --- a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go +++ b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go @@ -51,6 +51,7 @@ func (a *AdminCommandDisallowListTestSuite) TestAdminCommandDisallowList() { // in normal situations if the node is not disallow-listed, these messages would be considered // legit and hence would be delivered to the recipients. a.Orchestrator.sendExpectedBlockedMsgs(a.T()) + // The sleep is unavoidable for the following reasons these messages are sent between 2 running libp2p nodes we don't have any hooks in between // These are messages sent after the node is blocked meaning that these messages are not expected to be delivered to the receiver node, // so we sleep this approximate amount of time to ensure all messages were attempted, processed and dropped. From 546cfd5753d438066520cc52c1d948bc1b127f37 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 13:45:35 -0700 Subject: [PATCH 596/815] adds readme --- integration/tests/bft/framework/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 integration/tests/bft/framework/README.md diff --git a/integration/tests/bft/framework/README.md b/integration/tests/bft/framework/README.md new file mode 100644 index 00000000000..178714c75dd --- /dev/null +++ b/integration/tests/bft/framework/README.md @@ -0,0 +1,10 @@ +# Framework BFT Tests +The framework BFT tests are designed to assess the health of the BFT testing framework. + +## Passthrough Sealing and Verification Test +The `PassThroughTestSuite` test within the `framework` package and includes a specific test method `TestSealingAndVerificationPassThrough`. +This test evaluates the health of the BFT testing framework for Byzantine Fault Tolerance (BFT) testing. +1. **Simulates a Scenario**: It sets up a scenario with two corrupted execution nodes and one corrupted verification node, controlled by a dummy orchestrator that lets all incoming events pass through. +2. **Deploys Transaction and Verifies Chunks**: Deploys a transaction leading to an execution result with multiple chunks, assigns them to a verification node, and verifies the generation of result approvals for all chunks. +3. **Sealing and Verification**: Enables sealing based on result approvals and verifies the sealing of a block with a specific multi-chunk execution result. +4. **Evaluates Events**: The test also assesses whether critical sealing-and-verification-related events from corrupted nodes are passed through the orchestrator, by checking both egress and ingress events. From 30c9b78c3b0b381ccd56fe8fb90538b3fc4c4643 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 14:32:48 -0700 Subject: [PATCH 597/815] fixes lint --- .../signature/requirement/signature_requirement_test.go | 5 +---- .../bft/gossipsub/topicvalidator/topic_validator_test.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go b/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go index d9d5453b33d..0ef58021b06 100644 --- a/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go +++ b/integration/tests/bft/gossipsub/signature/requirement/signature_requirement_test.go @@ -36,9 +36,6 @@ func (s *GossipSubSignatureRequirementTestSuite) TestGossipSubSignatureRequireme // messages with correct message signatures are expected to always pass libp2p signature verification and be delivered to the victim EN. require.Eventually(s.T(), func() bool { - if s.Orchestrator.authorizedEventsReceived.Load() == int64(numOfAuthorizedEvents) { - return true - } - return false + return s.Orchestrator.authorizedEventsReceived.Load() == int64(numOfAuthorizedEvents) }, 5*time.Second, 500*time.Millisecond, fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, s.Orchestrator.unauthorizedEventsReceived.Load())) } diff --git a/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go b/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go index 323baf0f5b4..aff4fc6b6a0 100644 --- a/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go +++ b/integration/tests/bft/gossipsub/topicvalidator/topic_validator_test.go @@ -44,10 +44,7 @@ func (s *TopicValidatorTestSuite) TestTopicValidatorE2E() { // Victim nodes should receive all the authorized events sent. require.Eventually(s.T(), func() bool { - if len(s.Orchestrator.authorizedEventsReceived) == numOfAuthorizedEvents { - return true - } - return false + return len(s.Orchestrator.authorizedEventsReceived) == numOfAuthorizedEvents }, 5*time.Second, 500*time.Millisecond, fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, len(s.Orchestrator.unauthorizedEventsReceived))) } From a796c53a046428350548828f04f4776923c3d40a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 14:54:12 -0700 Subject: [PATCH 598/815] renames a method --- network/p2p/mock/unicast_manager.go | 2 +- network/p2p/p2pnode/libp2pNode.go | 2 +- network/p2p/unicast/manager.go | 4 ++-- network/p2p/unicast_manager.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/network/p2p/mock/unicast_manager.go b/network/p2p/mock/unicast_manager.go index 212f678ccc9..bd90927d6b0 100644 --- a/network/p2p/mock/unicast_manager.go +++ b/network/p2p/mock/unicast_manager.go @@ -70,7 +70,7 @@ func (_m *UnicastManager) Register(unicast protocols.ProtocolName) error { } // WithDefaultHandler provides a mock function with given fields: defaultHandler -func (_m *UnicastManager) WithDefaultHandler(defaultHandler network.StreamHandler) { +func (_m *UnicastManager) SetDefaultHandler(defaultHandler network.StreamHandler) { _m.Called(defaultHandler) } diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 9fcbcc9a466..0c98eb3edcb 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -390,7 +390,7 @@ func (n *Node) Host() host.Host { // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. func (n *Node) WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error { - n.uniMgr.WithDefaultHandler(defaultHandler) + n.uniMgr.SetDefaultHandler(defaultHandler) for _, p := range preferred { err := n.uniMgr.Register(p) if err != nil { diff --git a/network/p2p/unicast/manager.go b/network/p2p/unicast/manager.go index d8af81ed49d..43365ebecca 100644 --- a/network/p2p/unicast/manager.go +++ b/network/p2p/unicast/manager.go @@ -65,9 +65,9 @@ func NewUnicastManager(logger zerolog.Logger, } } -// WithDefaultHandler sets the default stream handler for this unicast manager. The default handler is utilized +// SetDefaultHandler sets the default stream handler for this unicast manager. The default handler is utilized // as the core handler for other unicast protocols, e.g., compressions. -func (m *Manager) WithDefaultHandler(defaultHandler libp2pnet.StreamHandler) { +func (m *Manager) SetDefaultHandler(defaultHandler libp2pnet.StreamHandler) { defaultProtocolID := protocols.FlowProtocolID(m.sporkId) if len(m.protocols) > 0 { panic("default handler must be set only once before any unicast registration") diff --git a/network/p2p/unicast_manager.go b/network/p2p/unicast_manager.go index 0a106b538f8..2eff5497baf 100644 --- a/network/p2p/unicast_manager.go +++ b/network/p2p/unicast_manager.go @@ -14,7 +14,7 @@ import ( type UnicastManager interface { // WithDefaultHandler sets the default stream handler for this unicast manager. The default handler is utilized // as the core handler for other unicast protocols, e.g., compressions. - WithDefaultHandler(defaultHandler libp2pnet.StreamHandler) + SetDefaultHandler(defaultHandler libp2pnet.StreamHandler) // Register registers given protocol name as preferred unicast. Each invocation of register prioritizes the current protocol // over previously registered ones. // All errors returned from this function can be considered benign. From ae3e9d8cfeb01cfe43e4384d4076fd7223cbc8cd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 22 Aug 2023 15:21:38 -0700 Subject: [PATCH 599/815] develops open protected stream logic --- network/p2p/middleware/middleware.go | 4 ++ network/p2p/p2pnode/libp2pNode.go | 79 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 90a82fc9cb6..334a305019b 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -345,6 +345,10 @@ func (m *Middleware) OnAllowListNotification(notification *network.AllowListingU } } +func (m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) { + return m.libP2PNode.OpenProtectedStream(ctx, peerID, protectionTag, writingLogic) +} + // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous // direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized // as the router. diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 0c98eb3edcb..4a998ee492e 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -179,6 +179,85 @@ func (n *Node) GetPeersForProtocol(pid protocol.ID) peer.IDSlice { return peers } +func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error { + n.host.ConnManager().Protect(peerID, protectionTag) + defer n.host.ConnManager().Unprotect(peerID, protectionTag) + + // streams don't need to be reused and are fairly inexpensive to be created for each send. + // A stream creation does NOT incur an RTT as stream negotiation happens on an existing connection. + s, err := n.createStream(ctx, peerID) + if err != nil { + return fmt.Errorf("failed to create stream for %s: %w", peerID, err) + } + + deadline, _ := ctx.Deadline() + err = s.SetWriteDeadline(deadline) + if err != nil { + return fmt.Errorf("failed to set write deadline for stream: %w", err) + } + + // create a gogo protobuf writer + err = writingLogic(s) + + if err != nil { + // reset the stream to ensure that the next stream creation is not affected by the error. + resetErr := s.Reset() + if resetErr != nil { + n.logger.Error(). + Str("target_peer_id", peerID.String()). + Err(resetErr). + Msg("failed to reset stream") + } + + return fmt.Errorf("writing logic failed for %s: %w", peerID, err) + } + + // close the stream immediately + err = s.Close() + if err != nil { + err = fmt.Errorf("failed to close the stream for %s: %w", peerID, err) + } + + return nil +} + +func (n *Node) createStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) { + lg := n.logger.With().Str("peer_id", peerID.String()).Logger() + + // If we do not currently have any addresses for the given peer, stream creation will almost + // certainly fail. If this Node was configured with a routing system, we can try to use it to + // look up the address of the peer. + if len(n.host.Peerstore().Addrs(peerID)) == 0 && n.routing != nil { + lg.Info().Msg("address not found in peer store, searching for peer in routing system") + + var err error + func() { + timedCtx, cancel := context.WithTimeout(ctx, findPeerQueryTimeout) + defer cancel() + // try to find the peer using the routing system + _, err = n.routing.FindPeer(timedCtx, peerID) + }() + + if err != nil { + lg.Warn().Err(err).Msg("address not found in both peer store and routing system") + } else { + lg.Debug().Msg("address not found in peer store, but found in routing system search") + } + } + + stream, dialAddrs, err := n.uniMgr.CreateStream(ctx, peerID, MaxConnectAttempt) + if err != nil { + return nil, flownet.NewPeerUnreachableError(fmt.Errorf("could not create stream (peer_id: %s, dialing address(s): %v): %w", peerID, + dialAddrs, err)) + } + + lg.Info(). + Str("networking_protocol_id", string(stream.Protocol())). + Str("dial_address", fmt.Sprintf("%v", dialAddrs)). + Msg("stream successfully created to remote peer") + return stream, nil +} + // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. // All errors returned from this function can be considered benign. func (n *Node) CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) { From 2b8f0b191d6f3c90b0c312d77e8f00853a521b38 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 23 Aug 2023 05:08:39 -0400 Subject: [PATCH 600/815] lint fix --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 3c07e382ec6..213d05fd1f4 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -320,7 +320,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio alspmgr.WithDecayFunc(fastDecayFunc), } - ids, nodes, _ := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) From 551fad7ea6180bfb14021e25ee70057ec47b6fc5 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 23 Aug 2023 13:56:31 +0100 Subject: [PATCH 601/815] Revert import changes --- engine/access/state_stream/mock/get_execution_data_func.go | 3 +-- engine/access/state_stream/mock/get_start_height_func.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/engine/access/state_stream/mock/get_execution_data_func.go b/engine/access/state_stream/mock/get_execution_data_func.go index 506cc8d3d1c..50fe8087e21 100644 --- a/engine/access/state_stream/mock/get_execution_data_func.go +++ b/engine/access/state_stream/mock/get_execution_data_func.go @@ -5,9 +5,8 @@ package mock import ( context "context" - mock "github.com/stretchr/testify/mock" - execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" + mock "github.com/stretchr/testify/mock" ) // GetExecutionDataFunc is an autogenerated mock type for the GetExecutionDataFunc type diff --git a/engine/access/state_stream/mock/get_start_height_func.go b/engine/access/state_stream/mock/get_start_height_func.go index d1c93d2ed1c..b97a77e1d39 100644 --- a/engine/access/state_stream/mock/get_start_height_func.go +++ b/engine/access/state_stream/mock/get_start_height_func.go @@ -3,9 +3,8 @@ package mock import ( - mock "github.com/stretchr/testify/mock" - flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" ) // GetStartHeightFunc is an autogenerated mock type for the GetStartHeightFunc type From 9ddec833d25bdb41645a69a8aee2cc7249db6f39 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 23 Aug 2023 15:44:54 +0100 Subject: [PATCH 602/815] Fix compilation error in rate_limit_test --- engine/access/rpc/rate_limit_test.go | 62 ++++++++++++---------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index 5777a4b3aee..a816dc4a41e 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -8,18 +8,6 @@ import ( "testing" "time" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc/backend" "github.com/onflow/flow-go/model/flow" @@ -32,6 +20,17 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" ) type RateLimitTestSuite struct { @@ -146,30 +145,21 @@ func (suite *RateLimitTestSuite) SetupTest() { block := unittest.BlockHeaderFixture() suite.snapshot.On("Head").Return(block, nil) - - bnd := backend.New( - suite.state, - suite.collClient, - nil, - suite.blocks, - suite.headers, - suite.collections, - suite.transactions, - nil, - nil, - suite.chainID, - suite.metrics, - nil, - false, - 0, - nil, - nil, - suite.log, - 0, - nil, - backend.NewNodeCommunicator(false), - false, - ) + + bnd := backend.New(backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + MaxHeightRange: 0, + Log: suite.log, + SnapshotHistoryLimit: 0, + Communicator: backend.NewNodeCommunicator(false), + }) rpcEngBuilder, err := NewBuilder( suite.log, From dabbdb33419de1a2d2614d97d0dc6f35d10f641c Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 10:15:44 -0700 Subject: [PATCH 603/815] adds a comment explaning tag observables and their limitations --- network/test/middleware_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 65ce4f7cca3..1f3311a2da0 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -44,10 +44,27 @@ import ( const testChannel = channels.TestNetworkChannel +// tagsObserver is used to observe peer connections and their tags within a GossipSub mesh. +// It plays a crucial role in verifying the formation of a mesh within unit tests. // libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSUb mesh, see: // https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go // One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSubD such calls, // and that's what we do with tagsObserver. +// Usage: +// The tagsObserver struct observes the OnNext, OnError, and OnComplete events related to peer tags. +// A channel 'tags' is used to communicate these tags, and the observer is subscribed to the observable connection manager. +// Advantages: +// Using tag observables helps understand the connectivity between different peers, +// and can be valuable in testing scenarios where network connectivity is critical. +// Issues: +// - Deviation from Production Code: This tag observation might be unique to the test environment, +// and therefore not reflect the behavior of the production code. +// - Mask Issues in the Production Environment: The observables are tied to testing and might +// lead to behaviors or errors that are masked or not evident within the actual production environment. +// +// TODO: Evaluate the necessity of tag observables in this test. Consider addressing the deviation from +// production code and potential mask issues in the production environment. Evaluate the possibility +// of removing this part eventually. type tagsObserver struct { tags chan string log zerolog.Logger From 78c30a2b1eea7562604aab66b4db7eedf80fefea Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 10:22:52 -0700 Subject: [PATCH 604/815] adds a comment explaining tag observables and their limitations --- network/test/meshengine_test.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index e7a5684f2f9..bb519ccb143 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -53,8 +53,9 @@ func TestMeshNetTestSuite(t *testing.T) { suite.Run(t, new(MeshEngineTestSuite)) } -// SetupTest is executed prior to each test in this test suit -// it creates and initializes a set of network instances +// SetupTest is executed prior to each test in this test suite. It creates and initializes +// a set of network instances, sets up connection managers, nodes, identities, observables, etc. +// This setup ensures that all necessary configurations are in place before running the tests. func (suite *MeshEngineTestSuite) SetupTest() { // defines total number of nodes in our network (minimum 3 needed to use 1-k messaging) const count = 10 @@ -63,6 +64,16 @@ func (suite *MeshEngineTestSuite) SetupTest() { // set up a channel to receive pubsub tags from connManagers of the nodes peerChannel := make(chan string) + + // Tag Observables Usage Explanation: + // The tagsObserver is used to observe connections tagged by pubsub messages. This is instrumental in understanding + // the connectivity between different peers and verifying the formation of the mesh within this test suite. + // Issues: + // - Deviation from Production Code: The usage of tag observables here may not reflect the behavior in the production environment. + // - Mask Issues in the Production Environment: The observables tied to testing might lead to behaviors or errors that are + // masked or not evident within the actual production code. + // TODO: Evaluate the necessity of tag observables in this test and consider addressing the deviation from production + // code and potential mask issues. Evaluate the possibility of removing this part eventually. ob := tagsObserver{ tags: peerChannel, log: logger, @@ -197,9 +208,11 @@ func (suite *MeshEngineTestSuite) TestUnregister_Unicast() { suite.conduitCloseScenario(suite.Unicast) } -// allToAllScenario creates a complete mesh of the engines -// each engine x then sends a "hello from node x" to other engines -// it evaluates the correctness of message delivery as well as content of the message +// allToAllScenario creates a complete mesh of the engines, where each engine x sends a +// "hello from node x" to other engines. It then evaluates the correctness of message +// delivery as well as the content of the messages. This scenario tests the capability of +// the engines to communicate in a fully connected graph, ensuring both the reachability +// of messages and the integrity of their contents. func (suite *MeshEngineTestSuite) allToAllScenario(send testutils.ConduitSendWrapperFunc) { // allows nodes to find each other in case of Mulitcast and Publish testutils.OptionalSleep(send) From d899924114f47da1b514acf437b7f5bcb2e956fc Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 23 Aug 2023 10:30:26 -0700 Subject: [PATCH 605/815] Update network/p2p/test/topic_validator_test.go Co-authored-by: Khalil Claybon --- network/p2p/test/topic_validator_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 0245985dc27..c7959ee1a02 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -309,7 +309,6 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { // publish fails because the topic conversion fails require.Error(t, err) - fmt.Println(err) // ensure the correct error is contained in the logged error require.Contains(t, hook.Logs(), "could not convert topic to channel") } From 7a34ebf892381924a729f4ab51954fa4422f1084 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 23 Aug 2023 10:31:02 -0700 Subject: [PATCH 606/815] Update network/p2p/scoring/app_score_test.go Co-authored-by: Misha --- network/p2p/scoring/app_score_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 1cebe6f710c..11a4054e15d 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -125,7 +125,7 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // testGossipConnectivityUnderNetworkPartition tests that whether two honest nodes are in each others topic mesh on GossipSub // when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. // If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. -// A true return value means that the two honest nodes have each other in each others' mesh. +// A true return value means that the two honest nodes have each other in each other's mesh. // A false return value means that the two honest nodes do not have each other in each others' mesh (at least one of them is partitioned). func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { ctx, cancel := context.WithCancel(context.Background()) From a77c0bc3d236fa625b3aaae95458b833408e0703 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 23 Aug 2023 10:31:32 -0700 Subject: [PATCH 607/815] Update network/p2p/scoring/app_score_test.go Co-authored-by: Misha --- network/p2p/scoring/app_score_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 11a4054e15d..adc6f064f8f 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -126,7 +126,7 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. // If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. // A true return value means that the two honest nodes have each other in each other's mesh. -// A false return value means that the two honest nodes do not have each other in each others' mesh (at least one of them is partitioned). +// A false return value means that the two honest nodes do not have each other in each other's mesh (at least one of them is partitioned). func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) From 7048be2fb5118a41110cdb18aaef06aeb1c096a1 Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 10:53:49 -0700 Subject: [PATCH 608/815] consolidates the test --- network/p2p/scoring/app_score_test.go | 28 +++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 1cebe6f710c..012378a874b 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -109,25 +109,14 @@ func TestFullGossipSubConnectivity(t *testing.T) { } } -// TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority is part two of testing pushing access nodes to the edges of the network. +// TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority tests pushing access nodes to the edges of the network. // This test proves that if access nodes are PUSHED to the edge of the network, even their malicious majority cannot partition // the network of honest nodes. +// The scenario tests that whether two honest nodes are in each others topic mesh on GossipSub +// when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. +// The honest nodes (i.e., non-Access nodes) are enabled with peer scoring, then the honest nodes are enabled with peer scoring. func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testing.T) { // Note: if this test is ever flaky, this means a bug in our scoring system. Please escalate to the team instead of skipping. - if !testGossipConnectivityUnderNetworkPartition(t, true) { - // even one failure should not happen, as it means that malicious majority can partition the network - // with our peer scoring parameters. - require.Fail(t, "honest nodes are not on each others' topic mesh on GossipSub") - } - -} - -// testGossipConnectivityUnderNetworkPartition tests that whether two honest nodes are in each others topic mesh on GossipSub -// when the network topology is a complete graph (i.e., full topology) and a malicious majority of access nodes are present. -// If honestPeerScoring is true, then the honest nodes are enabled with peer scoring. -// A true return value means that the two honest nodes have each other in each others' mesh. -// A false return value means that the two honest nodes do not have each other in each others' mesh (at least one of them is partitioned). -func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring bool) bool { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) sporkId := unittest.IdentifierFixture() @@ -135,9 +124,7 @@ func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring idProvider := mock.NewIdentityProvider(t) // two (honest) consensus nodes opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithRole(flow.RoleConsensus)} - if honestPeerScoring { - opts = append(opts, p2ptest.EnablePeerScoringWithOverride(p2p.PeerScoringConfigNoOverride)) - } + opts = append(opts, p2ptest.EnablePeerScoringWithOverride(p2p.PeerScoringConfigNoOverride)) defaultConfig, err := config.DefaultConfig() require.NoError(t, err) @@ -235,12 +222,11 @@ func testGossipConnectivityUnderNetworkPartition(t *testing.T, honestPeerScoring } if con2HasCon1 && con1HasCon2 { - return true + return } case <-timeoutCh: - t.Log("timed out waiting for con1 to have con2 in its mesh") - return false + require.Fail(t, "timed out waiting for con1 to have con2 in its mesh; honest nodes are not on each others' topic mesh on GossipSub") } } } From 7fefe6be5f00735ed650594dba933a81010c6160 Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 12:11:00 -0700 Subject: [PATCH 609/815] fixes merge conflicts --- network/p2p/scoring/app_score_test.go | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index e15aafd9e01..771a442a213 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -201,14 +201,6 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi // let nodes reside on a full topology, hence no partition is caused by the topology. p2ptest.LetNodesDiscoverEachOther(t, ctx, allNodes, allIds) - outgoingMessageScope, err := message.NewOutgoingScope( - allIds.NodeIDs(), - channels.TopicFromChannel(channels.PushBlocks, sporkId), - unittest.ProposalFixture(), - unittest.NetworkCodec().Encode, - message.ProtocolTypePubSub) - require.NoError(t, err) - require.NoError(t, con1Node.Publish(ctx, outgoingMessageScope)) // checks whether con1 and con2 are in the same mesh tick := time.Second // Set the tick duration as needed timeout := 5 * time.Second // Set the timeout duration as needed @@ -246,20 +238,6 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi require.Fail(t, "timed out waiting for con1 to have con2 in its mesh; honest nodes are not on each others' topic mesh on GossipSub") } } - // we check that whether within a one-second window the message is received by the other honest consensus node. - // the one-second window is important because it triggers the heartbeat of the con1Node to perform a lazy pull (iHave). - // And con1Node may randomly choose con2Node as the peer to perform the lazy pull. - // However, under a network partition con2Node is not in the mesh of con1Node, and hence is deprived of the eager push from con1Node. - // - // If no honest peer scoring is enabled, then con1Node and con2Node are less-likely to be in the same mesh, and hence the message is not delivered. - // If honest peer scoring is enabled, then con1Node and con2Node are certainly in the same mesh, and hence the message is delivered. - ctx1s, cancel1s := context.WithTimeout(ctx, 1*time.Second) - defer cancel1s() - - expectedReceivedData, err := outgoingMessageScope.Proto().Marshal() - require.NoError(t, err) - - return p2pfixtures.HasSubReceivedMessage(t, ctx1s, expectedReceivedData, con2Sub) } // maliciousAppSpecificScore returns a malicious app specific penalty function that rewards the malicious node and From daddb3157ea72c27858ac00a4a63fd696129e992 Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 13:32:27 -0700 Subject: [PATCH 610/815] fixes lint --- network/alsp/manager/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 3f6e5a1629d..a02bc5b3002 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -323,7 +323,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) From 9dc8da3034832983247a082b70014ae648c27856 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Wed, 23 Aug 2023 22:26:09 +0100 Subject: [PATCH 611/815] Add node communicator mock --- .../access/rpc/backend/mock/communicator.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 engine/access/rpc/backend/mock/communicator.go diff --git a/engine/access/rpc/backend/mock/communicator.go b/engine/access/rpc/backend/mock/communicator.go new file mode 100644 index 00000000000..ab7498ac8f8 --- /dev/null +++ b/engine/access/rpc/backend/mock/communicator.go @@ -0,0 +1,42 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" +) + +// Communicator is an autogenerated mock type for the Communicator type +type Communicator struct { + mock.Mock +} + +// CallAvailableNode provides a mock function with given fields: nodes, call, shouldTerminateOnError +func (_m *Communicator) CallAvailableNode(nodes flow.IdentityList, call func(*flow.Identity) error, shouldTerminateOnError func(*flow.Identity, error) bool) error { + ret := _m.Called(nodes, call, shouldTerminateOnError) + + var r0 error + if rf, ok := ret.Get(0).(func(flow.IdentityList, func(*flow.Identity) error, func(*flow.Identity, error) bool) error); ok { + r0 = rf(nodes, call, shouldTerminateOnError) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewCommunicator interface { + mock.TestingT + Cleanup(func()) +} + +// NewCommunicator creates a new instance of Communicator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewCommunicator(t mockConstructorTestingTNewCommunicator) *Communicator { + mock := &Communicator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From df35fcab0ee1106a1dd830007e8d10c0b543a46d Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 16:52:26 -0700 Subject: [PATCH 612/815] migrates the logic of stream creation to libp2p --- .../node_builder/access_node_builder.go | 24 +++++++++-------- cmd/observer/node_builder/observer_builder.go | 24 +++++++++-------- cmd/scaffold.go | 24 +++++++++-------- follower/follower_builder.go | 24 +++++++++-------- network/internal/testutils/testUtil.go | 26 +++++++++++-------- network/middleware.go | 19 ++++++++++++++ network/p2p/libp2pNode.go | 19 ++++++++++++++ network/p2p/middleware/middleware.go | 17 +++++++++++- network/p2p/p2pnode/libp2pNode.go | 19 +++++++++++++- 9 files changed, 139 insertions(+), 57 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index e87f1e6327d..bc3c3e45199 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -792,17 +792,19 @@ func (builder *FlowAccessNodeBuilder) initNetwork(nodeID module.Local, ) (*p2p.Network, error) { // creates network instance net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, + Logger: builder.Logger, + Codec: cborcodec.NewCodec(), + Me: nodeID, + MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, + Topology: topology, + SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), + Metrics: networkMetrics, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 1e294e70d01..cad4a6f6972 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -483,17 +483,19 @@ func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, receiveCache *netcache.ReceiveCache, ) (*p2p.Network, error) { net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, + Logger: builder.Logger, + Codec: cborcodec.NewCodec(), + Me: nodeID, + MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, + Topology: topology, + SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), + Metrics: networkMetrics, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 61d9680f524..11d359c2ae7 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -462,17 +462,19 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( // creates network instance net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: fnb.Logger, - Codec: fnb.CodecFactory(), - Me: fnb.Me, - SporkId: fnb.SporkID, - MiddlewareFactory: func() (network.Middleware, error) { return fnb.Middleware, nil }, - Topology: topology.NewFullyConnectedTopology(), - SubscriptionManager: subscriptionManager, - Metrics: fnb.Metrics.Network, - IdentityProvider: fnb.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: cf, + Logger: fnb.Logger, + Codec: fnb.CodecFactory(), + Me: fnb.Me, + SporkId: fnb.SporkID, + MiddlewareFactory: func() (network.Middleware, error) { return fnb.Middleware, nil }, + Topology: topology.NewFullyConnectedTopology(), + SubscriptionManager: subscriptionManager, + Metrics: fnb.Metrics.Network, + IdentityProvider: fnb.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: cf, + UnicastMessageTimeout: fnb.FlowConfig.NetworkConfig.UnicastMessageTimeout, + IdentityTranslator: fnb.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: fnb.Logger, SpamRecordCacheSize: fnb.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 4b561916fe4..a650846244f 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -370,17 +370,19 @@ func (builder *FollowerServiceBuilder) initNetwork(nodeID module.Local, receiveCache *netcache.ReceiveCache, ) (*p2p.Network, error) { net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, + Logger: builder.Logger, + Codec: cborcodec.NewCodec(), + Me: nodeID, + MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, + Topology: topology, + SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), + Metrics: networkMetrics, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index e08482e6acd..4df135e9d06 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -126,6 +126,7 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // flow.IdentityList - list of identities created for the nodes, one for each node. // // []p2p.LibP2PNode - list of libp2p nodes created. +// TODO: several test cases only need a single node, consider encapsulating this function in a single node fixture. func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode) { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) @@ -226,23 +227,26 @@ func NetworkConfigFixture( defaultFlowConfig, err := config.DefaultConfig() require.NoError(t, err) + idProvider := id.NewFixedIdentityProvider(allIds) receiveCache := netcache.NewHeroReceiveCache( defaultFlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, unittest.Logger(), metrics.NewNoopCollector()) subMgr := subscription.NewChannelSubscriptionManager(mw) params := &p2p.NetworkConfig{ - Logger: unittest.Logger(), - Codec: unittest.NetworkCodec(), - Me: me, - MiddlewareFactory: func() (network.Middleware, error) { return mw, nil }, - Topology: unittest.NetworkTopology(), - SubscriptionManager: subMgr, - Metrics: metrics.NewNoopCollector(), - IdentityProvider: id.NewFixedIdentityProvider(allIds), - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: sporkId, + Logger: unittest.Logger(), + Codec: unittest.NetworkCodec(), + Me: me, + MiddlewareFactory: func() (network.Middleware, error) { return mw, nil }, + Topology: unittest.NetworkTopology(), + SubscriptionManager: subMgr, + Metrics: metrics.NewNoopCollector(), + IdentityProvider: idProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: sporkId, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: translator.NewIdentityProviderIDTranslator(idProvider), AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: unittest.Logger(), SpamRecordCacheSize: defaultFlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, diff --git a/network/middleware.go b/network/middleware.go index a7ac5820b19..c60abfb3355 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -3,7 +3,9 @@ package network import ( + "context" "github.com/ipfs/go-datastore" + libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" @@ -34,6 +36,23 @@ type Middleware interface { // All errors returned from this function can be considered benign. SendDirect(msg OutgoingMessageScope) error + // OpenProtectedStream acts as a short-circuit method that delegates the opening of a protected stream to the underlying + // libP2PNode. This method is intended to be temporary and is going to be removed in the long term. Users should plan + // to interact with the libP2P node directly in the future. + // + // Parameters: + // ctx: The context used to control the stream's lifecycle. + // peerID: The ID of the peer to open the stream to. + // protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive. + // writingLogic: A callback function that contains the logic for writing to the stream. + // + // Returns: + // error: An error, if any occurred during the process. All returned errors are benign. + // + // Note: This method is subject to removal in future versions and direct use of the libp2p node is encouraged. + // TODO: Remove this method in the future. + OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) error + // Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or // a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best // effort. diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 8426bd18ba6..c85e4b198d2 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -54,6 +54,25 @@ type LibP2PNode interface { RemovePeer(peerID peer.ID) error // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. GetPeersForProtocol(pid protocol.ID) peer.IDSlice + // OpenProtectedStream opens a new stream to a peer with a protection tag. The protection tag can be used to ensure + // that the connection to the peer is maintained for a particular purpose. The stream is opened to the given peerID + // and writingLogic is executed on the stream. The created stream does not need to be reused and can be inexpensively + // created for each send. Moreover, the stream creation does not incur a round-trip time as the stream negotiation happens + // on an existing connection. + // + // Args: + // - ctx: The context used to control the stream's lifecycle. + // - peerID: The ID of the peer to open the stream to. + // - protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive, and + // won't prune the connection while the tag is active. + // - writingLogic: A callback function that contains the logic for writing to the stream. It allows an external caller to + // write to the stream without having to worry about the stream creation and management. + // + // Returns: + // error: An error, if any occurred during the process. This includes failure in creating the stream, setting the write + // deadline, executing the writing logic, resetting the stream if the writing logic fails, or closing the stream. + // All returned errors during this process can be considered benign. + OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) // GetIPPort returns the IP and Port the libp2p node is listening on. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 334a305019b..4751cf71b98 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -345,7 +345,22 @@ func (m *Middleware) OnAllowListNotification(notification *network.AllowListingU } } -func (m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) { +// OpenProtectedStream acts as a short-circuit method that delegates the opening of a protected stream to the underlying +// libP2PNode. This method is intended to be temporary and is going to be removed in the long term. Users should plan +// to interact with the libP2P node directly in the future. +// +// Parameters: +// ctx: The context used to control the stream's lifecycle. +// peerID: The ID of the peer to open the stream to. +// protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive. +// writingLogic: A callback function that contains the logic for writing to the stream. +// +// Returns: +// error: An error, if any occurred during the process. All returned errors are benign. +// +// Note: This method is subject to removal in future versions and direct use of the libp2p node is encouraged. +// TODO: Remove this method in the future. +func (m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) error { return m.libP2PNode.OpenProtectedStream(ctx, peerID, protectionTag, writingLogic) } diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 4a998ee492e..c66918a0487 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -179,6 +179,24 @@ func (n *Node) GetPeersForProtocol(pid protocol.ID) peer.IDSlice { return peers } +// OpenProtectedStream opens a new stream to a peer with a protection tag. The protection tag can be used to ensure +// that the connection to the peer is maintained for a particular purpose. The stream is opened to the given peerID +// and writingLogic is executed on the stream. The created stream does not need to be reused and can be inexpensively +// created for each send. Moreover, the stream creation does not incur a round-trip time as the stream negotiation happens +// on an existing connection. +// +// Args: +// - ctx: The context used to control the stream's lifecycle. +// - peerID: The ID of the peer to open the stream to. +// - protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive, and +// won't prune the connection while the tag is active. +// - writingLogic: A callback function that contains the logic for writing to the stream. It allows an external caller to +// write to the stream without having to worry about the stream creation and management. +// +// Returns: +// error: An error, if any occurred during the process. This includes failure in creating the stream, setting the write +// deadline, executing the writing logic, resetting the stream if the writing logic fails, or closing the stream. +// All returned errors during this process can be considered benign. func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error { n.host.ConnManager().Protect(peerID, protectionTag) defer n.host.ConnManager().Unprotect(peerID, protectionTag) @@ -198,7 +216,6 @@ func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protecti // create a gogo protobuf writer err = writingLogic(s) - if err != nil { // reset the stream to ensure that the next stream creation is not affected by the error. resetErr := s.Reset() From da2085cf1f40e049bb2afc8e848bf7cf3d937335 Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 16:55:00 -0700 Subject: [PATCH 613/815] replaces send direct in network with open protected stream --- network/p2p/network.go | 137 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 14 deletions(-) diff --git a/network/p2p/network.go b/network/p2p/network.go index a3225f59925..f0fe782d983 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -1,8 +1,12 @@ package p2p import ( + "bufio" + "context" "errors" "fmt" + ggio "github.com/gogo/protobuf/io" + libp2pnet "github.com/libp2p/go-libp2p/core/network" "sync" "time" @@ -27,6 +31,28 @@ import ( "github.com/onflow/flow-go/utils/logging" ) +const ( + _ = iota + _ = 1 << (10 * iota) + mb + gb +) + +const ( + // DefaultMaxUnicastMsgSize defines maximum message size in unicast mode for most messages + DefaultMaxUnicastMsgSize = 10 * mb // 10 mb + + // LargeMsgMaxUnicastMsgSize defines maximum message size in unicast mode for large messages + LargeMsgMaxUnicastMsgSize = gb // 1 gb + + // DefaultUnicastTimeout is the default maximum time to wait for a default unicast request to complete + // assuming at least a 1mb/sec connection + DefaultUnicastTimeout = 5 * time.Second + + // LargeMsgUnicastTimeout is the maximum time to wait for a unicast request to complete for large message size + LargeMsgUnicastTimeout = 1000 * time.Second +) + // NotEjectedFilter is an identity filter that, when applied to the identity // table at a given snapshot, returns all nodes that we should communicate with // over the networking layer. @@ -40,8 +66,10 @@ var NotEjectedFilter = filter.Not(filter.Ejected) type Network struct { sync.RWMutex *component.ComponentManager + ctx context.Context sporkId flow.Identifier identityProvider module.IdentityProvider + identityTranslator IDTranslator logger zerolog.Logger codec network.Codec me module.Local @@ -56,6 +84,7 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager slashingViolationsConsumer network.ViolationsConsumer + unicastMessageTimeout time.Duration } var _ network.Network = &Network{} @@ -89,18 +118,20 @@ var ErrNetworkShutdown = errors.New("network has already shutdown") // NetworkConfig is a configuration struct for the network. It contains all the // necessary components to create a new network. type NetworkConfig struct { - Logger zerolog.Logger - Codec network.Codec - Me module.Local - MiddlewareFactory func() (network.Middleware, error) - Topology network.Topology - SubscriptionManager network.SubscriptionManager - Metrics module.NetworkCoreMetrics - IdentityProvider module.IdentityProvider - ReceiveCache *netcache.ReceiveCache - ConduitFactory network.ConduitFactory - AlspCfg *alspmgr.MisbehaviorReportManagerConfig - SporkId flow.Identifier + Logger zerolog.Logger + Codec network.Codec + Me module.Local + MiddlewareFactory func() (network.Middleware, error) + Topology network.Topology + SubscriptionManager network.SubscriptionManager + Metrics module.NetworkCoreMetrics + IdentityProvider module.IdentityProvider + IdentityTranslator IDTranslator + ReceiveCache *netcache.ReceiveCache + ConduitFactory network.ConduitFactory + AlspCfg *alspmgr.MisbehaviorReportManagerConfig + SporkId flow.Identifier + UnicastMessageTimeout time.Duration } // NetworkConfigOption is a function that can be used to override network config parmeters. @@ -170,6 +201,8 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { registerBlobServiceRequests: make(chan *registerBlobServiceRequest), misbehaviorReportManager: misbehaviorMngr, sporkId: param.SporkId, + identityTranslator: param.IdentityTranslator, + unicastMessageTimeout: param.UnicastMessageTimeout, } for _, opt := range opts { @@ -202,6 +235,15 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { <-n.misbehaviorReportManager.Done() n.logger.Debug().Msg("misbehavior manager stopped") }). + AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + n.logger.Debug().Msg("setting up network context") + n.ctx = ctx + + ready() + + <-ctx.Done() + n.logger.Debug().Msg("network context is done") + }). AddWorker(n.runMiddleware). AddWorker(n.processRegisterEngineRequests). AddWorker(n.processRegisterBlobServiceRequests).Build() @@ -441,13 +483,57 @@ func (n *Network) UnicastOnChannel(channel channels.Channel, payload interface{} n.metrics.UnicastMessageSendingStarted(channel.String()) defer n.metrics.UnicastMessageSendingCompleted(channel.String()) - err = n.mw.SendDirect(msg) + + // since it is a unicast, we only need to get the first peer ID. + peerID, err := n.identityTranslator.GetPeerID(msg.TargetIds()[0]) + if err != nil { + return fmt.Errorf("could not find peer id for target id: %w", err) + } + + maxMsgSize := unicastMaxMsgSize(msg.PayloadType()) + if msg.Size() > maxMsgSize { + // message size goes beyond maximum size that the serializer can handle. + // proceeding with this message results in closing the connection by the target side, and + // delivery failure. + return fmt.Errorf("message size %d exceeds configured max message size %d", msg.Size(), maxMsgSize) + } + + maxTimeout := n.unicastMaxMsgDuration(msg.PayloadType()) + + // pass in a context with timeout to make the unicast call fail fast + ctx, cancel := context.WithTimeout(n.ctx, maxTimeout) + defer cancel() + + // protect the underlying connection from being inadvertently pruned by the peer manager while the stream and + // connection creation is being attempted, and remove it from protected list once stream created. + channel, ok := channels.ChannelFromTopic(msg.Topic()) + if !ok { + return fmt.Errorf("could not find channel for topic %s", msg.Topic()) + } + streamProtectionTag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) + + err = n.mw.OpenProtectedStream(ctx, peerID, streamProtectionTag, func(stream libp2pnet.Stream) error { + bufw := bufio.NewWriter(stream) + writer := ggio.NewDelimitedWriter(bufw) + + err = writer.WriteMsg(msg.Proto()) + if err != nil { + return fmt.Errorf("failed to send message to target id %x with peer id %s: %w", msg.TargetIds()[0], peerID, err) + } + + // flush the stream + err = bufw.Flush() + if err != nil { + return fmt.Errorf("failed to flush stream for target id %x with peer id %s: %w", msg.TargetIds()[0], peerID, err) + } + + return nil + }) if err != nil { return fmt.Errorf("failed to send message to %x: %w", targetID, err) } n.metrics.OutboundMessageSent(msg.Size(), channel.String(), message.ProtocolTypeUnicast.String(), msg.PayloadType()) - return nil } @@ -581,3 +667,26 @@ func (n *Network) Topology() flow.IdentityList { func (n *Network) ReportMisbehaviorOnChannel(channel channels.Channel, report network.MisbehaviorReport) { n.misbehaviorReportManager.HandleMisbehaviorReport(channel, report) } + +// unicastMaxMsgSize returns the max permissible size for a unicast message +func unicastMaxMsgSize(messageType string) int { + switch messageType { + case "*messages.ChunkDataResponse": + return LargeMsgMaxUnicastMsgSize + default: + return DefaultMaxUnicastMsgSize + } +} + +// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete +func (n *Network) unicastMaxMsgDuration(messageType string) time.Duration { + switch messageType { + case "messages.ChunkDataResponse": + if LargeMsgUnicastTimeout > n.unicastMessageTimeout { + return LargeMsgUnicastTimeout + } + return n.unicastMessageTimeout + default: + return n.unicastMessageTimeout + } +} From d88a0b5135c514a3222f7af233746479d9a19d49 Mon Sep 17 00:00:00 2001 From: Yahya Date: Wed, 23 Aug 2023 16:55:12 -0700 Subject: [PATCH 614/815] wip refactoring middleware tests --- network/test/middleware_test.go | 60 ++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 90ef9709627..a7a0444573c 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -201,6 +201,15 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer) + networkCfg := testutils.NetworkConfigFixture( + m.T(), + *ids[0], + m.ids, + m.sporkId, + mws[0]) + newNet, err := p2p.NewNetwork(networkCfg) + require.NoError(m.T(), err) + require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) @@ -212,38 +221,30 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { newMw.SetOverlay(overlay) // start up nodes and peer managers - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) - defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - newMw.Start(irrecoverableCtx) - defer testutils.StopComponents(m.T(), mws, 100*time.Millisecond) - unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) + newNet.Start(irrecoverableCtx) + defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) idList := flow.IdentityList(append(m.ids, newId)) // needed to enable ID translation m.providers[0].SetIdentities(idList) - outMsg, err := message.NewOutgoingScope( - flow.IdentifierList{newId.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: "TestUpdateNodeAddresses", - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // message should fail to send because no address is known yet - // for the new identity - err = m.mws[0].SendDirect(outMsg) + // unicast should fail to send because no address is known yet for the new identity + err = newNet.UnicastOnChannel(channels.TestNetworkChannel, &libp2pmessage.TestMessage{ + Text: "TestUpdateNodeAddresses", + }, m.ids[1].NodeID) require.True(m.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) // update the addresses m.mws[0].UpdateNodeAddresses() // now the message should send successfully - err = m.mws[0].SendDirect(outMsg) - require.NoError(m.T(), err) + // err = m.mws[0].SendDirect(outMsg) + // require.NoError(m.T(), err) cancel() unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) @@ -302,6 +303,15 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) + networkCfg := testutils.NetworkConfigFixture( + m.T(), + *ids[0], + m.ids, + m.sporkId, + mws[0]) + newNet, err := p2p.NewNetwork(networkCfg) + require.NoError(m.T(), err) + require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) @@ -327,11 +337,12 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { ctx, cancel := context.WithCancel(m.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) - defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - newMw.Start(irrecoverableCtx) - unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) + newNet.Start(irrecoverableCtx) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) @@ -352,6 +363,9 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream // handling of streams. for i := 0; i < 10; i++ { + newNet.UnicastOnChannel(channels.TestNetworkChannel, &libp2pmessage.TestMessage{ + Text: fmt.Sprintf("hello-%d", i), + }, newId.NodeID) msg, err := message.NewOutgoingScope( flow.IdentifierList{newId.NodeID}, channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), From c20fbeba779d1561ccaa5fb1d4fbf98f253cdfa9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Aug 2023 18:31:43 -0700 Subject: [PATCH 615/815] adds updatable identity provider to network fixtures --- network/alsp/manager/manager_test.go | 18 +++++++++++++----- network/internal/testutils/testUtil.go | 23 +++++++++++++++-------- network/test/blob_service_test.go | 2 +- network/test/echoengine_test.go | 2 +- network/test/epochtransition_test.go | 2 +- network/test/meshengine_test.go | 2 +- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 9ffd384386f..1f7ae297d23 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" @@ -56,6 +57,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + idProvider := id.NewFixedIdentityProvider(ids) mws, _ := testutils.MiddlewareFixtures( t, ids, @@ -63,7 +65,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0]) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0]) net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) require.NoError(t, err) @@ -120,13 +122,14 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + idProvider := id.NewFixedIdentityProvider(ids) mws, _ := testutils.MiddlewareFixtures( t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -230,7 +233,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + + idProvider := id.NewFixedIdentityProvider(ids) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -301,8 +306,10 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) // the pruned spammer nodes are established. func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() + // create 1 victim node, 1 honest node and a node for each slashing violation ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). + idProvider := id.NewFixedIdentityProvider(ids) mws, _ := testutils.MiddlewareFixtures( t, ids, @@ -312,7 +319,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t networkCfg := testutils.NetworkConfigFixture( t, *ids[0], - ids, + idProvider, sporkId, mws[0], p2p.WithAlspConfig(managerCfgFixture(t))) @@ -406,8 +413,9 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + idProvider := id.NewFixedIdentityProvider(ids) mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 4df135e9d06..c4215b6a188 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -18,7 +18,6 @@ import ( "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/mock" @@ -172,7 +171,14 @@ func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware. // Returns: // - a list of middlewares - one for each identity. // - a list of UpdatableIDProvider - one for each identity. -func MiddlewareFixtures(t *testing.T, identities flow.IdentityList, libP2PNodes []p2p.LibP2PNode, cfg *middleware.Config, consumer network.ViolationsConsumer, opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { +func MiddlewareFixtures( + t *testing.T, + identities flow.IdentityList, + libP2PNodes []p2p.LibP2PNode, + cfg *middleware.Config, + consumer network.ViolationsConsumer, + opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { + require.Equal(t, len(identities), len(libP2PNodes)) mws := make([]network.Middleware, len(identities)) @@ -194,27 +200,29 @@ func MiddlewareFixtures(t *testing.T, identities flow.IdentityList, libP2PNodes func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, - mws []network.Middleware) []network.Network { + mws []network.Middleware) ([]network.Network, []*unittest.UpdatableIDProvider) { count := len(ids) nets := make([]network.Network, 0) + idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { - - params := NetworkConfigFixture(t, *ids[i], ids, sporkId, mws[i]) + idProvider := unittest.NewUpdatableIDProvider(ids) + params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, mws[i]) net, err := p2p.NewNetwork(params) require.NoError(t, err) nets = append(nets, net) + idProviders = append(idProviders, idProvider) } - return nets + return nets, idProviders } func NetworkConfigFixture( t *testing.T, myId flow.Identity, - allIds flow.IdentityList, + idProvider module.IdentityProvider, sporkId flow.Identifier, mw network.Middleware, opts ...p2p.NetworkConfigOption) *p2p.NetworkConfig { @@ -227,7 +235,6 @@ func NetworkConfigFixture( defaultFlowConfig, err := config.DefaultConfig() require.NoError(t, err) - idProvider := id.NewFixedIdentityProvider(allIds) receiveCache := netcache.NewHeroReceiveCache( defaultFlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, unittest.Logger(), diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index f42d74ab140..8b9cf72058f 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -99,7 +99,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.networks = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) blobExchangeChannel := channels.Channel("blob-exchange") diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index e329a83e780..d4c5d6e01ef 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -62,7 +62,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) + suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 0632f2a9a44..652c5b2debe 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -201,7 +201,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - nets := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) + nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) suite.cancels = append(suite.cancels, cancel) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index c2e2311a5af..dda3b0504ae 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -108,7 +108,7 @@ func (suite *MeshEngineTestSuite) SetupTest() { libP2PNodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) + suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) for _, observableConnMgr := range tagObservables { From 6c98e307112c0e348bfe00c8d49a782ea1336e11 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 23 Aug 2023 18:31:55 -0700 Subject: [PATCH 616/815] wip fixes middleware tests --- network/test/middleware_test.go | 109 ++++++++++++++------------------ 1 file changed, 46 insertions(+), 63 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index a7a0444573c..d3ae428322d 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -24,6 +24,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/observable" @@ -67,11 +68,13 @@ func (co *tagsObserver) OnComplete() { close(co.tags) } +// TODO: eventually this should be renamed to NetworkTesTSuite and should be moved to the p2p.network package. type MiddlewareTestSuite struct { suite.Suite sync.RWMutex size int // used to determine number of middlewares under test - nodes []p2p.LibP2PNode + libP2PNodes []p2p.LibP2PNode + networks []network.Network mws []network.Middleware // used to keep track of middlewares under test ov []*mocknetwork.Overlay obs chan string // used to keep track of Protect events tagged by pubsub messages @@ -137,14 +140,16 @@ func (m *MiddlewareTestSuite) SetupTest() { idProvider.SetIdentities(identities) m.ids = identities - m.nodes = libP2PNodes + m.libP2PNodes = libP2PNodes - m.mws, m.providers = testutils.MiddlewareFixtures( + m.mws, _ = testutils.MiddlewareFixtures( m.T(), m.ids, - m.nodes, + m.libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), m.slashingViolationsConsumer) + + m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.mws) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } @@ -164,24 +169,23 @@ func (m *MiddlewareTestSuite) SetupTest() { m.mwCtx = irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(m.mwCtx, m.T(), m.nodes, 100*time.Millisecond) + testutils.StartNodes(m.mwCtx, m.T(), m.libP2PNodes, 1*time.Second) - for i, mw := range m.mws { - mw.SetOverlay(m.ov[i]) - mw.Start(m.mwCtx) - unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, mw) - require.NoError(m.T(), mw.Subscribe(channels.TestNetworkChannel)) + for i, net := range m.networks { + net.Start(m.mwCtx) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, net) + require.NoError(m.T(), m.mws[i].Subscribe(channels.TestNetworkChannel)) } } func (m *MiddlewareTestSuite) TearDownTest() { m.mwCancel() - testutils.StopComponents(m.T(), m.mws, 100*time.Millisecond) - testutils.StopComponents(m.T(), m.nodes, 100*time.Millisecond) + testutils.StopComponents(m.T(), m.networks, 1*time.Second) + testutils.StopComponents(m.T(), m.libP2PNodes, 1*time.Second) m.mws = nil - m.nodes = nil + m.libP2PNodes = nil m.ov = nil m.ids = nil m.size = 0 @@ -195,6 +199,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) + idProvider := id.NewFixedIdentityProvider(ids) mws, providers := testutils.MiddlewareFixtures( m.T(), ids, @@ -204,7 +209,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { networkCfg := testutils.NetworkConfigFixture( m.T(), *ids[0], - m.ids, + idProvider, m.sporkId, mws[0]) newNet, err := p2p.NewNetwork(networkCfg) @@ -225,8 +230,8 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) newNet.Start(irrecoverableCtx) - defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + defer testutils.StopComponents(m.T(), []network.Middleware{newMw}, 1*time.Second) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) idList := flow.IdentityList(append(m.ids, newId)) @@ -234,20 +239,23 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { m.providers[0].SetIdentities(idList) // unicast should fail to send because no address is known yet for the new identity - err = newNet.UnicastOnChannel(channels.TestNetworkChannel, &libp2pmessage.TestMessage{ + con, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + err = con.Unicast(&libp2pmessage.TestMessage{ Text: "TestUpdateNodeAddresses", - }, m.ids[1].NodeID) + }, newId.NodeID) require.True(m.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) // update the addresses m.mws[0].UpdateNodeAddresses() // now the message should send successfully - // err = m.mws[0].SendDirect(outMsg) - // require.NoError(m.T(), err) + err = con.Unicast(&libp2pmessage.TestMessage{ + Text: "TestUpdateNodeAddresses", + }, newId.NodeID) + require.NoError(m.T(), err) cancel() - unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) } func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { @@ -303,15 +311,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) - networkCfg := testutils.NetworkConfigFixture( - m.T(), - *ids[0], - m.ids, - m.sporkId, - mws[0]) - newNet, err := p2p.NewNetwork(networkCfg) - require.NoError(m.T(), err) - require.Len(m.T(), ids, 1) require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) @@ -331,7 +330,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { close(ch) } }) - newMw.SetOverlay(overlay) ctx, cancel := context.WithCancel(m.mwCtx) @@ -340,10 +338,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - newNet.Start(irrecoverableCtx) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) - defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) - + newMw.Start(irrecoverableCtx) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) // needed to enable ID translation @@ -356,26 +352,19 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // that connections to peers that are rate limited are completely prune. IsConnected will // return true only if the node is a direct peer of the other, after rate limiting this direct // peer should be removed by the peer manager. - p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - p2ptest.TryConnectionAndEnsureConnected(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}) + p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) + p2ptest.TryConnectionAndEnsureConnected(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}) + + con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) // with the rate limit configured to 5 msg/sec we send 10 messages at once and expect the rate limiter // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream // handling of streams. for i := 0; i < 10; i++ { - newNet.UnicastOnChannel(channels.TestNetworkChannel, &libp2pmessage.TestMessage{ + err = con0.Unicast(&libp2pmessage.TestMessage{ Text: fmt.Sprintf("hello-%d", i), }, newId.NodeID) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{newId.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: fmt.Sprintf("hello-%d", i), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - err = m.mws[0].SendDirect(msg) require.NoError(m.T(), err) } // wait for all rate limits before shutting down middleware @@ -385,21 +374,15 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { time.Sleep(1 * time.Second) // ensure connection to rate limited peer is pruned - p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.nodes[0]}) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.nodes[0]}) + p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) // eventually the rate limited node should be able to reconnect and send messages require.Eventually(m.T(), func() bool { - msg, err := message.NewOutgoingScope( - flow.IdentifierList{newId.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: "hello", - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - return m.mws[0].SendDirect(msg) == nil + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: "hello", + }, newId.NodeID) + return err == nil }, 3*time.Second, 100*time.Millisecond) // shutdown our middleware so that each message can be processed @@ -519,7 +502,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // that connections to peers that are rate limited are completely prune. IsConnected will // return true only if the node is a direct peer of the other, after rate limiting this direct // peer should be removed by the peer manager. - p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.nodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) + p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited // as it is more than our allowed bandwidth of 1000 bytes. @@ -535,8 +518,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { time.Sleep(1 * time.Second) // ensure connection to rate limited peer is pruned - p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.nodes[0]}) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.nodes[0]}) + p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) // eventually the rate limited node should be able to reconnect and send messages require.Eventually(m.T(), func() bool { From e30417aae022973fe325f9badfa3c29a430473d9 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 24 Aug 2023 10:18:02 +0100 Subject: [PATCH 617/815] fix goimports --- engine/access/rpc/backend/mock/communicator.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/access/rpc/backend/mock/communicator.go b/engine/access/rpc/backend/mock/communicator.go index ab7498ac8f8..9989e30753b 100644 --- a/engine/access/rpc/backend/mock/communicator.go +++ b/engine/access/rpc/backend/mock/communicator.go @@ -3,8 +3,9 @@ package mock import ( - flow "github.com/onflow/flow-go/model/flow" mock "github.com/stretchr/testify/mock" + + flow "github.com/onflow/flow-go/model/flow" ) // Communicator is an autogenerated mock type for the Communicator type From 455147a08f62bace4ff3fb18c2cc6ae018d3ca68 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 24 Aug 2023 10:53:21 +0100 Subject: [PATCH 618/815] Address code review feedback on libp2pnode decomposition --- network/p2p/libp2pNode.go | 57 +++++++++++++++------------------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index d040ff9c051..bee76c51add 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -9,7 +9,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/routing" - "github.com/onflow/flow-go/engine/collection" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" @@ -19,8 +18,8 @@ import ( "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) -// Node single node management capabilities -type Node interface { +// P2PService service management capabilities +type P2PService interface { // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. @@ -29,16 +28,21 @@ type Node interface { GetIPPort() (string, string, error) // Host returns pointer to host object of node. Host() host.Host -} - -// PeerManagement set of node traits related to its lifecycle and metadata retrieval -type PeerManagement interface { // SetComponentManager sets the component manager for the node. // SetComponentManager may be called at most once. SetComponentManager(cm *component.ComponentManager) +} - // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. - WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error +// PeerManagement set of node traits related to its lifecycle and metadata retrieval +type PeerManagement interface { + // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. + AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error + // RemovePeer closes the connection with the peer. + RemovePeer(peerID peer.ID) error + // ListPeers returns list of peer IDs for peers subscribed to the topic. + ListPeers(topic string) []peer.ID + // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. + GetPeersForProtocol(pid protocol.ID) peer.IDSlice // WithPeersProvider sets the PeersProvider for the peer manager. // If a peer manager factory is set, this method will set the peer manager's PeersProvider. WithPeersProvider(peersProvider PeersProvider) @@ -59,22 +63,12 @@ type Routable interface { Routing() routing.Routing } -// PeerToPeer peers list operations -type PeerToPeer interface { - // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. - AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error - // RemovePeer closes the connection with the peer. - RemovePeer(peerID peer.ID) error - // ListPeers returns list of peer IDs for peers subscribed to the topic. - ListPeers(topic string) []peer.ID - // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. - GetPeersForProtocol(pid protocol.ID) peer.IDSlice -} - // StreamManagement peer to peer stream management functions -type StreamManagement interface { +type UnicastManagement interface { // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) + // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. + WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error } // PubSub publish subscribe features for node @@ -90,13 +84,9 @@ type PubSub interface { SetPubSub(ps PubSubAdapter) } -// LibP2PNode represents a flow libp2p node. It provides the network layer with the necessary interface to -// control the underlying libp2p node. It is essentially the flow wrapper around the libp2p node, and allows +// LibP2PNode represents a Flow libp2p node. It provides the network layer with the necessary interface to +// control the underlying libp2p node. It is essentially the Flow wrapper around the libp2p node, and allows // us to define different types of libp2p nodes that can operate in different ways by overriding these methods. -// TODO: this interface is highly coupled with the current implementation of the libp2p node. We should -// -// consider refactoring it to be more generic and less coupled with the current implementation. -// https://github.com/dapperlabs/flow-go/issues/6575 type LibP2PNode interface { module.ReadyDoneAware Subscriptions @@ -116,8 +106,8 @@ type LibP2PNode interface { // DisallowListOracle exposes the disallow list oracle API for external consumers to query about the disallow list. DisallowListOracle - // Node single node management capabilities - Node + // P2PService service management capabilities + P2PService // PeerManagement current peer management functions PeerManagement @@ -128,11 +118,8 @@ type LibP2PNode interface { // PubSub publish subscribe features for node PubSub - // PeerToPeer peers list operations - PeerToPeer - - // StreamManagement node stream management - StreamManagement + // UnicastManagement node stream management + UnicastManagement } // Subscriptions set of funcs related to current subscription info of a node. From 302637715091d497cc9712f0b8e688cacb0d6362 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 24 Aug 2023 09:13:34 -0400 Subject: [PATCH 619/815] rename to SpamReportConfig --- engine/common/synchronization/alsp.go | 16 ++++----- engine/common/synchronization/engine.go | 14 ++++---- engine/common/synchronization/engine_test.go | 35 ++++++++++++++------ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/engine/common/synchronization/alsp.go b/engine/common/synchronization/alsp.go index 39664a58691..f07845144e9 100644 --- a/engine/common/synchronization/alsp.go +++ b/engine/common/synchronization/alsp.go @@ -1,16 +1,16 @@ package synchronization -// probabilityFactorMultiplier is used to convert probability factor to an integer as well as a maximum value - 1 +// spamProbabilityMultiplier is used to convert probability factor to an integer as well as a maximum value - 1 // random number that can be generated by the random number generator. -const probabilityFactorMultiplier = 1001 +const spamProbabilityMultiplier = 1001 -type Alsp struct { - syncRequestProbabilityFactor float32 +type SpamReportConfig struct { + syncRequestProbability float32 } -func NewAlsp() *Alsp { - return &Alsp{ - // create misbehavior report 1/1000 times - syncRequestProbabilityFactor: 0.001, +func NewSpamReportConfig() *SpamReportConfig { + return &SpamReportConfig{ + // create misbehavior report 1/100 message requests + syncRequestProbability: 0.01, } } diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index d48345606dd..1d8c5f639d7 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -56,8 +56,8 @@ type Engine struct { core module.SyncCore participantsProvider module.IdentifierProvider - requestHandler *RequestHandler // component responsible for handling requests - alsp *Alsp + requestHandler *RequestHandler // component responsible for handling requests + spamReportConfig *SpamReportConfig pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse pendingBlockResponses engine.MessageStore // message store for *message.BlockResponse @@ -108,7 +108,7 @@ func New( pollInterval: opt.PollInterval, scanInterval: opt.ScanInterval, participantsProvider: participantsProvider, - alsp: NewAlsp(), + spamReportConfig: NewSpamReportConfig(), } // register the engine with the network layer and store the conduit @@ -486,8 +486,8 @@ func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.I } func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { - // Generate a random integer between 1 and probabilityFactorMultiplier (exclusive) - n, err := rand.Uint32n(probabilityFactorMultiplier) + // Generate a random integer between 1 and spamProbabilityMultiplier (exclusive) + n, err := rand.Uint32n(spamProbabilityMultiplier) if err != nil { // failing to generate a random number is unlikely. If an error is encountered while @@ -499,8 +499,8 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f } // to avoid creating a misbehavior report for every sync request received, use a probabilistic approach. - // Create a report with a probability of alsp.syncRequestProbabilityFactor - if float32(n) < e.alsp.syncRequestProbabilityFactor*probabilityFactorMultiplier { + // Create a report with a probability of spamReportConfig.syncRequestProbability + if float32(n) < e.spamReportConfig.syncRequestProbability*spamProbabilityMultiplier { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index b4f227e5c20..853b732dbce 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -252,7 +252,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - ss.e.alsp.syncRequestProbabilityFactor = 0.0 // force not creating misbehavior report + ss.e.spamReportConfig.syncRequestProbability = 0.0 // force not creating misbehavior report require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) @@ -295,8 +295,8 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.core.AssertNotCalled(ss.T(), "WithinTolerance") ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - // force creating misbehavior report by setting syncRequestProbabilityFactor to 1.0 (i.e. report misbehavior 100% of the time) - ss.e.alsp.syncRequestProbabilityFactor = 1.0 + // force creating misbehavior report by setting syncRequestProbability to 1.0 (i.e. report misbehavior 100% of the time) + ss.e.spamReportConfig.syncRequestProbability = 1.0 require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) @@ -309,7 +309,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load load tests that a sync request that's higher // than the receiver's height doesn't trigger a response, even if outside tolerance. It checks that an ALSP -// spamming misbehavior report was generated and that the number of misbehavior reports is within a reasonable range. +// spamming misbehavor report was generated and that the number of misbehavior reports is within a reasonable range. func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load() { ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) @@ -326,13 +326,26 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance loadGroups := []loadGroup{} + // expect to never get misbehavior report + loadGroups = append(loadGroups, loadGroup{0.0, 0, 0}) + + // expect to get misbehavior report between 10% of the time loadGroups = append(loadGroups, loadGroup{0.1, 75, 140}) + + // expect to get misbehavior report between 1% of the time + loadGroups = append(loadGroups, loadGroup{0.01, 5, 15}) + + // expect to get misbehavior report between 0.1% of the time (1 in 1000 requests) loadGroups = append(loadGroups, loadGroup{0.001, 0, 7}) + + // expect to get misbehavior report between 50% of the time loadGroups = append(loadGroups, loadGroup{0.5, 450, 550}) + + // expect to get misbehavior report between 90% of the time loadGroups = append(loadGroups, loadGroup{0.9, 850, 950}) // reset misbehavior report counter for each subtest - misbehaviorReported := 0 + misbehaviorsCounter := 0 for _, loadGroup := range loadGroups { ss.T().Run(fmt.Sprintf("load test; pfactor=%f lower=%d upper=%d", loadGroup.syncRequestProbabilityFactor, loadGroup.expectedMisbehaviorsLower, loadGroup.expectedMisbehaviorsUpper), func(t *testing.T) { @@ -362,10 +375,10 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // count misbehavior reports over the course of a load test ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Maybe().Run( func(args mock.Arguments) { - misbehaviorReported++ + misbehaviorsCounter++ }, ) - ss.e.alsp.syncRequestProbabilityFactor = loadGroup.syncRequestProbabilityFactor + ss.e.spamReportConfig.syncRequestProbability = loadGroup.syncRequestProbabilityFactor require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } @@ -376,10 +389,12 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance // check that correct range of misbehavior reports were generated (between 1-2 reports per 1000 requests) // since we're using a random method to generate misbehavior reports, we can't guarantee the exact number, so we // check that it's within a larger range, but that at least 1 misbehavior report was generated - assert.GreaterOrEqual(ss.T(), misbehaviorReported, loadGroup.expectedMisbehaviorsLower) - assert.LessOrEqual(ss.T(), misbehaviorReported, loadGroup.expectedMisbehaviorsUpper) // too many reports would indicate a bug - misbehaviorReported = 0 // reset counter for next subtest + ss.T().Logf("misbehaviors counter after load test: %d (expected lower bound: %d expected upper bound: %d)", misbehaviorsCounter, loadGroup.expectedMisbehaviorsLower, loadGroup.expectedMisbehaviorsUpper) + assert.GreaterOrEqual(ss.T(), misbehaviorsCounter, loadGroup.expectedMisbehaviorsLower) + assert.LessOrEqual(ss.T(), misbehaviorsCounter, loadGroup.expectedMisbehaviorsUpper) // too many reports would indicate a bug + + misbehaviorsCounter = 0 // reset counter for next subtest }) } } From 80bc0cf5b31f54509e0d4b092b4af64c517a236b Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 24 Aug 2023 09:19:06 -0400 Subject: [PATCH 620/815] moved spam config to config.go --- engine/common/synchronization/alsp.go | 16 ---------------- engine/common/synchronization/config.go | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 engine/common/synchronization/alsp.go diff --git a/engine/common/synchronization/alsp.go b/engine/common/synchronization/alsp.go deleted file mode 100644 index f07845144e9..00000000000 --- a/engine/common/synchronization/alsp.go +++ /dev/null @@ -1,16 +0,0 @@ -package synchronization - -// spamProbabilityMultiplier is used to convert probability factor to an integer as well as a maximum value - 1 -// random number that can be generated by the random number generator. -const spamProbabilityMultiplier = 1001 - -type SpamReportConfig struct { - syncRequestProbability float32 -} - -func NewSpamReportConfig() *SpamReportConfig { - return &SpamReportConfig{ - // create misbehavior report 1/100 message requests - syncRequestProbability: 0.01, - } -} diff --git a/engine/common/synchronization/config.go b/engine/common/synchronization/config.go index 59d8c4dc1ea..f2230b7c597 100644 --- a/engine/common/synchronization/config.go +++ b/engine/common/synchronization/config.go @@ -36,3 +36,18 @@ func WithScanInterval(interval time.Duration) OptionFunc { cfg.ScanInterval = interval } } + +// spamProbabilityMultiplier is used to convert probability factor to an integer as well as a maximum value - 1 +// random number that can be generated by the random number generator. +const spamProbabilityMultiplier = 1001 + +type SpamReportConfig struct { + syncRequestProbability float32 +} + +func NewSpamReportConfig() *SpamReportConfig { + return &SpamReportConfig{ + // create misbehavior report 1/100 message requests + syncRequestProbability: 0.01, + } +} From 8b7341c8f8018acaa400a28c03c87ba72f24b644 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 24 Aug 2023 09:19:55 -0400 Subject: [PATCH 621/815] clean up --- engine/common/synchronization/engine_alsp_test.go | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 engine/common/synchronization/engine_alsp_test.go diff --git a/engine/common/synchronization/engine_alsp_test.go b/engine/common/synchronization/engine_alsp_test.go deleted file mode 100644 index 2f74a05db05..00000000000 --- a/engine/common/synchronization/engine_alsp_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package synchronization - -import "testing" - -func TestValidateSyncRequest(t *testing.T) { - -} - -func TestOnSyncRequest_DisallowListing_Integration(t *testing.T) { - -} From 3e15e0a23518bcad804b8e58f382d33eb161328a Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 24 Aug 2023 10:05:49 -0400 Subject: [PATCH 622/815] load test for probability factor=1.0 --- engine/common/synchronization/engine_test.go | 65 ++++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 853b732dbce..84e124338a5 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -263,54 +263,65 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.con.AssertExpectations(ss.T()) } -// TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport tests that a sync request that's higher +// TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTolerance_AlwaysReportSpam tests that a sync request that's higher // than the receiver's height doesn't trigger a response, even if outside tolerance and generates ALSP // spamming misbehavior report (simulating the unlikely probability). -func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_MisbehaviorReport() { +// This load test ensures that a misbehavior report is generated every time when the probability factor is set to 1.0. +func (ss *SyncSuite) TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTolerance_AlwaysReportSpam() { ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) defer cancel() - // generate origin and request message - originID := unittest.IdentifierFixture() + load := 1000 - nonce, err := rand.Uint64() - require.NoError(ss.T(), err, "should generate nonce") + // reset misbehavior report counter for each subtest + misbehaviorsCounter := 0 - req := &messages.SyncRequest{ - Nonce: nonce, - Height: 0, - } + for i := 0; i < load; i++ { + // generate origin and request message + originID := unittest.IdentifierFixture() - // if request height is higher than local finalized, we should not respond - req.Height = ss.head.Height + 1 + nonce, err := rand.Uint64() + require.NoError(ss.T(), err, "should generate nonce") - // assert that misbehavior is reported - ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Once() + req := &messages.SyncRequest{ + Nonce: nonce, + Height: 0, + } - // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported - // also, check that response is never sent - ss.core.AssertNotCalled(ss.T(), "HandleHeight") - ss.core.AssertNotCalled(ss.T(), "WithinTolerance") - ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) + // if request height is higher than local finalized, we should not respond + req.Height = ss.head.Height + 1 - // force creating misbehavior report by setting syncRequestProbability to 1.0 (i.e. report misbehavior 100% of the time) - ss.e.spamReportConfig.syncRequestProbability = 1.0 + // assert that HandleHeight, WithinTolerance are not called because misbehavior is reported + // also, check that response is never sent + ss.core.AssertNotCalled(ss.T(), "HandleHeight") + ss.core.AssertNotCalled(ss.T(), "WithinTolerance") + ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + // count misbehavior reports over the course of a load test + ss.con.On("ReportMisbehavior", mock.Anything).Return(mock.Anything).Run( + func(args mock.Arguments) { + misbehaviorsCounter++ + }, + ) - // give at least some time to process items - time.Sleep(time.Millisecond * 100) + // force creating misbehavior report by setting syncRequestProbability to 1.0 (i.e. report misbehavior 100% of the time) + ss.e.spamReportConfig.syncRequestProbability = 1.0 + + require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) + } ss.core.AssertExpectations(ss.T()) ss.con.AssertExpectations(ss.T()) + assert.Equal(ss.T(), misbehaviorsCounter, load) // should generate misbehavior report every time } -// TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load load tests that a sync request that's higher +// TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTolerance_SometimesReportSpam load tests that a sync request that's higher // than the receiver's height doesn't trigger a response, even if outside tolerance. It checks that an ALSP -// spamming misbehavor report was generated and that the number of misbehavior reports is within a reasonable range. -func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance_Load() { +// spam misbehavior report was generated and that the number of misbehavior reports is within a reasonable range. +// This load test ensures that a misbehavior report is generated an appropriate range of times when the probability factor is set to different values. +func (ss *SyncSuite) TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTolerance_SometimesReportSpam() { ctx, cancel := irrecoverable.NewMockSignalerContextWithCancel(ss.T(), context.Background()) ss.e.Start(ctx) unittest.AssertClosesBefore(ss.T(), ss.e.Ready(), time.Second) From 5957375f34248c9cdb69867b6753cb1261d4d8e1 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 23 Aug 2023 17:03:42 +0200 Subject: [PATCH 623/815] change script execution state interface --- engine/execution/scripts/engine.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index 35ab0dbec55..f6e1a2a87dd 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "fmt" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/rs/zerolog" @@ -15,12 +16,23 @@ import ( "github.com/onflow/flow-go/state/protocol" ) +type ScriptExecutionState interface { + // NewStorageSnapshot creates a new ready-only view at the given state commitment. + NewStorageSnapshot(flow.StateCommitment) snapshot.StorageSnapshot + + // StateCommitmentByBlockID returns the final state commitment for the provided block ID. + StateCommitmentByBlockID(context.Context, flow.Identifier) (flow.StateCommitment, error) + + // HasState returns true if the state with the given state commitment exists in memory + HasState(flow.StateCommitment) bool +} + type Engine struct { unit *engine.Unit log zerolog.Logger state protocol.State computationManager computation.ComputationManager - execState state.ExecutionState + execState ScriptExecutionState } var _ execution.ScriptExecutor = (*Engine)(nil) From 9d880f01da18a185fb606e5df25722e2371637bb Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 24 Aug 2023 15:27:36 +0200 Subject: [PATCH 624/815] make script engine consume query executor instead of computation manager as it doesn't need it --- engine/execution/scripts/engine.go | 31 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index f6e1a2a87dd..d46d0716549 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -4,14 +4,13 @@ import ( "context" "encoding/hex" "fmt" + "github.com/onflow/flow-go/engine/execution/computation/query" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/rs/zerolog" "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/execution" - "github.com/onflow/flow-go/engine/execution/computation" - "github.com/onflow/flow-go/engine/execution/state" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" ) @@ -28,11 +27,11 @@ type ScriptExecutionState interface { } type Engine struct { - unit *engine.Unit - log zerolog.Logger - state protocol.State - computationManager computation.ComputationManager - execState ScriptExecutionState + unit *engine.Unit + log zerolog.Logger + state protocol.State + queryExecutor *query.QueryExecutor + execState ScriptExecutionState } var _ execution.ScriptExecutor = (*Engine)(nil) @@ -40,15 +39,15 @@ var _ execution.ScriptExecutor = (*Engine)(nil) func New( logger zerolog.Logger, state protocol.State, - computationManager computation.ComputationManager, - execState state.ExecutionState, + queryExecutor *query.QueryExecutor, + execState ScriptExecutionState, ) *Engine { return &Engine{ - unit: engine.NewUnit(), - log: logger.With().Str("engine", "scripts").Logger(), - state: state, - execState: execState, - computationManager: computationManager, + unit: engine.NewUnit(), + log: logger.With().Str("engine", "scripts").Logger(), + state: state, + execState: execState, + queryExecutor: queryExecutor, } } @@ -85,7 +84,7 @@ func (e *Engine) ExecuteScriptAtBlockID( blockSnapshot := e.execState.NewStorageSnapshot(stateCommit) - return e.computationManager.ExecuteScript( + return e.queryExecutor.ExecuteScript( ctx, script, arguments, @@ -143,5 +142,5 @@ func (e *Engine) GetAccount( blockSnapshot := e.execState.NewStorageSnapshot(stateCommit) - return e.computationManager.GetAccount(ctx, addr, block, blockSnapshot) + return e.queryExecutor.GetAccount(ctx, addr, block, blockSnapshot) } From 3d3373a619ce4135f0ecbe3ef916dc18f446936c Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 24 Aug 2023 16:08:13 +0200 Subject: [PATCH 625/815] extract default options --- engine/execution/computation/manager.go | 43 ++++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/engine/execution/computation/manager.go b/engine/execution/computation/manager.go index 907e17cd8e7..a238fc5ec7d 100644 --- a/engine/execution/computation/manager.go +++ b/engine/execution/computation/manager.go @@ -106,25 +106,7 @@ func New( } chainID := vmCtx.Chain.ChainID() - - options := []fvm.Option{ - fvm.WithReusableCadenceRuntimePool( - reusableRuntime.NewReusableCadenceRuntimePool( - ReusableCadenceRuntimePoolSize, - runtime.Config{ - TracingEnabled: params.CadenceTracing, - AccountLinkingEnabled: true, - // Attachments are enabled everywhere except for Mainnet - AttachmentsEnabled: chainID != flow.Mainnet, - // Capability Controllers are enabled everywhere except for Mainnet - CapabilityControllersEnabled: chainID != flow.Mainnet, - }, - )), - } - if params.ExtensiveTracing { - options = append(options, fvm.WithExtensiveTracing()) - } - + options := DefaultFVMOptions(chainID, params.CadenceTracing, params.ExtensiveTracing) vmCtx = fvm.NewContextFromParent(vmCtx, options...) blockComputer, err := computer.NewBlockComputer( @@ -239,3 +221,26 @@ func (e *Manager) GetAccount( blockHeader, snapshot) } + +func DefaultFVMOptions(chainID flow.ChainID, cadenceTracing bool, extensiveTracing bool) []fvm.Option { + options := []fvm.Option{ + fvm.WithReusableCadenceRuntimePool( + reusableRuntime.NewReusableCadenceRuntimePool( + ReusableCadenceRuntimePoolSize, + runtime.Config{ + TracingEnabled: cadenceTracing, + AccountLinkingEnabled: true, + // Attachments are enabled everywhere except for Mainnet + AttachmentsEnabled: chainID != flow.Mainnet, + // Capability Controllers are enabled everywhere except for Mainnet + CapabilityControllersEnabled: chainID != flow.Mainnet, + }, + )), + } + + if extensiveTracing { + options = append(options, fvm.WithExtensiveTracing()) + } + + return options +} From bbf985869454871d4eca2fb686d29bdf38a9b1e1 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 24 Aug 2023 16:48:34 +0200 Subject: [PATCH 626/815] script execution builder with query config --- cmd/execution_builder.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 0c60c2cec0b..850b66dd7c6 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/onflow/flow-go/engine/execution/computation/query" + "github.com/onflow/flow-go/fvm/storage/derived" "os" "path" "path/filepath" @@ -902,10 +904,29 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( // create scripts engine for handling script execution func (exeNode *ExecutionNode) LoadScriptsEngine(node *NodeConfig) (module.ReadyDoneAware, error) { // for RPC to load it + vm := fvm.NewVirtualMachine() + + options := computation.DefaultFVMOptions(node.RootChainID, false, false) + vmCtx := fvm.NewContext(options...) + + derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize) + if err != nil { + return nil, err + } + + queryExecutor := query.NewQueryExecutor( + exeNode.exeConf.computationConfig.QueryConfig, + node.Logger, + metrics.NewExecutionCollector(node.Tracer), + vm, + vmCtx, + derivedChainData, + ) + exeNode.scriptsEng = scripts.New( node.Logger, node.State, - exeNode.computationManager, + queryExecutor, exeNode.executionState, ) From b59818fc585db56877b229546d9e597e82ca00d3 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Thu, 24 Aug 2023 08:44:13 -0700 Subject: [PATCH 627/815] Update network/p2p/middleware/middleware.go Co-authored-by: Misha --- network/p2p/middleware/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 6cabb7103b0..37cbce33c6e 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -767,7 +767,7 @@ func (m *Middleware) processMessage(scope network.IncomingMessageScope) { // - the libP2P node fails to publish the message. // // All errors returned from this function can be considered benign. -// TODO: publish has made ready to be removed from middleware, and instead the libp2pNode.Publish should be used directly. +// TODO: DO NOT USE. Publish is ready to be removed from middleware. Use libp2pNode.Publish directly. func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { return m.libP2PNode.Publish(m.ctx, msg) } From 701e14d8dd940b6da2749925d6c01b858bf3cdd1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Aug 2023 09:04:33 -0700 Subject: [PATCH 628/815] removes maybe from the test --- network/p2p/test/topic_validator_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index af72371d0df..f5ea66d44a2 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -298,11 +298,10 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { // overrides the topic to be an invalid topic corruptOutgoingMessageScope := mocknetwork.NewOutgoingMessageScope(t) - corruptOutgoingMessageScope.On("TargetIds").Return(dummyMessageScope.TargetIds()).Maybe() - corruptOutgoingMessageScope.On("Topic").Return(topic).Maybe() - corruptOutgoingMessageScope.On("Proto").Return(dummyMessageScope.Proto()).Maybe() - corruptOutgoingMessageScope.On("PayloadType").Return(dummyMessageScope.PayloadType()).Maybe() - corruptOutgoingMessageScope.On("Size").Return(dummyMessageScope.Size()).Maybe() + corruptOutgoingMessageScope.On("Topic").Return(topic) + corruptOutgoingMessageScope.On("Proto").Return(dummyMessageScope.Proto()) + corruptOutgoingMessageScope.On("PayloadType").Return(dummyMessageScope.PayloadType()) + corruptOutgoingMessageScope.On("Size").Return(dummyMessageScope.Size()) // create a dummy block proposal to publish from our SN node err = sn2.Publish(timedCtx, corruptOutgoingMessageScope) From 848a708cd6ddf8f33694d2fa196b3a8e4cf52e9a Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:33:24 -0400 Subject: [PATCH 629/815] add trace level log to improve debugging --- .../control_message_validation_inspector.go | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 768c9c36711..620b96f286b 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -210,18 +210,21 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e // - DuplicateFoundErr: if there are any duplicate message ids found in across any of the iWants. // - IWantCacheMissThresholdErr: if the rate of cache misses exceeds the configured allowed threshold. // - error: if any error occurs while sampling the iWants, all returned errors are benign and should not cause the node to crash. -func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.ControlIWant) error { +func (c *ControlMsgValidationInspector) inspectIWant(from peer.ID, iWants []*pubsub_pb.ControlIWant) error { + lastHighest := c.rpcTracker.LastHighestIHaveRPCSize() + lg := c.logger.With(). + Str("peer_id", from.String()). + Uint("max_sample_size", c.config.IWantRPCInspectionConfig.MaxSampleSize). + Int64("last_highest_ihave_rpc_size", lastHighest). + Logger() + if len(iWants) == 0 { return nil } - sampleSize := uint(10 * c.rpcTracker.LastHighestIHaveRPCSize()) + sampleSize := uint(10 * lastHighest) if sampleSize == 0 || sampleSize > c.config.IWantRPCInspectionConfig.MaxSampleSize { - c.logger.Warn(). - Uint("sample_size", sampleSize). - Uint("max_sample_size", c.config.IWantRPCInspectionConfig.MaxSampleSize). - Str(logging.KeySuspicious, "true"). // max sample size is suspicious - Str(logging.KeyNetworkingSecurity, "true"). // zero sample size is a security hole - Msg("zero or invalid sample size, using default max sample size") + // invalid or 0 sample size is suspicious + lg.Warn().Str(logging.KeySuspicious, "true").Msg("zero or invalid sample size, using default max sample size") sampleSize = c.config.IWantRPCInspectionConfig.MaxSampleSize } @@ -249,21 +252,28 @@ func (c *ControlMsgValidationInspector) inspectIWant(iWants []*pubsub_pb.Control tracker := make(duplicateStrTracker) cacheMisses := 0 - numOfAllowedCacheMisses := float64(sampleSize) * c.config.IWantRPCInspectionConfig.CacheMissThreshold + allowedCacheMissesThreshold := float64(sampleSize) * c.config.IWantRPCInspectionConfig.CacheMissThreshold duplicates := 0 - numOfAllowedDuplicates := float64(sampleSize) * c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold + allowedDuplicatesThreshold := float64(sampleSize) * c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold + + lg.Trace(). + Uint("sample_size", sampleSize). + Float64("allowed_cache_misses_threshold", allowedCacheMissesThreshold). + Float64("allowed_duplicates_threshold", allowedDuplicatesThreshold). + Msg("validating sample of message ids from iwant control message") + for _, messageID := range iWantMsgIDPool[:sampleSize] { // check duplicate allowed threshold if tracker.isDuplicate(messageID) { duplicates++ - if float64(duplicates) > numOfAllowedDuplicates { + if float64(duplicates) > allowedDuplicatesThreshold { return NewIWantDuplicateMsgIDThresholdErr(duplicates, sampleSize, c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold) } } // check cache miss threshold if !c.rpcTracker.WasIHaveRPCSent(messageID) { cacheMisses++ - if float64(cacheMisses) > numOfAllowedCacheMisses { + if float64(cacheMisses) > allowedCacheMissesThreshold { return NewIWantCacheMissThresholdErr(cacheMisses, sampleSize, c.config.IWantRPCInspectionConfig.CacheMissThreshold) } } @@ -420,7 +430,7 @@ func (c *ControlMsgValidationInspector) processInspectMsgReq(req *InspectMsgRequ // iWant validation uses new sample size validation. This will be updated for all other control message types. switch req.validationConfig.ControlMsg { case p2pmsg.CtrlMsgIWant: - if err := c.inspectIWant(req.ctrlMsg.GetIwant()); err != nil { + if err := c.inspectIWant(req.Peer, req.ctrlMsg.GetIwant()); err != nil { if IsIWantCacheMissThresholdErr(err) || IsIWantDuplicateMsgIDThresholdErr(err) { c.logAndDistributeAsyncInspectErr(req, count, err) return nil From dbb0db4df38d785dbac7d9e4f906958b08099131 Mon Sep 17 00:00:00 2001 From: Misha Date: Thu, 24 Aug 2023 12:36:27 -0400 Subject: [PATCH 630/815] clean up --- engine/common/synchronization/config.go | 1 + engine/common/synchronization/engine.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/engine/common/synchronization/config.go b/engine/common/synchronization/config.go index f2230b7c597..48f1dfcfc93 100644 --- a/engine/common/synchronization/config.go +++ b/engine/common/synchronization/config.go @@ -48,6 +48,7 @@ type SpamReportConfig struct { func NewSpamReportConfig() *SpamReportConfig { return &SpamReportConfig{ // create misbehavior report 1/100 message requests + // TODO: make this configurable as a start up flag for the engine syncRequestProbability: 0.01, } } diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 1d8c5f639d7..d89ce6ed6d7 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -473,18 +473,22 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy } } +// TODO: implement spam reporting similar to validateSyncRequestForALSP func (e *Engine) validateBatchRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } +// TODO: implement spam reporting similar to validateSyncRequestForALSP func (e *Engine) validateBlockResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } +// TODO: implement spam reporting similar to validateSyncRequestForALSP func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } +// validateSyncRequestForALSP checks if a sync request should be reported as spam. It returns a misbehavior report and a boolean indicating whether the request should be reported. func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { // Generate a random integer between 1 and spamProbabilityMultiplier (exclusive) n, err := rand.Uint32n(spamProbabilityMultiplier) @@ -520,6 +524,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f return nil, false } +// TODO: implement spam reporting similar to validateSyncRequestForALSP func (e *Engine) validateSyncResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) { return nil, false } From efdeb97e9f269bd7cdc21b4351cfa091ad126081 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:37:07 -0400 Subject: [PATCH 631/815] add debug log with cache miss and duplicate counts --- .../control_message_validation_inspector.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 620b96f286b..3429c28e39a 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -256,11 +256,12 @@ func (c *ControlMsgValidationInspector) inspectIWant(from peer.ID, iWants []*pub duplicates := 0 allowedDuplicatesThreshold := float64(sampleSize) * c.config.IWantRPCInspectionConfig.DuplicateMsgIDThreshold - lg.Trace(). + lg = lg.With(). Uint("sample_size", sampleSize). Float64("allowed_cache_misses_threshold", allowedCacheMissesThreshold). - Float64("allowed_duplicates_threshold", allowedDuplicatesThreshold). - Msg("validating sample of message ids from iwant control message") + Float64("allowed_duplicates_threshold", allowedDuplicatesThreshold).Logger() + + lg.Trace().Msg("validating sample of message ids from iwant control message") for _, messageID := range iWantMsgIDPool[:sampleSize] { // check duplicate allowed threshold @@ -279,6 +280,12 @@ func (c *ControlMsgValidationInspector) inspectIWant(from peer.ID, iWants []*pub } tracker.set(messageID) } + + lg.Debug(). + Int("cache_misses", cacheMisses). + Int("duplicates", duplicates). + Msg("iwant control message validation complete") + return nil } From c9e1dab3cfd7d55b3deddfdcd59a31cb1e2fa811 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:37:41 -0400 Subject: [PATCH 632/815] fix flag --- network/netconf/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/netconf/flags.go b/network/netconf/flags.go index 18bf88e4423..de2208a955d 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -141,7 +141,7 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { flags.Uint(iwantMaxSampleSize, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.MaxSampleSize, "max number of iwants to sample when performing validation") flags.Float64(iwantCacheMissThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max number of iwants to sample when performing validation") - flags.Float64(iwantDuplicateMsgIDThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.CacheMissThreshold, "max allowed duplicate message IDs in a single iWant control message") + flags.Float64(iwantDuplicateMsgIDThreshold, config.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs.IWantRPCInspectionConfig.DuplicateMsgIDThreshold, "max allowed duplicate message IDs in a single iWant control message") } // rpcInspectorValidationLimits utility func that adds flags for each of the validation limits for each control message type. From fb521fcda685b9bc39c8e67a9561de712f26b178 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:39:34 -0400 Subject: [PATCH 633/815] add logging.KeyNetworkingSecurity to all error logs --- .../validation/control_message_validation_inspector.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 3429c28e39a..27c818ce8e8 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -285,7 +285,7 @@ func (c *ControlMsgValidationInspector) inspectIWant(from peer.ID, iWants []*pub Int("cache_misses", cacheMisses). Int("duplicates", duplicates). Msg("iwant control message validation complete") - + return nil } @@ -333,6 +333,7 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingRpc(from peer.ID, v lg.Error(). Err(err). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Msg("failed to distribute invalid control message notification") return err } @@ -389,12 +390,14 @@ func (c *ControlMsgValidationInspector) blockingPreprocessingSampleRpc(from peer lg.Warn(). Err(err). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Msg("topic validation pre-processing failed rejecting rpc control message") disErr := c.distributor.Distribute(p2p.NewInvalidControlMessageNotification(from, validationConfig.ControlMsg, count, err)) if disErr != nil { lg.Error(). Err(disErr). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Msg("failed to distribute invalid control message notification") return disErr } @@ -477,12 +480,14 @@ func (c *ControlMsgValidationInspector) processInspectMsgReq(req *InspectMsgRequ lg.Error(). Err(validationErr). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Msg("rpc control message async inspection failed") err := c.distributor.Distribute(p2p.NewInvalidControlMessageNotification(req.Peer, req.validationConfig.ControlMsg, count, validationErr)) if err != nil { lg.Error(). Err(err). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Msg("failed to distribute invalid control message notification") } } @@ -720,6 +725,7 @@ func (c *ControlMsgValidationInspector) checkClusterPrefixHardThreshold(nodeID f func (c *ControlMsgValidationInspector) logAndDistributeAsyncInspectErr(req *InspectMsgRequest, count uint64, err error) { lg := c.logger.With(). Bool(logging.KeySuspicious, true). + Bool(logging.KeyNetworkingSecurity, true). Str("peer_id", req.Peer.String()). Str("ctrl_msg_type", string(req.validationConfig.ControlMsg)). Uint64("ctrl_msg_count", count).Logger() From ba7406e0aa4ba280fd40822c66774229fd596d29 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:39:41 -0400 Subject: [PATCH 634/815] Update config/default-config.yml Co-authored-by: Misha --- config/default-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/default-config.yml b/config/default-config.yml index 13a17d8750a..864e51f49c8 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -109,7 +109,7 @@ network-config: # Max number of iwant messages in a sample to be inspected gossipsub-rpc-iwant-max-sample-size: 1_000_000 - # The allowed threshold of iWant messages receievd without a corresponding tracked iHave message that was sent + # The allowed threshold of iWant messages received without a corresponding tracked iHave message that was sent gossipsub-rpc-iwant-cache-miss-threshold: .5 # The max allowed duplicate message IDs in a single iWant control message gossipsub-rpc-iwant-duplicate-message-id-threshold: .15 From 6d226adce4e8761b2e3af14f7476c88034dab425 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:39:49 -0400 Subject: [PATCH 635/815] Update insecure/corruptlibp2p/fixtures.go Co-authored-by: Misha --- insecure/corruptlibp2p/fixtures.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insecure/corruptlibp2p/fixtures.go b/insecure/corruptlibp2p/fixtures.go index 9b37a046435..2e281a6bbd1 100644 --- a/insecure/corruptlibp2p/fixtures.go +++ b/insecure/corruptlibp2p/fixtures.go @@ -46,8 +46,8 @@ func WithIHave(msgCount, msgSize int, topicId string) GossipSubCtrlOption { // The message IDs are generated randomly. // Args: // -// msgCount: number of iWant messages to add. -// msgIdsPerIWant: number of message IDs to add to each iWant message. +// msgCount: number of iWant messages to add. +// msgIdsPerIWant: number of message IDs to add to each iWant message. // // Returns: // A GossipSubCtrlOption that adds iWant messages to the control message. From acb84a024440cdaf6003ba5253688fb8cc864be4 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:41:07 -0400 Subject: [PATCH 636/815] rename inspectDisseminatedNotif -> inspectDisseminatedNotifyFunc --- .../validation_inspector_test.go | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index a3cb5e869b4..39d55e06b1e 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -155,7 +155,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { invPruneNotifCount := atomic.NewUint64(0) done := make(chan struct{}) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -184,7 +184,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(2, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(2, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -255,7 +255,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { invIhaveNotifCount := atomic.NewUint64(0) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -283,7 +283,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -351,7 +351,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { invPruneNotifCount := atomic.NewUint64(0) done := make(chan struct{}) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -381,7 +381,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(4, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(4, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -481,7 +481,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { done := make(chan struct{}) expectedNumOfTotalNotif := 9 // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -515,7 +515,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -621,7 +621,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { expectedNumOfTotalNotif := 2 invGraftNotifCount := atomic.NewUint64(0) invPruneNotifCount := atomic.NewUint64(0) - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -651,7 +651,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -731,7 +731,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { expectedNumOfTotalNotif := 2 invGraftNotifCount := atomic.NewUint64(0) invPruneNotifCount := atomic.NewUint64(0) - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -760,7 +760,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -833,7 +833,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T done := make(chan struct{}) expectedNumOfTotalNotif := 5 invGraftNotifCount := atomic.NewUint64(0) - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -860,7 +860,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -928,7 +928,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T done := make(chan struct{}) expectedNumOfTotalNotif := 5 invPruneNotifCount := atomic.NewUint64(0) - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -955,7 +955,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -1031,7 +1031,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { expectedNumOfTotalNotif := 2 invGraftNotifCount := atomic.NewUint64(0) invPruneNotifCount := atomic.NewUint64(0) - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -1060,7 +1060,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(expectedNumOfTotalNotif, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -1130,7 +1130,7 @@ func TestValidationInspector_InspectIWants_DuplicateMessageIDs(t *testing.T) { duplicateMessageIDNotifCount := atomic.NewUint64(0) done := make(chan struct{}) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) @@ -1152,7 +1152,7 @@ func TestValidationInspector_InspectIWants_DuplicateMessageIDs(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(2, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(2, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := mockp2p.NewPubSubTracer(t) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -1223,7 +1223,7 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { cacheMissThresholdNotifCount := atomic.NewUint64(0) done := make(chan struct{}) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) @@ -1245,7 +1245,7 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), @@ -1319,7 +1319,7 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) controlMessageCount := int64(1) done := make(chan struct{}) // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotif := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { return func(args mockery.Arguments) { defer close(done) notification, ok := args[0].(*p2p.InvCtrlMsgNotif) @@ -1338,7 +1338,7 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(1, inspectDisseminatedNotif)(distributor, spammer) + withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) meshTracer := meshTracerFixture(flowConfig, idProvider) validationInspector, err := validation.NewControlMsgValidationInspector( unittest.Logger(), From 4090cb028094b6a1657c37e30e6753c78c18f6ea Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:42:27 -0400 Subject: [PATCH 637/815] move utils to utils file --- .../test/gossipsub/rpc_inspector/utils.go | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go index d10a036ab12..2382a0c56a1 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go @@ -2,14 +2,19 @@ package rpc_inspector import ( "context" + "fmt" "testing" "time" + mockery "github.com/stretchr/testify/mock" + + "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p" + mockp2p "github.com/onflow/flow-go/network/p2p/mock" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" ) @@ -31,3 +36,20 @@ func stopTestComponents(t *testing.T, cancel context.CancelFunc, nodes []p2p.Lib p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) unittest.RequireComponentsDoneBefore(t, time.Second, components...) } + +func randomClusterPrefixedTopic() channels.Topic { + return channels.Topic(channels.SyncCluster(flow.ChainID(fmt.Sprintf("%d", rand.Uint64())))) +} + +type onNotificationDissemination func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) +type mockDistributorOption func(*mockp2p.GossipSubInspectorNotificationDistributor, *corruptlibp2p.GossipSubRouterSpammer) + +func withExpectedNotificationDissemination(expectedNumOfTotalNotif int, f onNotificationDissemination) mockDistributorOption { + return func(distributor *mockp2p.GossipSubInspectorNotificationDistributor, spammer *corruptlibp2p.GossipSubRouterSpammer) { + distributor. + On("Distribute", mockery.Anything). + Times(expectedNumOfTotalNotif). + Run(f(spammer)). + Return(nil) + } +} From 45e77ff25386203eff692ea3b5907f94227a46c8 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:42:30 -0400 Subject: [PATCH 638/815] Update validation_inspector_test.go --- .../rpc_inspector/validation_inspector_test.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 39d55e06b1e..256cba9bd60 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -3,7 +3,6 @@ package rpc_inspector import ( "context" "fmt" - "math/rand" "os" "testing" "time" @@ -1388,23 +1387,6 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") } -func randomClusterPrefixedTopic() channels.Topic { - return channels.Topic(channels.SyncCluster(flow.ChainID(fmt.Sprintf("%d", rand.Uint64())))) -} - -type onNotificationDissemination func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) -type mockDistributorOption func(*mockp2p.GossipSubInspectorNotificationDistributor, *corruptlibp2p.GossipSubRouterSpammer) - -func withExpectedNotificationDissemination(expectedNumOfTotalNotif int, f onNotificationDissemination) mockDistributorOption { - return func(distributor *mockp2p.GossipSubInspectorNotificationDistributor, spammer *corruptlibp2p.GossipSubRouterSpammer) { - distributor. - On("Distribute", mockery.Anything). - Times(expectedNumOfTotalNotif). - Run(f(spammer)). - Return(nil) - } -} - // TestGossipSubSpamMitigationIntegration tests that the spam mitigation feature of GossipSub is working as expected. // The test puts toghether the spam detection (through the GossipSubInspector) and the spam mitigation (through the // scoring system) and ensures that the mitigation is triggered when the spam detection detects spam. From a6d9eaa13b7aff050096cce4a1f707a4b3552f23 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:43:59 -0400 Subject: [PATCH 639/815] move utils to utils file --- .../test/gossipsub/rpc_inspector/utils.go | 34 +++++++++++++++++++ .../validation_inspector_test.go | 32 ----------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go index 2382a0c56a1..5b1a0b64e73 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go @@ -3,19 +3,23 @@ package rpc_inspector import ( "context" "fmt" + "math/rand" "testing" "time" mockery "github.com/stretchr/testify/mock" + "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p" mockp2p "github.com/onflow/flow-go/network/p2p/mock" p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/utils/unittest" ) @@ -53,3 +57,33 @@ func withExpectedNotificationDissemination(expectedNumOfTotalNotif int, f onNoti Return(nil) } } + +// mockDistributorReadyDoneAware mocks the Ready and Done methods of the distributor to return a channel that is already closed, +// so that the distributor is considered ready and done when the test needs. +func mockDistributorReadyDoneAware(d *mockp2p.GossipSubInspectorNotificationDistributor) { + d.On("Start", mockery.Anything).Return().Maybe() + d.On("Ready").Return(func() <-chan struct{} { + ch := make(chan struct{}) + close(ch) + return ch + }()).Maybe() + d.On("Done").Return(func() <-chan struct{} { + ch := make(chan struct{}) + close(ch) + return ch + }()).Maybe() +} + +func meshTracerFixture(flowConfig *config.FlowConfig, idProvider module.IdentityProvider) *tracer.GossipSubMeshTracer { + meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ + Logger: unittest.Logger(), + Metrics: metrics.NewNoopCollector(), + IDProvider: idProvider, + LoggerInterval: time.Second, + HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), + RpcSentTrackerCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, + RpcSentTrackerWorkerQueueCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, + RpcSentTrackerNumOfWorkers: flowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, + } + return tracer.NewGossipSubMeshTracer(meshTracerCfg) +} diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 256cba9bd60..0038e430ba9 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -19,7 +19,6 @@ import ( "github.com/onflow/flow-go/insecure/corruptlibp2p" "github.com/onflow/flow-go/insecure/internal" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/mock" @@ -29,7 +28,6 @@ import ( p2pmsg "github.com/onflow/flow-go/network/p2p/message" mockp2p "github.com/onflow/flow-go/network/p2p/mock" p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/utils/unittest" ) @@ -1494,33 +1492,3 @@ func TestGossipSubSpamMitigationIntegration(t *testing.T) { return unittest.ProposalFixture() }) } - -// mockDistributorReadyDoneAware mocks the Ready and Done methods of the distributor to return a channel that is already closed, -// so that the distributor is considered ready and done when the test needs. -func mockDistributorReadyDoneAware(d *mockp2p.GossipSubInspectorNotificationDistributor) { - d.On("Start", mockery.Anything).Return().Maybe() - d.On("Ready").Return(func() <-chan struct{} { - ch := make(chan struct{}) - close(ch) - return ch - }()).Maybe() - d.On("Done").Return(func() <-chan struct{} { - ch := make(chan struct{}) - close(ch) - return ch - }()).Maybe() -} - -func meshTracerFixture(flowConfig *config.FlowConfig, idProvider module.IdentityProvider) *tracer.GossipSubMeshTracer { - meshTracerCfg := &tracer.GossipSubMeshTracerConfig{ - Logger: unittest.Logger(), - Metrics: metrics.NewNoopCollector(), - IDProvider: idProvider, - LoggerInterval: time.Second, - HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), - RpcSentTrackerCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerCacheSize, - RpcSentTrackerWorkerQueueCacheSize: flowConfig.NetworkConfig.GossipSubConfig.RPCSentTrackerQueueCacheSize, - RpcSentTrackerNumOfWorkers: flowConfig.NetworkConfig.GossipSubConfig.RpcSentTrackerNumOfWorkers, - } - return tracer.NewGossipSubMeshTracer(meshTracerCfg) -} From e249547f36426b32725c0dba1d3e91d24eaf68a7 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 12:44:47 -0400 Subject: [PATCH 640/815] Update network/p2p/inspector/validation/control_message_validation_inspector.go Co-authored-by: Misha --- .../validation/control_message_validation_inspector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/inspector/validation/control_message_validation_inspector.go b/network/p2p/inspector/validation/control_message_validation_inspector.go index 27c818ce8e8..e24ddfbe3fe 100644 --- a/network/p2p/inspector/validation/control_message_validation_inspector.go +++ b/network/p2p/inspector/validation/control_message_validation_inspector.go @@ -207,7 +207,7 @@ func (c *ControlMsgValidationInspector) Inspect(from peer.ID, rpc *pubsub.RPC) e // Args: // - iWant: the list of iWant control messages. // Returns: -// - DuplicateFoundErr: if there are any duplicate message ids found in across any of the iWants. +// - DuplicateFoundErr: if there are any duplicate message ids found in any of the iWants. // - IWantCacheMissThresholdErr: if the rate of cache misses exceeds the configured allowed threshold. // - error: if any error occurs while sampling the iWants, all returned errors are benign and should not cause the node to crash. func (c *ControlMsgValidationInspector) inspectIWant(from peer.ID, iWants []*pubsub_pb.ControlIWant) error { From eb6434d342b07740e95c499ada3801904f1fe9fb Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 14:37:19 -0400 Subject: [PATCH 641/815] fix test --- .../functional/test/gossipsub/rpc_inspector/utils.go | 4 ++-- network/p2p/tracer/internal/rpc_sent_tracker_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go index 5b1a0b64e73..4760a598510 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/utils.go @@ -25,7 +25,7 @@ import ( // StartNodesAndEnsureConnected starts the victim and spammer node and ensures they are both connected. func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContext, nodes []p2p.LibP2PNode, sporkID flow.Identifier) { - p2ptest.StartNodes(t, ctx, nodes, 5*time.Second) + p2ptest.StartNodes(t, ctx, nodes) // prior to the test we should ensure that spammer and victim connect. // this is vital as the spammer will circumvent the normal pubsub subscription mechanism and send iHAVE messages directly to the victim. // without a prior connection established, directly spamming pubsub messages may cause a race condition in the pubsub implementation. @@ -37,7 +37,7 @@ func startNodesAndEnsureConnected(t *testing.T, ctx irrecoverable.SignalerContex } func stopTestComponents(t *testing.T, cancel context.CancelFunc, nodes []p2p.LibP2PNode, components ...module.ReadyDoneAware) { - p2ptest.StopNodes(t, nodes, cancel, 5*time.Second) + p2ptest.StopNodes(t, nodes, cancel) unittest.RequireComponentsDoneBefore(t, time.Second, components...) } diff --git a/network/p2p/tracer/internal/rpc_sent_tracker_test.go b/network/p2p/tracer/internal/rpc_sent_tracker_test.go index 9b571e3d5a6..208bbc42940 100644 --- a/network/p2p/tracer/internal/rpc_sent_tracker_test.go +++ b/network/p2p/tracer/internal/rpc_sent_tracker_test.go @@ -83,13 +83,13 @@ func TestRPCSentTracker_DuplicateMessageID(t *testing.T) { processedWorkLogs := atomic.NewInt64(0) hook := zerolog.HookFunc(func(e *zerolog.Event, level zerolog.Level, message string) { - if level == zerolog.InfoLevel { + if level == zerolog.DebugLevel { if message == iHaveRPCTrackedLog { processedWorkLogs.Inc() } } }) - logger := zerolog.New(os.Stdout).Level(zerolog.InfoLevel).Hook(hook) + logger := zerolog.New(os.Stdout).Level(zerolog.DebugLevel).Hook(hook) tracker := mockTracker(t, time.Minute) require.NotNil(t, tracker) From 897cb98946d715d25a9048b7cee53cd9770c2498 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Thu, 24 Aug 2023 14:52:10 -0400 Subject: [PATCH 642/815] remove old test --- .../validation_inspector_test.go | 93 ------------------- 1 file changed, 93 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 14b54aaa702..bb88c1e1562 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -1110,99 +1110,6 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { require.Equal(t, uint64(1), invPruneNotifCount.Load()) } -// TestValidationInspector_InspectIWants_DuplicateMessageIDs ensures that expected invalid control message notification is disseminated when iWant control messages containing -// duplicate messages Ids are encountered during validation. -func TestValidationInspector_InspectIWants_DuplicateMessageIDs(t *testing.T) { - t.Parallel() - role := flow.RoleConsensus - sporkID := unittest.IdentifierFixture() - // create our RPC validation inspector - flowConfig, err := config.DefaultConfig() - require.NoError(t, err) - inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs - inspectorConfig.NumberOfWorkers = 1 - - messageCount := 2 - controlMessageCount := int64(1) - duplicateMessageIDNotifCount := atomic.NewUint64(0) - done := make(chan struct{}) - // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { - return func(args mockery.Arguments) { - notification, ok := args[0].(*p2p.InvCtrlMsgNotif) - require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) - require.Equal(t, uint64(messageCount), notification.Count) - require.True(t, validation.IsDuplicateFoundErr(notification.Err)) - duplicateMessageIDNotifCount.Inc() - if duplicateMessageIDNotifCount.Load() == 2 { - close(done) - } - } - } - - idProvider := mock.NewIdentityProvider(t) - spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) - - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - - distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) - mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(2, inspectDisseminatedNotifyFunc)(distributor, spammer) - meshTracer := mockp2p.NewPubSubTracer(t) - validationInspector, err := validation.NewControlMsgValidationInspector( - unittest.Logger(), - sporkID, - &inspectorConfig, - distributor, - metrics.NewNoopCollector(), - metrics.NewNoopCollector(), - idProvider, - metrics.NewNoopCollector(), - meshTracer) - require.NoError(t, err) - corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) - victimNode, victimIdentity := p2ptest.NodeFixture( - t, - sporkID, - t.Name(), - idProvider, - p2ptest.WithRole(role), - p2ptest.WithGossipSubTracer(meshTracer), - internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), - corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), - ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() - - duplicateMessageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) - // create iWants control messages where the first 2 iwants contain a duplicate message ID "duplicate" - iWantFactory := func() []pb.ControlMessage { - ctlWithiWantDuplicateMsgIDs := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) - // add some non duplicates to our duplicate message IDs - ctlWithiWantDuplicateMsgIDs[0].Iwant[0].MessageIDs = append(duplicateMessageIDs, corruptlibp2p.GossipSubMessageIdsFixture(5)...) - ctlWithiWantDuplicateMsgIDs[0].Iwant[1].MessageIDs = append(duplicateMessageIDs, corruptlibp2p.GossipSubMessageIdsFixture(5)...) - return ctlWithiWantDuplicateMsgIDs - } - - // avoid cache miss threshold errors - meshTracer.On("WasIHaveRPCSent", mockery.AnythingOfType("string")).Return(true).Maybe() - meshTracer.On("LastHighestIHaveRPCSize").Return(int64(100)) - - validationInspector.Start(signalerCtx) - nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} - startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) - spammer.Start(t) - defer stopTestComponents(t, cancel, nodes, validationInspector) - - // spam the victim with 2 iWant control messages that contain duplicate message IDs, we expect to observe 2 invalid control message notifications disseminated - spammer.SpamControlMessage(t, victimNode, iWantFactory()) - spammer.SpamControlMessage(t, victimNode, iWantFactory()) - - unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") -} - // TestValidationInspector_InspectIWants_CacheMissThreshold ensures that expected invalid control message notification is disseminated when the number of iWant message Ids // without a corresponding iHave message sent with the same message ID exceeds the configured cache miss threshold. func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { From cf81e765ab88e39383cfd747e8dec64875084713 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Aug 2023 12:37:02 -0700 Subject: [PATCH 643/815] fixes tests echo and ping --- network/test/middleware_test.go | 295 ++++++++++++++------------------ 1 file changed, 130 insertions(+), 165 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index d3ae428322d..9e3cd5d9872 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -466,12 +466,11 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { ctx, cancel := context.WithCancel(m.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) - defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) newMw.Start(irrecoverableCtx) - unittest.RequireComponentsReadyBefore(m.T(), 100*time.Millisecond, newMw) - + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) idList := flow.IdentityList(append(m.ids, newId)) @@ -479,22 +478,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // needed to enable ID translation m.providers[0].SetIdentities(idList) - // create message with about 400bytes (300 random bytes + 100bytes message info) - b := make([]byte, 300) - for i := range b { - b[i] = byte('X') - } - - msg, err := message.NewOutgoingScope( - flow.IdentifierList{newId.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: string(b), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // update the addresses m.mws[0].UpdateNodeAddresses() @@ -504,10 +487,19 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // peer should be removed by the peer manager. p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) + // create message with about 400bytes (300 random bytes + 100bytes message info) + b := make([]byte, 300) + for i := range b { + b[i] = byte('X') + } // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited // as it is more than our allowed bandwidth of 1000 bytes. + con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) for i := 0; i < 3; i++ { - err = m.mws[0].SendDirect(msg) + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: string(b), + }, newId.NodeID) require.NoError(m.T(), err) } @@ -523,16 +515,10 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // eventually the rate limited node should be able to reconnect and send messages require.Eventually(m.T(), func() bool { - msg, err = message.NewOutgoingScope( - flow.IdentifierList{newId.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: "", - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - return m.mws[0].SendDirect(msg) == nil + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: "", + }, newId.NodeID) + return err == nil }, 3*time.Second, 100*time.Millisecond) // shutdown our middleware so that each message can be processed @@ -559,19 +545,6 @@ func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvid return overlay } -// TestMultiPing tests the middleware against type of received payload -// of distinct messages that are sent concurrently from a node to another -func (m *MiddlewareTestSuite) TestMultiPing() { - // one distinct message - m.MultiPing(1) - - // two distinct messages - m.MultiPing(2) - - // 10 distinct messages - m.MultiPing(10) -} - // TestPing sends a message from the first middleware of the test suit to the last one and checks that the // last middleware receives the message and that the message is correctly decoded. func (m *MiddlewareTestSuite) TestPing() { @@ -581,45 +554,58 @@ func (m *MiddlewareTestSuite) TestPing() { var err error // mocks Overlay.Receive for middleware.Overlay.Receive(*nodeID, payload) - firstNodeIndex := 0 - lastNodeIndex := m.size - 1 + senderNodeIndex := 0 + targetNodeIndex := m.size - 1 expectedPayload := "TestPingContentReception" - msg, err := message.NewOutgoingScope( - flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: expectedPayload, - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + // mocks a target engine on the last node of the test suit that will receive the message on the test channel. + targetEngine := &mocknetwork.MessageProcessor{} // target engine on the last node of the test suit that will receive the message + _, err = m.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) require.NoError(m.T(), err) - - m.ov[lastNodeIndex].On("Receive", mockery.Anything).Return(nil).Once(). + // target engine must receive the message once with the expected payload + targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { receiveWG.Done() - msg, ok := args[0].(network.IncomingMessageScope) + msgChannel, ok := args[0].(channels.Channel) require.True(m.T(), ok) + require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel - require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel - require.Equal(m.T(), m.ids[firstNodeIndex].NodeID, msg.OriginId()) // sender id - require.Equal(m.T(), m.ids[lastNodeIndex].NodeID, msg.TargetIDs()[0]) // target id - require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - require.Equal(m.T(), expectedPayload, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload - }) + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id + + msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.Equal(m.T(), expectedPayload, msgPayload.Text) // payload + }).Return(nil).Once() // sends a direct message from first node to the last node - err = m.mws[firstNodeIndex].SendDirect(msg) + con0, err := m.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: expectedPayload, + }, m.ids[targetNodeIndex].NodeID) require.NoError(m.Suite.T(), err) - unittest.RequireReturnsBefore(m.T(), receiveWG.Wait, 1000*time.Millisecond, "did not receive message") + unittest.RequireReturnsBefore(m.T(), receiveWG.Wait, 1*time.Second, "did not receive message") +} - // evaluates the mock calls - for i := 1; i < m.size; i++ { - m.ov[i].AssertExpectations(m.T()) - } +// TestMultiPing_TwoPings sends two messages concurrently from the first middleware of the test suit to the last one, +// and checks that the last middleware receives the messages and that the messages are correctly decoded. +func (m *MiddlewareTestSuite) TestMultiPing_TwoPings() { + m.MultiPing(2) +} + +// TestMultiPing_FourPings sends four messages concurrently from the first middleware of the test suit to the last one, +// and checks that the last middleware receives the messages and that the messages are correctly decoded. +func (m *MiddlewareTestSuite) TestMultiPing_FourPings() { + m.MultiPing(4) +} +// TestMultiPing_EightPings sends eight messages concurrently from the first middleware of the test suit to the last one, +// and checks that the last middleware receives the messages and that the messages are correctly decoded. +func (m *MiddlewareTestSuite) TestMultiPing_EightPings() { + m.MultiPing(8) } // MultiPing sends count-many distinct messages concurrently from the first middleware of the test suit to the last one. @@ -628,52 +614,56 @@ func (m *MiddlewareTestSuite) TestPing() { func (m *MiddlewareTestSuite) MultiPing(count int) { receiveWG := sync.WaitGroup{} sendWG := sync.WaitGroup{} - // extracts sender id based on the mock option - // mocks Overlay.Receive for middleware.Overlay.Receive(*nodeID, payload) - firstNodeIndex := 0 - lastNodeIndex := m.size - 1 + senderNodeIndex := 0 + targetNodeIndex := m.size - 1 receivedPayloads := unittest.NewProtectedMap[string, struct{}]() // keep track of unique payloads received. // regex to extract the payload from the message regex := regexp.MustCompile(`^hello from: \d`) + // creates a conduit on sender to send messages to the target on the test channel. + con0, err := m.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) + + // mocks a target engine on the last node of the test suit that will receive the message on the test channel. + targetEngine := &mocknetwork.MessageProcessor{} + _, err = m.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) + targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). + Run(func(args mockery.Arguments) { + receiveWG.Done() + + msgChannel, ok := args[0].(channels.Channel) + require.True(m.T(), ok) + require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id + + msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + // payload + require.True(m.T(), regex.MatchString(msgPayload.Text)) + require.False(m.T(), receivedPayloads.Has(msgPayload.Text)) // payload must be unique + receivedPayloads.Add(msgPayload.Text, struct{}{}) + }).Return(nil) + for i := 0; i < count; i++ { receiveWG.Add(1) sendWG.Add(1) expectedPayloadText := fmt.Sprintf("hello from: %d", i) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{m.ids[lastNodeIndex].NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: expectedPayloadText, - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) require.NoError(m.T(), err) + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: expectedPayloadText, + }, m.ids[targetNodeIndex].NodeID) + require.NoError(m.Suite.T(), err) - m.ov[lastNodeIndex].On("Receive", mockery.Anything).Return(nil).Once(). - Run(func(args mockery.Arguments) { - receiveWG.Done() - - msg, ok := args[0].(network.IncomingMessageScope) - require.True(m.T(), ok) - - require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel - require.Equal(m.T(), m.ids[firstNodeIndex].NodeID, msg.OriginId()) // sender id - require.Equal(m.T(), m.ids[lastNodeIndex].NodeID, msg.TargetIDs()[0]) // target id - require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - - // payload - decodedPayload := msg.DecodedPayload().(*libp2pmessage.TestMessage).Text - require.True(m.T(), regex.MatchString(decodedPayload)) - require.False(m.T(), receivedPayloads.Has(decodedPayload)) // payload must be unique - receivedPayloads.Add(decodedPayload, struct{}{}) - }) go func() { // sends a direct message from first node to the last node - err := m.mws[firstNodeIndex].SendDirect(msg) + err := con0.Unicast(&libp2pmessage.TestMessage{ + Text: expectedPayloadText, + }, m.ids[targetNodeIndex].NodeID) require.NoError(m.Suite.T(), err) sendWG.Done() @@ -682,11 +672,6 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { unittest.RequireReturnsBefore(m.T(), sendWG.Wait, 1*time.Second, "could not send unicasts on time") unittest.RequireReturnsBefore(m.T(), receiveWG.Wait, 1*time.Second, "could not receive unicasts on time") - - // evaluates the mock calls - for i := 1; i < m.size; i++ { - m.ov[i].AssertExpectations(m.T()) - } } // TestEcho sends an echo message from first middleware to the last middleware @@ -701,86 +686,66 @@ func (m *MiddlewareTestSuite) TestEcho() { // mocks Overlay.Receive for middleware.Overlay.Receive(*nodeID, payload) first := 0 last := m.size - 1 - firstNode := m.ids[first].NodeID - lastNode := m.ids[last].NodeID + // mocks a target engine on the first and last nodes of the test suit that will receive the message on the test channel. + targetEngine1 := &mocknetwork.MessageProcessor{} + con1, err := m.networks[first].Register(channels.TestNetworkChannel, targetEngine1) + require.NoError(m.T(), err) + targetEngine2 := &mocknetwork.MessageProcessor{} + con2, err := m.networks[last].Register(channels.TestNetworkChannel, targetEngine2) + require.NoError(m.T(), err) // message sent from first node to the last node. expectedSendMsg := "TestEcho" - sendMsg, err := message.NewOutgoingScope( - flow.IdentifierList{lastNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: expectedSendMsg, - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // reply from last node to the first node. expectedReplyMsg := "TestEcho response" - replyMsg, err := message.NewOutgoingScope( - flow.IdentifierList{firstNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: expectedReplyMsg, - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // last node - m.ov[last].On("Receive", mockery.Anything).Return(nil).Once(). + // mocks the target engine on the last node of the test suit that will receive the message on the test channel, and + // echos back the reply message to the sender. + targetEngine2.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { wg.Done() - // sanity checks the message content. - msg, ok := args[0].(network.IncomingMessageScope) + msgChannel, ok := args[0].(channels.Channel) require.True(m.T(), ok) + require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel - require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel - require.Equal(m.T(), m.ids[first].NodeID, msg.OriginId()) // sender id - require.Equal(m.T(), lastNode, msg.TargetIDs()[0]) // target id - require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - require.Equal(m.T(), expectedSendMsg, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload - // event id - eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) - require.NoError(m.T(), err) - require.True(m.T(), bytes.Equal(eventId, msg.EventID())) + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[first].NodeID, msgOriginID) // sender id - // echos back the same message back to the sender - err = m.mws[last].SendDirect(replyMsg) - assert.NoError(m.T(), err) - }) + msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.Equal(m.T(), expectedSendMsg, msgPayload.Text) // payload - // first node - m.ov[first].On("Receive", mockery.Anything).Return(nil).Once(). + // echos back the same message back to the sender + require.NoError(m.T(), con2.Unicast(&libp2pmessage.TestMessage{ + Text: expectedReplyMsg, + }, m.ids[first].NodeID)) + }).Return(nil).Once() + + // mocks the target engine on the last node of the test suit that will receive the message on the test channel, and + // echos back the reply message to the sender. + targetEngine1.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { wg.Done() - // sanity checks the message content. - msg, ok := args[0].(network.IncomingMessageScope) + + msgChannel, ok := args[0].(channels.Channel) require.True(m.T(), ok) + require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel - require.Equal(m.T(), channels.TestNetworkChannel, msg.Channel()) // channel - require.Equal(m.T(), m.ids[last].NodeID, msg.OriginId()) // sender id - require.Equal(m.T(), firstNode, msg.TargetIDs()[0]) // target id - require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - require.Equal(m.T(), expectedReplyMsg, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload - // event id - eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) - require.NoError(m.T(), err) - require.True(m.T(), bytes.Equal(eventId, msg.EventID())) - }) + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[last].NodeID, msgOriginID) // sender id - // sends a direct message from first node to the last node - err = m.mws[first].SendDirect(sendMsg) - require.NoError(m.Suite.T(), err) + msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.Equal(m.T(), expectedReplyMsg, msgPayload.Text) // payload + }).Return(nil) - unittest.RequireReturnsBefore(m.T(), wg.Wait, 100*time.Second, "could not receive unicast on time") + // sends a direct message from first node to the last node + require.NoError(m.T(), con1.Unicast(&libp2pmessage.TestMessage{ + Text: expectedSendMsg, + }, m.ids[last].NodeID)) - // evaluates the mock calls - for i := 1; i < m.size; i++ { - m.ov[i].AssertExpectations(m.T()) - } + unittest.RequireReturnsBefore(m.T(), wg.Wait, 5*time.Second, "could not receive unicast on time") } // TestMaxMessageSize_SendDirect evaluates that invoking SendDirect method of the middleware on a message From fd03e0f42f703e432b0bd4a641341def89df512a Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 24 Aug 2023 22:50:05 +0100 Subject: [PATCH 644/815] fix goimports --- .../access/rpc/backend/mock/communicator.go | 3 +-- engine/access/rpc/rate_limit_test.go | 25 ++++++++++--------- .../mock/get_execution_data_func.go | 3 ++- .../mock/get_start_height_func.go | 3 ++- module/metrics.go | 3 ++- module/metrics/access.go | 5 ++-- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/engine/access/rpc/backend/mock/communicator.go b/engine/access/rpc/backend/mock/communicator.go index 9989e30753b..ab7498ac8f8 100644 --- a/engine/access/rpc/backend/mock/communicator.go +++ b/engine/access/rpc/backend/mock/communicator.go @@ -3,9 +3,8 @@ package mock import ( - mock "github.com/stretchr/testify/mock" - flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" ) // Communicator is an autogenerated mock type for the Communicator type diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index a816dc4a41e..ef0bcbc1258 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -8,6 +8,18 @@ import ( "testing" "time" + accessproto "github.com/onflow/flow/protobuf/go/flow/access" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rpc/backend" "github.com/onflow/flow-go/model/flow" @@ -20,17 +32,6 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/unittest" - accessproto "github.com/onflow/flow/protobuf/go/flow/access" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" ) type RateLimitTestSuite struct { @@ -145,7 +146,7 @@ func (suite *RateLimitTestSuite) SetupTest() { block := unittest.BlockHeaderFixture() suite.snapshot.On("Head").Return(block, nil) - + bnd := backend.New(backend.Params{ State: suite.state, CollectionRPC: suite.collClient, diff --git a/engine/access/state_stream/mock/get_execution_data_func.go b/engine/access/state_stream/mock/get_execution_data_func.go index 50fe8087e21..506cc8d3d1c 100644 --- a/engine/access/state_stream/mock/get_execution_data_func.go +++ b/engine/access/state_stream/mock/get_execution_data_func.go @@ -5,8 +5,9 @@ package mock import ( context "context" - execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" mock "github.com/stretchr/testify/mock" + + execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" ) // GetExecutionDataFunc is an autogenerated mock type for the GetExecutionDataFunc type diff --git a/engine/access/state_stream/mock/get_start_height_func.go b/engine/access/state_stream/mock/get_start_height_func.go index b97a77e1d39..d1c93d2ed1c 100644 --- a/engine/access/state_stream/mock/get_start_height_func.go +++ b/engine/access/state_stream/mock/get_start_height_func.go @@ -3,8 +3,9 @@ package mock import ( - flow "github.com/onflow/flow-go/model/flow" mock "github.com/stretchr/testify/mock" + + flow "github.com/onflow/flow-go/model/flow" ) // GetStartHeightFunc is an autogenerated mock type for the GetStartHeightFunc type diff --git a/module/metrics.go b/module/metrics.go index b1bfbad81ae..3c1d6dfdc37 100644 --- a/module/metrics.go +++ b/module/metrics.go @@ -6,11 +6,12 @@ import ( "github.com/libp2p/go-libp2p/core/peer" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + httpmetrics "github.com/slok/go-http-metrics/metrics" + "github.com/onflow/flow-go/model/chainsync" "github.com/onflow/flow-go/model/cluster" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/network/channels" - httpmetrics "github.com/slok/go-http-metrics/metrics" ) type EntriesFunc func() uint diff --git a/module/metrics/access.go b/module/metrics/access.go index effb24c4326..1116f87f433 100644 --- a/module/metrics/access.go +++ b/module/metrics/access.go @@ -1,10 +1,11 @@ package metrics import ( - "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/counters" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/counters" ) type AccessCollectorOpts func(*AccessCollector) From 04d7ec1fb50330c2eba37a5879b47cdeeca220c9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 24 Aug 2023 15:22:55 -0700 Subject: [PATCH 645/815] fixes all tests in middleware test suite --- network/p2p/p2pnode/libp2pNode.go | 5 +- .../p2p/subscription/subscriptionManager.go | 2 +- network/test/middleware_test.go | 186 ++++++++---------- 3 files changed, 86 insertions(+), 107 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index c66918a0487..ac8dcb6fe85 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -416,14 +416,13 @@ func (n *Node) unsubscribeTopic(topic channels.Topic) error { } if err := n.pubSub.UnregisterTopicValidator(topic.String()); err != nil { - n.logger.Err(err).Str("topic", topic.String()).Msg("failed to unregister topic validator") + return fmt.Errorf("failed to unregister topic validator: %w", err) } // attempt to close the topic err := tp.Close() if err != nil { - err = fmt.Errorf("could not close topic (%s): %w", topic, err) - return err + return fmt.Errorf("could not close topic (%s): %w", topic, err) } n.topics[topic] = nil delete(n.topics, topic) diff --git a/network/p2p/subscription/subscriptionManager.go b/network/p2p/subscription/subscriptionManager.go index 72aba58d1b9..391468f58bc 100644 --- a/network/p2p/subscription/subscriptionManager.go +++ b/network/p2p/subscription/subscriptionManager.go @@ -60,7 +60,7 @@ func (sm *ChannelSubscriptionManager) Unregister(channel channels.Channel) error err := sm.mw.Unsubscribe(channel) if err != nil { - return fmt.Errorf("subscriptionManager: failed to unregister from channel %s", channel) + return fmt.Errorf("subscriptionManager: failed to unregister from channel %s: %w", channel, err) } delete(sm.engines, channel) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 9e3cd5d9872..f9aee08817f 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -1,7 +1,6 @@ package test import ( - "bytes" "context" "fmt" "regexp" @@ -24,6 +23,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/model/messages" "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" @@ -32,7 +32,6 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" @@ -90,7 +89,7 @@ type MiddlewareTestSuite struct { // TestMiddlewareTestSuit runs all the test methods in this test suit func TestMiddlewareTestSuite(t *testing.T) { - t.Parallel() + // should not run in parallel, some tests are stateful. suite.Run(t, new(MiddlewareTestSuite)) } @@ -127,7 +126,9 @@ func (m *MiddlewareTestSuite) SetupTest() { &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) require.NoError(m.T(), err) - opts = append(opts, p2ptest.WithConnectionManager(connManager)) + opts = append(opts, + p2ptest.WithConnectionManager(connManager), + p2ptest.WithRole(flow.RoleExecution)) node, nodeId := p2ptest.NodeFixture(m.T(), m.sporkId, m.T().Name(), @@ -172,9 +173,9 @@ func (m *MiddlewareTestSuite) SetupTest() { testutils.StartNodes(m.mwCtx, m.T(), m.libP2PNodes, 1*time.Second) for i, net := range m.networks { + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, libP2PNodes[i]) net.Start(m.mwCtx) unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, net) - require.NoError(m.T(), m.mws[i].Subscribe(channels.TestNetworkChannel)) } } @@ -265,6 +266,10 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // burst per interval burst := 5 + for _, mw := range m.mws { + require.NoError(m.T(), mw.Subscribe(channels.TestNetworkChannel)) + } + messageRateLimiter := ratelimiter.NewRateLimiter(limit, burst, 3*time.Second) // we only expect messages from the first middleware on the test suite @@ -538,7 +543,7 @@ func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvid return provider.Identities(filter.Any) }, nil) // this test is not testing the topic validator, especially in spoofing, - // so we always return a valid identity. We only care about the node role for the test TestMaxMessageSize_SendDirect + // so we always return a valid identity. We only care about the node role for the test TestMaxMessageSize_Unicast // where EN are the only node authorized to send chunk data response. identityOpts := unittest.WithRole(flow.RoleExecution) overlay.On("Identity", mockery.AnythingOfType("peer.ID")).Maybe().Return(unittest.IdentityFixture(identityOpts), true) @@ -642,6 +647,7 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.True(m.T(), ok) // payload require.True(m.T(), regex.MatchString(msgPayload.Text)) require.False(m.T(), receivedPayloads.Has(msgPayload.Text)) // payload must be unique @@ -748,12 +754,11 @@ func (m *MiddlewareTestSuite) TestEcho() { unittest.RequireReturnsBefore(m.T(), wg.Wait, 5*time.Second, "could not receive unicast on time") } -// TestMaxMessageSize_SendDirect evaluates that invoking SendDirect method of the middleware on a message +// TestMaxMessageSize_Unicast evaluates that invoking Unicast method of the middleware on a message // size beyond the permissible unicast message size returns an error. -func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { +func (m *MiddlewareTestSuite) TestMaxMessageSize_Unicast() { first := 0 last := m.size - 1 - lastNode := m.ids[last].NodeID // creates a network payload beyond the maximum message size // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, @@ -765,17 +770,10 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { Text: string(payload), } - msg, err := message.NewOutgoingScope( - flow.IdentifierList{lastNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - event, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // sends a direct message from first node to the last node - err = m.mws[first].SendDirect(msg) - require.Error(m.Suite.T(), err) + con0, err := m.networks[first].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) + require.Error(m.T(), con0.Unicast(event, m.ids[last].NodeID)) } // TestLargeMessageSize_SendDirect asserts that a ChunkDataResponse is treated as a large message and can be unicasted @@ -783,59 +781,50 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_SendDirect() { func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { sourceIndex := 0 targetIndex := m.size - 1 - targetNode := m.ids[targetIndex].NodeID - targetMW := m.mws[targetIndex] - - // subscribe to channels.ProvideChunks so that the message is not dropped - require.NoError(m.T(), targetMW.Subscribe(channels.ProvideChunks)) + targetId := m.ids[targetIndex].NodeID // creates a network payload with a size greater than the default max size using a known large message type targetSize := uint64(middleware.DefaultMaxUnicastMsgSize) + 1000 event := unittest.ChunkDataResponseMsgFixture(unittest.IdentifierFixture(), unittest.WithApproximateSize(targetSize)) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{targetNode}, - channels.TopicFromChannel(channels.ProvideChunks, m.sporkId), - event, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) - // expect one message to be received by the target ch := make(chan struct{}) - m.ov[targetIndex].On("Receive", mockery.Anything).Return(nil).Once(). + // mocks a target engine on the last node of the test suit that will receive the message on the test channel. + targetEngine := &mocknetwork.MessageProcessor{} + _, err := m.networks[targetIndex].Register(channels.ProvideChunks, targetEngine) + require.NoError(m.T(), err) + targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { - msg, ok := args[0].(network.IncomingMessageScope) + defer close(ch) + + msgChannel, ok := args[0].(channels.Channel) require.True(m.T(), ok) + require.Equal(m.T(), channels.ProvideChunks, msgChannel) // channel - require.Equal(m.T(), channels.ProvideChunks, msg.Channel()) - require.Equal(m.T(), m.ids[sourceIndex].NodeID, msg.OriginId()) - require.Equal(m.T(), targetNode, msg.TargetIDs()[0]) - require.Equal(m.T(), message.ProtocolTypeUnicast, msg.Protocol()) + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[sourceIndex].NodeID, msgOriginID) // sender id - eventId, err := message.EventId(msg.Channel(), msg.Proto().Payload) - require.NoError(m.T(), err) - require.True(m.T(), bytes.Equal(eventId, msg.EventID())) - close(ch) - }) + msgPayload, ok := args[2].(*messages.ChunkDataResponse) + require.True(m.T(), ok) + require.Equal(m.T(), event, msgPayload) // payload + }).Return(nil).Once() // sends a direct message from source node to the target node - err = m.mws[sourceIndex].SendDirect(msg) - // SendDirect should not error since this is a known large message - require.NoError(m.Suite.T(), err) + con0, err := m.networks[sourceIndex].Register(channels.ProvideChunks, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) + require.NoError(m.T(), con0.Unicast(event, targetId)) // check message reception on target - unittest.RequireCloseBefore(m.T(), ch, 60*time.Second, "source node failed to send large message to target") - - m.ov[targetIndex].AssertExpectations(m.T()) + unittest.RequireCloseBefore(m.T(), ch, 5*time.Second, "source node failed to send large message to target") } // TestMaxMessageSize_Publish evaluates that invoking Publish method of the middleware on a message // size beyond the permissible publish message size returns an error. func (m *MiddlewareTestSuite) TestMaxMessageSize_Publish() { - first := 0 - last := m.size - 1 - lastNode := m.ids[last].NodeID + firstIndex := 0 + lastIndex := m.size - 1 + lastNodeId := m.ids[lastIndex].NodeID // creates a network payload beyond the maximum message size // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, @@ -846,26 +835,47 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_Publish() { event := &libp2pmessage.TestMessage{ Text: string(payload), } - msg, err := message.NewOutgoingScope( - flow.IdentifierList{lastNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - event, - unittest.NetworkCodec().Encode, - message.ProtocolTypePubSub) + con0, err := m.networks[firstIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(m.T(), err) - // sends a direct message from first node to the last node - err = m.mws[first].Publish(msg) + err = con0.Publish(event, lastNodeId) require.Error(m.Suite.T(), err) + require.ErrorContains(m.T(), err, "exceeds configured max message size") } // TestUnsubscribe tests that an engine can unsubscribe from a topic it was earlier subscribed to and stop receiving // messages. func (m *MiddlewareTestSuite) TestUnsubscribe() { - first := 0 - last := m.size - 1 - firstNode := m.ids[first].NodeID - lastNode := m.ids[last].NodeID + senderIndex := 0 + targetIndex := m.size - 1 + targetId := m.ids[targetIndex].NodeID + + msgRcvd := make(chan struct{}, 2) + msgRcvdFun := func() { + <-msgRcvd + } + + targetEngine := &mocknetwork.MessageProcessor{} + con2, err := m.networks[targetIndex].Register(channels.TestNetworkChannel, targetEngine) + targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). + Run(func(args mockery.Arguments) { + msgChannel, ok := args[0].(channels.Channel) + require.True(m.T(), ok) + require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + + msgOriginID, ok := args[1].(flow.Identifier) + require.True(m.T(), ok) + require.Equal(m.T(), m.ids[senderIndex].NodeID, msgOriginID) // sender id + + msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.True(m.T(), ok) + require.True(m.T(), msgPayload.Text == "hello1") // payload, we only expect hello 1 that was sent before unsubscribe. + msgRcvd <- struct{}{} + }).Return(nil) + + // first test that when both nodes are subscribed to the channel, the target node receives the message + con1, err := m.networks[senderIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) // set up waiting for m.size pubsub tags indicating a mesh has formed for i := 0; i < m.size; i++ { @@ -876,50 +886,20 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { } } - msgRcvd := make(chan struct{}, 2) - msgRcvdFun := func() { - <-msgRcvd - } - - message1, err := message.NewOutgoingScope( - flow.IdentifierList{lastNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: string("hello1"), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + err = con1.Publish(&libp2pmessage.TestMessage{ + Text: string("hello1"), + }, targetId) require.NoError(m.T(), err) - m.ov[last].On("Receive", mockery.Anything).Return(nil).Run(func(args mockery.Arguments) { - msg, ok := args[0].(network.IncomingMessageScope) - require.True(m.T(), ok) - require.Equal(m.T(), firstNode, msg.OriginId()) - msgRcvd <- struct{}{} - }) - - // first test that when both nodes are subscribed to the channel, the target node receives the message - err = m.mws[first].Publish(message1) - assert.NoError(m.T(), err) - - unittest.RequireReturnsBefore(m.T(), msgRcvdFun, 2*time.Second, "message not received") + unittest.RequireReturnsBefore(m.T(), msgRcvdFun, 3*time.Second, "message not received") // now unsubscribe the target node from the channel - err = m.mws[last].Unsubscribe(channels.TestNetworkChannel) - assert.NoError(m.T(), err) - - // create and send a new message on the channel from the origin node - message2, err := message.NewOutgoingScope( - flow.IdentifierList{lastNode}, - channels.TopicFromChannel(channels.TestNetworkChannel, m.sporkId), - &libp2pmessage.TestMessage{ - Text: string("hello2"), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(m.T(), err) + require.NoError(m.T(), con2.Close()) - err = m.mws[first].Publish(message2) + // now publish a new message from the first node + err = con1.Publish(&libp2pmessage.TestMessage{ + Text: string("hello2"), + }, targetId) assert.NoError(m.T(), err) // assert that the new message is not received by the target node From b92cb812142ec1dfff0c6123f3c59257eef7c4ca Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Fri, 25 Aug 2023 09:55:59 +0100 Subject: [PATCH 646/815] update state stream mock imports --- engine/access/state_stream/mock/get_execution_data_func.go | 3 +-- engine/access/state_stream/mock/get_start_height_func.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/engine/access/state_stream/mock/get_execution_data_func.go b/engine/access/state_stream/mock/get_execution_data_func.go index 506cc8d3d1c..50fe8087e21 100644 --- a/engine/access/state_stream/mock/get_execution_data_func.go +++ b/engine/access/state_stream/mock/get_execution_data_func.go @@ -5,9 +5,8 @@ package mock import ( context "context" - mock "github.com/stretchr/testify/mock" - execution_data "github.com/onflow/flow-go/module/executiondatasync/execution_data" + mock "github.com/stretchr/testify/mock" ) // GetExecutionDataFunc is an autogenerated mock type for the GetExecutionDataFunc type diff --git a/engine/access/state_stream/mock/get_start_height_func.go b/engine/access/state_stream/mock/get_start_height_func.go index d1c93d2ed1c..b97a77e1d39 100644 --- a/engine/access/state_stream/mock/get_start_height_func.go +++ b/engine/access/state_stream/mock/get_start_height_func.go @@ -3,9 +3,8 @@ package mock import ( - mock "github.com/stretchr/testify/mock" - flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" ) // GetStartHeightFunc is an autogenerated mock type for the GetStartHeightFunc type From f8d6ba8880ff3aee7947dcd64a7b49e4e08c640b Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Fri, 25 Aug 2023 16:11:52 +0200 Subject: [PATCH 647/815] change remaining uses of passing zerolog by reference --- .../execution_state_extract.go | 2 +- fvm/environment/contract_updater.go | 8 ++--- fvm/environment/env.go | 2 +- fvm/environment/mock/environment.go | 10 +++---- fvm/environment/program_logger.go | 8 ++--- fvm/environment/system_contracts.go | 4 +-- fvm/transactionInvoker.go | 10 ++++--- ledger/complete/checkpoint_benchmark_test.go | 4 +-- ledger/complete/compactor.go | 2 +- ledger/complete/wal/checkpoint_v6_test.go | 30 +++++++++---------- ledger/complete/wal/checkpoint_v6_writer.go | 8 ++--- ledger/complete/wal/checkpointer.go | 2 +- 12 files changed, 45 insertions(+), 45 deletions(-) diff --git a/cmd/util/cmd/execution-state-extract/execution_state_extract.go b/cmd/util/cmd/execution-state-extract/execution_state_extract.go index fe5bdf93471..1436f0ccc94 100644 --- a/cmd/util/cmd/execution-state-extract/execution_state_extract.go +++ b/cmd/util/cmd/execution-state-extract/execution_state_extract.go @@ -142,7 +142,7 @@ func createCheckpoint( return ledger.State(hash.DummyHash), fmt.Errorf("could not create output dir %s: %w", outputDir, err) } - err = wal.StoreCheckpointV6Concurrently([]*trie.MTrie{newTrie}, outputDir, outputFile, &log) + err = wal.StoreCheckpointV6Concurrently([]*trie.MTrie{newTrie}, outputDir, outputFile, log) // Writing the checkpoint takes time to write and copy. // Without relying on an exit code or stdout, we need to know when the copy is complete. diff --git a/fvm/environment/contract_updater.go b/fvm/environment/contract_updater.go index 2185b4d09da..3c438b24193 100644 --- a/fvm/environment/contract_updater.go +++ b/fvm/environment/contract_updater.go @@ -194,7 +194,7 @@ func (impl *contractUpdaterStubsImpl) getIsContractDeploymentRestricted() ( common.MustBytesToAddress(service.Bytes()), blueprints.IsContractDeploymentRestrictedPath) if err != nil { - impl.logger.Logger(). + impl.logger. Debug(). Msg("Failed to read IsContractDeploymentRestricted from the " + "service account. Using value from context instead.") @@ -202,7 +202,7 @@ func (impl *contractUpdaterStubsImpl) getIsContractDeploymentRestricted() ( } restrictedCadence, ok := value.(cadence.Bool) if !ok { - impl.logger.Logger(). + impl.logger. Debug(). Msg("Failed to parse IsContractDeploymentRestricted from the " + "service account. Using value from context instead.") @@ -244,12 +244,12 @@ func (impl *contractUpdaterStubsImpl) GetAuthorizedAccounts( "service account. using default behaviour instead." if err != nil { - impl.logger.Logger().Warn().Msg(warningMsg) + impl.logger.Warn().Msg(warningMsg) return defaultAccounts } addresses, ok := cadenceValueToAddressSlice(value) if !ok { - impl.logger.Logger().Warn().Msg(warningMsg) + impl.logger.Warn().Msg(warningMsg) return defaultAccounts } return addresses diff --git a/fvm/environment/env.go b/fvm/environment/env.go index 518b49a737a..a08a27970d9 100644 --- a/fvm/environment/env.go +++ b/fvm/environment/env.go @@ -32,7 +32,7 @@ type Environment interface { TransactionInfo // ProgramLogger - Logger() *zerolog.Logger + Logger() zerolog.Logger Logs() []string // EventEmitter diff --git a/fvm/environment/mock/environment.go b/fvm/environment/mock/environment.go index 9d860227990..e8a9f9bca9d 100644 --- a/fvm/environment/mock/environment.go +++ b/fvm/environment/mock/environment.go @@ -982,16 +982,14 @@ func (_m *Environment) LimitAccountStorage() bool { } // Logger provides a mock function with given fields: -func (_m *Environment) Logger() *zerolog.Logger { +func (_m *Environment) Logger() zerolog.Logger { ret := _m.Called() - var r0 *zerolog.Logger - if rf, ok := ret.Get(0).(func() *zerolog.Logger); ok { + var r0 zerolog.Logger + if rf, ok := ret.Get(0).(func() zerolog.Logger); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*zerolog.Logger) - } + r0 = ret.Get(0).(zerolog.Logger) } return r0 diff --git a/fvm/environment/program_logger.go b/fvm/environment/program_logger.go index 44b7e859bcf..639e2dde9ac 100644 --- a/fvm/environment/program_logger.go +++ b/fvm/environment/program_logger.go @@ -79,12 +79,12 @@ func NewProgramLogger( } } -func (logger *ProgramLogger) Logger() *zerolog.Logger { - return &logger.ProgramLoggerParams.Logger +func (logger *ProgramLogger) Logger() zerolog.Logger { + return logger.ProgramLoggerParams.Logger } func (logger *ProgramLogger) ImplementationDebugLog(message string) error { - logger.Logger().Debug().Msgf("Cadence: %s", message) + logger.Debug().Msgf("Cadence: %s", message) return nil } @@ -98,7 +98,7 @@ func (logger *ProgramLogger) ProgramLog(message string) error { // emulator or emulator based tools), // we log the message to the zerolog logger so that they can be tracked // while stepping through a transaction/script. - logger.Logger(). + logger. Debug(). Msgf("Cadence log: %s", message) diff --git a/fvm/environment/system_contracts.go b/fvm/environment/system_contracts.go index 06a14acd337..606826314fa 100644 --- a/fvm/environment/system_contracts.go +++ b/fvm/environment/system_contracts.go @@ -75,8 +75,8 @@ func (sys *SystemContracts) Invoke( spec.ArgumentTypes, ) if err != nil { - sys.logger.Logger(). - Info(). + log := sys.logger.Logger() + log.Info(). Err(err). Str("contract", contractLocation.String()). Str("function", spec.FunctionName). diff --git a/fvm/transactionInvoker.go b/fvm/transactionInvoker.go index 2e46664f13f..68f3861c849 100644 --- a/fvm/transactionInvoker.go +++ b/fvm/transactionInvoker.go @@ -286,7 +286,8 @@ func (executor *transactionExecutor) deductTransactionFees() (err error) { // logExecutionIntensities logs execution intensities of the transaction func (executor *transactionExecutor) logExecutionIntensities() { - if !executor.env.Logger().Debug().Enabled() { + log := executor.env.Logger() + if !log.Debug().Enabled() { return } @@ -298,7 +299,7 @@ func (executor *transactionExecutor) logExecutionIntensities() { for s, u := range executor.txnState.MemoryIntensities() { memory.Uint(strconv.FormatUint(uint64(s), 10), u) } - executor.env.Logger().Debug(). + log.Debug(). Uint64("ledgerInteractionUsed", executor.txnState.InteractionUsed()). Uint64("computationUsed", executor.txnState.TotalComputationUsed()). Uint64("memoryEstimate", executor.txnState.TotalMemoryEstimate()). @@ -391,7 +392,8 @@ func (executor *transactionExecutor) normalExecution() ( // Clear changes and try to deduct fees again. func (executor *transactionExecutor) errorExecution() { // log transaction as failed - executor.env.Logger().Info(). + log := executor.env.Logger() + log.Info(). Err(executor.errs.ErrorOrNil()). Msg("transaction executed with error") @@ -408,7 +410,7 @@ func (executor *transactionExecutor) errorExecution() { // if fee deduction fails just do clean up and exit if feesError != nil { - executor.env.Logger().Info(). + log.Info(). Err(feesError). Msg("transaction fee deduction executed with error") diff --git a/ledger/complete/checkpoint_benchmark_test.go b/ledger/complete/checkpoint_benchmark_test.go index 5d5ae9f726c..5aba09403cd 100644 --- a/ledger/complete/checkpoint_benchmark_test.go +++ b/ledger/complete/checkpoint_benchmark_test.go @@ -72,9 +72,9 @@ func benchmarkStoreCheckpoint(b *testing.B, version int, concurrent bool) { err = wal.StoreCheckpointV5(outputDir, fileName, log, tries...) case 6: if concurrent { - err = wal.StoreCheckpointV6Concurrently(tries, outputDir, fileName, &log) + err = wal.StoreCheckpointV6Concurrently(tries, outputDir, fileName, log) } else { - err = wal.StoreCheckpointV6SingleThread(tries, outputDir, fileName, &log) + err = wal.StoreCheckpointV6SingleThread(tries, outputDir, fileName, log) } } diff --git a/ledger/complete/compactor.go b/ledger/complete/compactor.go index cafc9ed2a48..ef603900af1 100644 --- a/ledger/complete/compactor.go +++ b/ledger/complete/compactor.go @@ -332,7 +332,7 @@ func createCheckpoint(checkpointer *realWAL.Checkpointer, logger zerolog.Logger, startTime := time.Now() fileName := realWAL.NumberToFilename(checkpointNum) - err := realWAL.StoreCheckpointV6SingleThread(tries, checkpointer.Dir(), fileName, &logger) + err := realWAL.StoreCheckpointV6SingleThread(tries, checkpointer.Dir(), fileName, logger) if err != nil { return fmt.Errorf("error serializing checkpoint (%d): %w", checkpointNum, err) } diff --git a/ledger/complete/wal/checkpoint_v6_test.go b/ledger/complete/wal/checkpoint_v6_test.go index 78a52624bbc..20c815d7c09 100644 --- a/ledger/complete/wal/checkpoint_v6_test.go +++ b/ledger/complete/wal/checkpoint_v6_test.go @@ -262,7 +262,7 @@ func TestWriteAndReadCheckpointV6EmptyTrie(t *testing.T) { tries := []*trie.MTrie{trie.NewEmptyMTrie()} fileName := "checkpoint-empty-trie" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) @@ -274,7 +274,7 @@ func TestWriteAndReadCheckpointV6SimpleTrie(t *testing.T) { tries := createSimpleTrie(t) fileName := "checkpoint" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) @@ -286,7 +286,7 @@ func TestWriteAndReadCheckpointV6MultipleTries(t *testing.T) { tries := createMultipleRandomTries(t) fileName := "checkpoint-multi-file" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) requireTriesEqual(t, tries, decoded) @@ -298,8 +298,8 @@ func TestCheckpointV6IsDeterminstic(t *testing.T) { unittest.RunWithTempDir(t, func(dir string) { tries := createMultipleRandomTries(t) logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint1", &logger), "fail to store checkpoint") - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint2", &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint1", logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint2", logger), "fail to store checkpoint") partFiles1 := filePaths(dir, "checkpoint1", subtrieLevel) partFiles2 := filePaths(dir, "checkpoint2", subtrieLevel) for i, partFile1 := range partFiles1 { @@ -317,7 +317,7 @@ func TestWriteAndReadCheckpointV6LeafEmptyTrie(t *testing.T) { tries := []*trie.MTrie{trie.NewEmptyMTrie()} fileName := "checkpoint-empty-trie" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") bufSize := 10 leafNodesCh := make(chan *LeafNode, bufSize) @@ -336,7 +336,7 @@ func TestWriteAndReadCheckpointV6LeafSimpleTrie(t *testing.T) { tries := createSimpleTrie(t) fileName := "checkpoint" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") bufSize := 1 leafNodesCh := make(chan *LeafNode, bufSize) go func() { @@ -359,7 +359,7 @@ func TestWriteAndReadCheckpointV6LeafMultipleTries(t *testing.T) { fileName := "checkpoint-multi-leaf-file" tries := createMultipleRandomTriesMini(t) logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") bufSize := 5 leafNodesCh := make(chan *LeafNode, bufSize) go func() { @@ -447,7 +447,7 @@ func TestWriteAndReadCheckpointV6ThenBackToV5(t *testing.T) { logger := unittest.Logger() // store tries into v6 then read back, then store into v5 - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", logger), "fail to store checkpoint") decoded, err := OpenAndReadCheckpointV6(dir, "checkpoint-v6", logger) require.NoErrorf(t, err, "fail to read checkpoint %v/checkpoint-v6", dir) require.NoErrorf(t, storeCheckpointV5(decoded, dir, "checkpoint-v6-v5", logger), "fail to store checkpoint") @@ -476,7 +476,7 @@ func TestCleanupOnErrorIfNotExist(t *testing.T) { logger := unittest.Logger() // store tries into v6 then read back, then store into v5 - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", logger), "fail to store checkpoint") require.NoError(t, deleteCheckpointFiles(dir, "checkpoint-v6")) // verify all files are removed @@ -505,7 +505,7 @@ func TestAllPartFileExist(t *testing.T) { require.NoErrorf(t, err, "fail to find sub trie file path") logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") // delete i-th part file, then the error should mention i-th file missing err = os.Remove(fileToDelete) @@ -533,7 +533,7 @@ func TestAllPartFileExistLeafReader(t *testing.T) { require.NoErrorf(t, err, "fail to find sub trie file path") logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") // delete i-th part file, then the error should mention i-th file missing err = os.Remove(fileToDelete) @@ -553,9 +553,9 @@ func TestCannotStoreTwice(t *testing.T) { tries := createSimpleTrie(t) fileName := "checkpoint" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") // checkpoint already exist, can't store again - require.Error(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger)) + require.Error(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger)) }) } @@ -580,7 +580,7 @@ func TestCopyCheckpointFileV6(t *testing.T) { tries := createSimpleTrie(t) fileName := "checkpoint" logger := unittest.Logger() - require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, &logger), "fail to store checkpoint") + require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") to := filepath.Join(dir, "newfolder") newPaths, err := CopyCheckpointFile(fileName, dir, to) require.NoError(t, err) diff --git a/ledger/complete/wal/checkpoint_v6_writer.go b/ledger/complete/wal/checkpoint_v6_writer.go index ea2021f2156..35ed41351ff 100644 --- a/ledger/complete/wal/checkpoint_v6_writer.go +++ b/ledger/complete/wal/checkpoint_v6_writer.go @@ -29,13 +29,13 @@ func subtrieCountByLevel(level uint16) int { // StoreCheckpointV6SingleThread stores checkpoint file in v6 in a single threaded manner, // useful when EN is executing block. -func StoreCheckpointV6SingleThread(tries []*trie.MTrie, outputDir string, outputFile string, logger *zerolog.Logger) error { +func StoreCheckpointV6SingleThread(tries []*trie.MTrie, outputDir string, outputFile string, logger zerolog.Logger) error { return StoreCheckpointV6(tries, outputDir, outputFile, logger, 1) } // StoreCheckpointV6Concurrently stores checkpoint file in v6 in max workers, // useful during state extraction -func StoreCheckpointV6Concurrently(tries []*trie.MTrie, outputDir string, outputFile string, logger *zerolog.Logger) error { +func StoreCheckpointV6Concurrently(tries []*trie.MTrie, outputDir string, outputFile string, logger zerolog.Logger) error { return StoreCheckpointV6(tries, outputDir, outputFile, logger, 16) } @@ -49,7 +49,7 @@ func StoreCheckpointV6Concurrently(tries []*trie.MTrie, outputDir string, output // // nWorker specifies how many workers to encode subtrie concurrently, valid range [1,16] func StoreCheckpointV6( - tries []*trie.MTrie, outputDir string, outputFile string, logger *zerolog.Logger, nWorker uint) error { + tries []*trie.MTrie, outputDir string, outputFile string, logger zerolog.Logger, nWorker uint) error { err := storeCheckpointV6(tries, outputDir, outputFile, logger, nWorker) if err != nil { cleanupErr := deleteCheckpointFiles(outputDir, outputFile) @@ -63,7 +63,7 @@ func StoreCheckpointV6( } func storeCheckpointV6( - tries []*trie.MTrie, outputDir string, outputFile string, logger *zerolog.Logger, nWorker uint) error { + tries []*trie.MTrie, outputDir string, outputFile string, logger zerolog.Logger, nWorker uint) error { if len(tries) == 0 { logger.Info().Msg("no tries to be checkpointed") return nil diff --git a/ledger/complete/wal/checkpointer.go b/ledger/complete/wal/checkpointer.go index 5e58ff95235..cad90ec37e8 100644 --- a/ledger/complete/wal/checkpointer.go +++ b/ledger/complete/wal/checkpointer.go @@ -246,7 +246,7 @@ func (c *Checkpointer) Checkpoint(to int) (err error) { fileName := NumberToFilename(to) - err = StoreCheckpointV6SingleThread(tries, c.wal.dir, fileName, &c.wal.log) + err = StoreCheckpointV6SingleThread(tries, c.wal.dir, fileName, c.wal.log) if err != nil { return fmt.Errorf("could not create checkpoint for %v: %w", to, err) From b2fadda9eb818f75ea0196499c052aedcc8c0559 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Fri, 25 Aug 2023 10:00:34 -0700 Subject: [PATCH 648/815] cleans up the overlay from middleware --- network/test/middleware_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index f9aee08817f..cee3159d7fb 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -75,8 +75,7 @@ type MiddlewareTestSuite struct { libP2PNodes []p2p.LibP2PNode networks []network.Network mws []network.Middleware // used to keep track of middlewares under test - ov []*mocknetwork.Overlay - obs chan string // used to keep track of Protect events tagged by pubsub messages + obs chan string // used to keep track of Protect events tagged by pubsub messages ids []*flow.Identity metrics *metrics.NoopCollector // no-op performance monitoring simulation logger zerolog.Logger @@ -160,11 +159,6 @@ func (m *MiddlewareTestSuite) SetupTest() { require.Len(m.Suite.T(), m.ids, m.size) require.Len(m.Suite.T(), m.mws, m.size) - // create the mock overlays - for i := 0; i < m.size; i++ { - m.ov = append(m.ov, m.createOverlay(m.providers[i])) - } - ctx, cancel := context.WithCancel(context.Background()) m.mwCancel = cancel @@ -187,7 +181,6 @@ func (m *MiddlewareTestSuite) TearDownTest() { m.mws = nil m.libP2PNodes = nil - m.ov = nil m.ids = nil m.size = 0 } From e606490d927e7fcc37eb3e3ab8d5904ca84cc963 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Fri, 25 Aug 2023 11:52:37 -0700 Subject: [PATCH 649/815] Update integration/tests/bft/protocol/README.md Co-authored-by: Misha --- integration/tests/bft/protocol/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/protocol/README.md b/integration/tests/bft/protocol/README.md index 9bea5f714ad..dc76414739b 100644 --- a/integration/tests/bft/protocol/README.md +++ b/integration/tests/bft/protocol/README.md @@ -12,7 +12,7 @@ The entire suite assures that the disallow-listing command behaves as intended, ## Wintermute Attack Test The `WintermuteTestSuite` in the `wintermute` package is focused on validating a specific attack scenario within the network, termed the "wintermute attack." -This attack involves an orchestrator corrupting an execution result and then leveraging corrupted verification nodes to verify it. +This attack involves an Attack Orchestrator corrupting an execution result and then leveraging corrupted verification nodes to verify it. The suite includes a constant timeout to define the attack window and a detailed test sequence. The `TestWintermuteAttack` method carries out the attack process. It first waits for an execution result to be corrupted and identifies the corresponding victim block. From 7acb9edf2c6bc87cadb716c55b3651224e2a6e72 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sun, 27 Aug 2023 15:25:32 +0100 Subject: [PATCH 650/815] Fix observer builder formatting --- cmd/observer/node_builder/observer_builder.go | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index a33fdae79ab..26aed858b91 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -13,6 +13,9 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" + "github.com/rs/zerolog" + "github.com/spf13/pflag" + "github.com/onflow/flow-go/cmd" "github.com/onflow/flow-go/consensus" "github.com/onflow/flow-go/consensus/hotstuff" @@ -70,8 +73,6 @@ import ( "github.com/onflow/flow-go/state/protocol/events/gadgets" "github.com/onflow/flow-go/utils/grpcutils" "github.com/onflow/flow-go/utils/io" - "github.com/rs/zerolog" - "github.com/spf13/pflag" ) // ObserverBuilder extends cmd.NodeBuilder and declares additional functions needed to bootstrap an Access node @@ -925,29 +926,27 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { backendConfig.CircuitBreakerConfig, ), } - - accessBackend := backend.New(backend.Params{ - State:node.State, - Blocks: node.Storage.Blocks, - Headers: node.Storage.Headers, - Collections: node.Storage.Collections, - Transactions: node.Storage.Transactions, - ExecutionReceipts: node.Storage.Receipts, - ExecutionResults: node.Storage.Results, - ChainID: node.RootChainID, - AccessMetrics: accessMetrics, - ConnFactory: connFactory, - RetryEnabled: false, - MaxHeightRange: backendConfig.MaxHeightRange, + State: node.State, + Blocks: node.Storage.Blocks, + Headers: node.Storage.Headers, + Collections: node.Storage.Collections, + Transactions: node.Storage.Transactions, + ExecutionReceipts: node.Storage.Receipts, + ExecutionResults: node.Storage.Results, + ChainID: node.RootChainID, + AccessMetrics: accessMetrics, + ConnFactory: connFactory, + RetryEnabled: false, + MaxHeightRange: backendConfig.MaxHeightRange, PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, - FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, - Log:node.Logger, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - ArchiveAddressList: backendConfig.ArchiveAddressList, - Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - ScriptExecValidation: backendConfig.ScriptExecValidation, + FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, + Log: node.Logger, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + ArchiveAddressList: backendConfig.ArchiveAddressList, + Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + ScriptExecValidation: backendConfig.ScriptExecValidation, }) observerCollector := metrics.NewObserverCollector() From 96c1d4fe214263b806a2616051d582d4a53330fb Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Sun, 27 Aug 2023 10:47:07 -0400 Subject: [PATCH 651/815] improve geometric test flakiness --- network/p2p/scoring/decay_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/network/p2p/scoring/decay_test.go b/network/p2p/scoring/decay_test.go index 28ff6dabe7f..5bbd525fc4b 100644 --- a/network/p2p/scoring/decay_test.go +++ b/network/p2p/scoring/decay_test.go @@ -116,13 +116,14 @@ func TestGeometricDecay(t *testing.T) { wantErr: fmt.Errorf("last updated time cannot be in the future"), }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := scoring.GeometricDecay(tt.args.penalty, tt.args.decay, tt.args.lastUpdated) if tt.wantErr != nil { assert.Errorf(t, err, tt.wantErr.Error()) } - assert.Less(t, math.Abs(got-tt.want), 1e-3) + assert.LessOrEqual(t, truncateFloat(math.Abs(got-tt.want), 3), 1e-3) }) } } @@ -250,3 +251,8 @@ func TestDefaultDecayFunction(t *testing.T) { }) } } + +func truncateFloat(number float64, decimalPlaces int) float64 { + pow := math.Pow(10, float64(decimalPlaces)) + return float64(int(number*pow)) / pow +} From 2e3a710490184846efaddf63d8d2fbb1bda270b3 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Sun, 27 Aug 2023 11:10:40 -0400 Subject: [PATCH 652/815] improve echoengine test flakiness --- network/test/echoengine_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index e329a83e780..6637ccf9f66 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -264,7 +264,10 @@ func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.Condui }() } unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not send message on time") - time.Sleep(1 * time.Second) + + require.Eventually(suite.T(), func() bool { + return len(receiver.seen) > 0 + }, 3*time.Second, 500*time.Millisecond) // receiver should only see the message once, and the rest should be dropped due to // duplication From a8658092ae0bd84a28f47b19fc6b5b5b353fe4b3 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Sun, 27 Aug 2023 11:30:31 -0400 Subject: [PATCH 653/815] add 500 millisecond sleep before lauching second batch of goroutines --- network/p2p/inspector/internal/cache/tracker_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/network/p2p/inspector/internal/cache/tracker_test.go b/network/p2p/inspector/internal/cache/tracker_test.go index 4918efc0d1f..7dac5e6b67a 100644 --- a/network/p2p/inspector/internal/cache/tracker_test.go +++ b/network/p2p/inspector/internal/cache/tracker_test.go @@ -58,6 +58,7 @@ func TestClusterPrefixedMessagesReceivedTracker_ConcurrentIncAndLoad(t *testing. id := unittest.IdentifierFixture() var wg sync.WaitGroup wg.Add(10) + go func() { for i := float64(0); i < n; i++ { go func() { @@ -67,6 +68,9 @@ func TestClusterPrefixedMessagesReceivedTracker_ConcurrentIncAndLoad(t *testing. }() } }() + + // slight sleep so that each goroutine does not start at the same exact time + time.Sleep(500 * time.Millisecond) go func() { for i := float64(0); i < n; i++ { go func() { From 99857725d6d4f7002f141bbe79f97d59e84ead7e Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 28 Aug 2023 05:47:30 -0400 Subject: [PATCH 654/815] clean up logging --- engine/common/synchronization/engine.go | 30 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index d89ce6ed6d7..b55ed64c455 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -210,7 +210,11 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve report, misbehavior := e.validateBatchRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP - e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid batch request from %x: %v", originID[:], misbehavior) + e.log. + Warn(). + Hex("origin_id", logging.ID(originID)). + Str(logging.KeySuspicious, "true"). + Msgf("received invalid batch request from %x: %v", originID[:], misbehavior) e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBatchRequest) return nil } @@ -219,7 +223,11 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve report, misbehavior := e.validateRangeRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP - e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid range request from %x: %v", originID[:], misbehavior) + e.log. + Warn(). + Hex("origin_id", logging.ID(originID)). + Str(logging.KeySuspicious, "true"). + Msgf("received invalid range request from %x: %v", originID[:], misbehavior) e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageRangeRequest) return nil } @@ -229,7 +237,11 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve report, misbehavior := e.validateSyncRequestForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP - e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync request from %x: %v", originID[:], misbehavior) + e.log. + Warn(). + Hex("origin_id", logging.ID(originID)). + Str(logging.KeySuspicious, "true"). + Msgf("received invalid sync request from %x: %v", originID[:], misbehavior) e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncRequest) return nil } @@ -239,7 +251,11 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve report, misbehavior := e.validateBlockResponseForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP - e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid block response from %x: %v", originID[:], misbehavior) + e.log. + Warn(). + Hex("origin_id", logging.ID(originID)). + Str(logging.KeySuspicious, "true"). + Msgf("received invalid block response from %x: %v", originID[:], misbehavior) e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBlockResponse) return nil } @@ -249,7 +265,11 @@ func (e *Engine) process(channel channels.Channel, originID flow.Identifier, eve report, misbehavior := e.validateSyncResponseForALSP(channel, originID, event) if misbehavior { e.con.ReportMisbehavior(report) // report misbehavior to ALSP - e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync response from %x: %v", originID[:], misbehavior) + e.log. + Warn(). + Hex("origin_id", logging.ID(originID)). + Str(logging.KeySuspicious, "true"). + Msgf("received invalid sync response from %x: %v", originID[:], misbehavior) e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncResponse) return nil } From 1aa198a940b4f8a63bff3b722253932820988817 Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 28 Aug 2023 05:59:06 -0400 Subject: [PATCH 655/815] add network security logging flag --- engine/common/synchronization/engine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index b55ed64c455..b6431e73d51 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -518,6 +518,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f // generating a random number it indicates a bug and processing can not proceed. e.log.Fatal(). Err(err). + Bool(logging.KeyNetworkingSecurity, true). Str("originID", originID.String()). Msg("failed to generate random number") } @@ -534,6 +535,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f // creating the misbehavior report it indicates a bug and processing can not proceed. e.log.Fatal(). Err(err). + Bool(logging.KeyNetworkingSecurity, true). Str("originID", originID.String()). Msg("failed to create misbehavior report") } From 2967f4a70a86560c9265ebf56559dbf6bf66166d Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 28 Aug 2023 06:03:39 -0400 Subject: [PATCH 656/815] removed potential-spam misbehavior type --- engine/common/synchronization/engine.go | 2 +- network/alsp/misbehavior.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index b6431e73d51..bac643bafe6 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -528,7 +528,7 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f if float32(n) < e.spamReportConfig.syncRequestProbability*spamProbabilityMultiplier { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") - report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam) + report, err := alsp.NewMisbehaviorReport(originID, alsp.ResourceIntensiveRequest) if err != nil { // failing to create the misbehavior report is unlikely. If an error is encountered while diff --git a/network/alsp/misbehavior.go b/network/alsp/misbehavior.go index ee938331ef4..af4921cd06a 100644 --- a/network/alsp/misbehavior.go +++ b/network/alsp/misbehavior.go @@ -43,9 +43,6 @@ const ( // UnauthorizedPublishOnChannel is a misbehavior that is reported when a message not authorized to be sent via pubsub is received via pubsub. UnauthorizedPublishOnChannel network.Misbehavior = "unauthorized-pubsub-on-channel" - - // PotentialSpam is a misbehavior that is reported when a message is potentially spam. - PotentialSpam network.Misbehavior = "potential-spam" ) func AllMisbehaviorTypes() []network.Misbehavior { From 7c719df145fea69a73e9baf6ddaa738433f72b50 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Mon, 28 Aug 2023 11:13:21 +0100 Subject: [PATCH 657/815] Rename corep2p interface --- network/p2p/libp2pNode.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index bee76c51add..3803e6fd741 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -18,8 +18,8 @@ import ( "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) -// P2PService service management capabilities -type P2PService interface { +// CoreP2P service management capabilities +type CoreP2P interface { // Start the libp2p node. Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. @@ -106,8 +106,8 @@ type LibP2PNode interface { // DisallowListOracle exposes the disallow list oracle API for external consumers to query about the disallow list. DisallowListOracle - // P2PService service management capabilities - P2PService + // CoreP2P service management capabilities + CoreP2P // PeerManagement current peer management functions PeerManagement From bdb589999a738c627f17278519647aafb3174b0b Mon Sep 17 00:00:00 2001 From: Misha Date: Mon, 28 Aug 2023 06:35:44 -0400 Subject: [PATCH 658/815] renamed to SpamDetectionConfig; SpamDetectionConfig received by New() --- cmd/access/node_builder/access_node_builder.go | 1 + cmd/collection/main.go | 1 + cmd/consensus/main.go | 1 + cmd/execution_builder.go | 1 + cmd/observer/node_builder/observer_builder.go | 1 + cmd/verification_builder.go | 1 + consensus/integration/nodes_test.go | 1 + engine/common/synchronization/config.go | 6 +++--- engine/common/synchronization/engine.go | 11 ++++++----- engine/common/synchronization/engine_test.go | 9 +++++---- engine/testutil/nodes.go | 1 + follower/follower_builder.go | 1 + 12 files changed, 23 insertions(+), 12 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index e87f1e6327d..345696c1922 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -410,6 +410,7 @@ func (builder *FlowAccessNodeBuilder) buildSyncEngine() *FlowAccessNodeBuilder { builder.FollowerEng, builder.SyncCore, builder.SyncEngineParticipantsProviderFactory(), + synceng.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not create synchronization engine: %w", err) diff --git a/cmd/collection/main.go b/cmd/collection/main.go index 27faa959ca2..e53762ffb35 100644 --- a/cmd/collection/main.go +++ b/cmd/collection/main.go @@ -366,6 +366,7 @@ func main() { followerEng, mainChainSyncCore, node.SyncEngineIdentifierProvider, + consync.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not create synchronization engine: %w", err) diff --git a/cmd/consensus/main.go b/cmd/consensus/main.go index 8fb294fedf4..2ef9380f661 100644 --- a/cmd/consensus/main.go +++ b/cmd/consensus/main.go @@ -849,6 +849,7 @@ func main() { comp, syncCore, node.SyncEngineIdentifierProvider, + synceng.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not initialize synchronization engine: %w", err) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 0c60c2cec0b..01d22346437 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -1081,6 +1081,7 @@ func (exeNode *ExecutionNode) LoadSynchronizationEngine( exeNode.followerEng, exeNode.syncCore, node.SyncEngineIdentifierProvider, + synchronization.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not initialize synchronization engine: %w", err) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 1e294e70d01..9ff2a3fd61e 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -396,6 +396,7 @@ func (builder *ObserverServiceBuilder) buildSyncEngine() *ObserverServiceBuilder builder.FollowerEng, builder.SyncCore, builder.SyncEngineParticipantsProviderFactory(), + synceng.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not create synchronization engine: %w", err) diff --git a/cmd/verification_builder.go b/cmd/verification_builder.go index 948857fb196..e7e42d224f7 100644 --- a/cmd/verification_builder.go +++ b/cmd/verification_builder.go @@ -405,6 +405,7 @@ func (v *VerificationNodeBuilder) LoadComponentsAndModules() { followerEng, syncCore, node.SyncEngineIdentifierProvider, + commonsync.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not create synchronization engine: %w", err) diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index 79fc57a56f1..ddfad8ee52a 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -638,6 +638,7 @@ func createNode( comp, syncCore, idProvider, + synceng.NewSpamDetectionConfig(), func(cfg *synceng.Config) { // use a small pool and scan interval for sync engine cfg.ScanInterval = 500 * time.Millisecond diff --git a/engine/common/synchronization/config.go b/engine/common/synchronization/config.go index 48f1dfcfc93..646238098c0 100644 --- a/engine/common/synchronization/config.go +++ b/engine/common/synchronization/config.go @@ -41,12 +41,12 @@ func WithScanInterval(interval time.Duration) OptionFunc { // random number that can be generated by the random number generator. const spamProbabilityMultiplier = 1001 -type SpamReportConfig struct { +type SpamDetectionConfig struct { syncRequestProbability float32 } -func NewSpamReportConfig() *SpamReportConfig { - return &SpamReportConfig{ +func NewSpamDetectionConfig() *SpamDetectionConfig { + return &SpamDetectionConfig{ // create misbehavior report 1/100 message requests // TODO: make this configurable as a start up flag for the engine syncRequestProbability: 0.01, diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index bac643bafe6..5ee427546c2 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -56,8 +56,8 @@ type Engine struct { core module.SyncCore participantsProvider module.IdentifierProvider - requestHandler *RequestHandler // component responsible for handling requests - spamReportConfig *SpamReportConfig + requestHandler *RequestHandler // component responsible for handling requests + spamDetectionConfig *SpamDetectionConfig pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse pendingBlockResponses engine.MessageStore // message store for *message.BlockResponse @@ -78,6 +78,7 @@ func New( comp consensus.Compliance, core module.SyncCore, participantsProvider module.IdentifierProvider, + spamDetectionConfig *SpamDetectionConfig, opts ...OptionFunc, ) (*Engine, error) { @@ -108,7 +109,7 @@ func New( pollInterval: opt.PollInterval, scanInterval: opt.ScanInterval, participantsProvider: participantsProvider, - spamReportConfig: NewSpamReportConfig(), + spamDetectionConfig: spamDetectionConfig, } // register the engine with the network layer and store the conduit @@ -524,8 +525,8 @@ func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID f } // to avoid creating a misbehavior report for every sync request received, use a probabilistic approach. - // Create a report with a probability of spamReportConfig.syncRequestProbability - if float32(n) < e.spamReportConfig.syncRequestProbability*spamProbabilityMultiplier { + // Create a report with a probability of spamDetectionConfig.syncRequestProbability + if float32(n) < e.spamDetectionConfig.syncRequestProbability*spamProbabilityMultiplier { // create a misbehavior report e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report") report, err := alsp.NewMisbehaviorReport(originID, alsp.ResourceIntensiveRequest) diff --git a/engine/common/synchronization/engine_test.go b/engine/common/synchronization/engine_test.go index 84e124338a5..0c81d3fda5e 100644 --- a/engine/common/synchronization/engine_test.go +++ b/engine/common/synchronization/engine_test.go @@ -176,7 +176,8 @@ func (ss *SyncSuite) SetupTest() { filter.Not(filter.HasNodeID(ss.me.NodeID())), ), idCache, - )) + ), + NewSpamDetectionConfig()) require.NoError(ss.T(), err, "should pass engine initialization") ss.e = e @@ -252,7 +253,7 @@ func (ss *SyncSuite) TestProcess_SyncRequest_HigherThanReceiver_OutsideTolerance ss.con.AssertNotCalled(ss.T(), "Unicast", mock.Anything, mock.Anything) - ss.e.spamReportConfig.syncRequestProbability = 0.0 // force not creating misbehavior report + ss.e.spamDetectionConfig.syncRequestProbability = 0.0 // force not creating misbehavior report require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) @@ -307,7 +308,7 @@ func (ss *SyncSuite) TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTole ) // force creating misbehavior report by setting syncRequestProbability to 1.0 (i.e. report misbehavior 100% of the time) - ss.e.spamReportConfig.syncRequestProbability = 1.0 + ss.e.spamDetectionConfig.syncRequestProbability = 1.0 require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } @@ -389,7 +390,7 @@ func (ss *SyncSuite) TestLoad_Process_SyncRequest_HigherThanReceiver_OutsideTole misbehaviorsCounter++ }, ) - ss.e.spamReportConfig.syncRequestProbability = loadGroup.syncRequestProbabilityFactor + ss.e.spamDetectionConfig.syncRequestProbability = loadGroup.syncRequestProbabilityFactor require.NoError(ss.T(), ss.e.Process(channels.SyncCommittee, originID, req)) } diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index 8639e1ee1f7..4e38cefc458 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -790,6 +790,7 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity *flow.Identity, identit ), idCache, ), + synchronization.NewSpamDetectionConfig(), synchronization.WithPollInterval(time.Duration(0)), ) require.NoError(t, err) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 4b561916fe4..4986a07416d 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -296,6 +296,7 @@ func (builder *FollowerServiceBuilder) buildSyncEngine() *FollowerServiceBuilder builder.FollowerEng, builder.SyncCore, builder.SyncEngineParticipantsProviderFactory(), + synceng.NewSpamDetectionConfig(), ) if err != nil { return nil, fmt.Errorf("could not create synchronization engine: %w", err) From 0694f382115b280d604ecd003557f1e7ad6bf0c8 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:41:49 -0400 Subject: [PATCH 659/815] Update integration/tests/bft/base_suite.go Co-authored-by: Misha --- integration/tests/bft/base_suite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/base_suite.go b/integration/tests/bft/base_suite.go index 19d3feeafe0..5d5e1871fc9 100644 --- a/integration/tests/bft/base_suite.go +++ b/integration/tests/bft/base_suite.go @@ -65,7 +65,7 @@ func (b *BaseSuite) SetupSuite() { b.NodeConfigs = append(b.NodeConfigs, nodeConfig) } - // setup verification nodes + // setup single verification node b.NodeConfigs = append(b.NodeConfigs, testnet.NewNodeConfig(flow.RoleVerification, testnet.WithLogLevel(zerolog.FatalLevel)), ) From b84c87f239d88d9e91a9f9c4c234e00a3ec1041b Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:41:58 -0400 Subject: [PATCH 660/815] Update integration/tests/bft/base_suite.go Co-authored-by: Misha --- integration/tests/bft/base_suite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/base_suite.go b/integration/tests/bft/base_suite.go index 5d5e1871fc9..c06455be153 100644 --- a/integration/tests/bft/base_suite.go +++ b/integration/tests/bft/base_suite.go @@ -48,7 +48,7 @@ func (b *BaseSuite) AccessClient() *testnet.Client { func (b *BaseSuite) SetupSuite() { b.Log = unittest.LoggerForTest(b.Suite.T(), zerolog.InfoLevel) - // setup access nodes + // setup single access node b.NodeConfigs = append(b.NodeConfigs, testnet.NewNodeConfig(flow.RoleAccess, testnet.WithLogLevel(zerolog.FatalLevel)), ) From ea84b9e5bc133ffbcbafde13d0145e456d281f3e Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:42:19 -0400 Subject: [PATCH 661/815] Update integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go Co-authored-by: Misha --- .../disallowlisting/admin_command_disallowlisting_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go index 8da4083e0ed..fab2ea72d9b 100644 --- a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go +++ b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go @@ -57,7 +57,7 @@ func (a *AdminCommandDisallowListTestSuite) TestAdminCommandDisallowList() { // so we sleep this approximate amount of time to ensure all messages were attempted, processed and dropped. time.Sleep(3 * time.Second) - // messages sent after the node is block listed are considered unauthorized, we don't expect to receive any of them. + // messages sent after the node is disallow-listed are considered unauthorized, we don't expect to receive any of them. require.Equal( a.T(), int64(0), From bf44848a842e84175d0adc9539d3e513ec39c2ba Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:42:35 -0400 Subject: [PATCH 662/815] Update integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go Co-authored-by: Misha --- .../disallowlisting/admin_command_disallowlisting_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go index fab2ea72d9b..145d10a2c04 100644 --- a/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go +++ b/integration/tests/bft/protocol/disallowlisting/admin_command_disallowlisting_test.go @@ -37,7 +37,7 @@ func (a *AdminCommandDisallowListTestSuite) TestAdminCommandDisallowList() { fmt.Sprintf("expected to receive %d authorized events got: %d", numOfAuthorizedEvents, a.Orchestrator.expectedBlockedEventsReceived.Load())) // after disallow-listing node (a.senderVN) we should not receive any messages from that node. - // This is an asynchronous process with a number of sub processes involved including not limited to; + // This is an asynchronous process with a number of sub processes involved including but not limited to: // - submitting request to admin server for node to be disallow-listed. // - node disallow-list must update. // - peer manager needs to prune the connection (takes at least 1 second). From 62cac9cb5135d254d558d830bac544eafc2f3af4 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:43:11 -0400 Subject: [PATCH 663/815] Update integration/testnet/node_config_test.go Co-authored-by: Misha --- integration/testnet/node_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/testnet/node_config_test.go b/integration/testnet/node_config_test.go index 44d5ed993c2..c3c08d4b55f 100644 --- a/integration/testnet/node_config_test.go +++ b/integration/testnet/node_config_test.go @@ -14,7 +14,7 @@ func TestFilter(t *testing.T) { configs := testnet.NewNodeConfigSet(5, flow.RoleAccess) filters := configs.Filter(func(n testnet.NodeConfig) bool { return n.Role == flow.RoleAccess }) - + assert.Len(t, filters, 5) // should exclude execution node for _, config := range filters { assert.Equal(t, flow.RoleAccess, config.Role) } From 8edf28be821682dec03ac5b68782b20b7b647165 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:43:33 -0400 Subject: [PATCH 664/815] Update integration/tests/bft/protocol/README.md Co-authored-by: Misha --- integration/tests/bft/protocol/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/protocol/README.md b/integration/tests/bft/protocol/README.md index dc76414739b..fab7e24a96c 100644 --- a/integration/tests/bft/protocol/README.md +++ b/integration/tests/bft/protocol/README.md @@ -16,7 +16,7 @@ This attack involves an Attack Orchestrator corrupting an execution result and t The suite includes a constant timeout to define the attack window and a detailed test sequence. The `TestWintermuteAttack` method carries out the attack process. It first waits for an execution result to be corrupted and identifies the corresponding victim block. -It ensures that the corrupted execution nodes generate the correct result for the victim block and then waits for a specific number of approvals from corrupted verification nodes for each chunk of the corrupted result. +It ensures that the corrupt execution nodes generate the correct result for the victim block and then waits for a specific number of approvals from corrupt verification nodes for each chunk of the corrupted result. Further, the test waits for a block height equal to the victim block height to be sealed and verifies that the original victim block is correctly identified. Additional methods and logging information assist in detailing and controlling the flow of the attack. The entire suite is instrumental in evaluating the system's behavior under this specific attack condition and ensuring that the expected actions and responses are observed. \ No newline at end of file From abb1572b16fe43ed7379415dd520ed6496dc895a Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:43:41 -0400 Subject: [PATCH 665/815] Update integration/tests/bft/protocol/README.md Co-authored-by: Misha --- integration/tests/bft/protocol/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/protocol/README.md b/integration/tests/bft/protocol/README.md index fab7e24a96c..7411bf8f25a 100644 --- a/integration/tests/bft/protocol/README.md +++ b/integration/tests/bft/protocol/README.md @@ -12,7 +12,7 @@ The entire suite assures that the disallow-listing command behaves as intended, ## Wintermute Attack Test The `WintermuteTestSuite` in the `wintermute` package is focused on validating a specific attack scenario within the network, termed the "wintermute attack." -This attack involves an Attack Orchestrator corrupting an execution result and then leveraging corrupted verification nodes to verify it. +This attack involves an Attack Orchestrator corrupting an execution result and then leveraging corrupt verification nodes to verify it. The suite includes a constant timeout to define the attack window and a detailed test sequence. The `TestWintermuteAttack` method carries out the attack process. It first waits for an execution result to be corrupted and identifies the corresponding victim block. From ca62488dba1290da421741a2257b31d9c9bfa39e Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:43:50 -0400 Subject: [PATCH 666/815] Update integration/tests/bft/gossipsub/README.md Co-authored-by: Misha --- integration/tests/bft/gossipsub/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 91a7a6ed038..1b546dc9580 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -24,7 +24,7 @@ The test is broken down into the following main parts: 2. **Authorized Messages Testing**: Messages sent by the authorized attacker, with the correct signature, must pass the libp2p signature verification process and be delivered to the victim. The test checks for all authorized messages received by the victim within a certain time frame. ## RPC Inspector False Positive Test -The `GossipsubRPCInspectorFalsePositiveNotificationsTestSuite` test within the `rpc_inspector` package test suite aims to ensure that the underlying libp2p libraries related to gossip sub RPC control message inspection do not trigger false positives during their validation processes. +The `GossipsubRPCInspectorFalsePositiveNotificationsTestSuite` test within the `rpc_inspector` package test suite aims to ensure that the underlying libp2p libraries related to GossipSub RPC control message inspection do not trigger false positives during their validation processes. Here's a breakdown of the `TestGossipsubRPCInspectorFalsePositiveNotifications` method: 1. **Configuration and Context Setup**: A specific duration for loading and intervals is defined, and a context with a timeout is created for the test scenario. 2. **Simulating Network Activity**: The method triggers a "loader loop" with a specific number of test accounts and intervals, intending to create artificial network activity. It does this by submitting transactions to create Flow accounts, waiting for them to be sealed. From 40c825f9542df93d3453156e98d869a6ba2a3ca9 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:44:00 -0400 Subject: [PATCH 667/815] Update integration/tests/bft/gossipsub/README.md Co-authored-by: Misha --- integration/tests/bft/gossipsub/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 1b546dc9580..4793150e297 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -18,7 +18,7 @@ The test confirms two main aspects: ## Signature Requirement Test The `TestGossipSubSignatureRequirement` method sets up a test environment consisting of three corrupted nodes:two attackers and one victim. One attacker is configured without message signing, intending to send unsigned messages that should be rejected by the victim. -The other attacker sends valid signed messages that should be received by the victim. +The other (benign) attacker sends valid signed messages that should be received by the victim. The test is broken down into the following main parts: 1. **Unauthorized Messages Testing**: The victim node should not receive any messages sent without correct signatures from the unauthorized attacker. The test checks for zero unauthorized messages received by the victim. 2. **Authorized Messages Testing**: Messages sent by the authorized attacker, with the correct signature, must pass the libp2p signature verification process and be delivered to the victim. The test checks for all authorized messages received by the victim within a certain time frame. From 0eabca5661e3c10943e83770f49b52d917a68ca4 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:44:09 -0400 Subject: [PATCH 668/815] Update integration/tests/bft/gossipsub/README.md Co-authored-by: Misha --- integration/tests/bft/gossipsub/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 4793150e297..37df91d12df 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -17,7 +17,7 @@ The test confirms two main aspects: ## Signature Requirement Test The `TestGossipSubSignatureRequirement` method sets up a test environment consisting of three corrupted nodes:two attackers and one victim. -One attacker is configured without message signing, intending to send unsigned messages that should be rejected by the victim. +One (malicious) attacker is configured without message signing, intending to send unsigned messages that should be rejected by the victim. The other (benign) attacker sends valid signed messages that should be received by the victim. The test is broken down into the following main parts: 1. **Unauthorized Messages Testing**: The victim node should not receive any messages sent without correct signatures from the unauthorized attacker. The test checks for zero unauthorized messages received by the victim. From 44c0894d6669bfdb2d7a8baf873bed8a882bd87f Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:45:02 -0400 Subject: [PATCH 669/815] Update integration/testnet/node_config_test.go Co-authored-by: Misha --- integration/testnet/node_config_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration/testnet/node_config_test.go b/integration/testnet/node_config_test.go index c3c08d4b55f..9566c9c0b98 100644 --- a/integration/testnet/node_config_test.go +++ b/integration/testnet/node_config_test.go @@ -13,6 +13,8 @@ func TestFilter(t *testing.T) { t.Run("filters by role", func(t *testing.T) { configs := testnet.NewNodeConfigSet(5, flow.RoleAccess) + // add another role to the set to ensure it is filtered out + configs = append(configs, testnet.NewNodeConfig(flow.RoleExecution)) filters := configs.Filter(func(n testnet.NodeConfig) bool { return n.Role == flow.RoleAccess }) assert.Len(t, filters, 5) // should exclude execution node for _, config := range filters { From 3ea2a6984fc85573140236e74194fb3cab7c4048 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:45:17 -0400 Subject: [PATCH 670/815] Update integration/tests/bft/framework/README.md Co-authored-by: Misha --- integration/tests/bft/framework/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/framework/README.md b/integration/tests/bft/framework/README.md index 178714c75dd..465462c9bc4 100644 --- a/integration/tests/bft/framework/README.md +++ b/integration/tests/bft/framework/README.md @@ -4,7 +4,7 @@ The framework BFT tests are designed to assess the health of the BFT testing fra ## Passthrough Sealing and Verification Test The `PassThroughTestSuite` test within the `framework` package and includes a specific test method `TestSealingAndVerificationPassThrough`. This test evaluates the health of the BFT testing framework for Byzantine Fault Tolerance (BFT) testing. -1. **Simulates a Scenario**: It sets up a scenario with two corrupted execution nodes and one corrupted verification node, controlled by a dummy orchestrator that lets all incoming events pass through. +1. **Simulates a Scenario**: It sets up a scenario with two corrupt execution nodes and one corrupt verification node, controlled by a dummy orchestrator that lets all incoming events pass through. 2. **Deploys Transaction and Verifies Chunks**: Deploys a transaction leading to an execution result with multiple chunks, assigns them to a verification node, and verifies the generation of result approvals for all chunks. 3. **Sealing and Verification**: Enables sealing based on result approvals and verifies the sealing of a block with a specific multi-chunk execution result. 4. **Evaluates Events**: The test also assesses whether critical sealing-and-verification-related events from corrupted nodes are passed through the orchestrator, by checking both egress and ingress events. From 2c0ae3f2c71dde4a8c4ef68d197bd0a92aff5bf2 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:45:28 -0400 Subject: [PATCH 671/815] Update integration/tests/bft/gossipsub/README.md Co-authored-by: Misha --- integration/tests/bft/gossipsub/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 37df91d12df..7467cb575bc 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -16,7 +16,7 @@ The test confirms two main aspects: 2. Authorized messages should be correctly delivered and processed by the victim node. ## Signature Requirement Test -The `TestGossipSubSignatureRequirement` method sets up a test environment consisting of three corrupted nodes:two attackers and one victim. +The `TestGossipSubSignatureRequirement` test sets up a test environment consisting of three corrupt nodes: two attackers and one victim. One (malicious) attacker is configured without message signing, intending to send unsigned messages that should be rejected by the victim. The other (benign) attacker sends valid signed messages that should be received by the victim. The test is broken down into the following main parts: From d8acfb9da80e677d0dba178a465d305a37024b7a Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:45:35 -0400 Subject: [PATCH 672/815] Update integration/tests/bft/gossipsub/README.md Co-authored-by: Misha --- integration/tests/bft/gossipsub/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/gossipsub/README.md b/integration/tests/bft/gossipsub/README.md index 7467cb575bc..29de8880900 100644 --- a/integration/tests/bft/gossipsub/README.md +++ b/integration/tests/bft/gossipsub/README.md @@ -6,7 +6,7 @@ The `TopicValidatorTestSuite` in the `topicvalidator` package is specifically de libp2p topic validator within a network scenario. This suite includes an end-to-end test to verify the topic validator's behavior in different situations, focusing on both unauthorized and authorized message handling. -The method `TestTopicValidatorE2E` is a comprehensive test that mimics an environment with a corrupted byzantine attacker node +The method `TestTopicValidatorE2E` is a comprehensive test that mimics an environment with a corrupt byzantine attacker node attempting to send unauthorized messages to a victim node. These messages should be dropped by the topic validator, as they fail the message authorization validation. The test simultaneously sends authorized messages to the victim node, ensuring that they are processed correctly, From 5af3a49869b29ac85cdb71df98196c0028658f58 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:46:14 -0400 Subject: [PATCH 673/815] Update integration/tests/bft/framework/README.md Co-authored-by: Misha --- integration/tests/bft/framework/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/framework/README.md b/integration/tests/bft/framework/README.md index 465462c9bc4..62c55c51049 100644 --- a/integration/tests/bft/framework/README.md +++ b/integration/tests/bft/framework/README.md @@ -7,4 +7,4 @@ This test evaluates the health of the BFT testing framework for Byzantine Fault 1. **Simulates a Scenario**: It sets up a scenario with two corrupt execution nodes and one corrupt verification node, controlled by a dummy orchestrator that lets all incoming events pass through. 2. **Deploys Transaction and Verifies Chunks**: Deploys a transaction leading to an execution result with multiple chunks, assigns them to a verification node, and verifies the generation of result approvals for all chunks. 3. **Sealing and Verification**: Enables sealing based on result approvals and verifies the sealing of a block with a specific multi-chunk execution result. -4. **Evaluates Events**: The test also assesses whether critical sealing-and-verification-related events from corrupted nodes are passed through the orchestrator, by checking both egress and ingress events. +4. **Evaluates Events**: The test also assesses whether critical sealing-and-verification-related events from corrupt nodes are passed through the orchestrator, by checking both egress and ingress events. From 47e54c85836af6d3a35f306bc79d6f4cdab8cf77 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Mon, 28 Aug 2023 11:46:21 -0400 Subject: [PATCH 674/815] Update integration/tests/bft/framework/README.md Co-authored-by: Misha --- integration/tests/bft/framework/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/bft/framework/README.md b/integration/tests/bft/framework/README.md index 62c55c51049..55423be21c0 100644 --- a/integration/tests/bft/framework/README.md +++ b/integration/tests/bft/framework/README.md @@ -2,7 +2,7 @@ The framework BFT tests are designed to assess the health of the BFT testing framework. ## Passthrough Sealing and Verification Test -The `PassThroughTestSuite` test within the `framework` package and includes a specific test method `TestSealingAndVerificationPassThrough`. +The `PassThroughTestSuite` test within the `framework` package includes a specific test method `TestSealingAndVerificationPassThrough`. This test evaluates the health of the BFT testing framework for Byzantine Fault Tolerance (BFT) testing. 1. **Simulates a Scenario**: It sets up a scenario with two corrupt execution nodes and one corrupt verification node, controlled by a dummy orchestrator that lets all incoming events pass through. 2. **Deploys Transaction and Verifies Chunks**: Deploys a transaction leading to an execution result with multiple chunks, assigns them to a verification node, and verifies the generation of result approvals for all chunks. From d49533c9e2ac7b808bf7cbce12ec4eaa956f5ccd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 11:53:14 -0400 Subject: [PATCH 675/815] extends the godoc of base suite --- integration/tests/bft/base_suite.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/integration/tests/bft/base_suite.go b/integration/tests/bft/base_suite.go index c06455be153..b50085a9e50 100644 --- a/integration/tests/bft/base_suite.go +++ b/integration/tests/bft/base_suite.go @@ -1,3 +1,4 @@ +// Package bft provides testing facilities for Flow BFT protocols. package bft import ( @@ -18,6 +19,19 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) +// BaseSuite serves as a base test suite offering various utility functions +// and default setup/teardown steps. It facilitates the creation of Flow networks +// with pre-configured nodes and allows for easier interaction with the network, +// reducing boilerplate code in individual tests. +// +// BaseSuite comes with a lot of functionality out-of-the-box, including the ability to: +// - Create a bare-minimum Flow network. +// - Start and stop the network. +// - Track messages over testnet using TestnetStateTracker. +// - Tear down the testnet environment. +// - Handle Ghost nodes and Orchestrator network. +// +// BaseSuite embeds testify's Suite to leverage setup, teardown, and assertion capabilities. type BaseSuite struct { suite.Suite Log zerolog.Logger @@ -30,7 +44,8 @@ type BaseSuite struct { OrchestratorNetwork *orchestrator.Network } -// Ghost returns a client to interact with the Ghost node on testnet. +// Ghost returns a client to interact with the Ghost node on the testnet. +// It is essential for observing the messages exchanged in the network. func (b *BaseSuite) Ghost() *client.GhostClient { c, err := b.Net.ContainerByID(b.GhostID).GhostClient() require.NoError(b.T(), err, "could not get ghost client") @@ -44,7 +59,9 @@ func (b *BaseSuite) AccessClient() *testnet.Client { return c } -// SetupSuite sets up node configs to run a bare minimum Flow network to function correctly. +// SetupSuite initializes the BaseSuite, setting up a bare-minimum Flow network. +// It configures nodes with roles such as access, consensus, verification, execution, +// and collection. It also sets up a Ghost node for observing messages exchanged on the network. func (b *BaseSuite) SetupSuite() { b.Log = unittest.LoggerForTest(b.Suite.T(), zerolog.InfoLevel) @@ -94,7 +111,8 @@ func (b *BaseSuite) SetupSuite() { b.NodeConfigs = append(b.NodeConfigs, ghostConfig) } -// TearDownSuite tears down the test network of Flow as well as the BFT testing orchestrator network. +// TearDownSuite cleans up the resources, stopping both the Flow network and the +// orchestrator network if they have been initialized. func (b *BaseSuite) TearDownSuite() { b.Net.Remove() b.Cancel() @@ -104,7 +122,10 @@ func (b *BaseSuite) TearDownSuite() { } } -// StartCorruptedNetwork starts the corrupted network with the configured node configs, this func should be used after test suite is setup. +// StartCorruptedNetwork initializes and starts a corrupted Flow network. +// This should be called after the test suite is set up. The function accepts +// configurations like the name of the network, the number of views in the staking auction, +// the number of views in an epoch, and a function to attack the orchestrator. func (b *BaseSuite) StartCorruptedNetwork(name string, viewsInStakingAuction, viewsInEpoch uint64, attackOrchestrator func() insecure.AttackOrchestrator) { // generates, initializes, and starts the Flow network netConfig := testnet.NewNetworkConfig( From 53a2434294c4240a01e813a8d23c0eaabdc17421 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 11:57:52 -0400 Subject: [PATCH 676/815] adds a comment for number of (un)authorized events --- .../bft/gossipsub/signature/requirement/orchestrator.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go b/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go index c498f4f2f22..5419d263015 100644 --- a/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go +++ b/integration/tests/bft/gossipsub/signature/requirement/orchestrator.go @@ -21,9 +21,15 @@ const ( // The numOfAuthorizedEvents allows us to wait for a certain number of authorized messages to be received, this should // give the network enough time to process the unauthorized messages. This ensures us that the unauthorized messages // were indeed dropped and not unprocessed. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test + // on an asynchronous network where event delivery can be unpredictable. numOfAuthorizedEvents = 5 // numOfUnauthorizedEvents the number of unauthorized events to send by the test orchestrator. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test + // on an asynchronous network where event delivery can be unpredictable. numOfUnauthorizedEvents = 5 ) From 7ac2c2b92f9424b3b89c846360affa82a2696706 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 11:59:34 -0400 Subject: [PATCH 677/815] adds a comment for number of (un)authorized events --- .../tests/bft/protocol/disallowlisting/orchestrator.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/integration/tests/bft/protocol/disallowlisting/orchestrator.go b/integration/tests/bft/protocol/disallowlisting/orchestrator.go index 8ff2a6612f9..41cedc4883b 100644 --- a/integration/tests/bft/protocol/disallowlisting/orchestrator.go +++ b/integration/tests/bft/protocol/disallowlisting/orchestrator.go @@ -17,9 +17,14 @@ import ( const ( // numOfAuthorizedEvents number of events to send before disallow-listing the sender node via the disallow-list command. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test numOfAuthorizedEvents = 5 // numOfUnauthorizedEvents number of events to send after disallow-listing the sender node via the disallow-list command. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test + // on an asynchronous network where event delivery can be unpredictable. numOfUnauthorizedEvents = 5 ) From 51c29eab4d7f0021691319aae05151619595eb89 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 12:01:48 -0400 Subject: [PATCH 678/815] adds a comment for number of (un)authorized events --- .../tests/bft/gossipsub/topicvalidator/orchestrator.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go b/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go index d7ed9bfcf8d..39c6a325ddf 100644 --- a/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go +++ b/integration/tests/bft/gossipsub/topicvalidator/orchestrator.go @@ -21,9 +21,15 @@ const ( // The numOfAuthorizedEvents allows us to wait for a certain number of authorized messages to be received, this should // give the network enough time to process the unauthorized messages. This ensures us that the unauthorized messages // were indeed dropped and not unprocessed. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test + // on an asynchronous network where event delivery can be unpredictable. numOfAuthorizedEvents = 5 // numOfUnauthorizedEvents the number of unauthorized events per type to send by the test orchestrator. + // This threshold must be set to a low value to make the test conclude faster + // by waiting for fewer events, which is beneficial when running the test + // on an asynchronous network where event delivery can be unpredictable. numOfUnauthorizedEvents = 5 ) From 88ab754157886759c2f46993b91bb813486f7717 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Mon, 28 Aug 2023 13:05:50 -0400 Subject: [PATCH 679/815] quarantine flaky tests --- network/p2p/scoring/decay_test.go | 59 ++++++++++++++++++++----------- network/test/echoengine_test.go | 2 ++ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/network/p2p/scoring/decay_test.go b/network/p2p/scoring/decay_test.go index 5bbd525fc4b..55e2c51ba2c 100644 --- a/network/p2p/scoring/decay_test.go +++ b/network/p2p/scoring/decay_test.go @@ -10,31 +10,48 @@ import ( "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/scoring" + "github.com/onflow/flow-go/utils/unittest" ) +type args struct { + penalty float64 + decay float64 + lastUpdated time.Time +} +type testCase struct { + name string + args args + want float64 + wantErr error +} + +// TestGeometricDecay_ValidParams tests geometric score with a valid penalty, decay, and time. This test case is separated from the other +// test cases due to flakiness. +func TestGeometricDecay_ValidParams(t *testing.T) { + unittest.SkipUnless(t, unittest.TEST_FLAKY, "test is flaky") + tt := testCase{ + name: "valid penalty, decay, and time", + args: args{ + penalty: 100, + decay: 0.9, + lastUpdated: time.Now().Add(-10 * time.Second), + }, + want: 100 * math.Pow(0.9, 10), + wantErr: nil, + } + + t.Run(tt.name, func(t *testing.T) { + got, err := scoring.GeometricDecay(tt.args.penalty, tt.args.decay, tt.args.lastUpdated) + if tt.wantErr != nil { + assert.Errorf(t, err, tt.wantErr.Error()) + } + assert.LessOrEqual(t, truncateFloat(math.Abs(got-tt.want), 3), 1e-3) + }) +} + // TestGeometricDecay tests the GeometricDecay function. func TestGeometricDecay(t *testing.T) { - type args struct { - penalty float64 - decay float64 - lastUpdated time.Time - } - tests := []struct { - name string - args args - want float64 - wantErr error - }{ - { - name: "valid penalty, decay, and time", - args: args{ - penalty: 100, - decay: 0.9, - lastUpdated: time.Now().Add(-10 * time.Second), - }, - want: 100 * math.Pow(0.9, 10), - wantErr: nil, - }, + tests := []testCase{ { name: "zero decay factor", args: args{ diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 6637ccf9f66..435a31f3c4b 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -126,6 +126,7 @@ func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { // It also evaluates the correct reception of an echo message back for each send. // Sender and receiver are not synchronized func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "test is flaky") // set to true for an echo expectation suite.multiMessageAsync(true, 10, suite.Multicast) } @@ -180,6 +181,7 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Publish methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "test is flaky") suite.duplicateMessageDifferentChan(suite.Publish) } From 8150c52ad60709dc8270ab107c1fd2f5dce6f076 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Mon, 28 Aug 2023 13:22:57 -0400 Subject: [PATCH 680/815] Update decay_test.go --- network/p2p/scoring/decay_test.go | 61 +++++++++++-------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/network/p2p/scoring/decay_test.go b/network/p2p/scoring/decay_test.go index 55e2c51ba2c..9506a09ffdf 100644 --- a/network/p2p/scoring/decay_test.go +++ b/network/p2p/scoring/decay_test.go @@ -10,48 +10,31 @@ import ( "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/scoring" - "github.com/onflow/flow-go/utils/unittest" ) -type args struct { - penalty float64 - decay float64 - lastUpdated time.Time -} -type testCase struct { - name string - args args - want float64 - wantErr error -} - -// TestGeometricDecay_ValidParams tests geometric score with a valid penalty, decay, and time. This test case is separated from the other -// test cases due to flakiness. -func TestGeometricDecay_ValidParams(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "test is flaky") - tt := testCase{ - name: "valid penalty, decay, and time", - args: args{ - penalty: 100, - decay: 0.9, - lastUpdated: time.Now().Add(-10 * time.Second), - }, - want: 100 * math.Pow(0.9, 10), - wantErr: nil, - } - - t.Run(tt.name, func(t *testing.T) { - got, err := scoring.GeometricDecay(tt.args.penalty, tt.args.decay, tt.args.lastUpdated) - if tt.wantErr != nil { - assert.Errorf(t, err, tt.wantErr.Error()) - } - assert.LessOrEqual(t, truncateFloat(math.Abs(got-tt.want), 3), 1e-3) - }) -} - // TestGeometricDecay tests the GeometricDecay function. func TestGeometricDecay(t *testing.T) { - tests := []testCase{ + type args struct { + penalty float64 + decay float64 + lastUpdated time.Time + } + tests := []struct { + name string + args args + want float64 + wantErr error + }{ + { + name: "valid penalty, decay, and time", + args: args{ + penalty: 100, + decay: 0.9, + lastUpdated: time.Now().Add(-10 * time.Second), + }, + want: 100 * math.Pow(0.9, 10), + wantErr: nil, + }, { name: "zero decay factor", args: args{ @@ -133,7 +116,7 @@ func TestGeometricDecay(t *testing.T) { wantErr: fmt.Errorf("last updated time cannot be in the future"), }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := scoring.GeometricDecay(tt.args.penalty, tt.args.decay, tt.args.lastUpdated) From 367b94c5e9a81b0108ab1f010a6d0c1911864e8d Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Mon, 28 Aug 2023 16:29:31 -0400 Subject: [PATCH 681/815] Update decay_test.go --- network/p2p/scoring/decay_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/decay_test.go b/network/p2p/scoring/decay_test.go index 9506a09ffdf..5bbd525fc4b 100644 --- a/network/p2p/scoring/decay_test.go +++ b/network/p2p/scoring/decay_test.go @@ -116,7 +116,7 @@ func TestGeometricDecay(t *testing.T) { wantErr: fmt.Errorf("last updated time cannot be in the future"), }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := scoring.GeometricDecay(tt.args.penalty, tt.args.decay, tt.args.lastUpdated) From b048e99010c548e02b7e8040177f7cecf15435d0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 16:38:49 -0400 Subject: [PATCH 682/815] moves set violation consumer to midddleware logic --- .../node_builder/access_node_builder.go | 12 +++ cmd/observer/node_builder/observer_builder.go | 12 +++ cmd/scaffold.go | 12 +++ follower/follower_builder.go | 12 +++ network/alsp/manager/manager_test.go | 18 ++-- network/internal/testutils/testUtil.go | 10 ++- network/p2p/middleware/middleware.go | 36 ++++---- network/p2p/network.go | 9 +- network/test/blob_service_test.go | 4 +- network/test/echoengine_test.go | 4 +- network/test/epochtransition_test.go | 4 +- network/test/meshengine_test.go | 4 +- network/test/middleware_test.go | 34 +++----- network/test/unicast_authorization_test.go | 86 ++++++++++--------- 14 files changed, 150 insertions(+), 107 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index bc3c3e45199..b1af4d217e9 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -80,6 +80,7 @@ import ( "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/unicast/protocols" relaynet "github.com/onflow/flow-go/network/relay" + "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/topology" "github.com/onflow/flow-go/network/validator" "github.com/onflow/flow-go/state/protocol" @@ -1411,6 +1412,17 @@ func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index cad4a6f6972..7491f1ac300 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -66,6 +66,7 @@ import ( "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/utils" + "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/validator" stateprotocol "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" @@ -1038,6 +1039,17 @@ func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, }, middleware.WithMessageValidators(validators...), // use default identifier provider ) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 11d359c2ae7..be491f17fd2 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -59,6 +59,7 @@ import ( "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" "github.com/onflow/flow-go/network/p2p/utils/ratelimiter" + "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/topology" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" @@ -444,6 +445,17 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( UnicastMessageTimeout: fnb.FlowConfig.NetworkConfig.UnicastMessageTimeout, IdTranslator: fnb.IDTranslator, Codec: fnb.CodecFactory(), + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := fnb.Network.(network.Adapter) + if !ok { + fnb.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network, adapter) + }, }, mwOpts...) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index a650846244f..f775ceac043 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -56,6 +56,7 @@ import ( "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/utils" + "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/validator" "github.com/onflow/flow-go/state/protocol" badgerState "github.com/onflow/flow-go/state/protocol/badger" @@ -765,6 +766,17 @@ func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, IdTranslator: builder.IDTranslator, Codec: builder.CodecFactory(), + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, }, middleware.WithMessageValidators(validators...), ) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 1f7ae297d23..b3b2a2f69ce 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -62,8 +62,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { t, ids, nodes, - testutils.MiddlewareConfigFixture(t, sporkId), - mocknetwork.NewViolationsConsumer(t)) + testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0]) net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) @@ -127,8 +126,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { t, ids, nodes, - testutils.MiddlewareConfigFixture(t, sporkId), - mocknetwork.NewViolationsConsumer(t)) + testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -231,8 +229,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) t, ids, nodes, - testutils.MiddlewareConfigFixture(t, sporkId), - mocknetwork.NewViolationsConsumer(t)) + testutils.MiddlewareConfigFixture(t, sporkId)) idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) @@ -314,8 +311,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t t, ids, nodes, - testutils.MiddlewareConfigFixture(t, sporkId), - mocknetwork.NewViolationsConsumer(t)) + testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture( t, *ids[0], @@ -414,7 +410,11 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) + mws, _ := testutils.MiddlewareFixtures( + t, + ids, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index c4215b6a188..f93f4cb2767 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -27,6 +27,7 @@ import ( netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/codec/cbor" + "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/netconf" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/conduit" @@ -176,7 +177,6 @@ func MiddlewareFixtures( identities flow.IdentityList, libP2PNodes []p2p.LibP2PNode, cfg *middleware.Config, - consumer network.ViolationsConsumer, opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { require.Equal(t, len(identities), len(libP2PNodes)) @@ -184,6 +184,13 @@ func MiddlewareFixtures( mws := make([]network.Middleware, len(identities)) idProviders := make([]*unittest.UpdatableIDProvider, len(identities)) + if cfg.SlashingViolationConsumerFactory == nil { + // use a mock slashing violation consumer factory if not provided + cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { + return mocknetwork.NewViolationsConsumer(t) + } + } + for i := 0; i < len(identities); i++ { i := i cfg.Libp2pNode = libP2PNodes[i] @@ -191,7 +198,6 @@ func MiddlewareFixtures( idProviders[i] = unittest.NewUpdatableIDProvider(identities) cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProviders[i]) mws[i] = middleware.NewMiddleware(cfg, opts...) - mws[i].SetSlashingViolationsConsumer(consumer) } return mws, idProviders } diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 4751cf71b98..bf9bfb2afa6 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -131,14 +131,15 @@ func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { // Config is the configuration for the middleware. type Config struct { - Logger zerolog.Logger - Libp2pNode p2p.LibP2PNode - FlowId flow.Identifier // This node's Flow ID - BitSwapMetrics module.BitswapMetrics - SporkId flow.Identifier - UnicastMessageTimeout time.Duration - IdTranslator p2p.IDTranslator - Codec network.Codec + Logger zerolog.Logger + Libp2pNode p2p.LibP2PNode + FlowId flow.Identifier // This node's Flow ID + BitSwapMetrics module.BitswapMetrics + SporkId flow.Identifier + UnicastMessageTimeout time.Duration + IdTranslator p2p.IDTranslator + Codec network.Codec + SlashingViolationConsumerFactory func() network.ViolationsConsumer } // Validate validates the configuration, and sets default values for any missing fields. @@ -162,15 +163,16 @@ func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { // create the node entity and inject dependencies & config mw := &Middleware{ - log: cfg.Logger, - libP2PNode: cfg.Libp2pNode, - bitswapMetrics: cfg.BitSwapMetrics, - sporkId: cfg.SporkId, - validators: DefaultValidators(cfg.Logger, cfg.FlowId), - unicastMessageTimeout: cfg.UnicastMessageTimeout, - idTranslator: cfg.IdTranslator, - codec: cfg.Codec, - unicastRateLimiters: ratelimit.NoopRateLimiters(), + log: cfg.Logger, + libP2PNode: cfg.Libp2pNode, + bitswapMetrics: cfg.BitSwapMetrics, + sporkId: cfg.SporkId, + validators: DefaultValidators(cfg.Logger, cfg.FlowId), + unicastMessageTimeout: cfg.UnicastMessageTimeout, + idTranslator: cfg.IdTranslator, + codec: cfg.Codec, + unicastRateLimiters: ratelimit.NoopRateLimiters(), + slashingViolationsConsumer: cfg.SlashingViolationConsumerFactory(), } for _, opt := range opts { diff --git a/network/p2p/network.go b/network/p2p/network.go index f0fe782d983..02bb147d98b 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -5,11 +5,12 @@ import ( "context" "errors" "fmt" - ggio "github.com/gogo/protobuf/io" - libp2pnet "github.com/libp2p/go-libp2p/core/network" "sync" "time" + ggio "github.com/gogo/protobuf/io" + libp2pnet "github.com/libp2p/go-libp2p/core/network" + "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" @@ -26,7 +27,6 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/queue" - "github.com/onflow/flow-go/network/slashing" _ "github.com/onflow/flow-go/utils/binstat" "github.com/onflow/flow-go/utils/logging" ) @@ -83,7 +83,6 @@ type Network struct { registerEngineRequests chan *registerEngineRequest registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager - slashingViolationsConsumer network.ViolationsConsumer unicastMessageTimeout time.Duration } @@ -209,8 +208,6 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { opt(n) } - n.slashingViolationsConsumer = slashing.NewSlashingViolationsConsumer(param.Logger, param.Metrics, n) - n.mw.SetSlashingViolationsConsumer(n.slashingViolationsConsumer) n.mw.SetOverlay(n) if err := n.conduitFactory.RegisterAdapter(n); err != nil { diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 8b9cf72058f..d582014776d 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -14,7 +14,6 @@ import ( "github.com/stretchr/testify/suite" "go.uber.org/atomic" - "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/dht" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" @@ -97,8 +96,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { suite.T(), ids, nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) + testutils.MiddlewareConfigFixture(suite.T(), sporkId)) suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index d4c5d6e01ef..cc7ddc5b238 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -19,7 +19,6 @@ import ( "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/utils/unittest" ) @@ -60,8 +59,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { suite.T(), suite.ids, nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) + testutils.MiddlewareConfigFixture(suite.T(), sporkId)) suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 652c5b2debe..e34822cd4c9 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -24,7 +24,6 @@ import ( "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" mockprotocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/utils/unittest" @@ -199,8 +198,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { suite.T(), ids, nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) + testutils.MiddlewareConfigFixture(suite.T(), sporkId)) nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) suite.cancels = append(suite.cancels, cancel) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index dda3b0504ae..1c762025d00 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -28,7 +28,6 @@ import ( "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pnode" @@ -106,8 +105,7 @@ func (suite *MeshEngineTestSuite) SetupTest() { suite.T(), suite.ids, libP2PNodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) + testutils.MiddlewareConfigFixture(suite.T(), sporkId)) suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index cee3159d7fb..bbd1629c83a 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -71,19 +71,18 @@ func (co *tagsObserver) OnComplete() { type MiddlewareTestSuite struct { suite.Suite sync.RWMutex - size int // used to determine number of middlewares under test - libP2PNodes []p2p.LibP2PNode - networks []network.Network - mws []network.Middleware // used to keep track of middlewares under test - obs chan string // used to keep track of Protect events tagged by pubsub messages - ids []*flow.Identity - metrics *metrics.NoopCollector // no-op performance monitoring simulation - logger zerolog.Logger - providers []*unittest.UpdatableIDProvider - sporkId flow.Identifier - mwCancel context.CancelFunc - mwCtx irrecoverable.SignalerContext - slashingViolationsConsumer network.ViolationsConsumer + size int // used to determine number of middlewares under test + libP2PNodes []p2p.LibP2PNode + networks []network.Network + mws []network.Middleware // used to keep track of middlewares under test + obs chan string // used to keep track of Protect events tagged by pubsub messages + ids []*flow.Identity + metrics *metrics.NoopCollector // no-op performance monitoring simulation + logger zerolog.Logger + providers []*unittest.UpdatableIDProvider + sporkId flow.Identifier + mwCancel context.CancelFunc + mwCtx irrecoverable.SignalerContext } // TestMiddlewareTestSuit runs all the test methods in this test suit @@ -106,7 +105,6 @@ func (m *MiddlewareTestSuite) SetupTest() { log: m.logger, } - m.slashingViolationsConsumer = mocknetwork.NewViolationsConsumer(m.T()) m.sporkId = unittest.IdentifierFixture() libP2PNodes := make([]p2p.LibP2PNode, 0) @@ -146,8 +144,7 @@ func (m *MiddlewareTestSuite) SetupTest() { m.T(), m.ids, m.libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - m.slashingViolationsConsumer) + testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.mws) for _, observableConnMgr := range tagObservables { @@ -198,8 +195,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { m.T(), ids, libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - m.slashingViolationsConsumer) + testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) networkCfg := testutils.NetworkConfigFixture( m.T(), *ids[0], @@ -305,7 +301,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { ids, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) @@ -448,7 +443,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { ids, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - m.slashingViolationsConsumer, middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) require.Len(m.T(), ids, 1) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index b2915fc1bd8..1534375ee96 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -39,8 +39,12 @@ type UnicastAuthorizationTestSuite struct { libP2PNodes []p2p.LibP2PNode // senderMW is the mw that will be sending the message senderMW network.Middleware + // senderNetwork is the networking layer instance that will be used to send the message. + senderNetwork network.Network // senderID the identity on the mw sending the message senderID *flow.Identity + // receiverNetwork is the networking layer instance that will be used to receive the message. + receiverNetwork network.Network // receiverMW is the mw that will be sending the message receiverMW network.Middleware // receiverID the identity on the mw sending the message @@ -77,11 +81,18 @@ func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingVio u.sporkId = unittest.IdentifierFixture() ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) - mws, providers := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg, slashingViolationsConsumer) + cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { + return slashingViolationsConsumer // override slashing violations consumer with the one passed as argument + } + mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) + nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws) require.Len(u.T(), ids, 2) require.Len(u.T(), providers, 2) require.Len(u.T(), mws, 2) + require.Len(u.T(), nets, 2) + u.senderNetwork = nets[0] + u.receiverNetwork = nets[1] u.senderID = ids[0] u.senderMW = mws[0] u.receiverID = ids[1] @@ -95,15 +106,11 @@ func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Ov ctx, cancel := context.WithCancel(context.Background()) sigCtx, _ := irrecoverable.WithSignaler(ctx) - testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 100*time.Millisecond) - - u.senderMW.SetOverlay(overlay) - u.senderMW.Start(sigCtx) - - u.receiverMW.SetOverlay(overlay) - u.receiverMW.Start(sigCtx) + testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) + testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) - unittest.RequireComponentsReadyBefore(u.T(), 100*time.Millisecond, u.senderMW, u.receiverMW) + unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderMW, u.receiverMW) + unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) u.cancel = cancel } @@ -111,8 +118,9 @@ func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Ov // stopMiddlewares will stop all middlewares. func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { u.cancel() - unittest.RequireCloseBefore(u.T(), u.senderMW.Done(), u.channelCloseDuration, "could not stop middleware on time") - unittest.RequireCloseBefore(u.T(), u.receiverMW.Done(), u.channelCloseDuration, "could not stop middleware on time") + + testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) + unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) } // TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. @@ -133,43 +141,39 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() Protocol: message.ProtocolTypeUnicast, Err: validator.ErrIdentityUnverified, } - slashingViolationsConsumer.On( - "OnUnAuthorizedSenderError", - expectedViolation, - ).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation).Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - - //NOTE: return (nil, false) simulating unstaked node - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(nil, false) - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", mockery.Anything) - - u.startMiddlewares(overlay) - - require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + //overlay := mocknetwork.NewOverlay(u.T()) + //overlay.On("Identities").Maybe().Return(func() flow.IdentityList { + // return u.providers[0].Identities(filter.Any) + //}) + //overlay.On("Topology").Maybe().Return(func() flow.IdentityList { + // return u.providers[0].Identities(filter.Any) + //}, nil) + // + ////NOTE: return (nil, false) simulating unstaked node + //overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(nil, false) + //// message will be rejected so assert overlay never receives it + //defer overlay.AssertNotCalled(u.T(), "Receive", mockery.Anything) + + u.startMiddlewares(nil) + u.providers[1].SetIdentities(nil) + + //require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) + //require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: string("hello"), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From 182d4912289a461b26f653ebae30705a36c084e5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 17:00:38 -0400 Subject: [PATCH 683/815] fixes TestNetworkPassesReportedMisbehavior --- network/alsp/manager/manager_test.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index b3b2a2f69ce..9abcd1a6f4b 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -307,11 +307,28 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t // create 1 victim node, 1 honest node and a node for each slashing violation ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). idProvider := id.NewFixedIdentityProvider(ids) + + // we want to override the middleware config to use the slashing violations consumer. However, we need the network + // instance to do that, but for the network instance we need the middleware config. So, we create the adapter first, which + // is a placeholder for the network instance, and then we create the network instance with the middleware config. + // Network adapter is a sub-interface of the network instance, so we can use it as a placeholder for the network instance. + // This is a bit of a chicken and egg problem; you see! that is why we must get rid of the middleware soon! + var adapter network.Adapter + middlewareConfig := testutils.MiddlewareConfigFixture(t, sporkId) + + // also a placeholder for the slashing violations consumer. + var violationsConsumer network.ViolationsConsumer + middlewareConfig.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { + violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) + return violationsConsumer + } + mws, _ := testutils.MiddlewareFixtures( t, ids, nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) + middlewareConfig, + ) networkCfg := testutils.NetworkConfigFixture( t, *ids[0], @@ -321,10 +338,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t p2p.WithAlspConfig(managerCfgFixture(t))) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) - - // create slashing violations consumer with victim node network providing the network.MisbehaviorReportConsumer interface - violationsConsumer := slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), victimNetwork) - mws[0].SetSlashingViolationsConsumer(violationsConsumer) + adapter = victimNetwork ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) From 3edcba0953e1f84bf91e5a408be74bb92b2f49e0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 17:02:01 -0400 Subject: [PATCH 684/815] removes set slashing violation consumer off the middleware --- network/middleware.go | 4 +--- network/p2p/middleware/middleware.go | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/network/middleware.go b/network/middleware.go index c60abfb3355..c19e4c54813 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -4,6 +4,7 @@ package network import ( "context" + "github.com/ipfs/go-datastore" libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -24,9 +25,6 @@ type Middleware interface { // SetOverlay sets the overlay used by the middleware. This must be called before the middleware can be Started. SetOverlay(Overlay) - // SetSlashingViolationsConsumer sets the slashing violations consumer. - SetSlashingViolationsConsumer(ViolationsConsumer) - // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous // direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized // as the router. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index bf9bfb2afa6..76db619dbb6 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -295,11 +295,6 @@ func (m *Middleware) SetOverlay(ov network.Overlay) { m.ov = ov } -// SetSlashingViolationsConsumer sets the slashing violations consumer. -func (m *Middleware) SetSlashingViolationsConsumer(consumer network.ViolationsConsumer) { - m.slashingViolationsConsumer = consumer -} - // authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as // well as who this node can connect to). // and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components From f1a65b805934f74ed13f61a8485859888c001e60 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 17:22:26 -0400 Subject: [PATCH 685/815] fixes TestUnicastAuthorization_EjectedPeer --- network/test/unicast_authorization_test.go | 62 +++++++--------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 1534375ee96..cc4e7557e8e 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -145,24 +145,12 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() close(u.waitCh) }) - //overlay := mocknetwork.NewOverlay(u.T()) - //overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - // return u.providers[0].Identities(filter.Any) - //}) - //overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - // return u.providers[0].Identities(filter.Any) - //}, nil) - // - ////NOTE: return (nil, false) simulating unstaked node - //overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(nil, false) - //// message will be rejected so assert overlay never receives it - //defer overlay.AssertNotCalled(u.T(), "Receive", mockery.Anything) - u.startMiddlewares(nil) - u.providers[1].SetIdentities(nil) - //require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - //require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + // overriding the identity provide of the receiver node to return an empty identity list so that the + // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message + // from the sender node + u.providers[1].SetIdentities(nil) _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -188,6 +176,11 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { //NOTE: setup ejected identity u.senderID.Ejected = true + // overriding the identity provide of the receiver node to return the ejected identity so that the + // sender node looks ejected to its networking layer and hence it sends a SenderEjectedError upon receiving a message + // from the sender node + u.providers[1].SetIdentities(flow.IdentityList{u.senderID}) + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -200,42 +193,23 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { Protocol: message.ProtocolTypeUnicast, Err: validator.ErrSenderEjected, } - slashingViolationsConsumer.On( - "OnSenderEjectedError", - expectedViolation, - ).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnSenderEjectedError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - //NOTE: return ejected identity causing validation to fail - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(u.senderID, true) - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", mockery.Anything) - - u.startMiddlewares(overlay) + u.startMiddlewares(nil) - require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: string("hello"), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From c0b9632d99ba8a65dd73c3d36b13f1cedcbc74fd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 17:28:54 -0400 Subject: [PATCH 686/815] fixes TestUnicastAuthorization_EjectedPeer --- network/test/unicast_authorization_test.go | 40 ++++++---------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index cc4e7557e8e..4d13378de04 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -235,42 +235,24 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee Err: message.ErrUnauthorizedMessageOnChannel, } - slashingViolationsConsumer.On( - "OnUnAuthorizedSenderError", - expectedViolation, - ).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(u.senderID, true) - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", mockery.Anything) - - u.startMiddlewares(overlay) + u.startMiddlewares(nil) - channel := channels.ConsensusCommittee - require.NoError(u.T(), u.receiverMW.Subscribe(channel)) - require.NoError(u.T(), u.senderMW.Subscribe(channel)) + _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.ConsensusCommittee, u.sporkId), - &libp2pmessage.TestMessage{ - Text: string("hello"), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) - // send message via unicast - err = u.senderMW.SendDirect(msg) + // send message via unicast; a test message must only be unicasted on the TestNetworkChannel, not on the ConsensusCommittee channel + // so we expect an unauthorized sender error + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From e5f33b227ecdcfb2efe6d8c122342b9d653f37d0 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:32:28 -0700 Subject: [PATCH 687/815] fix and cleanup en rpc engine tests --- engine/execution/rpc/engine.go | 2 +- engine/execution/rpc/engine_test.go | 199 ++++++++++++---------------- 2 files changed, 83 insertions(+), 118 deletions(-) diff --git a/engine/execution/rpc/engine.go b/engine/execution/rpc/engine.go index a69caaaa6d8..5a6a5b98892 100644 --- a/engine/execution/rpc/engine.go +++ b/engine/execution/rpc/engine.go @@ -247,7 +247,7 @@ func (h *handler) GetEventsForBlockIDs( } if len(blockIDs) > h.maxBlockRange { - return nil, status.Errorf(codes.InvalidArgument, "too many block IDs requested") + return nil, status.Errorf(codes.InvalidArgument, "too many block IDs requested: %d > %d", len(blockIDs), h.maxBlockRange) } results := make([]*execution.GetEventsForBlockIDsResponse_Result, len(blockIDs)) diff --git a/engine/execution/rpc/engine_test.go b/engine/execution/rpc/engine_test.go index ed85f6043e3..3ebce486b81 100644 --- a/engine/execution/rpc/engine_test.go +++ b/engine/execution/rpc/engine_test.go @@ -41,20 +41,21 @@ func TestHandler(t *testing.T) { func (suite *Suite) SetupTest() { suite.log = zerolog.Logger{} - suite.events = new(storage.Events) - suite.exeResults = new(storage.ExecutionResults) - suite.txResults = new(storage.TransactionResults) - suite.commits = new(storage.Commits) - suite.headers = new(storage.Headers) + suite.events = storage.NewEvents(suite.T()) + suite.exeResults = storage.NewExecutionResults(suite.T()) + suite.txResults = storage.NewTransactionResults(suite.T()) + suite.commits = storage.NewCommits(suite.T()) + suite.headers = storage.NewHeaders(suite.T()) } // TestExecuteScriptAtBlockID tests the ExecuteScriptAtBlockID API call func (suite *Suite) TestExecuteScriptAtBlockID() { // setup handler - mockEngine := new(mockEng.ScriptExecutor) + mockEngine := mockEng.NewScriptExecutor(suite.T()) handler := &handler{ - engine: mockEngine, - chain: flow.Mainnet, + engine: mockEngine, + chain: flow.Mainnet, + commits: suite.commits, } // setup dummy request/response @@ -72,6 +73,7 @@ func (suite *Suite) TestExecuteScriptAtBlockID() { } suite.Run("happy path with successful script execution", func() { + suite.commits.On("ByBlockID", mockIdentifier).Return(nil, nil).Once() mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). Return(scriptExecValue, nil).Once() response, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) @@ -80,7 +82,15 @@ func (suite *Suite) TestExecuteScriptAtBlockID() { mockEngine.AssertExpectations(suite.T()) }) + suite.Run("valid request for unknown block", func() { + suite.commits.On("ByBlockID", mockIdentifier).Return(nil, realstorage.ErrNotFound).Once() + _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) + suite.Require().Error(err) + errors.Is(err, status.Error(codes.NotFound, "")) + }) + suite.Run("valid request with script execution failure", func() { + suite.commits.On("ByBlockID", mockIdentifier).Return(nil, nil).Once() mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). Return(nil, status.Error(codes.InvalidArgument, "")).Once() _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) @@ -125,7 +135,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { eventMessages[j] = convert.EventToMessage(e) } // expect one call to lookup result for each block ID - //suite.exeResults.On("ByBlockID", id).Return(nil, nil).Once() suite.commits.On("ByBlockID", id).Return(nil, nil).Once() // expect one call to lookup events for each block ID @@ -150,6 +159,7 @@ func (suite *Suite) TestGetEventsForBlockIDs() { transactionResults: suite.txResults, commits: suite.commits, chain: flow.Mainnet, + maxBlockRange: DefaultMaxBlockRange, } concoctReq := func(errType string, blockIDs [][]byte) *execution.GetEventsForBlockIDsRequest { @@ -173,9 +183,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { actualResult := resp.GetResults() suite.Require().ElementsMatch(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) }) // failure path - empty even type in the request results in an error @@ -189,9 +196,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) // failure path - empty block ids in request results in an error @@ -205,9 +209,6 @@ func (suite *Suite) TestGetEventsForBlockIDs() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) // failure path - non-existent block id in request results in an error @@ -226,9 +227,23 @@ func (suite *Suite) TestGetEventsForBlockIDs() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.NotFound, "")) + }) - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) + // request for too many blocks - receives a InvalidArgument error + suite.Run("request for too many blocks", func() { + + // update range so it's smaller than list of blockIDs + handler.maxBlockRange = totalBlocks / 2 + + // create a valid API request + req := concoctReq(string(flow.EventAccountCreated), blockIDs) + + // execute the GetEventsForBlockIDs call + _, err := handler.GetEventsForBlockIDs(context.Background(), req) + + // check that an error was received + suite.Require().Error(err) + errors.Is(err, status.Error(codes.InvalidArgument, "")) }) } @@ -242,12 +257,13 @@ func (suite *Suite) TestGetAccountAtBlockID() { Address: serviceAddress, } - mockEngine := new(mockEng.ScriptExecutor) + mockEngine := mockEng.NewScriptExecutor(suite.T()) // create the handler handler := &handler{ - engine: mockEngine, - chain: flow.Mainnet, + engine: mockEngine, + chain: flow.Mainnet, + commits: suite.commits, } createReq := func(id []byte, address []byte) *execution.GetAccountAtBlockIDRequest { @@ -260,6 +276,8 @@ func (suite *Suite) TestGetAccountAtBlockID() { suite.Run("happy path with valid request", func() { // setup mock expectations + suite.commits.On("ByBlockID", id).Return(nil, nil).Once() + mockEngine.On("GetAccount", mock.Anything, serviceAddress, id).Return(&serviceAccount, nil).Once() req := createReq(id[:], serviceAddress.Bytes()) @@ -272,7 +290,19 @@ func (suite *Suite) TestGetAccountAtBlockID() { suite.Require().NoError(err) suite.Require().Empty( cmp.Diff(expectedAccount, actualAccount, protocmp.Transform())) - mockEngine.AssertExpectations(suite.T()) + }) + + suite.Run("invalid request with unknown block id", func() { + + // setup mock expectations + suite.commits.On("ByBlockID", id).Return(nil, realstorage.ErrNotFound).Once() + + req := createReq(id[:], serviceAddress.Bytes()) + + _, err := handler.GetAccountAtBlockID(context.Background(), req) + + suite.Require().Error(err) + errors.Is(err, status.Error(codes.NotFound, "")) }) suite.Run("invalid request with nil block id", func() { @@ -282,6 +312,7 @@ func (suite *Suite) TestGetAccountAtBlockID() { _, err := handler.GetAccountAtBlockID(context.Background(), req) suite.Require().Error(err) + errors.Is(err, status.Error(codes.InvalidArgument, "")) }) suite.Run("invalid request with nil root address", func() { @@ -291,6 +322,7 @@ func (suite *Suite) TestGetAccountAtBlockID() { _, err := handler.GetAccountAtBlockID(context.Background(), req) suite.Require().Error(err) + errors.Is(err, status.Error(codes.InvalidArgument, "")) }) } @@ -301,7 +333,7 @@ func (suite *Suite) TestGetRegisterAtBlockID() { serviceAddress := flow.Mainnet.Chain().ServiceAddress() validKey := []byte("exists") - mockEngine := new(mockEng.ScriptExecutor) + mockEngine := mockEng.NewScriptExecutor(suite.T()) // create the handler handler := &handler{ @@ -329,7 +361,6 @@ func (suite *Suite) TestGetRegisterAtBlockID() { value := resp.GetValue() suite.Require().NoError(err) suite.Require().True(len(value) > 0) - mockEngine.AssertExpectations(suite.T()) }) suite.Run("invalid request with bad address", func() { @@ -365,15 +396,13 @@ func (suite *Suite) TestGetTransactionResult() { // expect a call to lookup events by block ID and transaction ID suite.events.On("ByBlockIDTransactionID", bID, txID).Return(eventsForTx, nil) - // expect a call to lookup each block - suite.headers.On("ByID", block.ID()).Return(&block, true) - // create the handler createHandler := func(txResults *storage.TransactionResults) *handler { handler := &handler{ headers: suite.headers, events: suite.events, transactionResults: txResults, + commits: suite.commits, chain: flow.Mainnet, } return handler @@ -412,7 +441,7 @@ func (suite *Suite) TestGetTransactionResult() { } // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResult := flow.TransactionResult{ TransactionID: flow.Identifier{}, ErrorMessage: "", @@ -432,10 +461,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that all fields in response are as expected assertEqual(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResults.AssertExpectations(suite.T()) }) // happy path - valid requests receives all events for the given transaction by index @@ -449,7 +474,7 @@ func (suite *Suite) TestGetTransactionResult() { } // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResult := flow.TransactionResult{ TransactionID: flow.Identifier{}, ErrorMessage: "", @@ -472,10 +497,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that all fields in response are as expected assertEqual(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResults.AssertExpectations(suite.T()) }) // happy path - valid requests receives all events and an error for the given transaction @@ -489,7 +510,7 @@ func (suite *Suite) TestGetTransactionResult() { } // setup the storage to return a transaction error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResult := flow.TransactionResult{ TransactionID: txID, ErrorMessage: "runtime error", @@ -509,10 +530,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that all fields in response are as expected assertEqual(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResults.AssertExpectations(suite.T()) }) // happy path - valid requests receives all events and an error for the given transaction @@ -526,7 +543,7 @@ func (suite *Suite) TestGetTransactionResult() { } // setup the storage to return a transaction error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResult := flow.TransactionResult{ TransactionID: txID, ErrorMessage: "runtime error", @@ -551,8 +568,6 @@ func (suite *Suite) TestGetTransactionResult() { assertEqual(expectedResult, actualResult) // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResults.AssertExpectations(suite.T()) }) // failure path - nil transaction ID in the request results in an error @@ -561,11 +576,7 @@ func (suite *Suite) TestGetTransactionResult() { // create an API request with transaction ID as nil req := concoctReq(bID[:], nil) - // expect a call to lookup transaction result by block ID and transaction ID, return an error - txResults := new(storage.TransactionResults) - - txResults.On("ByBlockIDTransactionID", bID, nil).Return(nil, status.Error(codes.InvalidArgument, "")).Once() - + txResults := storage.NewTransactionResults(suite.T()) handler := createHandler(txResults) _, err := handler.GetTransactionResult(context.Background(), req) @@ -573,9 +584,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) // failure path - nil block id in the request results in an error @@ -584,10 +592,7 @@ func (suite *Suite) TestGetTransactionResult() { // create an API request with a nil block id req := concoctReq(nil, txID[:]) - txResults := new(storage.TransactionResults) - - txResults.On("ByBlockIDTransactionID", nil, txID).Return(nil, status.Error(codes.InvalidArgument, "")).Once() - + txResults := storage.NewTransactionResults(suite.T()) handler := createHandler(txResults) _, err := handler.GetTransactionResult(context.Background(), req) @@ -595,9 +600,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) // failure path - nil block id in the index request results in an error @@ -606,10 +608,7 @@ func (suite *Suite) TestGetTransactionResult() { // create an API request with a nil block id req := concoctIndexReq(nil, txIndex) - txResults := new(storage.TransactionResults) - - txResults.On("ByBlockIDTransactionIndex", nil, txID).Return(nil, status.Error(codes.InvalidArgument, "")).Once() - + txResults := storage.NewTransactionResults(suite.T()) handler := createHandler(txResults) _, err := handler.GetTransactionResultByIndex(context.Background(), req) @@ -617,9 +616,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) // failure path - non-existent transaction ID in request results in an error @@ -631,7 +627,7 @@ func (suite *Suite) TestGetTransactionResult() { req := concoctReq(bID[:], wrongTxID[:]) // expect a storage call for the invalid tx ID but return an error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, status.Error(codes.Internal, "")).Once() handler := createHandler(txResults) @@ -641,9 +637,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.Internal, "")) - - // check that one storage call was made - suite.events.AssertExpectations(suite.T()) }) // failure path - non-existent transaction index in request results in an error @@ -655,7 +648,7 @@ func (suite *Suite) TestGetTransactionResult() { req := concoctIndexReq(bID[:], wrongTxIndex) // expect a storage call for the invalid tx ID but return an error - txResults := new(storage.TransactionResults) + txResults := storage.NewTransactionResults(suite.T()) txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, status.Error(codes.Internal, "")).Once() handler := createHandler(txResults) @@ -665,9 +658,6 @@ func (suite *Suite) TestGetTransactionResult() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.Internal, "")) - - // check that one storage call was made - suite.events.AssertExpectations(suite.T()) }) } @@ -710,6 +700,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { headers: suite.headers, events: suite.events, transactionResults: txResults, + commits: suite.commits, chain: flow.Mainnet, } return handler @@ -736,6 +727,8 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { // happy path - valid requests receives all events for the given transaction suite.Run("happy path with valid events and no transaction error", func() { + suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() + // expect a call to lookup events by block ID and transaction ID suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() @@ -756,7 +749,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { } // expect a call to lookup transaction result by block ID return a result with no error - txResultsMock := new(storage.TransactionResults) + txResultsMock := storage.NewTransactionResults(suite.T()) txResults := []flow.TransactionResult{ { TransactionID: tx1ID, @@ -782,15 +775,13 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { // check that all fields in response are as expected assertEqual(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResultsMock.AssertExpectations(suite.T()) }) // happy path - valid requests receives all events and an error for the given transaction suite.Run("happy path with valid events and a transaction error", func() { + suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() + // expect a call to lookup events by block ID and transaction ID suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() @@ -811,7 +802,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { } // expect a call to lookup transaction result by block ID return a result with no error - txResultsMock := new(storage.TransactionResults) + txResultsMock := storage.NewTransactionResults(suite.T()) txResults := []flow.TransactionResult{ { TransactionID: tx1ID, @@ -837,10 +828,6 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { // check that all fields in response are as expected assertEqual(expectedResult, actualResult) - - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResultsMock.AssertExpectations(suite.T()) }) // failure path - nil block id in the request results in an error @@ -849,10 +836,7 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { // create an API request with a nil block id req := concoctReq(nil) - txResults := new(storage.TransactionResults) - - txResults.On("ByBlockID", nil).Return(nil, status.Error(codes.InvalidArgument, "")).Once() - + txResults := storage.NewTransactionResults(suite.T()) handler := createHandler(txResults) _, err := handler.GetTransactionResultsByBlockID(context.Background(), req) @@ -860,43 +844,24 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { // check that an error was received suite.Require().Error(err) errors.Is(err, status.Error(codes.InvalidArgument, "")) - - // check that no storage calls was made - suite.events.AssertExpectations(suite.T()) }) - // failure path - nonexisting block id in the request results in valid, but empty + // failure path - nonexisting block id in the request results in not found error suite.Run("request with nonexisting block ID", func() { - // expect a call to lookup events by block ID and transaction ID - suite.events.On("ByBlockID", nonexistingBlockID).Return(eventsForBlock, nil).Once() - - // create the expected result - expectedResult := &execution.GetTransactionResultsResponse{ - TransactionResults: []*execution.GetTransactionResultResponse{}, - } - - // expect a call to lookup transaction result by block ID return a result with no error - txResultsMock := new(storage.TransactionResults) - var txResults []flow.TransactionResult - txResultsMock.On("ByBlockID", nonexistingBlockID).Return(txResults, nil).Once() + suite.commits.On("ByBlockID", nonexistingBlockID).Return(nil, realstorage.ErrNotFound).Once() + txResultsMock := storage.NewTransactionResults(suite.T()) handler := createHandler(txResultsMock) // create a valid API request req := concoctReq(nonexistingBlockID[:]) // execute the GetTransactionResult call - actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) - - // check that a successful response is received - suite.Require().NoError(err) - - // check that all fields in response are as expected - assertEqual(expectedResult, actualResult) + _, err := handler.GetTransactionResultsByBlockID(context.Background(), req) - // check that appropriate storage calls were made - suite.events.AssertExpectations(suite.T()) - txResultsMock.AssertExpectations(suite.T()) + // check that an error was received + suite.Require().Error(err) + errors.Is(err, status.Error(codes.NotFound, "")) }) } From 0dd523f8b51cf9ef8bbc0b910c899481c03f194a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 18:18:57 -0400 Subject: [PATCH 688/815] fixes TestUnicastAuthorization_UnknownMsgCode --- network/internal/testutils/testUtil.go | 8 +- network/p2p/network.go | 8 ++ network/test/unicast_authorization_test.go | 102 +++++++++++++-------- 3 files changed, 80 insertions(+), 38 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index f93f4cb2767..bff65336b7f 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -206,7 +206,8 @@ func MiddlewareFixtures( func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, - mws []network.Middleware) ([]network.Network, []*unittest.UpdatableIDProvider) { + mws []network.Middleware, + configOpts ...func(*p2p.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { count := len(ids) nets := make([]network.Network, 0) @@ -215,6 +216,11 @@ func NetworksFixture(t *testing.T, for i := 0; i < count; i++ { idProvider := unittest.NewUpdatableIDProvider(ids) params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, mws[i]) + + for _, opt := range configOpts { + opt(params) + } + net, err := p2p.NewNetwork(params) require.NoError(t, err) diff --git a/network/p2p/network.go b/network/p2p/network.go index 02bb147d98b..c53b993122f 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -148,6 +148,14 @@ func WithAlspConfig(cfg *alspmgr.MisbehaviorReportManagerConfig) NetworkConfigOp } } +// WithCodec overrides the default codec (i.e., encoder and decoder). It is mostly used for testing purposes. +// Note: do not override the default codec in production unless you know what you are doing. +func WithCodec(codec network.Codec) NetworkConfigOption { + return func(params *NetworkConfig) { + params.Codec = codec + } +} + // NetworkOption is a function that can be used to override network attributes. // It is mostly used for testing purposes. // Note: do not override network attributes in production unless you know what you are doing. diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 4d13378de04..ad6c9b3f9c5 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -2,6 +2,8 @@ package test import ( "context" + "io" + "reflect" "testing" "time" @@ -36,6 +38,8 @@ type UnicastAuthorizationTestSuite struct { channelCloseDuration time.Duration logger zerolog.Logger + codec *overridableMessageEncoder + libP2PNodes []p2p.LibP2PNode // senderMW is the mw that will be sending the message senderMW network.Middleware @@ -82,10 +86,11 @@ func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingVio ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { - return slashingViolationsConsumer // override slashing violations consumer with the one passed as argument + return slashingViolationsConsumer } + u.codec = newUnknownMessageEncoder(u.T(), unittest.NetworkCodec()) mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) - nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws) + nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2p.WithCodec(u.codec)) require.Len(u.T(), ids, 2) require.Len(u.T(), providers, 2) require.Len(u.T(), mws, 2) @@ -270,6 +275,18 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( invalidMessageCode := codec.MessageCode(byte('X')) + // register a custom encoder that encodes the message with an invalid message code when encoding a string. + u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { + e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ + Text: v.(string), + }) + require.NoError(u.T(), err) + // manipulate message code byte + invalidMessageCode := codec.MessageCode(byte('X')) + e[0] = invalidMessageCode.Uint8() + return e, nil + }) + var nilID *flow.Identity expectedViolation := &network.Violation{ Identity: nilID, @@ -280,49 +297,21 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( Err: codec.NewUnknownMsgCodeErr(invalidMessageCode), } - slashingViolationsConsumer.On( - "OnUnknownMsgTypeError", - expectedViolation, - ).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnknownMsgTypeError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(u.senderID, true) - - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", u.senderID.NodeID, mock.AnythingOfType("*message.Message")) - - u.startMiddlewares(overlay) + u.startMiddlewares(nil) - require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: "hello", - }, - // we use a custom encoder that encodes the message with an invalid message code. - func(msg interface{}) ([]byte, error) { - e, err := unittest.NetworkCodec().Encode(msg) - require.NoError(u.T(), err) - // manipulate message code byte - e[0] = invalidMessageCode.Uint8() - return e, nil - }, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast("hello!", u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens @@ -634,3 +623,42 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") } + +type overridableMessageEncoder struct { + t *testing.T + codec network.Codec + specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) +} + +func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { + u.specificEncoder[t] = encoder +} + +func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { + return u.codec.NewEncoder(w) +} + +func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { + return u.codec.NewDecoder(r) +} + +func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { + if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { + return encoder(v) + } + return u.codec.Encode(v) +} + +func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { + return u.codec.Decode(data) +} + +var _ network.Codec = (*overridableMessageEncoder)(nil) + +func newUnknownMessageEncoder(t *testing.T, codec network.Codec) *overridableMessageEncoder { + return &overridableMessageEncoder{ + codec: codec, + t: t, + specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), + } +} From 6dae80a21af2d059e0c5f2e6be1818f53b80476d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Mon, 28 Aug 2023 18:25:29 -0400 Subject: [PATCH 689/815] fixes TestUnicastAuthorization_WrongMsgCode --- network/test/unicast_authorization_test.go | 52 +++++++--------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index ad6c9b3f9c5..f4762428c95 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -274,7 +274,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( require.NoError(u.T(), err) invalidMessageCode := codec.MessageCode(byte('X')) - // register a custom encoder that encodes the message with an invalid message code when encoding a string. u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ @@ -328,6 +327,13 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() require.NoError(u.T(), err) modifiedMessageCode := codec.CodeDKGMessage + // register a custom encoder that overrides the message code when encoding a TestMessage. + u.codec.RegisterEncoder(reflect.TypeOf(&libp2pmessage.TestMessage{}), func(v interface{}) ([]byte, error) { + e, err := unittest.NetworkCodec().Encode(v) + require.NoError(u.T(), err) + e[0] = modifiedMessageCode.Uint8() + return e, nil + }) expectedViolation := &network.Violation{ Identity: u.senderID, @@ -339,49 +345,23 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() Err: message.ErrUnauthorizedMessageOnChannel, } - slashingViolationsConsumer.On( - "OnUnAuthorizedSenderError", - expectedViolation, - ).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", expectedSenderPeerID).Return(u.senderID, true) - - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", u.senderID.NodeID, mock.AnythingOfType("*message.Message")) - - u.startMiddlewares(overlay) + u.startMiddlewares(nil) - require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: "hello", - }, - // we use a custom encoder that encodes the message with an invalid message code. - func(msg interface{}) ([]byte, error) { - e, err := unittest.NetworkCodec().Encode(msg) - require.NoError(u.T(), err) - // manipulate message code byte - e[0] = modifiedMessageCode.Uint8() - return e, nil - }, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From 37cfa4d77852b9298da6ce7f765ec06840cfa454 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 29 Aug 2023 10:07:05 -0400 Subject: [PATCH 690/815] Update echoengine_test.go --- network/test/echoengine_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 435a31f3c4b..6637ccf9f66 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -126,7 +126,6 @@ func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { // It also evaluates the correct reception of an echo message back for each send. // Sender and receiver are not synchronized func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "test is flaky") // set to true for an echo expectation suite.multiMessageAsync(true, 10, suite.Multicast) } @@ -181,7 +180,6 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Publish methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "test is flaky") suite.duplicateMessageDifferentChan(suite.Publish) } From 7c1d963e04ba648d8cc40fa4454f9d668373f41c Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 29 Aug 2023 10:27:22 -0400 Subject: [PATCH 691/815] Update validation_inspector_test.go --- .../validation_inspector_test.go | 353 +++++++++--------- 1 file changed, 176 insertions(+), 177 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index bb88c1e1562..15fdb1e7dbe 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" @@ -1112,185 +1111,185 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { // TestValidationInspector_InspectIWants_CacheMissThreshold ensures that expected invalid control message notification is disseminated when the number of iWant message Ids // without a corresponding iHave message sent with the same message ID exceeds the configured cache miss threshold. -func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { - t.Parallel() - role := flow.RoleConsensus - sporkID := unittest.IdentifierFixture() - // create our RPC validation inspector - flowConfig, err := config.DefaultConfig() - require.NoError(t, err) - inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs - inspectorConfig.NumberOfWorkers = 1 - inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = .5 // set cache miss threshold to 50% - messageCount := 1 - controlMessageCount := int64(1) - cacheMissThresholdNotifCount := atomic.NewUint64(0) - done := make(chan struct{}) - // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { - return func(args mockery.Arguments) { - notification, ok := args[0].(*p2p.InvCtrlMsgNotif) - require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) - require.Equal(t, uint64(messageCount), notification.Count) - require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) - cacheMissThresholdNotifCount.Inc() - if cacheMissThresholdNotifCount.Load() == 1 { - close(done) - } - } - } - - idProvider := mock.NewIdentityProvider(t) - spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) - - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - - distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) - mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) - meshTracer := meshTracerFixture(flowConfig, idProvider) - validationInspector, err := validation.NewControlMsgValidationInspector( - unittest.Logger(), - sporkID, - &inspectorConfig, - distributor, - metrics.NewNoopCollector(), - metrics.NewNoopCollector(), - idProvider, - metrics.NewNoopCollector(), - meshTracer) - require.NoError(t, err) - corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) - victimNode, victimIdentity := p2ptest.NodeFixture( - t, - sporkID, - t.Name(), - idProvider, - p2ptest.WithRole(role), - p2ptest.WithGossipSubTracer(meshTracer), - internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), - corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), - ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() - - messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) - - // create control message with iWant that contains 5 message IDs that were not tracked - ctlWithIWants := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) - ctlWithIWants[0].Iwant[0].MessageIDs = messageIDs // the first 5 message ids will not have a corresponding iHave - - // create control message with iHave that contains only the last 4 message IDs, this will force cache misses for the other 6 message IDs - ctlWithIhaves := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIHave(messageCount, messageCount, channels.PushBlocks.String())) - ctlWithIhaves[0].Ihave[0].MessageIDs = messageIDs[6:] - - validationInspector.Start(signalerCtx) - nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} - startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) - spammer.Start(t) - meshTracer.Start(signalerCtx) - defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) - - // simulate tracking some message IDs - meshTracer.SendRPC(&pubsub.RPC{ - RPC: pb.RPC{ - Control: &ctlWithIhaves[0], - }, - }, "") - - // spam the victim with iWant message that contains message IDs that do not have a corresponding iHave - spammer.SpamControlMessage(t, victimNode, ctlWithIWants) - - unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") -} +//func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { +// t.Parallel() +// role := flow.RoleConsensus +// sporkID := unittest.IdentifierFixture() +// // create our RPC validation inspector +// flowConfig, err := config.DefaultConfig() +// require.NoError(t, err) +// inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs +// inspectorConfig.NumberOfWorkers = 1 +// inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = .5 // set cache miss threshold to 50% +// messageCount := 1 +// controlMessageCount := int64(1) +// cacheMissThresholdNotifCount := atomic.NewUint64(0) +// done := make(chan struct{}) +// // ensure expected notifications are disseminated with expected error +// inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { +// return func(args mockery.Arguments) { +// notification, ok := args[0].(*p2p.InvCtrlMsgNotif) +// require.True(t, ok) +// require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) +// require.Equal(t, uint64(messageCount), notification.Count) +// require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) +// cacheMissThresholdNotifCount.Inc() +// if cacheMissThresholdNotifCount.Load() == 1 { +// close(done) +// } +// } +// } +// +// idProvider := mock.NewIdentityProvider(t) +// spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) +// +// ctx, cancel := context.WithCancel(context.Background()) +// signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) +// +// distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) +// mockDistributorReadyDoneAware(distributor) +// withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) +// meshTracer := meshTracerFixture(flowConfig, idProvider) +// validationInspector, err := validation.NewControlMsgValidationInspector( +// unittest.Logger(), +// sporkID, +// &inspectorConfig, +// distributor, +// metrics.NewNoopCollector(), +// metrics.NewNoopCollector(), +// idProvider, +// metrics.NewNoopCollector(), +// meshTracer) +// require.NoError(t, err) +// corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) +// victimNode, victimIdentity := p2ptest.NodeFixture( +// t, +// sporkID, +// t.Name(), +// idProvider, +// p2ptest.WithRole(role), +// p2ptest.WithGossipSubTracer(meshTracer), +// internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), +// corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), +// ) +// idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() +// idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() +// +// messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) +// +// // create control message with iWant that contains 5 message IDs that were not tracked +// ctlWithIWants := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) +// ctlWithIWants[0].Iwant[0].MessageIDs = messageIDs // the first 5 message ids will not have a corresponding iHave +// +// // create control message with iHave that contains only the last 4 message IDs, this will force cache misses for the other 6 message IDs +// ctlWithIhaves := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIHave(messageCount, messageCount, channels.PushBlocks.String())) +// ctlWithIhaves[0].Ihave[0].MessageIDs = messageIDs[6:] +// +// validationInspector.Start(signalerCtx) +// nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} +// startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) +// spammer.Start(t) +// meshTracer.Start(signalerCtx) +// defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) +// +// // simulate tracking some message IDs +// meshTracer.SendRPC(&pubsub.RPC{ +// RPC: pb.RPC{ +// Control: &ctlWithIhaves[0], +// }, +// }, "") +// +// // spam the victim with iWant message that contains message IDs that do not have a corresponding iHave +// spammer.SpamControlMessage(t, victimNode, ctlWithIWants) +// +// unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +//} // TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold ensures that expected invalid control message notification is disseminated when the number // of duplicate message Ids in a single iWant control message exceeds the configured duplicate message ID threshold. -func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) { - t.Parallel() - role := flow.RoleConsensus - sporkID := unittest.IdentifierFixture() - // create our RPC validation inspector - flowConfig, err := config.DefaultConfig() - require.NoError(t, err) - inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs - inspectorConfig.NumberOfWorkers = 1 - inspectorConfig.IWantRPCInspectionConfig.DuplicateMsgIDThreshold = .5 // set duplicate message id threshold to 50% - inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = 2 // avoid throwing cache miss threshold error - messageCount := 1 - controlMessageCount := int64(1) - done := make(chan struct{}) - // ensure expected notifications are disseminated with expected error - inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { - return func(args mockery.Arguments) { - defer close(done) - notification, ok := args[0].(*p2p.InvCtrlMsgNotif) - require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) - require.Equal(t, uint64(messageCount), notification.Count) - require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) - } - } - - idProvider := mock.NewIdentityProvider(t) - spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) - - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - - distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) - mockDistributorReadyDoneAware(distributor) - withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) - meshTracer := meshTracerFixture(flowConfig, idProvider) - validationInspector, err := validation.NewControlMsgValidationInspector( - unittest.Logger(), - sporkID, - &inspectorConfig, - distributor, - metrics.NewNoopCollector(), - metrics.NewNoopCollector(), - idProvider, - metrics.NewNoopCollector(), - meshTracer) - require.NoError(t, err) - corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) - victimNode, victimIdentity := p2ptest.NodeFixture( - t, - sporkID, - t.Name(), - idProvider, - p2ptest.WithRole(role), - p2ptest.WithGossipSubTracer(meshTracer), - internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), - corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), - ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() - - messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) - - // create control message with iWant that contains 5 message IDs that were not tracked - ctlMsg := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) - ctlMsg[0].Iwant[0].MessageIDs = messageIDs - // set first 6 message ids to duplicate id > 50% duplicate message ID threshold - for i := 0; i <= 6; i++ { - ctlMsg[0].Iwant[0].MessageIDs[i] = messageIDs[0] - } - - validationInspector.Start(signalerCtx) - nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} - startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) - spammer.Start(t) - meshTracer.Start(signalerCtx) - defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) - - // spam the victim with iWant message that contains 60% duplicate message ids more than the configured 50% allowed threshold - spammer.SpamControlMessage(t, victimNode, ctlMsg) - - unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") -} +//func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) { +// t.Parallel() +// role := flow.RoleConsensus +// sporkID := unittest.IdentifierFixture() +// // create our RPC validation inspector +// flowConfig, err := config.DefaultConfig() +// require.NoError(t, err) +// inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs +// inspectorConfig.NumberOfWorkers = 1 +// inspectorConfig.IWantRPCInspectionConfig.DuplicateMsgIDThreshold = .5 // set duplicate message id threshold to 50% +// inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = 2 // avoid throwing cache miss threshold error +// messageCount := 1 +// controlMessageCount := int64(1) +// done := make(chan struct{}) +// // ensure expected notifications are disseminated with expected error +// inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { +// return func(args mockery.Arguments) { +// defer close(done) +// notification, ok := args[0].(*p2p.InvCtrlMsgNotif) +// require.True(t, ok) +// require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) +// require.Equal(t, uint64(messageCount), notification.Count) +// require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) +// } +// } +// +// idProvider := mock.NewIdentityProvider(t) +// spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) +// +// ctx, cancel := context.WithCancel(context.Background()) +// signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) +// +// distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) +// mockDistributorReadyDoneAware(distributor) +// withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) +// meshTracer := meshTracerFixture(flowConfig, idProvider) +// validationInspector, err := validation.NewControlMsgValidationInspector( +// unittest.Logger(), +// sporkID, +// &inspectorConfig, +// distributor, +// metrics.NewNoopCollector(), +// metrics.NewNoopCollector(), +// idProvider, +// metrics.NewNoopCollector(), +// meshTracer) +// require.NoError(t, err) +// corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) +// victimNode, victimIdentity := p2ptest.NodeFixture( +// t, +// sporkID, +// t.Name(), +// idProvider, +// p2ptest.WithRole(role), +// p2ptest.WithGossipSubTracer(meshTracer), +// internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), +// corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), +// ) +// idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() +// idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() +// +// messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) +// +// // create control message with iWant that contains 5 message IDs that were not tracked +// ctlMsg := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) +// ctlMsg[0].Iwant[0].MessageIDs = messageIDs +// // set first 6 message ids to duplicate id > 50% duplicate message ID threshold +// for i := 0; i <= 6; i++ { +// ctlMsg[0].Iwant[0].MessageIDs[i] = messageIDs[0] +// } +// +// validationInspector.Start(signalerCtx) +// nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} +// startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) +// spammer.Start(t) +// meshTracer.Start(signalerCtx) +// defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) +// +// // spam the victim with iWant message that contains 60% duplicate message ids more than the configured 50% allowed threshold +// spammer.SpamControlMessage(t, victimNode, ctlMsg) +// +// unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +//} // TestGossipSubSpamMitigationIntegration tests that the spam mitigation feature of GossipSub is working as expected. // The test puts toghether the spam detection (through the GossipSubInspector) and the spam mitigation (through the From a4fcdb565d7413d9e958b91ae13804bf0473e463 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Tue, 29 Aug 2023 10:50:49 -0400 Subject: [PATCH 692/815] Update validation_inspector_test.go --- .../validation_inspector_test.go | 353 +++++++++--------- 1 file changed, 177 insertions(+), 176 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 15fdb1e7dbe..bb88c1e1562 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/rs/zerolog" @@ -1111,185 +1112,185 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { // TestValidationInspector_InspectIWants_CacheMissThreshold ensures that expected invalid control message notification is disseminated when the number of iWant message Ids // without a corresponding iHave message sent with the same message ID exceeds the configured cache miss threshold. -//func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { -// t.Parallel() -// role := flow.RoleConsensus -// sporkID := unittest.IdentifierFixture() -// // create our RPC validation inspector -// flowConfig, err := config.DefaultConfig() -// require.NoError(t, err) -// inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs -// inspectorConfig.NumberOfWorkers = 1 -// inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = .5 // set cache miss threshold to 50% -// messageCount := 1 -// controlMessageCount := int64(1) -// cacheMissThresholdNotifCount := atomic.NewUint64(0) -// done := make(chan struct{}) -// // ensure expected notifications are disseminated with expected error -// inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { -// return func(args mockery.Arguments) { -// notification, ok := args[0].(*p2p.InvCtrlMsgNotif) -// require.True(t, ok) -// require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) -// require.Equal(t, uint64(messageCount), notification.Count) -// require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) -// cacheMissThresholdNotifCount.Inc() -// if cacheMissThresholdNotifCount.Load() == 1 { -// close(done) -// } -// } -// } -// -// idProvider := mock.NewIdentityProvider(t) -// spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) -// -// ctx, cancel := context.WithCancel(context.Background()) -// signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) -// -// distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) -// mockDistributorReadyDoneAware(distributor) -// withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) -// meshTracer := meshTracerFixture(flowConfig, idProvider) -// validationInspector, err := validation.NewControlMsgValidationInspector( -// unittest.Logger(), -// sporkID, -// &inspectorConfig, -// distributor, -// metrics.NewNoopCollector(), -// metrics.NewNoopCollector(), -// idProvider, -// metrics.NewNoopCollector(), -// meshTracer) -// require.NoError(t, err) -// corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) -// victimNode, victimIdentity := p2ptest.NodeFixture( -// t, -// sporkID, -// t.Name(), -// idProvider, -// p2ptest.WithRole(role), -// p2ptest.WithGossipSubTracer(meshTracer), -// internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), -// corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), -// ) -// idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() -// idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() -// -// messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) -// -// // create control message with iWant that contains 5 message IDs that were not tracked -// ctlWithIWants := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) -// ctlWithIWants[0].Iwant[0].MessageIDs = messageIDs // the first 5 message ids will not have a corresponding iHave -// -// // create control message with iHave that contains only the last 4 message IDs, this will force cache misses for the other 6 message IDs -// ctlWithIhaves := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIHave(messageCount, messageCount, channels.PushBlocks.String())) -// ctlWithIhaves[0].Ihave[0].MessageIDs = messageIDs[6:] -// -// validationInspector.Start(signalerCtx) -// nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} -// startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) -// spammer.Start(t) -// meshTracer.Start(signalerCtx) -// defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) -// -// // simulate tracking some message IDs -// meshTracer.SendRPC(&pubsub.RPC{ -// RPC: pb.RPC{ -// Control: &ctlWithIhaves[0], -// }, -// }, "") -// -// // spam the victim with iWant message that contains message IDs that do not have a corresponding iHave -// spammer.SpamControlMessage(t, victimNode, ctlWithIWants) -// -// unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") -//} +func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { + t.Parallel() + role := flow.RoleConsensus + sporkID := unittest.IdentifierFixture() + // create our RPC validation inspector + flowConfig, err := config.DefaultConfig() + require.NoError(t, err) + inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs + inspectorConfig.NumberOfWorkers = 1 + inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = .5 // set cache miss threshold to 50% + messageCount := 1 + controlMessageCount := int64(1) + cacheMissThresholdNotifCount := atomic.NewUint64(0) + done := make(chan struct{}) + // ensure expected notifications are disseminated with expected error + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + return func(args mockery.Arguments) { + notification, ok := args[0].(*p2p.InvCtrlMsgNotif) + require.True(t, ok) + require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, uint64(messageCount), notification.Count) + require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) + cacheMissThresholdNotifCount.Inc() + if cacheMissThresholdNotifCount.Load() == 1 { + close(done) + } + } + } + + idProvider := mock.NewIdentityProvider(t) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) + mockDistributorReadyDoneAware(distributor) + withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) + validationInspector, err := validation.NewControlMsgValidationInspector( + unittest.Logger(), + sporkID, + &inspectorConfig, + distributor, + metrics.NewNoopCollector(), + metrics.NewNoopCollector(), + idProvider, + metrics.NewNoopCollector(), + meshTracer) + require.NoError(t, err) + corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) + victimNode, victimIdentity := p2ptest.NodeFixture( + t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), + ) + idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + + messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) + + // create control message with iWant that contains 5 message IDs that were not tracked + ctlWithIWants := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) + ctlWithIWants[0].Iwant[0].MessageIDs = messageIDs // the first 5 message ids will not have a corresponding iHave + + // create control message with iHave that contains only the last 4 message IDs, this will force cache misses for the other 6 message IDs + ctlWithIhaves := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIHave(messageCount, messageCount, channels.PushBlocks.String())) + ctlWithIhaves[0].Ihave[0].MessageIDs = messageIDs[6:] + + validationInspector.Start(signalerCtx) + nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} + startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) + spammer.Start(t) + meshTracer.Start(signalerCtx) + defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) + + // simulate tracking some message IDs + meshTracer.SendRPC(&pubsub.RPC{ + RPC: pb.RPC{ + Control: &ctlWithIhaves[0], + }, + }, "") + + // spam the victim with iWant message that contains message IDs that do not have a corresponding iHave + spammer.SpamControlMessage(t, victimNode, ctlWithIWants) + + unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +} // TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold ensures that expected invalid control message notification is disseminated when the number // of duplicate message Ids in a single iWant control message exceeds the configured duplicate message ID threshold. -//func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) { -// t.Parallel() -// role := flow.RoleConsensus -// sporkID := unittest.IdentifierFixture() -// // create our RPC validation inspector -// flowConfig, err := config.DefaultConfig() -// require.NoError(t, err) -// inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs -// inspectorConfig.NumberOfWorkers = 1 -// inspectorConfig.IWantRPCInspectionConfig.DuplicateMsgIDThreshold = .5 // set duplicate message id threshold to 50% -// inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = 2 // avoid throwing cache miss threshold error -// messageCount := 1 -// controlMessageCount := int64(1) -// done := make(chan struct{}) -// // ensure expected notifications are disseminated with expected error -// inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { -// return func(args mockery.Arguments) { -// defer close(done) -// notification, ok := args[0].(*p2p.InvCtrlMsgNotif) -// require.True(t, ok) -// require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) -// require.Equal(t, uint64(messageCount), notification.Count) -// require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) -// } -// } -// -// idProvider := mock.NewIdentityProvider(t) -// spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) -// -// ctx, cancel := context.WithCancel(context.Background()) -// signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) -// -// distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) -// mockDistributorReadyDoneAware(distributor) -// withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) -// meshTracer := meshTracerFixture(flowConfig, idProvider) -// validationInspector, err := validation.NewControlMsgValidationInspector( -// unittest.Logger(), -// sporkID, -// &inspectorConfig, -// distributor, -// metrics.NewNoopCollector(), -// metrics.NewNoopCollector(), -// idProvider, -// metrics.NewNoopCollector(), -// meshTracer) -// require.NoError(t, err) -// corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) -// victimNode, victimIdentity := p2ptest.NodeFixture( -// t, -// sporkID, -// t.Name(), -// idProvider, -// p2ptest.WithRole(role), -// p2ptest.WithGossipSubTracer(meshTracer), -// internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), -// corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), -// ) -// idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() -// idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() -// -// messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) -// -// // create control message with iWant that contains 5 message IDs that were not tracked -// ctlMsg := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) -// ctlMsg[0].Iwant[0].MessageIDs = messageIDs -// // set first 6 message ids to duplicate id > 50% duplicate message ID threshold -// for i := 0; i <= 6; i++ { -// ctlMsg[0].Iwant[0].MessageIDs[i] = messageIDs[0] -// } -// -// validationInspector.Start(signalerCtx) -// nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} -// startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) -// spammer.Start(t) -// meshTracer.Start(signalerCtx) -// defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) -// -// // spam the victim with iWant message that contains 60% duplicate message ids more than the configured 50% allowed threshold -// spammer.SpamControlMessage(t, victimNode, ctlMsg) -// -// unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") -//} +func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) { + t.Parallel() + role := flow.RoleConsensus + sporkID := unittest.IdentifierFixture() + // create our RPC validation inspector + flowConfig, err := config.DefaultConfig() + require.NoError(t, err) + inspectorConfig := flowConfig.NetworkConfig.GossipSubConfig.GossipSubRPCInspectorsConfig.GossipSubRPCValidationInspectorConfigs + inspectorConfig.NumberOfWorkers = 1 + inspectorConfig.IWantRPCInspectionConfig.DuplicateMsgIDThreshold = .5 // set duplicate message id threshold to 50% + inspectorConfig.IWantRPCInspectionConfig.CacheMissThreshold = 2 // avoid throwing cache miss threshold error + messageCount := 1 + controlMessageCount := int64(1) + done := make(chan struct{}) + // ensure expected notifications are disseminated with expected error + inspectDisseminatedNotifyFunc := func(spammer *corruptlibp2p.GossipSubRouterSpammer) func(args mockery.Arguments) { + return func(args mockery.Arguments) { + defer close(done) + notification, ok := args[0].(*p2p.InvCtrlMsgNotif) + require.True(t, ok) + require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, uint64(messageCount), notification.Count) + require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) + } + } + + idProvider := mock.NewIdentityProvider(t) + spammer := corruptlibp2p.NewGossipSubRouterSpammer(t, sporkID, role, idProvider) + + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) + + distributor := mockp2p.NewGossipSubInspectorNotificationDistributor(t) + mockDistributorReadyDoneAware(distributor) + withExpectedNotificationDissemination(1, inspectDisseminatedNotifyFunc)(distributor, spammer) + meshTracer := meshTracerFixture(flowConfig, idProvider) + validationInspector, err := validation.NewControlMsgValidationInspector( + unittest.Logger(), + sporkID, + &inspectorConfig, + distributor, + metrics.NewNoopCollector(), + metrics.NewNoopCollector(), + idProvider, + metrics.NewNoopCollector(), + meshTracer) + require.NoError(t, err) + corruptInspectorFunc := corruptlibp2p.CorruptInspectorFunc(validationInspector) + victimNode, victimIdentity := p2ptest.NodeFixture( + t, + sporkID, + t.Name(), + idProvider, + p2ptest.WithRole(role), + p2ptest.WithGossipSubTracer(meshTracer), + internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), + corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), + ) + idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + + messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) + + // create control message with iWant that contains 5 message IDs that were not tracked + ctlMsg := spammer.GenerateCtlMessages(int(controlMessageCount), corruptlibp2p.WithIWant(messageCount, messageCount)) + ctlMsg[0].Iwant[0].MessageIDs = messageIDs + // set first 6 message ids to duplicate id > 50% duplicate message ID threshold + for i := 0; i <= 6; i++ { + ctlMsg[0].Iwant[0].MessageIDs[i] = messageIDs[0] + } + + validationInspector.Start(signalerCtx) + nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} + startNodesAndEnsureConnected(t, signalerCtx, nodes, sporkID) + spammer.Start(t) + meshTracer.Start(signalerCtx) + defer stopTestComponents(t, cancel, nodes, validationInspector, meshTracer) + + // spam the victim with iWant message that contains 60% duplicate message ids more than the configured 50% allowed threshold + spammer.SpamControlMessage(t, victimNode, ctlMsg) + + unittest.RequireCloseBefore(t, done, 2*time.Second, "failed to inspect RPC messages on time") +} // TestGossipSubSpamMitigationIntegration tests that the spam mitigation feature of GossipSub is working as expected. // The test puts toghether the spam detection (through the GossipSubInspector) and the spam mitigation (through the From c94c1d904faa2ab0fb937c2ef62d2e95b830aafa Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:20:32 -0400 Subject: [PATCH 693/815] fixes TestUnicastAuthorization_PublicChannel --- network/test/unicast_authorization_test.go | 54 +++++++--------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index f4762428c95..ba9de639b05 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -373,50 +373,28 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.startMiddlewares(nil) - expectedPayload := "hello" - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channels.TestNetworkChannel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: expectedPayload, - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(u.T(), err) - - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(u.senderID, true) + msg := &libp2pmessage.TestMessage{ + Text: string("hello"), + } - // we should receive the message on our overlay, at this point close the waitCh - overlay.On("Receive", mockery.Anything).Return(nil). - Once(). - Run(func(args mockery.Arguments) { + // mock a message processor that will receive the message. + receiverEngine := &mocknetwork.MessageProcessor{} + receiverEngine.On("Process", channels.TestNetworkChannel, u.senderID.NodeID, msg).Run( + func(args mockery.Arguments) { close(u.waitCh) + }).Return(nil).Once() + _, err := u.receiverNetwork.Register(channels.TestNetworkChannel, receiverEngine) + require.NoError(u.T(), err) - msg, ok := args[0].(network.IncomingMessageScope) - require.True(u.T(), ok) - - require.Equal(u.T(), channels.TestNetworkChannel, msg.Channel()) // channel - require.Equal(u.T(), u.senderID.NodeID, msg.OriginId()) // sender id - require.Equal(u.T(), u.receiverID.NodeID, msg.TargetIDs()[0]) // target id - require.Equal(u.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - require.Equal(u.T(), expectedPayload, msg.DecodedPayload().(*libp2pmessage.TestMessage).Text) // payload - }) - - u.startMiddlewares(overlay) - - require.NoError(u.T(), u.receiverMW.Subscribe(channels.TestNetworkChannel)) - require.NoError(u.T(), u.senderMW.Subscribe(channels.TestNetworkChannel)) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From 11aed1efb3660100efc721c1642bb9009edc0529 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:29:44 -0400 Subject: [PATCH 694/815] fixes TestUnicastAuthorization_ReceiverHasNoSubscription --- network/test/unicast_authorization_test.go | 71 +++++----------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index ba9de639b05..6b1f291ccd7 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -423,44 +423,23 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni Err: message.ErrUnauthorizedUnicastOnChannel, } - slashingViolationsConsumer.On( - "OnUnauthorizedUnicastOnChannel", - expectedViolation, - ).Return(nil).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", expectedSenderPeerID).Return(u.senderID, true) - - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", u.senderID.NodeID, mock.AnythingOfType("*message.Message")) + u.startMiddlewares(nil) - u.startMiddlewares(overlay) + _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) - channel := channels.ConsensusCommittee - require.NoError(u.T(), u.receiverMW.Subscribe(channel)) - require.NoError(u.T(), u.senderMW.Subscribe(channel)) + senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel payload := unittest.ProposalFixture() - - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channel, u.sporkId), - payload, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(u.T(), err) - // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(payload, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens @@ -486,40 +465,20 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu Err: middleware.ErrUnicastMsgWithoutSub, } - slashingViolationsConsumer.On( - "OnUnauthorizedUnicastOnChannel", - expectedViolation, - ).Return(nil).Return(nil).Once().Run(func(args mockery.Arguments) { + slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { close(u.waitCh) }) - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - - // message will be rejected so assert overlay never receives it - defer overlay.AssertNotCalled(u.T(), "Receive", u.senderID.NodeID, mock.AnythingOfType("*message.Message")) - - u.startMiddlewares(overlay) - - channel := channels.TestNetworkChannel + u.startMiddlewares(nil) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channel, u.sporkId), - &libp2pmessage.TestMessage{ - Text: "TestUnicastAuthorization_ReceiverHasNoSubscription", - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From 0c671c92505549ac5c5d6345de42371c3a31e684 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:31:37 -0400 Subject: [PATCH 695/815] fixes TestUnicastAuthorization_PublicChannel --- network/test/unicast_authorization_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 6b1f291ccd7..22fb12993a8 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -381,14 +381,14 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() // mock a message processor that will receive the message. receiverEngine := &mocknetwork.MessageProcessor{} - receiverEngine.On("Process", channels.TestNetworkChannel, u.senderID.NodeID, msg).Run( + receiverEngine.On("Process", channels.PublicPushBlocks, u.senderID.NodeID, msg).Run( func(args mockery.Arguments) { close(u.waitCh) }).Return(nil).Once() - _, err := u.receiverNetwork.Register(channels.TestNetworkChannel, receiverEngine) + _, err := u.receiverNetwork.Register(channels.PublicPushBlocks, receiverEngine) require.NoError(u.T(), err) - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + senderCon, err := u.senderNetwork.Register(channels.PublicPushBlocks, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) // send message via unicast From 40874faa0b37162908c2d53f65758058966eff8a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:44:53 -0400 Subject: [PATCH 696/815] fixes TestUnicastAuthorization_ReceiverHasSubscription --- network/test/unicast_authorization_test.go | 51 ++++++---------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 22fb12993a8..514c14d814a 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -8,13 +8,11 @@ import ( "time" "github.com/rs/zerolog" - "github.com/stretchr/testify/mock" mockery "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/model/messages" "github.com/onflow/flow-go/module/irrecoverable" @@ -491,50 +489,29 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupMiddlewaresAndProviders(slashingViolationsConsumer) - channel := channels.RequestReceiptsByBlockID + u.startMiddlewares(nil) - msg, err := message.NewOutgoingScope( - flow.IdentifierList{u.receiverID.NodeID}, - channels.TopicFromChannel(channel, u.sporkId), - &messages.EntityRequest{}, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(u.T(), err) + msg := &messages.EntityRequest{ + EntityIDs: unittest.IdentifierListFixture(10), + } + // both sender and receiver must have an authorized role to send and receive messages on the ConsensusCommittee channel. u.senderID.Role = flow.RoleConsensus u.receiverID.Role = flow.RoleExecution - overlay := mocknetwork.NewOverlay(u.T()) - overlay.On("Identities").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }) - overlay.On("Topology").Maybe().Return(func() flow.IdentityList { - return u.providers[0].Identities(filter.Any) - }, nil) - overlay.On("Identity", mock.AnythingOfType("peer.ID")).Return(u.senderID, true) - - // we should receive the message on our overlay, at this point close the waitCh - overlay.On("Receive", mockery.Anything).Return(nil). - Once(). - Run(func(args mockery.Arguments) { + receiverEngine := &mocknetwork.MessageProcessor{} + receiverEngine.On("Process", channels.RequestReceiptsByBlockID, u.senderID.NodeID, msg).Run( + func(args mockery.Arguments) { close(u.waitCh) + }).Return(nil).Once() + _, err := u.receiverNetwork.Register(channels.RequestReceiptsByBlockID, receiverEngine) + require.NoError(u.T(), err) - msg, ok := args[0].(network.IncomingMessageScope) - require.True(u.T(), ok) - - require.Equal(u.T(), channel, msg.Channel()) // channel - require.Equal(u.T(), u.senderID.NodeID, msg.OriginId()) // sender id - require.Equal(u.T(), u.receiverID.NodeID, msg.TargetIDs()[0]) // target id - require.Equal(u.T(), message.ProtocolTypeUnicast, msg.Protocol()) // protocol - }) - - u.startMiddlewares(overlay) - - require.NoError(u.T(), u.receiverMW.Subscribe(channel)) - require.NoError(u.T(), u.senderMW.Subscribe(channel)) + senderCon, err := u.senderNetwork.Register(channels.RequestReceiptsByBlockID, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) // send message via unicast - err = u.senderMW.SendDirect(msg) + err = senderCon.Unicast(msg, u.receiverID.NodeID) require.NoError(u.T(), err) // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens From 143aec5dae0a63c1e3be25a7304f10cb19a2a92f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:51:29 -0400 Subject: [PATCH 697/815] cleans up middleware from the unicast authorization tests --- network/test/unicast_authorization_test.go | 60 +++++++++++----------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 514c14d814a..7ed453e3e2d 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -39,16 +39,12 @@ type UnicastAuthorizationTestSuite struct { codec *overridableMessageEncoder libP2PNodes []p2p.LibP2PNode - // senderMW is the mw that will be sending the message - senderMW network.Middleware // senderNetwork is the networking layer instance that will be used to send the message. senderNetwork network.Network // senderID the identity on the mw sending the message senderID *flow.Identity // receiverNetwork is the networking layer instance that will be used to receive the message. receiverNetwork network.Network - // receiverMW is the mw that will be sending the message - receiverMW network.Middleware // receiverID the identity on the mw sending the message receiverID *flow.Identity // providers id providers generated at beginning of a test run @@ -78,15 +74,15 @@ func (u *UnicastAuthorizationTestSuite) TearDownTest() { u.stopMiddlewares() } -// setupMiddlewaresAndProviders will setup 2 middlewares that will be used as a sender and receiver in each suite test. -func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingViolationsConsumer network.ViolationsConsumer) { +// setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. +func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { u.sporkId = unittest.IdentifierFixture() ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { return slashingViolationsConsumer } - u.codec = newUnknownMessageEncoder(u.T(), unittest.NetworkCodec()) + u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2p.WithCodec(u.codec)) require.Len(u.T(), ids, 2) @@ -97,9 +93,7 @@ func (u *UnicastAuthorizationTestSuite) setupMiddlewaresAndProviders(slashingVio u.senderNetwork = nets[0] u.receiverNetwork = nets[1] u.senderID = ids[0] - u.senderMW = mws[0] u.receiverID = ids[1] - u.receiverMW = mws[1] u.providers = providers u.libP2PNodes = libP2PNodes } @@ -111,8 +105,6 @@ func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Ov testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) - - unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderMW, u.receiverMW) unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) u.cancel = cancel @@ -130,7 +122,7 @@ func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -175,7 +167,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) //NOTE: setup ejected identity u.senderID.Ejected = true @@ -223,7 +215,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -266,7 +258,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -319,7 +311,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -370,7 +362,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) u.startMiddlewares(nil) msg := &libp2pmessage.TestMessage{ @@ -403,7 +395,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) // set sender id role to RoleConsensus to avoid unauthorized sender validation error u.senderID.Role = flow.RoleConsensus @@ -449,7 +441,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) require.NoError(u.T(), err) @@ -488,7 +480,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupMiddlewaresAndProviders(slashingViolationsConsumer) + u.setupNetworks(slashingViolationsConsumer) u.startMiddlewares(nil) msg := &messages.EntityRequest{ @@ -518,24 +510,39 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubs unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") } +// overridableMessageEncoder is a codec that allows to override the encoder for a specific type only for sake of testing. +// We specifically use this to override the encoder for the TestMessage type to encode it with an invalid message code. type overridableMessageEncoder struct { - t *testing.T codec network.Codec specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) } +var _ network.Codec = (*overridableMessageEncoder)(nil) + +func newOverridableMessageEncoder(codec network.Codec) *overridableMessageEncoder { + return &overridableMessageEncoder{ + codec: codec, + specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), + } +} + +// RegisterEncoder registers an encoder for a specific type, overriding the default encoder for that type. func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { u.specificEncoder[t] = encoder } +// NewEncoder creates a new encoder. func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { return u.codec.NewEncoder(w) } +// NewDecoder creates a new decoder. func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { return u.codec.NewDecoder(r) } +// Encode encodes a value into a byte slice. If a specific encoder is registered for the type of the value, it will be used. +// Otherwise, the default encoder will be used. func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { return encoder(v) @@ -543,16 +550,7 @@ func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { return u.codec.Encode(v) } +// Decode decodes a byte slice into a value. It uses the default decoder. func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { return u.codec.Decode(data) } - -var _ network.Codec = (*overridableMessageEncoder)(nil) - -func newUnknownMessageEncoder(t *testing.T, codec network.Codec) *overridableMessageEncoder { - return &overridableMessageEncoder{ - codec: codec, - t: t, - specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), - } -} From d0cf8487d6df5b6fd3cd2daf49b172de709119a2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 13:53:54 -0400 Subject: [PATCH 698/815] removes send direct from middleware interface --- network/middleware.go | 9 --- network/p2p/middleware/middleware.go | 97 ---------------------------- 2 files changed, 106 deletions(-) diff --git a/network/middleware.go b/network/middleware.go index c19e4c54813..74833b02b18 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -25,15 +25,6 @@ type Middleware interface { // SetOverlay sets the overlay used by the middleware. This must be called before the middleware can be Started. SetOverlay(Overlay) - // SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous - // direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized - // as the router. - // - // Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is - // a more efficient candidate. - // All errors returned from this function can be considered benign. - SendDirect(msg OutgoingMessageScope) error - // OpenProtectedStream acts as a short-circuit method that delegates the opening of a protected stream to the underlying // libP2PNode. This method is intended to be temporary and is going to be removed in the long term. Users should plan // to interact with the libP2P node directly in the future. diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 76db619dbb6..9d2ca421e98 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -3,7 +3,6 @@ package middleware import ( - "bufio" "context" "errors" "fmt" @@ -361,102 +360,6 @@ func (m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, pr return m.libP2PNode.OpenProtectedStream(ctx, peerID, protectionTag, writingLogic) } -// SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous -// direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized -// as the router. -// -// Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is -// a more efficient candidate. -// -// The following benign errors can be returned: -// - the peer ID for the target node ID cannot be found. -// - the msg size was too large. -// - failed to send message to peer. -// -// All errors returned from this function can be considered benign. -func (m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { - // since it is a unicast, we only need to get the first peer ID. - peerID, err := m.idTranslator.GetPeerID(msg.TargetIds()[0]) - if err != nil { - return fmt.Errorf("could not find peer id for target id: %w", err) - } - - maxMsgSize := unicastMaxMsgSize(msg.PayloadType()) - if msg.Size() > maxMsgSize { - // message size goes beyond maximum size that the serializer can handle. - // proceeding with this message results in closing the connection by the target side, and - // delivery failure. - return fmt.Errorf("message size %d exceeds configured max message size %d", msg.Size(), maxMsgSize) - } - - maxTimeout := m.unicastMaxMsgDuration(msg.PayloadType()) - - // pass in a context with timeout to make the unicast call fail fast - ctx, cancel := context.WithTimeout(m.ctx, maxTimeout) - defer cancel() - - // protect the underlying connection from being inadvertently pruned by the peer manager while the stream and - // connection creation is being attempted, and remove it from protected list once stream created. - channel, ok := channels.ChannelFromTopic(msg.Topic()) - if !ok { - return fmt.Errorf("could not find channel for topic %s", msg.Topic()) - } - tag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) - m.libP2PNode.Host().ConnManager().Protect(peerID, tag) - defer m.libP2PNode.Host().ConnManager().Unprotect(peerID, tag) - - // create new stream - // streams don't need to be reused and are fairly inexpensive to be created for each send. - // A stream creation does NOT incur an RTT as stream negotiation happens as part of the first message - // sent out the receiver - stream, err := m.libP2PNode.CreateStream(ctx, peerID) - if err != nil { - return fmt.Errorf("failed to create stream for %s: %w", msg.TargetIds()[0], err) - } - - success := false - - defer func() { - if success { - // close the stream immediately - err = stream.Close() - if err != nil { - err = fmt.Errorf("failed to close the stream for %s: %w", msg.TargetIds()[0], err) - } - } else { - resetErr := stream.Reset() - if resetErr != nil { - m.log.Err(resetErr).Msg("failed to reset stream") - } - } - }() - - deadline, _ := ctx.Deadline() - err = stream.SetWriteDeadline(deadline) - if err != nil { - return fmt.Errorf("failed to set write deadline for stream: %w", err) - } - - // create a gogo protobuf writer - bufw := bufio.NewWriter(stream) - writer := ggio.NewDelimitedWriter(bufw) - - err = writer.WriteMsg(msg.Proto()) - if err != nil { - return fmt.Errorf("failed to send message to %s: %w", msg.TargetIds()[0], err) - } - - // flush the stream - err = bufw.Flush() - if err != nil { - return fmt.Errorf("failed to flush stream for %s: %w", msg.TargetIds()[0], err) - } - - success = true - - return nil -} - // handleIncomingStream handles an incoming stream from a remote peer // it is a callback that gets called for each incoming stream by libp2p with a new stream object func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { From 1a906fe46d82150b542289a5c122062d6a9f54a5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:02:04 -0400 Subject: [PATCH 699/815] fixes TestConnGater --- network/mocknetwork/middleware.go | 42 ++++++++++++++------------ network/p2p/mock/lib_p2_p_node.go | 14 +++++++++ network/p2p/mock/unicast_manager.go | 2 +- network/p2p/p2pnode/libp2pNode_test.go | 28 ++++++++++++----- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index 2b0f7409df4..d67d6bf102d 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -3,15 +3,22 @@ package mocknetwork import ( - datastore "github.com/ipfs/go-datastore" + context "context" + channels "github.com/onflow/flow-go/network/channels" + corenetwork "github.com/libp2p/go-libp2p/core/network" + + datastore "github.com/ipfs/go-datastore" + irrecoverable "github.com/onflow/flow-go/module/irrecoverable" mock "github.com/stretchr/testify/mock" network "github.com/onflow/flow-go/network" + peer "github.com/libp2p/go-libp2p/core/peer" + protocol "github.com/libp2p/go-libp2p/core/protocol" ) @@ -85,6 +92,20 @@ func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpd _m.Called(_a0) } +// OpenProtectedStream provides a mock function with given fields: ctx, peerID, protectionTag, writingLogic +func (_m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(corenetwork.Stream) error) error { + ret := _m.Called(ctx, peerID, protectionTag, writingLogic) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(corenetwork.Stream) error) error); ok { + r0 = rf(ctx, peerID, protectionTag, writingLogic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Publish provides a mock function with given fields: msg func (_m *Middleware) Publish(msg network.OutgoingMessageScope) error { ret := _m.Called(msg) @@ -115,30 +136,11 @@ func (_m *Middleware) Ready() <-chan struct{} { return r0 } -// SendDirect provides a mock function with given fields: msg -func (_m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { - ret := _m.Called(msg) - - var r0 error - if rf, ok := ret.Get(0).(func(network.OutgoingMessageScope) error); ok { - r0 = rf(msg) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // SetOverlay provides a mock function with given fields: _a0 func (_m *Middleware) SetOverlay(_a0 network.Overlay) { _m.Called(_a0) } -// SetSlashingViolationsConsumer provides a mock function with given fields: _a0 -func (_m *Middleware) SetSlashingViolationsConsumer(_a0 network.ViolationsConsumer) { - _m.Called(_a0) -} - // Start provides a mock function with given fields: _a0 func (_m *Middleware) Start(_a0 irrecoverable.SignalerContext) { _m.Called(_a0) diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 2fcdc336ad7..228e80e83ab 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -252,6 +252,20 @@ func (_m *LibP2PNode) OnDisallowListNotification(id peer.ID, cause flow_gonetwor _m.Called(id, cause) } +// OpenProtectedStream provides a mock function with given fields: ctx, peerID, protectionTag, writingLogic +func (_m *LibP2PNode) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(network.Stream) error) error { + ret := _m.Called(ctx, peerID, protectionTag, writingLogic) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(network.Stream) error) error); ok { + r0 = rf(ctx, peerID, protectionTag, writingLogic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // PeerManagerComponent provides a mock function with given fields: func (_m *LibP2PNode) PeerManagerComponent() component.Component { ret := _m.Called() diff --git a/network/p2p/mock/unicast_manager.go b/network/p2p/mock/unicast_manager.go index bd90927d6b0..68c98fb944a 100644 --- a/network/p2p/mock/unicast_manager.go +++ b/network/p2p/mock/unicast_manager.go @@ -69,7 +69,7 @@ func (_m *UnicastManager) Register(unicast protocols.ProtocolName) error { return r0 } -// WithDefaultHandler provides a mock function with given fields: defaultHandler +// SetDefaultHandler provides a mock function with given fields: defaultHandler func (_m *UnicastManager) SetDefaultHandler(defaultHandler network.StreamHandler) { _m.Called(defaultHandler) } diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 519a0579163..cf11430ec6b 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -210,19 +210,31 @@ func TestConnGater(t *testing.T) { node1.Host().Peerstore().AddAddrs(node2Info.ID, node2Info.Addrs, peerstore.PermanentAddrTTL) node2.Host().Peerstore().AddAddrs(node1Info.ID, node1Info.Addrs, peerstore.PermanentAddrTTL) - _, err = node1.CreateStream(ctx, node2Info.ID) - assert.Error(t, err, "connection should not be possible") + err = node1.OpenProtectedStream(ctx, node2Info.ID, t.Name(), func(stream network.Stream) error { + // no-op, as the connection should not be possible + return nil + }) + require.ErrorContains(t, err, "target node is not on the approved list of nodes") - _, err = node2.CreateStream(ctx, node1Info.ID) - assert.Error(t, err, "connection should not be possible") + err = node2.OpenProtectedStream(ctx, node1Info.ID, t.Name(), func(stream network.Stream) error { + // no-op, as the connection should not be possible + return nil + }) + require.ErrorContains(t, err, "target node is not on the approved list of nodes") node1Peers.Add(node2Info.ID, struct{}{}) - _, err = node1.CreateStream(ctx, node2Info.ID) - assert.Error(t, err, "connection should not be possible") + err = node1.OpenProtectedStream(ctx, node2Info.ID, t.Name(), func(stream network.Stream) error { + // no-op, as the connection should not be possible + return nil + }) + require.Error(t, err) node2Peers.Add(node1Info.ID, struct{}{}) - _, err = node1.CreateStream(ctx, node2Info.ID) - assert.NoError(t, err, "connection should not be blocked") + err = node1.OpenProtectedStream(ctx, node2Info.ID, t.Name(), func(stream network.Stream) error { + // no-op, as the connection should not be possible + return nil + }) + require.NoError(t, err) } // TestNode_HasSubscription checks that when a node subscribes to a topic HasSubscription should return true. From 75384e7f5f38d44c1593a321aaabe848115349bc Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:11:07 -0400 Subject: [PATCH 700/815] wip replacing create stream with open protected stream --- network/internal/p2pfixtures/fixtures.go | 16 +++++++---- network/p2p/p2pnode/libp2pNode_test.go | 36 ++++++++++++++---------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 7b7365d57ab..521918f70e9 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -276,14 +276,18 @@ func EnsureMessageExchangeOverUnicast(t *testing.T, ctx context.Context, nodes [ if this == other { continue } - s, err := this.CreateStream(ctx, other.Host().ID()) - require.NoError(t, err) - rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) - _, err = rw.WriteString(msg) + err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) + _, err := rw.WriteString(msg) + require.NoError(t, err) + + // Flush the stream + require.NoError(t, rw.Flush()) + + return nil + }) require.NoError(t, err) - // Flush the stream - require.NoError(t, rw.Flush()) } // wait for the message to be received by all other nodes diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index cf11430ec6b..acf7ab74aaa 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -289,22 +289,19 @@ func TestCreateStream_SinglePairwiseConnection(t *testing.T) { ctxWithTimeout, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() done := make(chan struct{}) - numOfStreamsPerNode := 100 // create large number of streams per node per connection to ensure the resource manager does not cause starvation of resources + numOfStreamsPerNode := 100 // create large number of streamChan per node per connection to ensure the resource manager does not cause starvation of resources expectedTotalNumOfStreams := 600 - // create a number of streams concurrently between each node - streams := make(chan network.Stream, expectedTotalNumOfStreams) + // create a number of streamChan concurrently between each node + streamChan := make(chan network.Stream, expectedTotalNumOfStreams) - go createConcurrentStreams(t, ctxWithTimeout, nodes, ids, numOfStreamsPerNode, streams, done) - unittest.RequireCloseBefore(t, done, 5*time.Second, "could not create streams on time") - require.Len(t, streams, expectedTotalNumOfStreams, fmt.Sprintf("expected %d total number of streams created got %d", expectedTotalNumOfStreams, len(streams))) + go createConcurrentStreams(t, ctxWithTimeout, nodes, ids, numOfStreamsPerNode, streamChan, done) + unittest.RequireCloseBefore(t, done, 5*time.Second, "could not create streamChan on time") + require.Len(t, streamChan, expectedTotalNumOfStreams, fmt.Sprintf("expected %d total number of streamChan created got %d", expectedTotalNumOfStreams, len(streamChan))) // ensure only a single connection exists between all nodes ensureSinglePairwiseConnection(t, nodes) - close(streams) - for s := range streams { - _ = s.Close() - } + close(streamChan) } // TestCreateStream_SinglePeerDial ensures that the unicast manager only attempts to dial a peer once, retries dialing a peer the expected max amount of times when an @@ -383,12 +380,16 @@ func TestCreateStream_SinglePeerDial(t *testing.T) { // attempt to create two concurrent streams go func() { defer wg.Done() - _, err := sender.CreateStream(ctx, receiver.Host().ID()) + err := sender.OpenProtectedStream(ctx, receiver.Host().ID(), t.Name(), func(stream network.Stream) error { + return nil + }) require.Error(t, err) }() go func() { defer wg.Done() - _, err := sender.CreateStream(ctx, receiver.Host().ID()) + err := sender.OpenProtectedStream(ctx, receiver.Host().ID(), t.Name(), func(stream network.Stream) error { + return nil + }) require.Error(t, err) }() @@ -480,9 +481,14 @@ func createConcurrentStreams(t *testing.T, ctx context.Context, nodes []p2p.LibP wg.Add(1) go func(sender p2p.LibP2PNode) { defer wg.Done() - s, err := sender.CreateStream(ctx, pInfo.ID) + err := sender.OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { + streams <- stream + + // wait for the done signal to close the stream + <-ctx.Done() + return nil + }) require.NoError(t, err) - streams <- s }(this) } } @@ -490,7 +496,7 @@ func createConcurrentStreams(t *testing.T, ctx context.Context, nodes []p2p.LibP // in 2 connections 1 created by each node, this happens because we are calling CreateStream concurrently. time.Sleep(500 * time.Millisecond) } - wg.Wait() + unittest.RequireReturnsBefore(t, wg.Wait, 3*time.Second, "could not create streams on time") } // ensureSinglePairwiseConnection ensure each node in the list has exactly one connection to every other node in the list. From aee14a6eeefec6e9a8c9bc06b024edf887f468ff Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:13:11 -0400 Subject: [PATCH 701/815] fixes TestStreamClosing --- network/p2p/p2pnode/libp2pStream_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 224a913aa8f..c303281c408 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -64,20 +64,20 @@ func TestStreamClosing(t *testing.T) { go func(i int) { // Create stream from node 1 to node 2 (reuse if one already exists) nodes[0].Host().Peerstore().AddAddrs(nodeInfo1.ID, nodeInfo1.Addrs, peerstore.AddressTTL) - s, err := nodes[0].CreateStream(ctx, nodeInfo1.ID) - assert.NoError(t, err) - w := bufio.NewWriter(s) + err := nodes[0].OpenProtectedStream(ctx, nodeInfo1.ID, t.Name(), func(s network.Stream) error { + w := bufio.NewWriter(s) - // Send message from node 1 to 2 - msg := fmt.Sprintf("hello%d\n", i) - _, err = w.WriteString(msg) - assert.NoError(t, err) + // Send message from node 1 to 2 + msg := fmt.Sprintf("hello%d\n", i) + _, err = w.WriteString(msg) + assert.NoError(t, err) - // Flush the stream - assert.NoError(t, w.Flush()) + // Flush the stream + require.NoError(t, w.Flush()) - // close the stream - err = s.Close() + // returning will close the stream + return nil + }) require.NoError(t, err) senderWG.Done() From 6380b6a8443840304b3214232858a624b7a09a21 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:16:42 -0400 Subject: [PATCH 702/815] fixes TestCrosstalkPreventionOnNetworkKeyChange --- network/p2p/test/sporking_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 41a0379b2ac..3216e13304a 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/stretchr/testify/assert" @@ -87,9 +88,11 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { // create stream from node 1 to node 2 node1.Host().Peerstore().AddAddrs(peerInfo2.ID, peerInfo2.Addrs, peerstore.AddressTTL) - s, err := node1.CreateStream(context.Background(), peerInfo2.ID) + err = node1.OpenProtectedStream(context.Background(), peerInfo2.ID, t.Name(), func(stream network.Stream) error { + require.NotNil(t, stream) + return nil + }) require.NoError(t, err) - assert.NotNil(t, s) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different networking key but on the same IP and port From 9c92615c167f57e078c2e643dab55a26caf56742 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:18:54 -0400 Subject: [PATCH 703/815] refactors fixtures with open protected stream --- network/internal/p2pfixtures/fixtures.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 521918f70e9..5e17e6cc0c3 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -335,7 +335,10 @@ func EnsureNoStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2P } require.Empty(t, other.Host().Network().ConnsToPeer(thisId)) - _, err := this.CreateStream(ctx, otherId) + err := this.OpenProtectedStream(ctx, otherId, t.Name(), func(stream network.Stream) error { + // no-op as the stream is never created. + return nil + }) // ensures that other node has never received a connection from this node. require.Equal(t, other.Host().Network().Connectedness(thisId), network.NotConnected) // a stream is established on top of a connection, so if there is no connection, there should be no stream. @@ -357,9 +360,12 @@ func EnsureStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2PNo require.Fail(t, "node is in both from and to lists") } // stream creation should pass without error - s, err := this.CreateStream(ctx, other.Host().ID()) + err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + require.NotNil(t, stream) + return nil + }) require.NoError(t, err) - require.NotNil(t, s) + } } } From e9ae29a0c028903f3dfb0af09697740ac60f23cd Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:20:10 -0400 Subject: [PATCH 704/815] fixes TestFindPeerWithDHT --- network/p2p/dht/dht_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 534cca7eaa6..5b8b3c76abd 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -87,7 +87,11 @@ func TestFindPeerWithDHT(t *testing.T) { // Try to create a stream from client i to client j. This should resort to a DHT // lookup since client i does not know client j's address. unittest.RequireReturnsBefore(t, func() { - _, err = dhtClientNodes[i].CreateStream(ctx, dhtClientNodes[j].Host().ID()) + err = dhtClientNodes[i].OpenProtectedStream(ctx, dhtClientNodes[j].Host().ID(), t.Name(), func(stream network.Stream) error { + // do nothing + require.NotNil(t, stream) + return nil + }) require.NoError(t, err) }, 1*time.Second, "could not create stream on time") } From 8cd91a2c7698794540e7756ce80fd59e85a029d2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:21:42 -0400 Subject: [PATCH 705/815] refactors fixtures with open protected stream --- network/p2p/test/fixtures.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index f124ac4d17b..424037b49ed 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -554,9 +554,13 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod continue } // stream creation should pass without error - s, err := this.CreateStream(ctx, other.Host().ID()) + err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + // do nothing + require.NotNil(t, stream) + return nil + }) require.NoError(t, err) - require.NotNil(t, s) + } } } From 3aff3296b5300f404925a07cf0850a46ac43d6b7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:45:37 -0400 Subject: [PATCH 706/815] refactors libp2p stream tests --- network/p2p/p2pnode/libp2pStream_test.go | 48 ++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index c303281c408..f7b3154018f 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -160,7 +160,6 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol p2ptest.WithPreferredUnicasts(unicasts)) idProvider.SetIdentities(identities) p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) id2 := identities[1] @@ -170,35 +169,38 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol // Now attempt to create another 100 outbound stream to the same destination by calling CreateStream streamCount := 100 var streams []network.Stream + allStreamsClosedWg := sync.WaitGroup{} for i := 0; i < streamCount; i++ { + allStreamsClosedWg.Add(1) pInfo, err := utils.PeerAddressInfo(*id2) require.NoError(t, err) nodes[0].Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) - anotherStream, err := nodes[0].CreateStream(ctx, pInfo.ID) - // Assert that a stream was returned without error - require.NoError(t, err) - require.NotNil(t, anotherStream) - // assert that the stream count within libp2p incremented (a new stream was created) - require.Equal(t, i+1, p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), protocolID, network.DirOutbound)) - // assert that the same connection is reused - require.Len(t, nodes[0].Host().Network().Conns(), 1) - streams = append(streams, anotherStream) - } - - // reverse loop to close all the streams - for i := streamCount - 1; i >= 0; i-- { - s := streams[i] - wg := sync.WaitGroup{} - wg.Add(1) go func() { - err := s.Close() - assert.NoError(t, err) - wg.Done() + err = nodes[0].OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { + require.NotNil(t, stream) + streams = append(streams, stream) + // if we return this function, the stream will be closed, but we need to keep it open for the test + // hence we wait for the context to be done + <-ctx.Done() + allStreamsClosedWg.Done() + return nil + }) + require.NoError(t, err) }() - unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "could not close streams on time") - // assert that the stream count within libp2p decremented - require.Equal(t, i, p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), protocolID, network.DirOutbound)) } + + require.Eventually(t, func() bool { + return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), protocolID, network.DirOutbound) + }, 5*time.Second, 100*time.Millisecond, "could not create streams on time") + + // checks that the number of connections is 1 despite the number of streams; i.e., all streams are created on the same connection + require.Len(t, nodes[0].Host().Network().Conns(), 1) + + // we don't use defer as the moment we stop the nodes, the streams will be closed, and we want to assess the number of streams + p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + + // wait for all streams to be closed + unittest.RequireReturnsBefore(t, allStreamsClosedWg.Wait, 1*time.Second, "could not close streams on time") } // TestCreateStream_FallBack checks two libp2p nodes with conflicting supported unicast protocols fall back From 832a23a9da063256989b46994d8470b336e36dc0 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 14:49:53 -0400 Subject: [PATCH 707/815] fixes TestCreateStream_FallBack --- network/p2p/p2pnode/libp2pStream_test.go | 60 ++++++++++++------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index f7b3154018f..93b5af0a3fa 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -227,8 +227,7 @@ func TestCreateStream_FallBack(t *testing.T) { idProvider.On("ByPeerID", node.Host().ID()).Return(&identities[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) - defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) // Assert that there is no outbound stream to the target yet (neither default nor preferred) defaultProtocolId := protocols.FlowProtocolID(sporkId) @@ -239,45 +238,44 @@ func TestCreateStream_FallBack(t *testing.T) { // Now attempt to create another 100 outbound stream to the same destination by calling CreateStream streamCount := 10 var streams []network.Stream + allStreamsClosedWg := sync.WaitGroup{} for i := 0; i < streamCount; i++ { + allStreamsClosedWg.Add(1) pInfo, err := utils.PeerAddressInfo(otherId) require.NoError(t, err) thisNode.Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) // a new stream must be created - anotherStream, err := thisNode.CreateStream(ctx, pInfo.ID) - require.NoError(t, err) - require.NotNil(t, anotherStream) - - // number of default-protocol streams must be incremented, while preferred ones must be zero, since the other node - // only supports default ones. - require.Equal(t, i+1, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), defaultProtocolId, network.DirOutbound)) - require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), preferredProtocolId, network.DirOutbound)) + go func() { + err = thisNode.OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { + require.NotNil(t, stream) + streams = append(streams, stream) - // assert that the same connection is reused - require.Len(t, thisNode.Host().Network().Conns(), 1) - streams = append(streams, anotherStream) + // if we return this function, the stream will be closed, but we need to keep it open for the test + // hence we wait for the context to be done + <-ctx.Done() + allStreamsClosedWg.Done() + return nil + }) + }() } - // reverse loop to close all the streams - for i := streamCount - 1; i >= 0; i-- { - fmt.Println("closing stream", i) - s := streams[i] - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - // not checking the error as per upgrade of libp2p it returns stream reset error. This is not a problem - // as we are closing the stream anyway and counting the number of streams at the end. - _ = s.Close() - wg.Done() - }() - unittest.RequireReturnsBefore(t, wg.Wait, 1*time.Second, "could not close streams on time") + // wait for the stream to be created on the default protocol id. + require.Eventually(t, func() bool { + return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), defaultProtocolId, network.DirOutbound) + }, 5*time.Second, 100*time.Millisecond, "could not create streams on time") - // number of default-protocol streams must be decremented, while preferred ones must be zero, since the other node - // only supports default ones. - require.Equal(t, i, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), defaultProtocolId, network.DirOutbound)) - require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), preferredProtocolId, network.DirOutbound)) - } + // no stream must be created on the preferred protocol id + require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), preferredProtocolId, network.DirOutbound)) + + // checks that the number of connections is 1 despite the number of streams; i.e., all streams are created on the same connection + require.Len(t, nodes[0].Host().Network().Conns(), 1) + + // we don't use defer as the moment we stop the nodes, the streams will be closed, and we want to assess the number of streams + p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + + // wait for all streams to be closed + unittest.RequireReturnsBefore(t, allStreamsClosedWg.Wait, 1*time.Second, "could not close streams on time") } // TestCreateStreamIsConcurrencySafe tests that the CreateStream is concurrency safe From 78aef2c292e97f848cf746408d31f52ff94a3818 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:18:17 -0400 Subject: [PATCH 708/815] fixes TestCreateStreamIsConcurrencySafe --- network/p2p/p2pnode/libp2pStream_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 93b5af0a3fa..8b4aaa81a66 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -301,8 +301,11 @@ func TestCreateStreamIsConcurrencySafe(t *testing.T) { createStream := func() { <-gate nodes[0].Host().Peerstore().AddAddrs(nodeInfo1.ID, nodeInfo1.Addrs, peerstore.AddressTTL) - _, err := nodes[0].CreateStream(ctx, nodeInfo1.ID) - assert.NoError(t, err) // assert that stream was successfully created + err := nodes[0].OpenProtectedStream(ctx, nodeInfo1.ID, t.Name(), func(stream network.Stream) error { + // no-op stream writer, we just check that the stream was created + return nil + }) + require.NoError(t, err) // assert that stream was successfully created wg.Done() } From f1e09dd1f26faea662bffde2662bfd7490e6a235 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:19:54 -0400 Subject: [PATCH 709/815] fixes TestNoBackoffWhenCreatingStream --- network/p2p/p2pnode/libp2pStream_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 8b4aaa81a66..a61334e16f1 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -382,9 +382,12 @@ func TestNoBackoffWhenCreatingStream(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, maxTimeToWait) unittest.RequireReturnsBefore(t, func() { - _, err = node1.CreateStream(ctx, pInfo.ID) + err = node1.OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { + // do nothing, this is a no-op stream writer, we just check that the stream was created + return nil + }) + require.Error(t, err) }, totalWaitTime, fmt.Sprintf("create stream did not error within %s", totalWaitTime.String())) - require.Error(t, err) require.NotContainsf(t, err.Error(), swarm.ErrDialBackoff.Error(), "swarm dialer unexpectedly did a back off for a one-to-one connection") cancel() } From 1264e92814dd079fb5dbb9cb4c4188d4f897086b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:21:16 -0400 Subject: [PATCH 710/815] fixes TestCreateStreamTimeoutWithUnresponsiveNode --- network/p2p/p2pnode/libp2pStream_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index a61334e16f1..0f280c3602b 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -525,10 +525,13 @@ func TestCreateStreamTimeoutWithUnresponsiveNode(t *testing.T) { unittest.AssertReturnsBefore(t, func() { nodes[0].Host().Peerstore().AddAddrs(silentNodeInfo.ID, silentNodeInfo.Addrs, peerstore.AddressTTL) - _, err = nodes[0].CreateStream(tctx, silentNodeInfo.ID) + err = nodes[0].OpenProtectedStream(tctx, silentNodeInfo.ID, t.Name(), func(stream network.Stream) error { + // do nothing, this is a no-op stream writer, we just check that the stream was created + return nil + }) + require.Error(t, err) }, timeout+grace) - assert.Error(t, err) } // TestCreateStreamIsConcurrent tests that CreateStream calls can be made concurrently such that one blocked call From 44f22e6e9b4040b38f2891fafa2107a820b588c9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:23:25 -0400 Subject: [PATCH 711/815] fixes TestCreateStreamIsConcurrent --- network/p2p/p2pnode/libp2pStream_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 0f280c3602b..fa2b55a4081 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -567,7 +567,12 @@ func TestCreateStreamIsConcurrent(t *testing.T) { blockedCallCh := unittest.RequireNeverReturnBefore(t, func() { goodNodes[0].Host().Peerstore().AddAddrs(silentNodeInfo.ID, silentNodeInfo.Addrs, peerstore.AddressTTL) - _, _ = goodNodes[0].CreateStream(ctx, silentNodeInfo.ID) // this call will block + // the subsequent call will be blocked + _ = goodNodes[0].OpenProtectedStream(ctx, silentNodeInfo.ID, t.Name(), func(stream network.Stream) error { + // do nothing, the stream creation will be blocked so this should never be called + require.Fail(t, "this should never be called") + return nil + }) }, 1*time.Second, "CreateStream attempt to the unresponsive peer did not block") From a8e254729d3077d29a01fc8ecedad717bc0d15f4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:25:11 -0400 Subject: [PATCH 712/815] fixes TestCreateStreamIsConcurrent --- network/p2p/p2pnode/libp2pStream_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index fa2b55a4081..a65cbd0d3fc 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -581,7 +581,10 @@ func TestCreateStreamIsConcurrent(t *testing.T) { unittest.RequireReturnsBefore(t, func() { goodNodes[0].Host().Peerstore().AddAddrs(goodNodeInfo1.ID, goodNodeInfo1.Addrs, peerstore.AddressTTL) - _, err := goodNodes[0].CreateStream(ctx, goodNodeInfo1.ID) + err := goodNodes[0].OpenProtectedStream(ctx, goodNodeInfo1.ID, t.Name(), func(stream network.Stream) error { + // do nothing, this is a no-op stream writer, we just check that the stream was created + return nil + }) require.NoError(t, err) }, 1*time.Second, "creating stream to a responsive node failed while concurrently blocked on unresponsive node") From cf07f4d75eaae364b28e8057e42a4ecdebee3b79 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:26:22 -0400 Subject: [PATCH 713/815] fixes TestOneToOneCrosstalkPrevention --- network/p2p/test/sporking_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 3216e13304a..ed0b66be1a5 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -157,9 +157,11 @@ func TestOneToOneCrosstalkPrevention(t *testing.T) { // create stream from node 1 to node 2 node2.Host().Peerstore().AddAddrs(peerInfo1.ID, peerInfo1.Addrs, peerstore.AddressTTL) - s, err := node2.CreateStream(context.Background(), peerInfo1.ID) + err = node2.OpenProtectedStream(context.Background(), peerInfo1.ID, t.Name(), func(stream network.Stream) error { + assert.NotNil(t, stream) + return nil + }) require.NoError(t, err) - assert.NotNil(t, s) // Simulate a hard-spoon: node1 is on the old chain, but node2 is moved from the old chain to the new chain // stop node 2 and start it again with a different libp2p protocol id to listen for From 771c92b0676ab6329b56bd59960ab0ece3562719 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:28:28 -0400 Subject: [PATCH 714/815] fixes testOneToOneMessagingFails --- network/p2p/test/sporking_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index ed0b66be1a5..78f87d27d31 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -313,9 +313,15 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { func testOneToOneMessagingFails(t *testing.T, sourceNode p2p.LibP2PNode, peerInfo peer.AddrInfo) { // create stream from source node to destination address sourceNode.Host().Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.AddressTTL) - _, err := sourceNode.CreateStream(context.Background(), peerInfo.ID) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + err := sourceNode.OpenProtectedStream(ctx, peerInfo.ID, t.Name(), func(stream network.Stream) error { + // this callback should never be called + assert.Fail(t, "stream creation should have failed") + return nil + }) // assert that stream creation failed - assert.Error(t, err) + require.Error(t, err) // assert that it failed with the expected error assert.Regexp(t, ".*failed to negotiate security protocol.*|.*protocols not supported.*", err) } From 1db8652e9c67d1f15f0b864b41d934b6ab800083 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:30:15 -0400 Subject: [PATCH 715/815] removes create stream from libp2p interface --- network/p2p/libp2pNode.go | 2 -- network/p2p/p2pnode/libp2pNode.go | 48 ++++++------------------------- 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index c85e4b198d2..98d4ecdc3db 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -73,8 +73,6 @@ type LibP2PNode interface { // deadline, executing the writing logic, resetting the stream if the writing logic fails, or closing the stream. // All returned errors during this process can be considered benign. OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error - // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. - CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) // GetIPPort returns the IP and Port the libp2p node is listening on. GetIPPort() (string, string, error) // RoutingTable returns the node routing table diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index ac8dcb6fe85..2ca895633d1 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -238,6 +238,15 @@ func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protecti return nil } +// createStream creates a new stream to the given peer. +// Args: +// - ctx: The context used to control the stream's lifecycle. +// - peerID: The ID of the peer to open the stream to. +// +// Returns: +// - libp2pnet.Stream: The created stream. +// - error: An error, if any occurred during the process. This includes failure in creating the stream. All returned +// errors during this process can be considered benign. func (n *Node) createStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) { lg := n.logger.With().Str("peer_id", peerID.String()).Logger() @@ -275,45 +284,6 @@ func (n *Node) createStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stre return stream, nil } -// CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. -// All errors returned from this function can be considered benign. -func (n *Node) CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) { - lg := n.logger.With().Str("peer_id", peerID.String()).Logger() - - // If we do not currently have any addresses for the given peer, stream creation will almost - // certainly fail. If this Node was configured with a routing system, we can try to use it to - // look up the address of the peer. - if len(n.host.Peerstore().Addrs(peerID)) == 0 && n.routing != nil { - lg.Info().Msg("address not found in peer store, searching for peer in routing system") - - var err error - func() { - timedCtx, cancel := context.WithTimeout(ctx, findPeerQueryTimeout) - defer cancel() - // try to find the peer using the routing system - _, err = n.routing.FindPeer(timedCtx, peerID) - }() - - if err != nil { - lg.Warn().Err(err).Msg("address not found in both peer store and routing system") - } else { - lg.Debug().Msg("address not found in peer store, but found in routing system search") - } - } - - stream, dialAddrs, err := n.uniMgr.CreateStream(ctx, peerID, MaxConnectAttempt) - if err != nil { - return nil, flownet.NewPeerUnreachableError(fmt.Errorf("could not create stream (peer_id: %s, dialing address(s): %v): %w", peerID, - dialAddrs, err)) - } - - lg.Info(). - Str("networking_protocol_id", string(stream.Protocol())). - Str("dial_address", fmt.Sprintf("%v", dialAddrs)). - Msg("stream successfully created to remote peer") - return stream, nil -} - // GetIPPort returns the IP and Port the libp2p node is listening on. // All errors returned from this function can be considered benign. func (n *Node) GetIPPort() (string, string, error) { From 37af4069c044d4f6fb8fa1c7bb7193987fd2f019 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:33:55 -0400 Subject: [PATCH 716/815] adds ID to libp2p node interface --- network/p2p/libp2pNode.go | 3 ++ network/p2p/mock/lib_p2_p_node.go | 72 +++++++++++++------------------ network/p2p/p2pnode/libp2pNode.go | 6 +++ 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 98d4ecdc3db..9325315e984 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -87,6 +87,9 @@ type LibP2PNode interface { Publish(ctx context.Context, messageScope network.OutgoingMessageScope) error // Host returns pointer to host object of node. Host() host.Host + // ID returns the peer.ID of the node, which is the unique identifier of the node at the libp2p level. + // For other libp2p nodes, the current node is identified by this ID. + ID() peer.ID // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error // WithPeersProvider sets the PeersProvider for the peer manager. diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 228e80e83ab..d68776766c6 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -8,9 +8,9 @@ import ( context "context" - flow "github.com/onflow/flow-go/model/flow" + corenetwork "github.com/libp2p/go-libp2p/core/network" - flow_gonetwork "github.com/onflow/flow-go/network" + flow "github.com/onflow/flow-go/model/flow" host "github.com/libp2p/go-libp2p/core/host" @@ -20,7 +20,7 @@ import ( mock "github.com/stretchr/testify/mock" - network "github.com/libp2p/go-libp2p/core/network" + network "github.com/onflow/flow-go/network" p2p "github.com/onflow/flow-go/network/p2p" @@ -57,32 +57,6 @@ func (_m *LibP2PNode) AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error return r0 } -// CreateStream provides a mock function with given fields: ctx, peerID -func (_m *LibP2PNode) CreateStream(ctx context.Context, peerID peer.ID) (network.Stream, error) { - ret := _m.Called(ctx, peerID) - - var r0 network.Stream - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, peer.ID) (network.Stream, error)); ok { - return rf(ctx, peerID) - } - if rf, ok := ret.Get(0).(func(context.Context, peer.ID) network.Stream); ok { - r0 = rf(ctx, peerID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(network.Stream) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, peer.ID) error); ok { - r1 = rf(ctx, peerID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // Done provides a mock function with given fields: func (_m *LibP2PNode) Done() <-chan struct{} { ret := _m.Called() @@ -176,6 +150,20 @@ func (_m *LibP2PNode) Host() host.Host { return r0 } +// ID provides a mock function with given fields: +func (_m *LibP2PNode) ID() peer.ID { + ret := _m.Called() + + var r0 peer.ID + if rf, ok := ret.Get(0).(func() peer.ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(peer.ID) + } + + return r0 +} + // IsConnected provides a mock function with given fields: peerID func (_m *LibP2PNode) IsConnected(peerID peer.ID) (bool, error) { ret := _m.Called(peerID) @@ -201,19 +189,19 @@ func (_m *LibP2PNode) IsConnected(peerID peer.ID) (bool, error) { } // IsDisallowListed provides a mock function with given fields: peerId -func (_m *LibP2PNode) IsDisallowListed(peerId peer.ID) ([]flow_gonetwork.DisallowListedCause, bool) { +func (_m *LibP2PNode) IsDisallowListed(peerId peer.ID) ([]network.DisallowListedCause, bool) { ret := _m.Called(peerId) - var r0 []flow_gonetwork.DisallowListedCause + var r0 []network.DisallowListedCause var r1 bool - if rf, ok := ret.Get(0).(func(peer.ID) ([]flow_gonetwork.DisallowListedCause, bool)); ok { + if rf, ok := ret.Get(0).(func(peer.ID) ([]network.DisallowListedCause, bool)); ok { return rf(peerId) } - if rf, ok := ret.Get(0).(func(peer.ID) []flow_gonetwork.DisallowListedCause); ok { + if rf, ok := ret.Get(0).(func(peer.ID) []network.DisallowListedCause); ok { r0 = rf(peerId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]flow_gonetwork.DisallowListedCause) + r0 = ret.Get(0).([]network.DisallowListedCause) } } @@ -243,21 +231,21 @@ func (_m *LibP2PNode) ListPeers(topic string) []peer.ID { } // OnAllowListNotification provides a mock function with given fields: id, cause -func (_m *LibP2PNode) OnAllowListNotification(id peer.ID, cause flow_gonetwork.DisallowListedCause) { +func (_m *LibP2PNode) OnAllowListNotification(id peer.ID, cause network.DisallowListedCause) { _m.Called(id, cause) } // OnDisallowListNotification provides a mock function with given fields: id, cause -func (_m *LibP2PNode) OnDisallowListNotification(id peer.ID, cause flow_gonetwork.DisallowListedCause) { +func (_m *LibP2PNode) OnDisallowListNotification(id peer.ID, cause network.DisallowListedCause) { _m.Called(id, cause) } // OpenProtectedStream provides a mock function with given fields: ctx, peerID, protectionTag, writingLogic -func (_m *LibP2PNode) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(network.Stream) error) error { +func (_m *LibP2PNode) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(corenetwork.Stream) error) error { ret := _m.Called(ctx, peerID, protectionTag, writingLogic) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(network.Stream) error) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(corenetwork.Stream) error) error); ok { r0 = rf(ctx, peerID, protectionTag, writingLogic) } else { r0 = ret.Error(0) @@ -299,11 +287,11 @@ func (_m *LibP2PNode) PeerScoreExposer() p2p.PeerScoreExposer { } // Publish provides a mock function with given fields: ctx, messageScope -func (_m *LibP2PNode) Publish(ctx context.Context, messageScope flow_gonetwork.OutgoingMessageScope) error { +func (_m *LibP2PNode) Publish(ctx context.Context, messageScope network.OutgoingMessageScope) error { ret := _m.Called(ctx, messageScope) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, flow_gonetwork.OutgoingMessageScope) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, network.OutgoingMessageScope) error); ok { r0 = rf(ctx, messageScope) } else { r0 = ret.Error(0) @@ -459,11 +447,11 @@ func (_m *LibP2PNode) Unsubscribe(topic channels.Topic) error { } // WithDefaultUnicastProtocol provides a mock function with given fields: defaultHandler, preferred -func (_m *LibP2PNode) WithDefaultUnicastProtocol(defaultHandler network.StreamHandler, preferred []protocols.ProtocolName) error { +func (_m *LibP2PNode) WithDefaultUnicastProtocol(defaultHandler corenetwork.StreamHandler, preferred []protocols.ProtocolName) error { ret := _m.Called(defaultHandler, preferred) var r0 error - if rf, ok := ret.Get(0).(func(network.StreamHandler, []protocols.ProtocolName) error); ok { + if rf, ok := ret.Get(0).(func(corenetwork.StreamHandler, []protocols.ProtocolName) error); ok { r0 = rf(defaultHandler, preferred) } else { r0 = ret.Error(0) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 2ca895633d1..bda7a16d27e 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -284,6 +284,12 @@ func (n *Node) createStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stre return stream, nil } +// ID returns the peer.ID of the node, which is the unique identifier of the node at the libp2p level. +// For other libp2p nodes, the current node is identified by this ID. +func (n *Node) ID() peer.ID { + return n.host.ID() +} + // GetIPPort returns the IP and Port the libp2p node is listening on. // All errors returned from this function can be considered benign. func (n *Node) GetIPPort() (string, string, error) { From c190fb97c19dd755844f7381535f5044ee01f5b6 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:40:32 -0400 Subject: [PATCH 717/815] replaces Host().ID() with ID() --- insecure/corruptlibp2p/gossipsub_spammer.go | 2 +- .../rpc_inspector/metrics_inspector_test.go | 2 +- .../validation_inspector_test.go | 68 +++++++++---------- .../test/gossipsub/scoring/ihave_spam_test.go | 28 ++++---- .../test/gossipsub/scoring/scoring_test.go | 36 +++++----- network/internal/p2pfixtures/fixtures.go | 12 ++-- network/p2p/connection/connManager_test.go | 6 +- .../p2p/connection/connection_gater_test.go | 30 ++++---- network/p2p/dht/dht_test.go | 18 ++--- network/p2p/p2pnode/disallow_listing_test.go | 16 ++--- network/p2p/p2pnode/libp2pNode_test.go | 24 +++---- network/p2p/p2pnode/libp2pStream_test.go | 18 ++--- network/p2p/scoring/scoring_test.go | 2 +- .../subscription/subscription_filter_test.go | 2 +- network/p2p/test/fixtures.go | 16 ++--- network/p2p/test/sporking_test.go | 2 +- network/p2p/test/topic_validator_test.go | 40 +++++------ .../p2p/tracer/gossipSubMeshTracer_test.go | 16 ++--- .../p2p/tracer/gossipSubScoreTracer_test.go | 26 +++---- network/test/blob_service_test.go | 2 +- network/test/epochtransition_test.go | 4 +- 21 files changed, 185 insertions(+), 185 deletions(-) diff --git a/insecure/corruptlibp2p/gossipsub_spammer.go b/insecure/corruptlibp2p/gossipsub_spammer.go index d38e1dfa12b..f169183b2b8 100644 --- a/insecure/corruptlibp2p/gossipsub_spammer.go +++ b/insecure/corruptlibp2p/gossipsub_spammer.go @@ -64,7 +64,7 @@ func NewGossipSubRouterSpammerWithRpcInspector(t *testing.T, sporkId flow.Identi // ctlMessages is the list of spam messages to send to the victim node. func (s *GossipSubRouterSpammer) SpamControlMessage(t *testing.T, victim p2p.LibP2PNode, ctlMessages []pb.ControlMessage, msgs ...*pb.Message) { for _, ctlMessage := range ctlMessages { - require.True(t, s.router.Get().SendControl(victim.Host().ID(), &ctlMessage, msgs...)) + require.True(t, s.router.Get().SendControl(victim.ID(), &ctlMessage, msgs...)) } } diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go index c62cfba2f8b..d0366f74422 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/metrics_inspector_test.go @@ -42,7 +42,7 @@ func TestMetricsInspector_ObserveRPC(t *testing.T) { Run(func(args mock.Arguments) { peerID, ok := args.Get(0).(peer.ID) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), peerID) + require.Equal(t, spammer.SpammerNode.ID(), peerID) rpc, ok := args.Get(1).(*pubsub.RPC) require.True(t, ok) // there are some default rpc messages exchanged between the nodes on startup diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 8d256bde9ba..bf42ed9683b 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -96,8 +96,8 @@ func TestValidationInspector_SafetyThreshold(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() messageCount := 5 controlMessageCount := int64(2) @@ -153,7 +153,7 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) switch notification.MsgType { case p2pmsg.CtrlMsgGraft: @@ -198,8 +198,8 @@ func TestValidationInspector_HardThreshold_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() validationInspector.Start(signalerCtx) nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} @@ -250,7 +250,7 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) require.True(t, channels.IsInvalidTopicErr(notification.Err)) switch notification.MsgType { @@ -294,8 +294,8 @@ func TestValidationInspector_HardThresholdIHave_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() validationInspector.Start(signalerCtx) nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} @@ -343,7 +343,7 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, validation.IsErrRateLimitedControlMsg(notification.Err)) require.Equal(t, uint64(messageCount), notification.Count) switch notification.MsgType { @@ -389,8 +389,8 @@ func TestValidationInspector_RateLimitedPeer_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() validationInspector.Start(signalerCtx) nodes := []p2p.LibP2PNode{victimNode, spammer.SpammerNode} @@ -470,7 +470,7 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, channels.IsInvalidTopicErr(notification.Err)) switch notification.MsgType { case p2pmsg.CtrlMsgGraft: @@ -520,8 +520,8 @@ func TestValidationInspector_InvalidTopicId_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() // create unknown topic unknownTopic := channels.Topic(fmt.Sprintf("%s/%s", corruptlibp2p.GossipSubTopicIdFixture(), sporkID)) @@ -607,7 +607,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, validation.IsErrDuplicateTopic(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { @@ -653,8 +653,8 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() // a topics spork ID is considered invalid if it does not match the current spork ID duplicateTopic := channels.Topic(fmt.Sprintf("%s/%s", channels.PushBlocks, sporkID)) @@ -714,7 +714,7 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, channels.IsUnknownClusterIDErr(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { @@ -759,8 +759,8 @@ func TestValidationInspector_UnknownClusterId_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Times(3) + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Times(3) // setup cluster prefixed topic with an invalid cluster ID unknownClusterID := channels.Topic(channels.SyncCluster("unknown-cluster-ID")) @@ -814,7 +814,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) require.True(t, validation.IsErrActiveClusterIDsNotSet(notification.Err)) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) switch notification.MsgType { case p2pmsg.CtrlMsgGraft: invGraftNotifCount.Inc() @@ -856,8 +856,8 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Graft_Detection(t *testing.T internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Times(int(controlMessageCount + 1)) + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Times(int(controlMessageCount + 1)) // we deliberately avoid setting the cluster IDs so that we eventually receive errors after we have exceeded the allowed cluster // prefixed hard threshold @@ -906,7 +906,7 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) require.True(t, validation.IsErrActiveClusterIDsNotSet(notification.Err)) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) switch notification.MsgType { case p2pmsg.CtrlMsgPrune: invPruneNotifCount.Inc() @@ -948,8 +948,8 @@ func TestValidationInspector_ActiveClusterIdsNotSet_Prune_Detection(t *testing.T internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Times(int(controlMessageCount + 1)) + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Times(int(controlMessageCount + 1)) // we deliberately avoid setting the cluster IDs so that we eventually receive errors after we have exceeded the allowed cluster // prefixed hard threshold @@ -1005,7 +1005,7 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, validation.IsErrUnstakedPeer(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { @@ -1050,8 +1050,8 @@ func TestValidationInspector_UnstakedNode_Detection(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(nil, false).Times(3) + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(nil, false).Times(3) // setup cluster prefixed topic with an invalid cluster ID clusterID := flow.ChainID("known-cluster-id") @@ -1120,7 +1120,7 @@ func setupTest(t *testing.T, logger zerolog.Logger, role flow.Role, sporkID flow internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() return signalerCtx, cancel, spammer, victimNode, victimIdentity, distributor, validationInspector, idProvider } @@ -1152,9 +1152,9 @@ func TestGossipSubSpamMitigationIntegration(t *testing.T) { idProvider.On("ByPeerID", mockery.Anything).Return( func(peerId peer.ID) *flow.Identity { switch peerId { - case victimNode.Host().ID(): + case victimNode.ID(): return &victimId - case spammer.SpammerNode.Host().ID(): + case spammer.SpammerNode.ID(): return &spammer.SpammerId default: return nil @@ -1162,9 +1162,9 @@ func TestGossipSubSpamMitigationIntegration(t *testing.T) { }, func(peerId peer.ID) bool { switch peerId { - case victimNode.Host().ID(): + case victimNode.ID(): fallthrough - case spammer.SpammerNode.Host().ID(): + case spammer.SpammerNode.ID(): return true default: return false diff --git a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go index d4a1187aca0..b9bdfa8f38a 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/ihave_spam_test.go @@ -77,8 +77,8 @@ func TestGossipSubIHaveBrokenPromises_Below_Threshold(t *testing.T) { }), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} @@ -99,7 +99,7 @@ func TestGossipSubIHaveBrokenPromises_Below_Threshold(t *testing.T) { // wait till victim counts the spam iHaves as broken promises (one per RPC for a total of 10). initialBehavioralPenalty := float64(0) // keeps track of the initial behavioral penalty of the spammer node for decay testing. require.Eventually(t, func() bool { - behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.Host().ID()) + behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.ID()) if !ok { return false } @@ -113,7 +113,7 @@ func TestGossipSubIHaveBrokenPromises_Below_Threshold(t *testing.T) { // seconds to be on the safe side. }, 10*time.Second, 100*time.Millisecond) - spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) require.True(t, ok, "sanity check failed, we should have a score for the spammer node") // since spammer is not yet considered to be penalized, its score must be greater than the gossipsub health thresholds. require.Greaterf(t, spammerScore, scoring.DefaultGossipThreshold, "sanity check failed, the score of the spammer node must be greater than gossip threshold: %f, actual: %f", scoring.DefaultGossipThreshold, spammerScore) @@ -122,7 +122,7 @@ func TestGossipSubIHaveBrokenPromises_Below_Threshold(t *testing.T) { // eventually, after a heartbeat the spammer behavioral counter must be decayed require.Eventually(t, func() bool { - behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.Host().ID()) + behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.ID()) if !ok { return false } @@ -193,8 +193,8 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { }), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} @@ -209,7 +209,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { return unittest.ProposalFixture() }) - initScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + initScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) require.True(t, ok, "score for spammer node must be present") // FIRST ROUND OF ATTACK: spammer sends 10 RPCs to the victim node, each containing 500 iHave messages. @@ -217,7 +217,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // wait till victim counts the spam iHaves as broken promises for the second round of attack (one per RPC for a total of 10). require.Eventually(t, func() bool { - behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.Host().ID()) + behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.ID()) if !ok { return false } @@ -232,7 +232,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // seconds to be on the safe side. }, 10*time.Second, 100*time.Millisecond) - scoreAfterFirstRound, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + scoreAfterFirstRound, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) require.True(t, ok, "score for spammer node must be present") // spammer score after first round must not be decreased severely, we account for 10% drop due to under-performing // (on sending fresh new messages since that is not part of the test). @@ -243,7 +243,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // wait till victim counts the spam iHaves as broken promises for the second round of attack (one per RPC for a total of 10). require.Eventually(t, func() bool { - behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.Host().ID()) + behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.ID()) if !ok { return false } @@ -259,7 +259,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // seconds to be on the safe side. }, 10*time.Second, 100*time.Millisecond) - spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) require.True(t, ok, "sanity check failed, we should have a score for the spammer node") // with the second round of the attack, the spammer is about 10 broken promises above the threshold (total ~20 broken promises, but the first 10 are not counted). // we expect the score to be dropped to initScore - 10 * 10 * 0.01 * scoring.MaxAppSpecificReward, however, instead of 10, we consider 8 about the threshold, to account for decays. @@ -278,7 +278,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // wait till victim counts the spam iHaves as broken promises for the third round of attack (one per RPC for a total of 10). require.Eventually(t, func() bool { - behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.Host().ID()) + behavioralPenalty, ok := victimNode.PeerScoreExposer().GetBehaviourPenalty(spammer.SpammerNode.ID()) if !ok { return false } @@ -293,7 +293,7 @@ func TestGossipSubIHaveBrokenPromises_Above_Threshold(t *testing.T) { // seconds to be on the safe side. }, 10*time.Second, 100*time.Millisecond) - spammerScore, ok = victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + spammerScore, ok = victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) require.True(t, ok, "sanity check failed, we should have a score for the spammer node") // with the third round of the attack, the spammer is about 20 broken promises above the threshold (total ~30 broken promises), hence its overall score must be below the gossip, publish, and graylist thresholds, meaning that // victim will not exchange messages with it anymore, and also that it will be graylisted meaning all incoming and outgoing RPCs to and from the spammer will be dropped by the victim. diff --git a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go index 11248d109da..67f6cb44b7e 100644 --- a/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go +++ b/insecure/integration/functional/test/gossipsub/scoring/scoring_test.go @@ -116,8 +116,8 @@ func testGossipSubInvalidMessageDeliveryScoring(t *testing.T, spamMsgFactory fun p2ptest.EnablePeerScoringWithOverride(p2p.PeerScoringConfigNoOverride), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() ids := flow.IdentityList{&spammer.SpammerId, &victimIdentity} nodes := []p2p.LibP2PNode{spammer.SpammerNode, victimNode} @@ -136,14 +136,14 @@ func testGossipSubInvalidMessageDeliveryScoring(t *testing.T, spamMsgFactory fun for i := 0; i <= totalSpamMessages; i++ { spammer.SpamControlMessage(t, victimNode, spammer.GenerateCtlMessages(1), - spamMsgFactory(spammer.SpammerNode.Host().ID(), victimNode.Host().ID(), blockTopic)) + spamMsgFactory(spammer.SpammerNode.ID(), victimNode.ID(), blockTopic)) } // wait for at most 3 seconds for the victim node to penalize the spammer node. // Each heartbeat is 1 second, so 3 heartbeats should be enough to penalize the spammer node. // Ideally, we should wait for 1 heartbeat, but the score may not be updated immediately after the heartbeat. require.Eventually(t, func() bool { - spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.Host().ID()) + spammerScore, ok := victimNode.PeerScoreExposer().GetScore(spammer.SpammerNode.ID()) if !ok { return false } @@ -163,7 +163,7 @@ func testGossipSubInvalidMessageDeliveryScoring(t *testing.T, spamMsgFactory fun return true }, 3*time.Second, 100*time.Millisecond) - topicsSnapshot, ok := victimNode.PeerScoreExposer().GetTopicScores(spammer.SpammerNode.Host().ID()) + topicsSnapshot, ok := victimNode.PeerScoreExposer().GetTopicScores(spammer.SpammerNode.ID()) require.True(t, ok) require.NotNil(t, topicsSnapshot, "topic scores must not be nil") require.NotEmpty(t, topicsSnapshot, "topic scores must not be empty") @@ -227,8 +227,8 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_SingleTopic(t *testing.T) { p2ptest.WithRole(role), ) - idProvider.On("ByPeerID", thisNode.Host().ID()).Return(&thisId, true).Maybe() - idProvider.On("ByPeerID", underPerformerNode.Host().ID()).Return(&underPerformerId, true).Maybe() + idProvider.On("ByPeerID", thisNode.ID()).Return(&thisId, true).Maybe() + idProvider.On("ByPeerID", underPerformerNode.ID()).Return(&underPerformerId, true).Maybe() ids := flow.IdentityList{&underPerformerId, &thisId} nodes := []p2p.LibP2PNode{underPerformerNode, thisNode} @@ -247,7 +247,7 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_SingleTopic(t *testing.T) { // The reason is in our scoring system, we reward the staked nodes by MaxAppSpecificReward, and the under-performing node is considered staked // as it is in the id provider of thisNode. require.Eventually(t, func() bool { - underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.Host().ID()) + underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.ID()) if !ok { return false } @@ -262,7 +262,7 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_SingleTopic(t *testing.T) { // however, after one decay interval, we expect the score of the under-performing node to be penalized by -0.05 * MaxAppSpecificReward as // it has not been able to deliver messages to this node in the topic mesh since the past decay interval. require.Eventually(t, func() bool { - underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.Host().ID()) + underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.ID()) if !ok { return false } @@ -333,8 +333,8 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_TwoTopics(t *testing.T) { p2ptest.WithRole(role), ) - idProvider.On("ByPeerID", thisNode.Host().ID()).Return(&thisId, true).Maybe() - idProvider.On("ByPeerID", underPerformerNode.Host().ID()).Return(&underPerformerId, true).Maybe() + idProvider.On("ByPeerID", thisNode.ID()).Return(&thisId, true).Maybe() + idProvider.On("ByPeerID", underPerformerNode.ID()).Return(&underPerformerId, true).Maybe() ids := flow.IdentityList{&underPerformerId, &thisId} nodes := []p2p.LibP2PNode{underPerformerNode, thisNode} @@ -356,7 +356,7 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_TwoTopics(t *testing.T) { // The reason is in our scoring system, we reward the staked nodes by MaxAppSpecificReward, and the under-performing node is considered staked // as it is in the id provider of thisNode. require.Eventually(t, func() bool { - underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.Host().ID()) + underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.ID()) if !ok { return false } @@ -372,7 +372,7 @@ func TestGossipSubMeshDeliveryScoring_UnderDelivery_TwoTopics(t *testing.T) { // however, after one decay interval, we expect the score of the under-performing node to be penalized by ~ 2 * -0.05 * MaxAppSpecificReward. require.Eventually(t, func() bool { - underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.Host().ID()) + underPerformingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(underPerformerNode.ID()) if !ok { return false } @@ -442,8 +442,8 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { p2ptest.WithRole(role), ) - idProvider.On("ByPeerID", thisNode.Host().ID()).Return(&thisId, true).Maybe() - idProvider.On("ByPeerID", replayingNode.Host().ID()).Return(&replayingId, true).Maybe() + idProvider.On("ByPeerID", thisNode.ID()).Return(&thisId, true).Maybe() + idProvider.On("ByPeerID", replayingNode.ID()).Return(&replayingId, true).Maybe() ids := flow.IdentityList{&replayingId, &thisId} nodes := []p2p.LibP2PNode{replayingNode, thisNode} @@ -463,7 +463,7 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { // as it is in the id provider of thisNode. initialReplayingNodeScore := float64(0) require.Eventually(t, func() bool { - replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.Host().ID()) + replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.ID()) if !ok { return false } @@ -490,7 +490,7 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { // as the replaying node is not penalized, we expect its score to be equal to the initial score. require.Eventually(t, func() bool { - replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.Host().ID()) + replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.ID()) if !ok { return false } @@ -524,7 +524,7 @@ func TestGossipSubMeshDeliveryScoring_Replay_Will_Not_Counted(t *testing.T) { // since the last decay interval, the replaying node has not delivered anything new, so its score should be penalized for under-performing. require.Eventually(t, func() bool { - replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.Host().ID()) + replayingNodeScore, ok := thisNode.PeerScoreExposer().GetScore(replayingNode.ID()) if !ok { return false } diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 5e17e6cc0c3..28634233e21 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -251,11 +251,11 @@ func EnsureNotConnected(t *testing.T, ctx context.Context, from []p2p.LibP2PNode if this == other { require.Fail(t, "overlapping nodes in from and to lists") } - thisId := this.Host().ID() + thisId := this.ID() // we intentionally do not check the error here, with libp2p v0.24 connection gating at the "InterceptSecured" level // does not cause the nodes to complain about the connection being rejected at the dialer side. // Hence, we instead check for any trace of the connection being established in the receiver side. - _ = this.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.Host().ID())) + _ = this.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.ID())) // ensures that other node has never received a connection from this node. require.Equal(t, network.NotConnected, other.Host().Network().Connectedness(thisId)) require.Empty(t, other.Host().Network().ConnsToPeer(thisId)) @@ -276,7 +276,7 @@ func EnsureMessageExchangeOverUnicast(t *testing.T, ctx context.Context, nodes [ if this == other { continue } - err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + err := this.OpenProtectedStream(ctx, other.ID(), t.Name(), func(stream network.Stream) error { rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) _, err := rw.WriteString(msg) require.NoError(t, err) @@ -326,8 +326,8 @@ func EnsureNoStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2P // we intentionally do not check the error here, with libp2p v0.24 connection gating at the "InterceptSecured" level // does not cause the nodes to complain about the connection being rejected at the dialer side. // Hence, we instead check for any trace of the connection being established in the receiver side. - otherId := other.Host().ID() - thisId := this.Host().ID() + otherId := other.ID() + thisId := this.ID() // closes all connections from other node to this node in order to isolate the connection attempt. for _, conn := range other.Host().Network().ConnsToPeer(thisId) { @@ -360,7 +360,7 @@ func EnsureStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2PNo require.Fail(t, "node is in both from and to lists") } // stream creation should pass without error - err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + err := this.OpenProtectedStream(ctx, other.ID(), t.Name(), func(stream network.Stream) error { require.NotNil(t, stream) return nil }) diff --git a/network/p2p/connection/connManager_test.go b/network/p2p/connection/connManager_test.go index 39016a09fc3..7821d301ca6 100644 --- a/network/p2p/connection/connManager_test.go +++ b/network/p2p/connection/connManager_test.go @@ -135,7 +135,7 @@ func TestConnectionManager_Watermarking(t *testing.T) { // connect this node to all other nodes. for _, otherNode := range otherNodes { - require.NoError(t, thisNode.Host().Connect(ctx, otherNode.Host().Peerstore().PeerInfo(otherNode.Host().ID()))) + require.NoError(t, thisNode.Host().Connect(ctx, otherNode.Host().Peerstore().PeerInfo(otherNode.ID()))) } // ensures this node is connected to all other nodes (based on the number of connections). @@ -153,8 +153,8 @@ func TestConnectionManager_Watermarking(t *testing.T) { // connects this node to one of the other nodes that is pruned by connection manager. for _, otherNode := range otherNodes { - if len(thisNode.Host().Network().ConnsToPeer(otherNode.Host().ID())) == 0 { - require.NoError(t, thisNode.Host().Connect(ctx, otherNode.Host().Peerstore().PeerInfo(otherNode.Host().ID()))) + if len(thisNode.Host().Network().ConnsToPeer(otherNode.ID())) == 0 { + require.NoError(t, thisNode.Host().Connect(ctx, otherNode.Host().Peerstore().PeerInfo(otherNode.ID()))) break // we only need to connect to one node. } } diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 672947adadc..e15f2c82c85 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -46,7 +46,7 @@ func TestConnectionGating(t *testing.T) { } return nil }))) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&node1Id, true).Maybe() + idProvider.On("ByPeerID", node1.ID()).Return(&node1Id, true).Maybe() node2Peers := unittest.NewProtectedMap[peer.ID, struct{}]() node2, node2Id := p2ptest.NodeFixture( @@ -60,7 +60,7 @@ func TestConnectionGating(t *testing.T) { } return nil }))) - idProvider.On("ByPeerID", node2.Host().ID()).Return(&node2Id, true).Maybe() + idProvider.On("ByPeerID", node2.ID()).Return(&node2Id, true).Maybe() nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} @@ -83,7 +83,7 @@ func TestConnectionGating(t *testing.T) { // the connection gater on the listening node is checking the allow-list upon accepting the connection. // add node2 to node1's allow list, but not the other way around. - node1Peers.Add(node2.Host().ID(), struct{}{}) + node1Peers.Add(node2.ID(), struct{}{}) // from node2 -> node1 should also NOT work, since node 1 is not in node2's allow list for dialing! p2pfixtures.EnsureNoStreamCreation(t, ctx, []p2p.LibP2PNode{node2}, []p2p.LibP2PNode{node1}, func(t *testing.T, err error) { @@ -98,8 +98,8 @@ func TestConnectionGating(t *testing.T) { t.Run("outbound connection to an approved node is allowed", func(t *testing.T) { // adding both nodes to each other's allow lists. - node1Peers.Add(node2.Host().ID(), struct{}{}) - node2Peers.Add(node1.Host().ID(), struct{}{}) + node1Peers.Add(node2.ID(), struct{}{}) + node2Peers.Add(node1.ID(), struct{}{}) // now both nodes should be able to connect to each other. p2ptest.EnsureStreamCreationInBothDirections(t, ctx, []p2p.LibP2PNode{node1, node2}) @@ -130,11 +130,11 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { // we expect the libp2p.identify service to be used to establish the connection. node2Metrics.On("AllowService", "libp2p.identify").Return() // we expect the node2 attaching node1 to the incoming connection. - node2Metrics.On("AllowPeer", node1.Host().ID()).Return() + node2Metrics.On("AllowPeer", node1.ID()).Return() // we expect node2 allocate memory for the incoming connection. node2Metrics.On("AllowMemory", mock.Anything) // we expect node2 to allow the stream to be created. - node2Metrics.On("AllowStream", node1.Host().ID(), mock.Anything) + node2Metrics.On("AllowStream", node1.ID(), mock.Anything) // we expect node2 to attach protocol to the created stream. node2Metrics.On("AllowProtocol", mock.Anything).Return() @@ -156,8 +156,8 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(p peer.ID) error { return nil // allow all connections. }))) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&node1Id, true).Maybe() - idProvider.On("ByPeerID", node2.Host().ID()).Return(&node2Id, true).Maybe() + idProvider.On("ByPeerID", node1.ID()).Return(&node1Id, true).Maybe() + idProvider.On("ByPeerID", node2.ID()).Return(&node2Id, true).Maybe() nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} @@ -201,8 +201,8 @@ func TestConnectionGating_ResourceAllocation_DisAllowListing(t *testing.T) { p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(p peer.ID) error { return fmt.Errorf("disallowed connection") // rejecting all connections. }))) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&node1Id, true).Maybe() - idProvider.On("ByPeerID", node2.Host().ID()).Return(&node2Id, true).Maybe() + idProvider.On("ByPeerID", node1.ID()).Return(&node1Id, true).Maybe() + idProvider.On("ByPeerID", node2.ID()).Return(&node2Id, true).Maybe() nodes := []p2p.LibP2PNode{node1, node2} ids := flow.IdentityList{&node1Id, &node2Id} @@ -265,10 +265,10 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { return list }), p2ptest.WithConnectionGater(connectionGater)) - idProvider.On("ByPeerID", node.Host().ID()).Return(&id, true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(&id, true).Maybe() nodes = append(nodes, node) identities = append(identities, &id) - allPeerIds = append(allPeerIds, node.Host().ID()) + allPeerIds = append(allPeerIds, node.ID()) inbounds = append(inbounds, inbound) } @@ -286,7 +286,7 @@ func TestConnectionGater_InterceptUpgrade(t *testing.T) { connectionGater.On("InterceptAccept", mock.Anything).Return(true) // adds first node to disallowed list - disallowedPeerIds.Add(nodes[0].Host().ID(), struct{}{}) + disallowedPeerIds.Add(nodes[0].ID(), struct{}{}) // starts the nodes p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) @@ -362,7 +362,7 @@ func TestConnectionGater_Disallow_Integration(t *testing.T) { return nil }) }))) - idProvider.On("ByPeerID", node.Host().ID()).Return(&id, true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(&id, true).Maybe() nodes = append(nodes, node) ids = append(ids, &id) diff --git a/network/p2p/dht/dht_test.go b/network/p2p/dht/dht_test.go index 5b8b3c76abd..5a3809aae5f 100644 --- a/network/p2p/dht/dht_test.go +++ b/network/p2p/dht/dht_test.go @@ -49,7 +49,7 @@ func TestFindPeerWithDHT(t *testing.T) { defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) getDhtServerAddr := func(i uint) peer.AddrInfo { - return peer.AddrInfo{ID: dhtServerNodes[i].Host().ID(), Addrs: dhtServerNodes[i].Host().Addrs()} + return peer.AddrInfo{ID: dhtServerNodes[i].ID(), Addrs: dhtServerNodes[i].Host().Addrs()} } // connect even numbered clients to the first DHT server, and odd number clients to the second @@ -82,12 +82,12 @@ func TestFindPeerWithDHT(t *testing.T) { for j := 1; j < len(dhtClientNodes); j += 2 { // client i should not yet know the address of client j, but we clear any addresses // here just in case. - dhtClientNodes[i].Host().Peerstore().ClearAddrs(dhtClientNodes[j].Host().ID()) + dhtClientNodes[i].Host().Peerstore().ClearAddrs(dhtClientNodes[j].ID()) // Try to create a stream from client i to client j. This should resort to a DHT // lookup since client i does not know client j's address. unittest.RequireReturnsBefore(t, func() { - err = dhtClientNodes[i].OpenProtectedStream(ctx, dhtClientNodes[j].Host().ID(), t.Name(), func(stream network.Stream) error { + err = dhtClientNodes[i].OpenProtectedStream(ctx, dhtClientNodes[j].ID(), t.Name(), func(stream network.Stream) error { // do nothing require.NotNil(t, stream) return nil @@ -138,7 +138,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { ids := append(serverIDs, clientIDs...) nodes := append(dhtServerNodes, dhtClientNodes...) for i, node := range nodes { - idProvider.On("ByPeerID", node.Host().ID()).Return(&ids[i], true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(&ids[i], true).Maybe() } p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) @@ -148,7 +148,7 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { // This has to be done before subscribing to any topic, otherwise the node gives up on advertising // its topics of interest and becomes undiscoverable by other nodes // (see: https://github.com/libp2p/go-libp2p-pubsub/issues/442) - dhtServerAddr := peer.AddrInfo{ID: dhtServerNode.Host().ID(), Addrs: dhtServerNode.Host().Addrs()} + dhtServerAddr := peer.AddrInfo{ID: dhtServerNode.ID(), Addrs: dhtServerNode.Host().Addrs()} for _, clientNode := range dhtClientNodes { err := clientNode.Host().Connect(ctx, dhtServerAddr) require.NoError(t, err) @@ -179,14 +179,14 @@ func TestPubSubWithDHTDiscovery(t *testing.T) { require.NotNil(t, msg) assert.Equal(t, messageScope.Proto().Payload, msg.Data) ch <- nodeID - }(s, n.Host().ID()) + }(s, n.ID()) } // fullyConnectedGraph checks that each node is directly connected to all the other nodes fullyConnectedGraph := func() bool { for i := 0; i < len(nodes); i++ { for j := i + 1; j < len(nodes); j++ { - if nodes[i].Host().Network().Connectedness(nodes[j].Host().ID()) == network.NotConnected { + if nodes[i].Host().Network().Connectedness(nodes[j].ID()) == network.NotConnected { return false } } @@ -211,8 +211,8 @@ loop: case <-time.After(3 * time.Second): var missing peer.IDSlice for _, n := range nodes { - if _, found := recv[n.Host().ID()]; !found { - missing = append(missing, n.Host().ID()) + if _, found := recv[n.ID()]; !found { + missing = append(missing, n.ID()) } } assert.Failf(t, "messages not received by some nodes", "%+v", missing) diff --git a/network/p2p/p2pnode/disallow_listing_test.go b/network/p2p/p2pnode/disallow_listing_test.go index 566b2d6ec5e..aa36c81ef83 100644 --- a/network/p2p/p2pnode/disallow_listing_test.go +++ b/network/p2p/p2pnode/disallow_listing_test.go @@ -49,19 +49,19 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { // the libp2p node. So, here, we don't need to do anything except just enabling the connection gater. return nil }))) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity1, true).Maybe() - peerIDSlice = append(peerIDSlice, node1.Host().ID()) + idProvider.On("ByPeerID", node1.ID()).Return(&identity1, true).Maybe() + peerIDSlice = append(peerIDSlice, node1.ID()) // node 2 is the node that will be disallow-listed by node 1. node2, identity2 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider) - idProvider.On("ByPeerID", node2.Host().ID()).Return(&identity2, true).Maybe() - peerIDSlice = append(peerIDSlice, node2.Host().ID()) + idProvider.On("ByPeerID", node2.ID()).Return(&identity2, true).Maybe() + peerIDSlice = append(peerIDSlice, node2.ID()) // node 3 is the node that will be connected to node 1 (to ensure that node 1 is still able to connect to other nodes // after disallow-listing node 2). node3, identity3 := p2ptest.NodeFixture(t, sporkID, t.Name(), idProvider) - idProvider.On("ByPeerID", node3.Host().ID()).Return(&identity3, true).Maybe() - peerIDSlice = append(peerIDSlice, node3.Host().ID()) + idProvider.On("ByPeerID", node3.ID()).Return(&identity3, true).Maybe() + peerIDSlice = append(peerIDSlice, node3.ID()) nodes := []p2p.LibP2PNode{node1, node2, node3} ids := flow.IdentityList{&identity1, &identity2, &identity3} @@ -75,7 +75,7 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { p2ptest.RequireConnectedEventually(t, nodes, 100*time.Millisecond, 2*time.Second) // phase-1: node 1 disallow-lists node 2. - node1.OnDisallowListNotification(node2.Host().ID(), network.DisallowListedCauseAlsp) + node1.OnDisallowListNotification(node2.ID(), network.DisallowListedCauseAlsp) // eventually node 1 should be disconnected from node 2 while other nodes should remain connected. // we choose a timeout of 2 seconds because peer manager updates peers every 1 second. @@ -91,7 +91,7 @@ func TestDisconnectingFromDisallowListedNode(t *testing.T) { p2ptest.EnsureNotConnectedBetweenGroups(t, ctx, []p2p.LibP2PNode{node1}, []p2p.LibP2PNode{node2}) // phase-2: now we allow-list node 1 back - node1.OnAllowListNotification(node2.Host().ID(), network.DisallowListedCauseAlsp) + node1.OnAllowListNotification(node2.ID(), network.DisallowListedCauseAlsp) // eventually node 1 should be connected to node 2 again, hence all nodes should be connected to each other. // we choose a timeout of 5 seconds because peer manager updates peers every 1 second and we need to wait for diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index acf7ab74aaa..345d3f09c01 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -178,7 +178,7 @@ func TestConnGater(t *testing.T) { } return nil }))) - idProvider.On("ByPeerID", node1.Host().ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", node1.ID()).Return(&identity1, true).Maybe() p2ptest.StartNode(t, signalerCtx, node1, 100*time.Millisecond) defer p2ptest.StopNode(t, node1, cancel, 100*time.Millisecond) @@ -197,7 +197,7 @@ func TestConnGater(t *testing.T) { } return nil }))) - idProvider.On("ByPeerID", node2.Host().ID()).Return(&identity2, + idProvider.On("ByPeerID", node2.ID()).Return(&identity2, true).Maybe() @@ -369,8 +369,8 @@ func TestCreateStream_SinglePeerDial(t *testing.T) { p2ptest.WithCreateStreamRetryDelay(10*time.Millisecond), p2ptest.WithLogger(logger)) - idProvider.On("ByPeerID", sender.Host().ID()).Return(&id1, true).Maybe() - idProvider.On("ByPeerID", receiver.Host().ID()).Return(&id2, true).Maybe() + idProvider.On("ByPeerID", sender.ID()).Return(&id1, true).Maybe() + idProvider.On("ByPeerID", receiver.ID()).Return(&id2, true).Maybe() p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}, 100*time.Millisecond) defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel, 100*time.Millisecond) @@ -380,14 +380,14 @@ func TestCreateStream_SinglePeerDial(t *testing.T) { // attempt to create two concurrent streams go func() { defer wg.Done() - err := sender.OpenProtectedStream(ctx, receiver.Host().ID(), t.Name(), func(stream network.Stream) error { + err := sender.OpenProtectedStream(ctx, receiver.ID(), t.Name(), func(stream network.Stream) error { return nil }) require.Error(t, err) }() go func() { defer wg.Done() - err := sender.OpenProtectedStream(ctx, receiver.Host().ID(), t.Name(), func(stream network.Stream) error { + err := sender.OpenProtectedStream(ctx, receiver.ID(), t.Name(), func(stream network.Stream) error { return nil }) require.Error(t, err) @@ -432,8 +432,8 @@ func TestCreateStream_InboundConnResourceLimit(t *testing.T) { p2ptest.WithDefaultResourceManager(), p2ptest.WithCreateStreamRetryDelay(10*time.Millisecond)) - idProvider.On("ByPeerID", sender.Host().ID()).Return(&id1, true).Maybe() - idProvider.On("ByPeerID", receiver.Host().ID()).Return(&id2, true).Maybe() + idProvider.On("ByPeerID", sender.ID()).Return(&id1, true).Maybe() + idProvider.On("ByPeerID", receiver.ID()).Return(&id2, true).Maybe() p2ptest.StartNodes(t, signalerCtx, []p2p.LibP2PNode{sender, receiver}, 100*time.Millisecond) defer p2ptest.StopNodes(t, []p2p.LibP2PNode{sender, receiver}, cancel, 100*time.Millisecond) @@ -452,14 +452,14 @@ func TestCreateStream_InboundConnResourceLimit(t *testing.T) { allStreamsCreated.Add(1) go func() { defer allStreamsCreated.Done() - _, err := sender.Host().NewStream(ctx, receiver.Host().ID(), defaultProtocolID) + _, err := sender.Host().NewStream(ctx, receiver.ID(), defaultProtocolID) require.NoError(t, err) }() } unittest.RequireReturnsBefore(t, allStreamsCreated.Wait, 2*time.Second, "could not create streams on time") - require.Len(t, receiver.Host().Network().ConnsToPeer(sender.Host().ID()), 1) - actualNumOfStreams := p2putils.CountStream(sender.Host(), receiver.Host().ID(), defaultProtocolID, network.DirOutbound) + require.Len(t, receiver.Host().Network().ConnsToPeer(sender.ID()), 1) + actualNumOfStreams := p2putils.CountStream(sender.Host(), receiver.ID(), defaultProtocolID, network.DirOutbound) require.Equal(t, expectedNumOfStreams, int64(actualNumOfStreams), fmt.Sprintf("expected to create %d number of streams got %d", expectedNumOfStreams, actualNumOfStreams)) } @@ -506,7 +506,7 @@ func ensureSinglePairwiseConnection(t *testing.T, nodes []p2p.LibP2PNode) { if this == other { continue } - require.Len(t, this.Host().Network().ConnsToPeer(other.Host().ID()), 1) + require.Len(t, this.Host().Network().ConnsToPeer(other.ID()), 1) } } } diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index a65cbd0d3fc..adb35b1f768 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -164,7 +164,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol id2 := identities[1] // Assert that there is no outbound stream to the target yet - require.Equal(t, 0, p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), protocolID, network.DirOutbound)) + require.Equal(t, 0, p2putils.CountStream(nodes[0].Host(), nodes[1].ID(), protocolID, network.DirOutbound)) // Now attempt to create another 100 outbound stream to the same destination by calling CreateStream streamCount := 100 @@ -190,7 +190,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol } require.Eventually(t, func() bool { - return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), protocolID, network.DirOutbound) + return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].ID(), protocolID, network.DirOutbound) }, 5*time.Second, 100*time.Millisecond, "could not create streams on time") // checks that the number of connections is 1 despite the number of streams; i.e., all streams are created on the same connection @@ -224,7 +224,7 @@ func TestCreateStream_FallBack(t *testing.T) { identities := []flow.Identity{thisID, otherId} nodes := []p2p.LibP2PNode{thisNode, otherNode} for i, node := range nodes { - idProvider.On("ByPeerID", node.Host().ID()).Return(&identities[i], true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(&identities[i], true).Maybe() } p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) @@ -232,8 +232,8 @@ func TestCreateStream_FallBack(t *testing.T) { // Assert that there is no outbound stream to the target yet (neither default nor preferred) defaultProtocolId := protocols.FlowProtocolID(sporkId) preferredProtocolId := protocols.FlowGzipProtocolId(sporkId) - require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), defaultProtocolId, network.DirOutbound)) - require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), preferredProtocolId, network.DirOutbound)) + require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.ID(), defaultProtocolId, network.DirOutbound)) + require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.ID(), preferredProtocolId, network.DirOutbound)) // Now attempt to create another 100 outbound stream to the same destination by calling CreateStream streamCount := 10 @@ -262,11 +262,11 @@ func TestCreateStream_FallBack(t *testing.T) { // wait for the stream to be created on the default protocol id. require.Eventually(t, func() bool { - return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].Host().ID(), defaultProtocolId, network.DirOutbound) + return streamCount == p2putils.CountStream(nodes[0].Host(), nodes[1].ID(), defaultProtocolId, network.DirOutbound) }, 5*time.Second, 100*time.Millisecond, "could not create streams on time") // no stream must be created on the preferred protocol id - require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.Host().ID(), preferredProtocolId, network.DirOutbound)) + require.Equal(t, 0, p2putils.CountStream(thisNode.Host(), otherNode.ID(), preferredProtocolId, network.DirOutbound)) // checks that the number of connections is 1 despite the number of streams; i.e., all streams are created on the same connection require.Len(t, nodes[0].Host().Network().Conns(), 1) @@ -430,7 +430,7 @@ func testUnicastOverStream(t *testing.T, opts ...p2ptest.NodeFixtureParameterOpt ids := flow.IdentityList{&id1, &id2} nodes := []p2p.LibP2PNode{node1, node2} for i, node := range nodes { - idProvider.On("ByPeerID", node.Host().ID()).Return(ids[i], true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(ids[i], true).Maybe() } p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) @@ -479,7 +479,7 @@ func TestUnicastOverStream_Fallback(t *testing.T) { ids := flow.IdentityList{&id1, &id2} nodes := []p2p.LibP2PNode{node1, node2} for i, node := range nodes { - idProvider.On("ByPeerID", node.Host().ID()).Return(ids[i], true).Maybe() + idProvider.On("ByPeerID", node.ID()).Return(ids[i], true).Maybe() } p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) diff --git a/network/p2p/scoring/scoring_test.go b/network/p2p/scoring/scoring_test.go index 9aa6644005c..3f22174194f 100644 --- a/network/p2p/scoring/scoring_test.go +++ b/network/p2p/scoring/scoring_test.go @@ -137,7 +137,7 @@ func TestInvalidCtrlMsgScoringIntegration(t *testing.T) { // now simulates node2 spamming node1 with invalid gossipsub control messages. for i := 0; i < 30; i++ { inspectorSuite1.consumer.OnInvalidControlMessageNotification(&p2p.InvCtrlMsgNotif{ - PeerID: node2.Host().ID(), + PeerID: node2.ID(), MsgType: p2pmsg.ControlMessageTypes()[rand.Intn(len(p2pmsg.ControlMessageTypes()))], Count: 1, Err: fmt.Errorf("invalid control message"), diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index ec43a039ab1..8c6677c54c4 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -68,7 +68,7 @@ func TestFilterSubscribe(t *testing.T) { // check that node1 and node2 don't accept unstakedNode as a peer require.Never(t, func() bool { for _, pid := range node1.ListPeers(badTopic.String()) { - if pid == unstakedNode.Host().ID() { + if pid == unstakedNode.ID() { return true } } diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 424037b49ed..af730179d19 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -489,11 +489,11 @@ func TryConnectionAndEnsureConnected(t *testing.T, ctx context.Context, nodes [] if node == other { continue } - require.NoError(t, node.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.Host().ID()))) + require.NoError(t, node.Host().Connect(ctx, other.Host().Peerstore().PeerInfo(other.ID()))) // the other node should be connected to this node - require.Equal(t, node.Host().Network().Connectedness(other.Host().ID()), network.Connected) + require.Equal(t, node.Host().Network().Connectedness(other.ID()), network.Connected) // at least one connection should be established - require.True(t, len(node.Host().Network().ConnsToPeer(other.Host().ID())) > 0) + require.True(t, len(node.Host().Network().ConnsToPeer(other.ID())) > 0) } } } @@ -511,10 +511,10 @@ func RequireConnectedEventually(t *testing.T, nodes []p2p.LibP2PNode, tick time. if node == other { continue } - if node.Host().Network().Connectedness(other.Host().ID()) != network.Connected { + if node.Host().Network().Connectedness(other.ID()) != network.Connected { return false } - if len(node.Host().Network().ConnsToPeer(other.Host().ID())) == 0 { + if len(node.Host().Network().ConnsToPeer(other.ID())) == 0 { return false } } @@ -534,10 +534,10 @@ func RequireEventuallyNotConnected(t *testing.T, groupA []p2p.LibP2PNode, groupB require.Eventually(t, func() bool { for _, node := range groupA { for _, other := range groupB { - if node.Host().Network().Connectedness(other.Host().ID()) == network.Connected { + if node.Host().Network().Connectedness(other.ID()) == network.Connected { return false } - if len(node.Host().Network().ConnsToPeer(other.Host().ID())) > 0 { + if len(node.Host().Network().ConnsToPeer(other.ID())) > 0 { return false } } @@ -554,7 +554,7 @@ func EnsureStreamCreationInBothDirections(t *testing.T, ctx context.Context, nod continue } // stream creation should pass without error - err := this.OpenProtectedStream(ctx, other.Host().ID(), t.Name(), func(stream network.Stream) error { + err := this.OpenProtectedStream(ctx, other.ID(), t.Name(), func(stream network.Stream) error { // do nothing require.NotNil(t, stream) return nil diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 78f87d27d31..e0553314673 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -69,7 +69,7 @@ func TestCrosstalkPreventionOnNetworkKeyChange(t *testing.T) { defer p2ptest.StopNode(t, node1, cancel1, 100*time.Millisecond) t.Logf(" %s node started on %s", id1.NodeID.String(), id1.Address) - t.Logf("libp2p ID for %s: %s", id1.NodeID.String(), node1.Host().ID()) + t.Logf("libp2p ID for %s: %s", id1.NodeID.String(), node1.ID()) // create and start node 2 on localhost and random port node2key := p2ptest.NetworkingKeyFixtures(t) diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 0245985dc27..07d5a3da2e2 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -45,8 +45,8 @@ func TestTopicValidator_Unstaked(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -124,8 +124,8 @@ func TestTopicValidator_PublicChannel(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -194,8 +194,8 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -256,8 +256,8 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -326,9 +326,9 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) an1, identity3 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleAccess)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() - idProvider.On("ByPeerID", an1.Host().ID()).Return(&identity3, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", an1.ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2, an1} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -343,7 +343,7 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { violation := &network.Violation{ Identity: &identity3, - PeerID: an1.Host().ID().String(), + PeerID: an1.ID().String(), OriginID: identity3.NodeID, MsgType: "*messages.BlockProposal", Channel: channel, @@ -461,8 +461,8 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", idProvider, p2ptest.WithRole(flow.RoleConsensus)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", idProvider, p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -544,9 +544,9 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", idProvider, p2ptest.WithRole(flow.RoleConsensus)) sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", idProvider, p2ptest.WithRole(flow.RoleConsensus)) an1, identity3 := p2ptest.NodeFixture(t, sporkId, "access_1", idProvider, p2ptest.WithRole(flow.RoleAccess)) - idProvider.On("ByPeerID", sn1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", sn2.Host().ID()).Return(&identity2, true).Maybe() - idProvider.On("ByPeerID", an1.Host().ID()).Return(&identity3, true).Maybe() + idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", an1.ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{sn1, sn2, an1} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) @@ -659,9 +659,9 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { ln1, identity1 := p2ptest.NodeFixture(t, sporkId, "collection_1", idProvider, p2ptest.WithRole(flow.RoleCollection)) ln2, identity2 := p2ptest.NodeFixture(t, sporkId, "collection_2", idProvider, p2ptest.WithRole(flow.RoleCollection)) ln3, identity3 := p2ptest.NodeFixture(t, sporkId, "collection_3", idProvider, p2ptest.WithRole(flow.RoleCollection)) - idProvider.On("ByPeerID", ln1.Host().ID()).Return(&identity1, true).Maybe() - idProvider.On("ByPeerID", ln2.Host().ID()).Return(&identity2, true).Maybe() - idProvider.On("ByPeerID", ln3.Host().ID()).Return(&identity3, true).Maybe() + idProvider.On("ByPeerID", ln1.ID()).Return(&identity1, true).Maybe() + idProvider.On("ByPeerID", ln2.ID()).Return(&identity2, true).Maybe() + idProvider.On("ByPeerID", ln3.ID()).Return(&identity3, true).Maybe() nodes := []p2p.LibP2PNode{ln1, ln2, ln3} p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) defer p2ptest.StopNodes(t, nodes, cancel, 100*time.Millisecond) diff --git a/network/p2p/tracer/gossipSubMeshTracer_test.go b/network/p2p/tracer/gossipSubMeshTracer_test.go index 6e7bc77e0f6..346914dcf46 100644 --- a/network/p2p/tracer/gossipSubMeshTracer_test.go +++ b/network/p2p/tracer/gossipSubMeshTracer_test.go @@ -86,7 +86,7 @@ func TestGossipSubMeshTracer(t *testing.T) { p2ptest.WithGossipSubTracer(meshTracer), p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", tracerNode.Host().ID()).Return(&tracerId, true).Maybe() + idProvider.On("ByPeerID", tracerNode.ID()).Return(&tracerId, true).Maybe() otherNode1, otherId1 := p2ptest.NodeFixture( t, @@ -94,7 +94,7 @@ func TestGossipSubMeshTracer(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", otherNode1.Host().ID()).Return(&otherId1, true).Maybe() + idProvider.On("ByPeerID", otherNode1.ID()).Return(&otherId1, true).Maybe() otherNode2, otherId2 := p2ptest.NodeFixture( t, @@ -102,7 +102,7 @@ func TestGossipSubMeshTracer(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", otherNode2.Host().ID()).Return(&otherId2, true).Maybe() + idProvider.On("ByPeerID", otherNode2.ID()).Return(&otherId2, true).Maybe() // create a node that does not have a valid flow identity to test whether mesh tracer logs a warning. unknownNode, unknownId := p2ptest.NodeFixture( @@ -111,7 +111,7 @@ func TestGossipSubMeshTracer(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", unknownNode.Host().ID()).Return(nil, false).Maybe() + idProvider.On("ByPeerID", unknownNode.ID()).Return(nil, false).Maybe() nodes := []p2p.LibP2PNode{tracerNode, otherNode1, otherNode2, unknownNode} ids := flow.IdentityList{&tracerId, &otherId1, &otherId2, &unknownId} @@ -153,14 +153,14 @@ func TestGossipSubMeshTracer(t *testing.T) { assert.Eventually(t, func() bool { topic1MeshSize := 0 for _, peer := range meshTracer.GetMeshPeers(topic1.String()) { - if peer == otherNode1.Host().ID() || peer == otherNode2.Host().ID() { + if peer == otherNode1.ID() || peer == otherNode2.ID() { topic1MeshSize++ } } topic2MeshSize := 0 for _, peer := range meshTracer.GetMeshPeers(topic2.String()) { - if peer == otherNode1.Host().ID() { + if peer == otherNode1.ID() { topic2MeshSize++ } } @@ -185,14 +185,14 @@ func TestGossipSubMeshTracer(t *testing.T) { assert.Eventually(t, func() bool { // eventually, the tracerNode should not have the other node in its mesh for topic1. for _, peer := range meshTracer.GetMeshPeers(topic1.String()) { - if peer == otherNode1.Host().ID() || peer == otherNode2.Host().ID() || peer == unknownNode.Host().ID() { + if peer == otherNode1.ID() || peer == otherNode2.ID() || peer == unknownNode.ID() { return false } } // but the tracerNode should still have the otherNode1 in its mesh for topic2. for _, peer := range meshTracer.GetMeshPeers(topic2.String()) { - if peer != otherNode1.Host().ID() { + if peer != otherNode1.ID() { return false } } diff --git a/network/p2p/tracer/gossipSubScoreTracer_test.go b/network/p2p/tracer/gossipSubScoreTracer_test.go index 2a3ea623eb0..59ef113adfb 100644 --- a/network/p2p/tracer/gossipSubScoreTracer_test.go +++ b/network/p2p/tracer/gossipSubScoreTracer_test.go @@ -123,7 +123,7 @@ func TestGossipSubScoreTracer(t *testing.T) { }), p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", tracerNode.Host().ID()).Return(&tracerId, true).Maybe() + idProvider.On("ByPeerID", tracerNode.ID()).Return(&tracerId, true).Maybe() consensusNode, consensusId := p2ptest.NodeFixture( t, @@ -131,7 +131,7 @@ func TestGossipSubScoreTracer(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) - idProvider.On("ByPeerID", consensusNode.Host().ID()).Return(&consensusId, true).Maybe() + idProvider.On("ByPeerID", consensusNode.ID()).Return(&consensusId, true).Maybe() accessNode, accessId := p2ptest.NodeFixture( t, @@ -139,7 +139,7 @@ func TestGossipSubScoreTracer(t *testing.T) { t.Name(), idProvider, p2ptest.WithRole(flow.RoleAccess)) - idProvider.On("ByPeerID", accessNode.Host().ID()).Return(&accessId, true).Maybe() + idProvider.On("ByPeerID", accessNode.ID()).Return(&accessId, true).Maybe() nodes := []p2p.LibP2PNode{tracerNode, consensusNode, accessNode} ids := flow.IdentityList{&tracerId, &consensusId, &accessId} @@ -189,50 +189,50 @@ func TestGossipSubScoreTracer(t *testing.T) { assert.Eventually(t, func() bool { // we expect the tracerNode to have the consensusNodes and accessNodes with the correct app scores. exposer := tracerNode.PeerScoreExposer() - score, ok := exposer.GetAppScore(consensusNode.Host().ID()) + score, ok := exposer.GetAppScore(consensusNode.ID()) if !ok || score != consensusScore { return false } - score, ok = exposer.GetAppScore(accessNode.Host().ID()) + score, ok = exposer.GetAppScore(accessNode.ID()) if !ok || score != accessScore { return false } // we expect the tracerNode to have the consensusNodes and accessNodes with a non-zero score. - score, ok = exposer.GetScore(consensusNode.Host().ID()) + score, ok = exposer.GetScore(consensusNode.ID()) if !ok || score == 0 { return false } - score, ok = exposer.GetScore(accessNode.Host().ID()) + score, ok = exposer.GetScore(accessNode.ID()) if !ok || score == 0 { return false } // we expect the tracerNode to have the consensusNodes and accessNodes with an existing behaviour score and ip score. - _, ok = exposer.GetBehaviourPenalty(consensusNode.Host().ID()) + _, ok = exposer.GetBehaviourPenalty(consensusNode.ID()) if !ok { return false } - _, ok = exposer.GetIPColocationFactor(consensusNode.Host().ID()) + _, ok = exposer.GetIPColocationFactor(consensusNode.ID()) if !ok { return false } - _, ok = exposer.GetBehaviourPenalty(accessNode.Host().ID()) + _, ok = exposer.GetBehaviourPenalty(accessNode.ID()) if !ok { return false } - _, ok = exposer.GetIPColocationFactor(accessNode.Host().ID()) + _, ok = exposer.GetIPColocationFactor(accessNode.ID()) if !ok { return false } // we expect the tracerNode to have the consensusNodes and accessNodes with an existing mesh score. - consensusMeshScores, ok := exposer.GetTopicScores(consensusNode.Host().ID()) + consensusMeshScores, ok := exposer.GetTopicScores(consensusNode.ID()) if !ok { return false } @@ -241,7 +241,7 @@ func TestGossipSubScoreTracer(t *testing.T) { return false } - accessMeshScore, ok := exposer.GetTopicScores(accessNode.Host().ID()) + accessMeshScore, ok := exposer.GetTopicScores(accessNode.ID()) if !ok { return false } diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index d582014776d..64b9b52e60a 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -119,7 +119,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { suite.Require().Eventually(func() bool { for i, libp2pNode := range nodes { for j := i + 1; j < suite.numNodes; j++ { - connected, err := libp2pNode.IsConnected(nodes[j].Host().ID()) + connected, err := libp2pNode.IsConnected(nodes[j].ID()) require.NoError(suite.T(), err) if !connected { return false diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index e34822cd4c9..a40e958e8db 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -332,7 +332,7 @@ func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, // we don't want to check if a node is connected to itself continue } - connected, err := thisNode.IsConnected(node.Host().ID()) + connected, err := thisNode.IsConnected(node.ID()) require.NoError(t, err) if connected { connections++ @@ -352,7 +352,7 @@ func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNo t := suite.T() require.Eventuallyf(t, func() bool { for _, node := range allNodes { - connected, err := thisNode.IsConnected(node.Host().ID()) + connected, err := thisNode.IsConnected(node.ID()) require.NoError(t, err) if connected { return false From 0ab2088f26055229dcefa001f840ee4336022345 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:48:21 -0400 Subject: [PATCH 718/815] renames add peer --- module/upstream/upstream_connector.go | 2 +- network/p2p/libp2pNode.go | 5 +++-- network/p2p/mock/lib_p2_p_node.go | 2 +- network/p2p/p2pnode/libp2pNode.go | 2 +- network/p2p/p2pnode/libp2pNode_test.go | 4 ++-- .../subscription/subscription_filter_test.go | 4 ++-- network/p2p/test/fixtures.go | 2 +- network/p2p/test/sporking_test.go | 2 +- network/p2p/test/topic_validator_test.go | 22 +++++++++---------- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/module/upstream/upstream_connector.go b/module/upstream/upstream_connector.go index 36eb362e4f2..1f052f667a6 100644 --- a/module/upstream/upstream_connector.go +++ b/module/upstream/upstream_connector.go @@ -102,7 +102,7 @@ func (connector *upstreamConnector) connect(ctx context.Context, bootstrapPeer f } // try and connect to the bootstrap server - return connector.unstakedNode.AddPeer(ctx, peerAddrInfo) + return connector.unstakedNode.ConnectToPeerAddrInfo(ctx, peerAddrInfo) } func (connector *upstreamConnector) Done() <-chan struct{} { diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 9325315e984..d4c6f6124fe 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -48,8 +48,9 @@ type LibP2PNode interface { Start(ctx irrecoverable.SignalerContext) // Stop terminates the libp2p node. Stop() error - // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. - AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error + // ConnectToPeerAddrInfo connects to the peer with the given peer address information. + // This method is used to connect to a peer that is not in the peer store. + ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error // RemovePeer closes the connection with the peer. RemovePeer(peerID peer.ID) error // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index d68776766c6..adde599654a 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -44,7 +44,7 @@ func (_m *LibP2PNode) ActiveClustersChanged(_a0 flow.ChainIDList) { } // AddPeer provides a mock function with given fields: ctx, peerInfo -func (_m *LibP2PNode) AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error { +func (_m *LibP2PNode) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { ret := _m.Called(ctx, peerInfo) var r0 error diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index bda7a16d27e..995def8bde4 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -148,7 +148,7 @@ func (n *Node) Stop() error { // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. // All errors returned from this function can be considered benign. -func (n *Node) AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error { +func (n *Node) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { return n.host.Connect(ctx, peerInfo) } diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 345d3f09c01..714952b617c 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -123,7 +123,7 @@ func TestAddPeers(t *testing.T) { for _, identity := range identities[1:] { peerInfo, err := utils.PeerAddressInfo(*identity) require.NoError(t, err) - require.NoError(t, nodes[0].AddPeer(ctx, peerInfo)) + require.NoError(t, nodes[0].ConnectToPeerAddrInfo(ctx, peerInfo)) } // Checks if both of the other nodes have been added as peers to the first node @@ -146,7 +146,7 @@ func TestRemovePeers(t *testing.T) { // add nodes two and three to the first node as its peers for _, pInfo := range peerInfos[1:] { - require.NoError(t, nodes[0].AddPeer(ctx, pInfo)) + require.NoError(t, nodes[0].ConnectToPeerAddrInfo(ctx, pInfo)) } // check if all other nodes have been added as peers to the first node diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 8c6677c54c4..c46436ce7d6 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -42,8 +42,8 @@ func TestFilterSubscribe(t *testing.T) { unstakedKey := unittest.NetworkingPrivKeyFixture() unstakedNode := p2pfixtures.CreateNode(t, unstakedKey, sporkId, zerolog.Nop(), ids) - require.NoError(t, node1.AddPeer(context.TODO(), *host.InfoFromHost(node2.Host()))) - require.NoError(t, node1.AddPeer(context.TODO(), *host.InfoFromHost(unstakedNode.Host()))) + require.NoError(t, node1.ConnectToPeerAddrInfo(context.TODO(), *host.InfoFromHost(node2.Host()))) + require.NoError(t, node1.ConnectToPeerAddrInfo(context.TODO(), *host.InfoFromHost(unstakedNode.Host()))) badTopic := channels.TopicFromChannel(channels.SyncCommittee, sporkId) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index af730179d19..1658e7c770e 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -476,7 +476,7 @@ func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.Li } otherPInfo, err := utils.PeerAddressInfo(*ids[i]) require.NoError(t, err) - require.NoError(t, node.AddPeer(ctx, otherPInfo)) + require.NoError(t, node.ConnectToPeerAddrInfo(ctx, otherPInfo)) } } } diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index e0553314673..631f936a710 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -239,7 +239,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { require.NoError(t, err) // add node 2 as a peer of node 1 - err = node1.AddPeer(ctx, pInfo2) + err = node1.ConnectToPeerAddrInfo(ctx, pInfo2) require.NoError(t, err) // let the two nodes form the mesh diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index 07d5a3da2e2..0c7d2fd809d 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -78,7 +78,7 @@ func TestTopicValidator_Unstaked(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) // sn1 will subscribe with is staked callback that should force the TopicValidator to drop the message received from sn2 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, isStaked)) @@ -139,7 +139,7 @@ func TestTopicValidator_PublicChannel(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) // sn1 & sn2 will subscribe with unauthenticated callback to allow it to send and receive unauthenticated messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -208,7 +208,7 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -269,7 +269,7 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -371,8 +371,8 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { // node1 is connected to node2, and the an1 is connected to node1 // an1 <-> sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) - require.NoError(t, an1.AddPeer(ctx, pInfo1)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, an1.ConnectToPeerAddrInfo(ctx, pInfo1)) // sn1 and sn2 subscribe to the topic with the topic validator sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -496,7 +496,7 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -582,8 +582,8 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // node1 is connected to node2, and the an1 is connected to node1 // an1 <-> sn1 <-> sn2 - require.NoError(t, sn1.AddPeer(ctx, pInfo2)) - require.NoError(t, an1.AddPeer(ctx, pInfo1)) + require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, an1.ConnectToPeerAddrInfo(ctx, pInfo1)) // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -695,8 +695,8 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { require.NoError(t, err) // ln3 <-> sn1 <-> sn2 - require.NoError(t, ln1.AddPeer(ctx, pInfo2)) - require.NoError(t, ln3.AddPeer(ctx, pInfo1)) + require.NoError(t, ln1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, ln3.ConnectToPeerAddrInfo(ctx, pInfo1)) sub1, err := ln1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) require.NoError(t, err) From 7b204d0542fd5e5aa8f7cd19dad4f5354b67920c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:50:11 -0400 Subject: [PATCH 719/815] fixes merge issues --- network/p2p/p2pnode/libp2pStream_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 4869941a186..7bd4e0eb4b3 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -159,7 +159,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol idProvider, p2ptest.WithPreferredUnicasts(unicasts)) idProvider.SetIdentities(identities) - p2ptest.StartNodes(t, signalerCtx, nodes, 100*time.Millisecond) + p2ptest.StartNodes(t, signalerCtx, nodes) id2 := identities[1] @@ -197,7 +197,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol require.Len(t, nodes[0].Host().Network().Conns(), 1) // we don't use defer as the moment we stop the nodes, the streams will be closed, and we want to assess the number of streams - p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StopNodes(t, nodes, cancel) // wait for all streams to be closed unittest.RequireReturnsBefore(t, allStreamsClosedWg.Wait, 1*time.Second, "could not close streams on time") @@ -227,7 +227,7 @@ func TestCreateStream_FallBack(t *testing.T) { idProvider.On("ByPeerID", node.ID()).Return(&identities[i], true).Maybe() } - p2ptest.StartNodes(t, signalerCtx, nodes, 1*time.Second) + p2ptest.StartNodes(t, signalerCtx, nodes) // Assert that there is no outbound stream to the target yet (neither default nor preferred) defaultProtocolId := protocols.FlowProtocolID(sporkId) From 374408acdfc7b46ee108303d643f43b945c8c1a9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 16:51:35 -0400 Subject: [PATCH 720/815] fixes merge issues --- .../validation_inspector_test.go | 21 ++++++++++++------- network/p2p/p2pnode/libp2pStream_test.go | 2 +- network/p2p/scoring/app_score_test.go | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index 334dd351ace..b0932ae22f6 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -623,7 +623,8 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.. + ID(), notification.PeerID) require.True(t, validation.IsDuplicateFoundErr(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { @@ -1131,7 +1132,8 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { return func(args mockery.Arguments) { notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.. + ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) cacheMissThresholdNotifCount.Inc() @@ -1173,8 +1175,10 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.. + ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.. + ID()).Return(&spammer.SpammerId, true).Maybe() messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) @@ -1228,7 +1232,8 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) defer close(done) notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.Host().ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.. + ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) } @@ -1266,8 +1271,10 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.Host().ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.Host().ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.. + ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.. + ID()).Return(&spammer.SpammerId, true).Maybe() messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 7bd4e0eb4b3..6d9384cff01 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -272,7 +272,7 @@ func TestCreateStream_FallBack(t *testing.T) { require.Len(t, nodes[0].Host().Network().Conns(), 1) // we don't use defer as the moment we stop the nodes, the streams will be closed, and we want to assess the number of streams - p2ptest.StopNodes(t, nodes, cancel, 1*time.Second) + p2ptest.StopNodes(t, nodes, cancel) // wait for all streams to be closed unittest.RequireReturnsBefore(t, allStreamsClosedWg.Wait, 1*time.Second, "could not close streams on time") diff --git a/network/p2p/scoring/app_score_test.go b/network/p2p/scoring/app_score_test.go index 771a442a213..39a0a405e8e 100644 --- a/network/p2p/scoring/app_score_test.go +++ b/network/p2p/scoring/app_score_test.go @@ -216,7 +216,7 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi case <-ticker.C: con1BlockTopicPeers := con1NodeTracer.GetMeshPeers(blockTopic.String()) for _, p := range con1BlockTopicPeers { - if p == con2Node.Host().ID() { + if p == con2Node.ID() { con2HasCon1 = true break // con1 has con2 in its mesh, break out of the current loop } @@ -224,7 +224,7 @@ func TestFullGossipSubConnectivityAmongHonestNodesWithMaliciousMajority(t *testi con2BlockTopicPeers := con2NodeTracer.GetMeshPeers(blockTopic.String()) for _, p := range con2BlockTopicPeers { - if p == con1Node.Host().ID() { + if p == con1Node.ID() { con1HasCon2 = true break // con2 has con1 in its mesh, break out of the current loop } From d8c91492899e274c837fa51d2bfa1d8e2a8edea6 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 17:22:50 -0400 Subject: [PATCH 721/815] fixes mocks --- network/p2p/mock/lib_p2_p_node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index adde599654a..5d0abf8318b 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -43,7 +43,7 @@ func (_m *LibP2PNode) ActiveClustersChanged(_a0 flow.ChainIDList) { _m.Called(_a0) } -// AddPeer provides a mock function with given fields: ctx, peerInfo +// ConnectToPeerAddrInfo provides a mock function with given fields: ctx, peerInfo func (_m *LibP2PNode) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { ret := _m.Called(ctx, peerInfo) From 24c7c32c341fa186d6de29fbe3a0602df093a95e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 17:25:08 -0400 Subject: [PATCH 722/815] fixes tests --- .../validation_inspector_test.go | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go index b0932ae22f6..155432ba3ba 100644 --- a/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go +++ b/insecure/integration/functional/test/gossipsub/rpc_inspector/validation_inspector_test.go @@ -623,8 +623,7 @@ func TestValidationInspector_DuplicateTopicId_Detection(t *testing.T) { count.Inc() notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.. - ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.True(t, validation.IsDuplicateFoundErr(notification.Err)) require.Equal(t, messageCount, notification.Count) switch notification.MsgType { @@ -1132,8 +1131,7 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { return func(args mockery.Arguments) { notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.. - ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) require.True(t, validation.IsIWantCacheMissThresholdErr(notification.Err)) cacheMissThresholdNotifCount.Inc() @@ -1175,10 +1173,8 @@ func TestValidationInspector_InspectIWants_CacheMissThreshold(t *testing.T) { internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.. - ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.. - ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) @@ -1232,8 +1228,7 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) defer close(done) notification, ok := args[0].(*p2p.InvCtrlMsgNotif) require.True(t, ok) - require.Equal(t, spammer.SpammerNode.. - ID(), notification.PeerID) + require.Equal(t, spammer.SpammerNode.ID(), notification.PeerID) require.Equal(t, uint64(messageCount), notification.Count) require.True(t, validation.IsIWantDuplicateMsgIDThresholdErr(notification.Err)) } @@ -1271,10 +1266,8 @@ func TestValidationInspector_InspectIWants_DuplicateMsgIDThreshold(t *testing.T) internal.WithCorruptGossipSub(corruptlibp2p.CorruptGossipSubFactory(), corruptlibp2p.CorruptGossipSubConfigFactoryWithInspector(corruptInspectorFunc)), ) - idProvider.On("ByPeerID", victimNode.. - ID()).Return(&victimIdentity, true).Maybe() - idProvider.On("ByPeerID", spammer.SpammerNode.. - ID()).Return(&spammer.SpammerId, true).Maybe() + idProvider.On("ByPeerID", victimNode.ID()).Return(&victimIdentity, true).Maybe() + idProvider.On("ByPeerID", spammer.SpammerNode.ID()).Return(&spammer.SpammerId, true).Maybe() messageIDs := corruptlibp2p.GossipSubMessageIdsFixture(10) From c5a1ce2f616d4099f923ae9616d6ebc4dd5fe5b5 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 17:40:53 -0400 Subject: [PATCH 723/815] lint fix --- network/p2p/middleware/middleware.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index 5af1b688098..bad11cf40f4 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -715,16 +715,3 @@ func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { maxSize := unicastMaxMsgSize(messageType) return maxSize, nil } - -// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete -func (m *Middleware) unicastMaxMsgDuration(messageType string) time.Duration { - switch messageType { - case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": - if LargeMsgUnicastTimeout > m.unicastMessageTimeout { - return LargeMsgUnicastTimeout - } - return m.unicastMessageTimeout - default: - return m.unicastMessageTimeout - } -} From e8b0e7ba3438cdb103634debbb780110dc87cff3 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 17:56:33 -0400 Subject: [PATCH 724/815] lint fix --- network/alsp/manager/manager_test.go | 5 +++-- network/p2p/p2pnode/libp2pNode.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 5362f74c55f..e5f1e8e5824 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -324,8 +324,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId), mocknetwork.NewViolationsConsumer(t)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], ids, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) + idProvider := unittest.NewUpdatableIDProvider(ids) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 995def8bde4..f8995465d29 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -232,7 +232,7 @@ func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protecti // close the stream immediately err = s.Close() if err != nil { - err = fmt.Errorf("failed to close the stream for %s: %w", peerID, err) + return fmt.Errorf("failed to close the stream for %s: %w", peerID, err) } return nil From f2e8d4e2ecab820dfa757ad52a14b955e3e7c811 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 18:19:50 -0400 Subject: [PATCH 725/815] fixes tests issues --- network/p2p/middleware/middleware.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go index bad11cf40f4..932446cd4d6 100644 --- a/network/p2p/middleware/middleware.go +++ b/network/p2p/middleware/middleware.go @@ -162,16 +162,15 @@ func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { // create the node entity and inject dependencies & config mw := &Middleware{ - log: cfg.Logger, - libP2PNode: cfg.Libp2pNode, - bitswapMetrics: cfg.BitSwapMetrics, - sporkId: cfg.SporkId, - validators: DefaultValidators(cfg.Logger, cfg.FlowId), - unicastMessageTimeout: cfg.UnicastMessageTimeout, - idTranslator: cfg.IdTranslator, - codec: cfg.Codec, - unicastRateLimiters: ratelimit.NoopRateLimiters(), - slashingViolationsConsumer: cfg.SlashingViolationConsumerFactory(), + log: cfg.Logger, + libP2PNode: cfg.Libp2pNode, + bitswapMetrics: cfg.BitSwapMetrics, + sporkId: cfg.SporkId, + validators: DefaultValidators(cfg.Logger, cfg.FlowId), + unicastMessageTimeout: cfg.UnicastMessageTimeout, + idTranslator: cfg.IdTranslator, + codec: cfg.Codec, + unicastRateLimiters: ratelimit.NoopRateLimiters(), } for _, opt := range opts { @@ -195,6 +194,10 @@ func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { ctx.Throw(fmt.Errorf("overlay has not been set")) } + // creation of slashing violations consumer should be postponed till here where the middleware + // is start and the overlay is set. + mw.slashingViolationsConsumer = cfg.SlashingViolationConsumerFactory() + mw.authorizedSenderValidator = validator.NewAuthorizedSenderValidator( mw.log, mw.slashingViolationsConsumer, From cd3aeb79ba17f14b03e5cd0f488d0f7e25f32817 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 18:37:34 -0400 Subject: [PATCH 726/815] fixes a test --- network/test/middleware_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index f46ad6c35d1..a8d8b656987 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -24,7 +24,6 @@ import ( "github.com/onflow/flow-go/model/flow/filter" libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" "github.com/onflow/flow-go/model/messages" - "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/module/observable" @@ -205,8 +204,8 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) - idProvider := id.NewFixedIdentityProvider(ids) - mws, providers := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws, _ := testutils.MiddlewareFixtures( m.T(), ids, libP2PNodes, @@ -221,12 +220,11 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { require.NoError(m.T(), err) require.Len(m.T(), ids, 1) - require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] newMw := mws[0] - overlay := m.createOverlay(providers[0]) + overlay := m.createOverlay(idProvider) overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) newMw.SetOverlay(overlay) @@ -452,6 +450,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { return nil }))) idProvider.SetIdentities(append(m.ids, ids...)) + m.providers[0].SetIdentities(append(m.ids, ids...)) // create middleware mws, providers := testutils.MiddlewareFixtures(m.T(), From 71d0f6609d632f0b561574746e5ec65e3c5c34ce Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 18:40:22 -0400 Subject: [PATCH 727/815] fixes lint issues --- network/test/middleware_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index a8d8b656987..ec451c02b88 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -243,6 +243,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // unicast should fail to send because no address is known yet for the new identity con, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) err = con.Unicast(&libp2pmessage.TestMessage{ Text: "TestUpdateNodeAddresses", }, newId.NodeID) @@ -582,6 +583,7 @@ func (m *MiddlewareTestSuite) TestPing() { require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.True(m.T(), ok) require.Equal(m.T(), expectedPayload, msgPayload.Text) // payload }).Return(nil).Once() @@ -721,6 +723,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), m.ids[first].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.True(m.T(), ok require.Equal(m.T(), expectedSendMsg, msgPayload.Text) // payload // echos back the same message back to the sender @@ -744,6 +747,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), m.ids[last].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) + require.True(m.T(), ok require.Equal(m.T(), expectedReplyMsg, msgPayload.Text) // payload }).Return(nil) @@ -858,6 +862,7 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { targetEngine := &mocknetwork.MessageProcessor{} con2, err := m.networks[targetIndex].Register(channels.TestNetworkChannel, targetEngine) + require.NoError(m.T(), err) targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { msgChannel, ok := args[0].(channels.Channel) From 0c5b76f835c27d76d0f683bde6baefd6c6230035 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 18:41:13 -0400 Subject: [PATCH 728/815] fixes lint issue --- network/test/middleware_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index ec451c02b88..e0887d889c6 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -723,7 +723,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), m.ids[first].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok + require.True(m.T(), ok) require.Equal(m.T(), expectedSendMsg, msgPayload.Text) // payload // echos back the same message back to the sender From 0df643cdfac4cdac5811ef406447d91e3f3e39aa Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 29 Aug 2023 18:41:57 -0400 Subject: [PATCH 729/815] fixes lint issues --- network/test/middleware_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index e0887d889c6..190f26026e7 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -747,7 +747,7 @@ func (m *MiddlewareTestSuite) TestEcho() { require.Equal(m.T(), m.ids[last].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok + require.True(m.T(), ok) require.Equal(m.T(), expectedReplyMsg, msgPayload.Text) // payload }).Return(nil) From 3f6a7935149e5ae3e93575c0f7851fc8c97d66d0 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Tue, 29 Aug 2023 16:28:12 +0200 Subject: [PATCH 730/815] add query mock and fix tests --- Makefile | 1 + .../computation/query/mock/executor.go | 84 +++++++++++++++++++ engine/execution/scripts/engine.go | 8 +- engine/execution/scripts/engine_test.go | 32 +++---- 4 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 engine/execution/computation/query/mock/executor.go diff --git a/Makefile b/Makefile index d8b37127d1d..561b55a9d8a 100644 --- a/Makefile +++ b/Makefile @@ -160,6 +160,7 @@ generate-mocks: install-mock-generators mockery --name 'ExecutionState' --dir=engine/execution/state --case=underscore --output="engine/execution/state/mock" --outpkg="mock" mockery --name 'BlockComputer' --dir=engine/execution/computation/computer --case=underscore --output="engine/execution/computation/computer/mock" --outpkg="mock" mockery --name 'ComputationManager' --dir=engine/execution/computation --case=underscore --output="engine/execution/computation/mock" --outpkg="mock" + mockery --name 'Executor' --dir=engine/execution/computation/query --case=underscore --output="engine/execution/computation/query/mock" --outpkg="mock" mockery --name 'EpochComponentsFactory' --dir=engine/collection/epochmgr --case=underscore --output="engine/collection/epochmgr/mock" --outpkg="mock" mockery --name 'Backend' --dir=engine/collection/rpc --case=underscore --output="engine/collection/rpc/mock" --outpkg="mock" mockery --name 'ProviderEngine' --dir=engine/execution/provider --case=underscore --output="engine/execution/provider/mock" --outpkg="mock" diff --git a/engine/execution/computation/query/mock/executor.go b/engine/execution/computation/query/mock/executor.go new file mode 100644 index 00000000000..4ffc343c6e5 --- /dev/null +++ b/engine/execution/computation/query/mock/executor.go @@ -0,0 +1,84 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + context "context" + + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" + + snapshot "github.com/onflow/flow-go/fvm/storage/snapshot" +) + +// Executor is an autogenerated mock type for the Executor type +type Executor struct { + mock.Mock +} + +// ExecuteScript provides a mock function with given fields: ctx, script, arguments, blockHeader, _a4 +func (_m *Executor) ExecuteScript(ctx context.Context, script []byte, arguments [][]byte, blockHeader *flow.Header, _a4 snapshot.StorageSnapshot) ([]byte, error) { + ret := _m.Called(ctx, script, arguments, blockHeader, _a4) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) ([]byte, error)); ok { + return rf(ctx, script, arguments, blockHeader, _a4) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) []byte); ok { + r0 = rf(ctx, script, arguments, blockHeader, _a4) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) error); ok { + r1 = rf(ctx, script, arguments, blockHeader, _a4) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccount provides a mock function with given fields: ctx, addr, header, _a3 +func (_m *Executor) GetAccount(ctx context.Context, addr flow.Address, header *flow.Header, _a3 snapshot.StorageSnapshot) (*flow.Account, error) { + ret := _m.Called(ctx, addr, header, _a3) + + var r0 *flow.Account + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) (*flow.Account, error)); ok { + return rf(ctx, addr, header, _a3) + } + if rf, ok := ret.Get(0).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) *flow.Account); ok { + r0 = rf(ctx, addr, header, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.Account) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) error); ok { + r1 = rf(ctx, addr, header, _a3) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewExecutor interface { + mock.TestingT + Cleanup(func()) +} + +// NewExecutor creates a new instance of Executor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewExecutor(t mockConstructorTestingTNewExecutor) *Executor { + mock := &Executor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index d46d0716549..46505ca802e 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -4,13 +4,13 @@ import ( "context" "encoding/hex" "fmt" - "github.com/onflow/flow-go/engine/execution/computation/query" - "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/rs/zerolog" "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/execution" + "github.com/onflow/flow-go/engine/execution/computation/query" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" ) @@ -30,7 +30,7 @@ type Engine struct { unit *engine.Unit log zerolog.Logger state protocol.State - queryExecutor *query.QueryExecutor + queryExecutor query.Executor execState ScriptExecutionState } @@ -39,7 +39,7 @@ var _ execution.ScriptExecutor = (*Engine)(nil) func New( logger zerolog.Logger, state protocol.State, - queryExecutor *query.QueryExecutor, + queryExecutor query.Executor, execState ScriptExecutionState, ) *Engine { return &Engine{ diff --git a/engine/execution/scripts/engine_test.go b/engine/execution/scripts/engine_test.go index 9f954530f0e..5b5c116830f 100644 --- a/engine/execution/scripts/engine_test.go +++ b/engine/execution/scripts/engine_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - computation "github.com/onflow/flow-go/engine/execution/computation/mock" + queryMock "github.com/onflow/flow-go/engine/execution/computation/query/mock" stateMock "github.com/onflow/flow-go/engine/execution/state/mock" "github.com/onflow/flow-go/model/flow" protocol "github.com/onflow/flow-go/state/protocol/mock" @@ -17,12 +17,12 @@ import ( ) type testingContext struct { - t *testing.T - engine *Engine - state *protocol.State - executionState *stateMock.ExecutionState - computationManager *computation.ComputationManager - mu *sync.Mutex + t *testing.T + engine *Engine + state *protocol.State + executionState *stateMock.ExecutionState + queryExecutor *queryMock.Executor + mu *sync.Mutex } func (ctx *testingContext) stateCommitmentExist(blockID flow.Identifier, commit flow.StateCommitment) { @@ -32,17 +32,17 @@ func (ctx *testingContext) stateCommitmentExist(blockID flow.Identifier, commit func runWithEngine(t *testing.T, fn func(ctx testingContext)) { log := unittest.Logger() - computationManager := new(computation.ComputationManager) + queryExecutor := new(queryMock.Executor) protocolState := new(protocol.State) execState := new(stateMock.ExecutionState) - engine := New(log, protocolState, computationManager, execState) + engine := New(log, protocolState, queryExecutor, execState) fn(testingContext{ - t: t, - engine: engine, - computationManager: computationManager, - executionState: execState, - state: protocolState, + t: t, + engine: engine, + queryExecutor: queryExecutor, + executionState: execState, + state: protocolState, }) } @@ -70,7 +70,7 @@ func TestExecuteScriptAtBlockID(t *testing.T) { ctx.executionState.On("HasState", *blockA.StartState).Return(true) // Successful call to computation manager - ctx.computationManager. + ctx.queryExecutor. On("ExecuteScript", mock.Anything, script, [][]byte(nil), blockA.Block.Header, nil). Return(scriptResult, nil) @@ -80,7 +80,7 @@ func TestExecuteScriptAtBlockID(t *testing.T) { assert.Equal(t, scriptResult, res) // Assert other components were called as expected - ctx.computationManager.AssertExpectations(t) + ctx.queryExecutor.AssertExpectations(t) ctx.executionState.AssertExpectations(t) ctx.state.AssertExpectations(t) }) From a49f39cff592a093575ccae0b640752e38a8bdad Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 10:11:14 -0400 Subject: [PATCH 731/815] extends startup duration and removes it as a parameter --- network/alsp/manager/manager_test.go | 12 ++++++------ network/internal/testutils/testUtil.go | 16 ++++++++-------- network/test/blob_service_test.go | 2 +- network/test/echoengine_test.go | 2 +- network/test/epochtransition_test.go | 2 +- network/test/meshengine_test.go | 2 +- network/test/middleware_test.go | 8 ++++---- network/test/unicast_authorization_test.go | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index a02bc5b3002..6f2ef5c8a00 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -69,7 +69,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -132,7 +132,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -243,7 +243,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -337,7 +337,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -494,7 +494,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -583,7 +583,7 @@ func TestMisbehaviorReportMetrics(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index c9fe52f2f4e..b71c3abd73a 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -280,11 +280,11 @@ func GenerateEngines(t *testing.T, nets []network.Network) []*MeshEngine { // - timeout: the timeout to use for waiting for the nodes and networks to start. // // This function fails the test if the nodes or networks do not start within the given timeout. -func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, nets []network.Network, timeout time.Duration) { - StartNetworks(ctx, t, nets, timeout) +func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, nets []network.Network) { + StartNetworks(ctx, t, nets) // start up nodes and Peer managers - StartNodes(ctx, t, nodes, timeout) + StartNodes(ctx, t, nodes) } // StartNetworks starts the provided networks using the provided irrecoverable context @@ -295,23 +295,23 @@ func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, node // - duration: the timeout to use for waiting for the networks to start. // // This function fails the test if the networks do not start within the given timeout. -func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.Network, duration time.Duration) { +func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.Network) { // start up networks (this will implicitly start middlewares) for _, net := range nets { net.Start(ctx) - unittest.RequireComponentsReadyBefore(t, duration, net) + unittest.RequireComponentsReadyBefore(t, 5*time.Second, net) } } // StartNodes starts the provided nodes and their peer managers using the provided irrecoverable context -func StartNodes(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, duration time.Duration) { +func StartNodes(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode) { for _, node := range nodes { node.Start(ctx) - unittest.RequireComponentsReadyBefore(t, duration, node) + unittest.RequireComponentsReadyBefore(t, 5*time.Second, node) pm := node.PeerManagerComponent() pm.Start(ctx) - unittest.RequireComponentsReadyBefore(t, duration, pm) + unittest.RequireComponentsReadyBefore(t, 5*time.Second, pm) } } diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index f42d74ab140..2f5b2d1af2c 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -100,7 +100,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) suite.networks = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks) blobExchangeChannel := channels.Channel("blob-exchange") diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 6637ccf9f66..4d798c97f03 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -63,7 +63,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets) } // TearDownTest closes the networks within a specified timeout diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 0632f2a9a44..32e4571d4ed 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -204,7 +204,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { nets := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) suite.cancels = append(suite.cancels, cancel) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets) // create the engines for the new nodes engines := testutils.GenerateEngines(suite.T(), nets) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index edf8222c223..e229be4b79d 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -120,7 +120,7 @@ func (suite *MeshEngineTestSuite) SetupTest() { testutils.MiddlewareConfigFixture(suite.T(), sporkId), mocknetwork.NewViolationsConsumer(suite.T())) suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) + testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index bf46249782e..8a3158df9a0 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -179,7 +179,7 @@ func (m *MiddlewareTestSuite) SetupTest() { m.mwCtx = irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(m.mwCtx, m.T(), m.nodes, 100*time.Millisecond) + testutils.StartNodes(m.mwCtx, m.T(), m.nodes) for i, mw := range m.mws { mw.SetOverlay(m.ov[i]) @@ -227,7 +227,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { newMw.SetOverlay(overlay) // start up nodes and peer managers - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes) defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) newMw.Start(irrecoverableCtx) @@ -342,7 +342,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { ctx, cancel := context.WithCancel(m.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes) defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) newMw.Start(irrecoverableCtx) @@ -484,7 +484,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { ctx, cancel := context.WithCancel(m.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes) defer testutils.StopComponents(m.T(), libP2PNodes, 100*time.Millisecond) newMw.Start(irrecoverableCtx) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index b2915fc1bd8..f318b2bd327 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -95,7 +95,7 @@ func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Ov ctx, cancel := context.WithCancel(context.Background()) sigCtx, _ := irrecoverable.WithSignaler(ctx) - testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 100*time.Millisecond) + testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes) u.senderMW.SetOverlay(overlay) u.senderMW.Start(sigCtx) From 45aa47a9b8cee928b6f674ff7094cba744a5574a Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 30 Aug 2023 16:15:55 +0200 Subject: [PATCH 732/815] lint imports --- cmd/execution_builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 850b66dd7c6..51348f3a7ef 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -4,8 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/onflow/flow-go/engine/execution/computation/query" - "github.com/onflow/flow-go/fvm/storage/derived" "os" "path" "path/filepath" @@ -49,6 +47,7 @@ import ( "github.com/onflow/flow-go/engine/execution/checker" "github.com/onflow/flow-go/engine/execution/computation" "github.com/onflow/flow-go/engine/execution/computation/committer" + "github.com/onflow/flow-go/engine/execution/computation/query" "github.com/onflow/flow-go/engine/execution/ingestion" "github.com/onflow/flow-go/engine/execution/ingestion/stop" "github.com/onflow/flow-go/engine/execution/ingestion/uploader" @@ -58,6 +57,7 @@ import ( "github.com/onflow/flow-go/engine/execution/state" "github.com/onflow/flow-go/engine/execution/state/bootstrap" "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/storage/derived" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/ledger/common/pathfinder" From 51c1308e78bd43f33082fff27cb36c4aa90a3523 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 10:16:15 -0400 Subject: [PATCH 733/815] relaxes the geometric decay test --- network/p2p/scoring/decay_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/scoring/decay_test.go b/network/p2p/scoring/decay_test.go index 5bbd525fc4b..b75aec0ae47 100644 --- a/network/p2p/scoring/decay_test.go +++ b/network/p2p/scoring/decay_test.go @@ -123,7 +123,7 @@ func TestGeometricDecay(t *testing.T) { if tt.wantErr != nil { assert.Errorf(t, err, tt.wantErr.Error()) } - assert.LessOrEqual(t, truncateFloat(math.Abs(got-tt.want), 3), 1e-3) + assert.LessOrEqual(t, truncateFloat(math.Abs(got-tt.want), 3), 1e-2) }) } } From e4390a1920c2b1fff896bf3eb5b77a37cbdf9819 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 10:25:41 -0400 Subject: [PATCH 734/815] fixes unknown identity tests --- network/p2p/scoring/registry_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/network/p2p/scoring/registry_test.go b/network/p2p/scoring/registry_test.go index 843ec2d87ae..2ad9167b4f0 100644 --- a/network/p2p/scoring/registry_test.go +++ b/network/p2p/scoring/registry_test.go @@ -330,7 +330,12 @@ func TestPersistingUnknownIdentityPenalty(t *testing.T) { }) // with reported spam, the app specific score should be the default unknown identity + the spam penalty. - require.Less(t, math.Abs(scoring.DefaultUnknownIdentityPenalty+penaltyValueFixtures().Graft-reg.AppSpecificScoreFunc()(peerID)), 10e-3) + diff := math.Abs(scoring.DefaultUnknownIdentityPenalty + penaltyValueFixtures().Graft - reg.AppSpecificScoreFunc()(peerID)) + normalizedDiff := diff / (scoring.DefaultUnknownIdentityPenalty + penaltyValueFixtures().Graft) + require.NotZero(t, normalizedDiff, "difference between the expected and actual app specific score should not be zero") + require.Less(t, + normalizedDiff, + 0.01, "normalized difference between the expected and actual app specific score should be less than 1%") // decays happen every second, so we wait for 1 second to make sure the penalty is updated. time.Sleep(1 * time.Second) From 7b05d1794591a053d76aaf219483e0dddf9b4c6f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 11:25:39 -0400 Subject: [PATCH 735/815] fixes middleware tests --- network/alsp/manager/manager_test.go | 33 +++++++----- network/internal/testutils/testUtil.go | 17 +++--- network/test/blob_service_test.go | 4 +- network/test/echoengine_test.go | 6 ++- network/test/epochtransition_test.go | 6 ++- network/test/meshengine_test.go | 5 +- network/test/middleware_test.go | 62 ++++++++++------------ network/test/unicast_authorization_test.go | 15 +++--- 8 files changed, 80 insertions(+), 68 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index e5f1e8e5824..4a8ed87a048 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -16,7 +16,6 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" @@ -57,10 +56,11 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( t, ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) @@ -121,10 +121,11 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( t, ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) @@ -225,13 +226,14 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(), nil)) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( t, ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) - idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -324,8 +326,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures(t, + ids, + idProvider, + nodes, + testutils.MiddlewareConfigFixture(t, sporkId)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) @@ -476,7 +483,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t // create 1 victim node, 1 honest node and a node for each slashing violation ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). - idProvider := id.NewFixedIdentityProvider(ids) + idProvider := unittest.NewUpdatableIDProvider(ids) // we want to override the middleware config to use the slashing violations consumer. However, we need the network // instance to do that, but for the network instance we need the middleware config. So, we create the adapter first, which @@ -493,9 +500,10 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t return violationsConsumer } - mws, _ := testutils.MiddlewareFixtures( + mws := testutils.MiddlewareFixtures( t, ids, + idProvider, nodes, middlewareConfig, ) @@ -593,10 +601,11 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( t, ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 28824628b4d..60917bb3ccb 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -175,14 +175,14 @@ func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware. func MiddlewareFixtures( t *testing.T, identities flow.IdentityList, + idProvider *unittest.UpdatableIDProvider, libP2PNodes []p2p.LibP2PNode, cfg *middleware.Config, - opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { + opts ...middleware.OptionFn) []network.Middleware { require.Equal(t, len(identities), len(libP2PNodes)) mws := make([]network.Middleware, len(identities)) - idProviders := make([]*unittest.UpdatableIDProvider, len(identities)) if cfg.SlashingViolationConsumerFactory == nil { // use a mock slashing violation consumer factory if not provided @@ -195,26 +195,24 @@ func MiddlewareFixtures( i := i cfg.Libp2pNode = libP2PNodes[i] cfg.FlowId = identities[i].NodeID - idProviders[i] = unittest.NewUpdatableIDProvider(identities) - cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProviders[i]) + cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProvider) mws[i] = middleware.NewMiddleware(cfg, opts...) } - return mws, idProviders + return mws } // NetworksFixture generates the network for the given middlewares func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, + idProvider *unittest.UpdatableIDProvider, mws []network.Middleware, - configOpts ...func(*p2p.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { + configOpts ...func(*p2p.NetworkConfig)) []network.Network { count := len(ids) nets := make([]network.Network, 0) - idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { - idProvider := unittest.NewUpdatableIDProvider(ids) params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, mws[i]) for _, opt := range configOpts { @@ -225,10 +223,9 @@ func NetworksFixture(t *testing.T, require.NoError(t, err) nets = append(nets, net) - idProviders = append(idProviders, idProvider) } - return nets, idProviders + return nets } func NetworkConfigFixture( diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 64b9b52e60a..00e4e38d442 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -92,9 +92,11 @@ func (suite *BlobServiceTestSuite) SetupTest() { ConnectionPruning: true, ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), }, nil)) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( suite.T(), ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 5087981624d..8a3a69583b7 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -54,13 +54,15 @@ func (suite *EchoEngineTestSuite) SetupTest() { // both nodes should be of the same role to get connected on epidemic dissemination var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() + idProvider := unittest.NewUpdatableIDProvider(suite.ids) suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - suite.mws, _ = testutils.MiddlewareFixtures( + suite.mws = testutils.MiddlewareFixtures( suite.T(), suite.ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) + suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, idProvider, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index a40e958e8db..30dcf185c7d 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -194,12 +194,14 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // create the ids, middlewares and networks sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - mws, _ := testutils.MiddlewareFixtures( + idProvider := unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( suite.T(), ids, + idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) + nets := testutils.NetworksFixture(suite.T(), sporkId, ids, idProvider, mws) suite.cancels = append(suite.cancels, cancel) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 84b5f9a5556..210e51ab668 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -112,12 +112,13 @@ func (suite *MeshEngineTestSuite) SetupTest() { idProvider.SetIdentities(identities) suite.ids = identities - suite.mws, _ = testutils.MiddlewareFixtures( + suite.mws = testutils.MiddlewareFixtures( suite.T(), suite.ids, + idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) + suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, idProvider, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) for _, observableConnMgr := range tagObservables { diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 190f26026e7..b0ee66110b4 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -93,7 +93,7 @@ type MiddlewareTestSuite struct { ids []*flow.Identity metrics *metrics.NoopCollector // no-op performance monitoring simulation logger zerolog.Logger - providers []*unittest.UpdatableIDProvider + provider *unittest.UpdatableIDProvider sporkId flow.Identifier mwCancel context.CancelFunc mwCtx irrecoverable.SignalerContext @@ -124,7 +124,7 @@ func (m *MiddlewareTestSuite) SetupTest() { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) - idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) + m.provider = unittest.NewUpdatableIDProvider(flow.IdentityList{}) defaultFlowConfig, err := config.DefaultConfig() require.NoError(m.T(), err) @@ -143,24 +143,26 @@ func (m *MiddlewareTestSuite) SetupTest() { node, nodeId := p2ptest.NodeFixture(m.T(), m.sporkId, m.T().Name(), - idProvider, + m.provider, opts...) libP2PNodes = append(libP2PNodes, node) identities = append(identities, &nodeId) tagObservables = append(tagObservables, connManager) } - idProvider.SetIdentities(identities) + + m.provider.SetIdentities(identities) m.ids = identities m.libP2PNodes = libP2PNodes - m.mws, _ = testutils.MiddlewareFixtures( + m.mws = testutils.MiddlewareFixtures( m.T(), m.ids, + m.provider, m.libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) - m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.mws) + m.networks = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.provider, m.mws) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } @@ -205,9 +207,10 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) idProvider := unittest.NewUpdatableIDProvider(ids) - mws, _ := testutils.MiddlewareFixtures( + mws := testutils.MiddlewareFixtures( m.T(), ids, + idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) networkCfg := testutils.NetworkConfigFixture( @@ -239,7 +242,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { idList := flow.IdentityList(append(m.ids, newId)) // needed to enable ID translation - m.providers[0].SetIdentities(idList) + m.provider.SetIdentities(idList) // unicast should fail to send because no address is known yet for the new identity con, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) @@ -311,23 +314,20 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { idProvider.SetIdentities(append(m.ids, ids...)) // create middleware - mws, providers := testutils.MiddlewareFixtures(m.T(), + mws := testutils.MiddlewareFixtures(m.T(), ids, + idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) require.Len(m.T(), ids, 1) - require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] newMw := mws[0] idList := flow.IdentityList(append(m.ids, newId)) - - providers[0].SetIdentities(idList) - - overlay := m.createOverlay(providers[0]) + overlay := m.createOverlay(idProvider) calls := atomic.NewUint64(0) ch := make(chan struct{}) @@ -350,7 +350,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) // needed to enable ID translation - m.providers[0].SetIdentities(idList) + m.provider.SetIdentities(idList) // update the addresses m.mws[0].UpdateNodeAddresses() @@ -369,13 +369,13 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream // handling of streams. for i := 0; i < 10; i++ { - err = con0.Unicast(&libp2pmessage.TestMessage{ + _ = con0.Unicast(&libp2pmessage.TestMessage{ Text: fmt.Sprintf("hello-%d", i), }, newId.NodeID) - require.NoError(m.T(), err) + } // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop rate limit test ch on time") + unittest.RequireCloseBefore(m.T(), ch, 1*time.Second, "could not stop rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) @@ -394,8 +394,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // shutdown our middleware so that each message can be processed cancel() - unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") - unittest.RequireCloseBefore(m.T(), newMw.Done(), 100*time.Millisecond, "could not stop middleware on time") + unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 1*time.Second, "could not stop libp2p node on time") + unittest.RequireCloseBefore(m.T(), newMw.Done(), 1*time.Second, "could not stop middleware on time") // expect our rate limited peer callback to be invoked once require.True(m.T(), rateLimits.Load() > 0) @@ -450,23 +450,23 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { return nil }))) - idProvider.SetIdentities(append(m.ids, ids...)) - m.providers[0].SetIdentities(append(m.ids, ids...)) + idList := append(m.ids, ids...) + idProvider.SetIdentities(idList) // create middleware - mws, providers := testutils.MiddlewareFixtures(m.T(), + mws := testutils.MiddlewareFixtures(m.T(), ids, + idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) require.Len(m.T(), ids, 1) - require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] newMw := mws[0] - overlay := m.createOverlay(providers[0]) - overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) + overlay := m.createOverlay(idProvider) + overlay.On("Receive", mockery.Anything).Return(nil) newMw.SetOverlay(overlay) @@ -480,11 +480,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) - idList := flow.IdentityList(append(m.ids, newId)) - - // needed to enable ID translation - m.providers[0].SetIdentities(idList) - + m.provider.SetIdentities(idList) // update the addresses m.mws[0].UpdateNodeAddresses() @@ -494,7 +490,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // peer should be removed by the peer manager. p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - // create message with about 400bytes (300 random bytes + 100bytes message info) + // create message with about 400bytes. b := make([]byte, 300) for i := range b { b[i] = byte('X') @@ -511,7 +507,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { } // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") + unittest.RequireCloseBefore(m.T(), ch, 1*time.Second, "could not stop on rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 7ed453e3e2d..a92bd54ca96 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -47,8 +47,7 @@ type UnicastAuthorizationTestSuite struct { receiverNetwork network.Network // receiverID the identity on the mw sending the message receiverID *flow.Identity - // providers id providers generated at beginning of a test run - providers []*unittest.UpdatableIDProvider + provider *unittest.UpdatableIDProvider // cancel is the cancel func from the context that was used to start the middlewares in a test run cancel context.CancelFunc sporkId flow.Identifier @@ -83,10 +82,15 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer return slashingViolationsConsumer } u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) - mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) - nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2p.WithCodec(u.codec)) + u.provider = unittest.NewUpdatableIDProvider(ids) + mws := testutils.MiddlewareFixtures( + u.T(), + ids, + u.provider, + libP2PNodes, + cfg) + nets := testutils.NetworksFixture(u.T(), u.sporkId, ids, u.provider, mws, p2p.WithCodec(u.codec)) require.Len(u.T(), ids, 2) - require.Len(u.T(), providers, 2) require.Len(u.T(), mws, 2) require.Len(u.T(), nets, 2) @@ -94,7 +98,6 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer u.receiverNetwork = nets[1] u.senderID = ids[0] u.receiverID = ids[1] - u.providers = providers u.libP2PNodes = libP2PNodes } From d1bdd4f056e2cbcb9fc7e4d475ef96c7f7270908 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 11:25:39 -0400 Subject: [PATCH 736/815] Revert "fixes middleware tests" This reverts commit 7b05d1794591a053d76aaf219483e0dddf9b4c6f. --- network/alsp/manager/manager_test.go | 33 +++++------- network/internal/testutils/testUtil.go | 17 +++--- network/test/blob_service_test.go | 4 +- network/test/echoengine_test.go | 6 +-- network/test/epochtransition_test.go | 6 +-- network/test/meshengine_test.go | 5 +- network/test/middleware_test.go | 62 ++++++++++++---------- network/test/unicast_authorization_test.go | 15 +++--- 8 files changed, 68 insertions(+), 80 deletions(-) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 4a8ed87a048..e5f1e8e5824 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/config" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/id" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/module/metrics" mockmodule "github.com/onflow/flow-go/module/mock" @@ -56,11 +57,10 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + idProvider := id.NewFixedIdentityProvider(ids) + mws, _ := testutils.MiddlewareFixtures( t, ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) @@ -121,11 +121,10 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + idProvider := id.NewFixedIdentityProvider(ids) + mws, _ := testutils.MiddlewareFixtures( t, ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) @@ -226,14 +225,13 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(), nil)) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + mws, _ := testutils.MiddlewareFixtures( t, ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) + idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -326,13 +324,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) + mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures(t, - ids, - idProvider, - nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) @@ -483,7 +476,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t // create 1 victim node, 1 honest node and a node for each slashing violation ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). - idProvider := unittest.NewUpdatableIDProvider(ids) + idProvider := id.NewFixedIdentityProvider(ids) // we want to override the middleware config to use the slashing violations consumer. However, we need the network // instance to do that, but for the network instance we need the middleware config. So, we create the adapter first, which @@ -500,10 +493,9 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t return violationsConsumer } - mws := testutils.MiddlewareFixtures( + mws, _ := testutils.MiddlewareFixtures( t, ids, - idProvider, nodes, middlewareConfig, ) @@ -601,11 +593,10 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + idProvider := id.NewFixedIdentityProvider(ids) + mws, _ := testutils.MiddlewareFixtures( t, ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 60917bb3ccb..28824628b4d 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -175,14 +175,14 @@ func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware. func MiddlewareFixtures( t *testing.T, identities flow.IdentityList, - idProvider *unittest.UpdatableIDProvider, libP2PNodes []p2p.LibP2PNode, cfg *middleware.Config, - opts ...middleware.OptionFn) []network.Middleware { + opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { require.Equal(t, len(identities), len(libP2PNodes)) mws := make([]network.Middleware, len(identities)) + idProviders := make([]*unittest.UpdatableIDProvider, len(identities)) if cfg.SlashingViolationConsumerFactory == nil { // use a mock slashing violation consumer factory if not provided @@ -195,24 +195,26 @@ func MiddlewareFixtures( i := i cfg.Libp2pNode = libP2PNodes[i] cfg.FlowId = identities[i].NodeID - cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProvider) + idProviders[i] = unittest.NewUpdatableIDProvider(identities) + cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProviders[i]) mws[i] = middleware.NewMiddleware(cfg, opts...) } - return mws + return mws, idProviders } // NetworksFixture generates the network for the given middlewares func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, - idProvider *unittest.UpdatableIDProvider, mws []network.Middleware, - configOpts ...func(*p2p.NetworkConfig)) []network.Network { + configOpts ...func(*p2p.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { count := len(ids) nets := make([]network.Network, 0) + idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { + idProvider := unittest.NewUpdatableIDProvider(ids) params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, mws[i]) for _, opt := range configOpts { @@ -223,9 +225,10 @@ func NetworksFixture(t *testing.T, require.NoError(t, err) nets = append(nets, net) + idProviders = append(idProviders, idProvider) } - return nets + return nets, idProviders } func NetworkConfigFixture( diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 00e4e38d442..64b9b52e60a 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -92,11 +92,9 @@ func (suite *BlobServiceTestSuite) SetupTest() { ConnectionPruning: true, ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), }, nil)) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + mws, _ := testutils.MiddlewareFixtures( suite.T(), ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 8a3a69583b7..5087981624d 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -54,15 +54,13 @@ func (suite *EchoEngineTestSuite) SetupTest() { // both nodes should be of the same role to get connected on epidemic dissemination var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() - idProvider := unittest.NewUpdatableIDProvider(suite.ids) suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - suite.mws = testutils.MiddlewareFixtures( + suite.mws, _ = testutils.MiddlewareFixtures( suite.T(), suite.ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, idProvider, suite.mws) + suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 30dcf185c7d..a40e958e8db 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -194,14 +194,12 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // create the ids, middlewares and networks sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + mws, _ := testutils.MiddlewareFixtures( suite.T(), ids, - idProvider, nodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - nets := testutils.NetworksFixture(suite.T(), sporkId, ids, idProvider, mws) + nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) suite.cancels = append(suite.cancels, cancel) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 210e51ab668..84b5f9a5556 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -112,13 +112,12 @@ func (suite *MeshEngineTestSuite) SetupTest() { idProvider.SetIdentities(identities) suite.ids = identities - suite.mws = testutils.MiddlewareFixtures( + suite.mws, _ = testutils.MiddlewareFixtures( suite.T(), suite.ids, - idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, idProvider, suite.mws) + suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) for _, observableConnMgr := range tagObservables { diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index b0ee66110b4..190f26026e7 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -93,7 +93,7 @@ type MiddlewareTestSuite struct { ids []*flow.Identity metrics *metrics.NoopCollector // no-op performance monitoring simulation logger zerolog.Logger - provider *unittest.UpdatableIDProvider + providers []*unittest.UpdatableIDProvider sporkId flow.Identifier mwCancel context.CancelFunc mwCtx irrecoverable.SignalerContext @@ -124,7 +124,7 @@ func (m *MiddlewareTestSuite) SetupTest() { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) - m.provider = unittest.NewUpdatableIDProvider(flow.IdentityList{}) + idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) defaultFlowConfig, err := config.DefaultConfig() require.NoError(m.T(), err) @@ -143,26 +143,24 @@ func (m *MiddlewareTestSuite) SetupTest() { node, nodeId := p2ptest.NodeFixture(m.T(), m.sporkId, m.T().Name(), - m.provider, + idProvider, opts...) libP2PNodes = append(libP2PNodes, node) identities = append(identities, &nodeId) tagObservables = append(tagObservables, connManager) } - - m.provider.SetIdentities(identities) + idProvider.SetIdentities(identities) m.ids = identities m.libP2PNodes = libP2PNodes - m.mws = testutils.MiddlewareFixtures( + m.mws, _ = testutils.MiddlewareFixtures( m.T(), m.ids, - m.provider, m.libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) - m.networks = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.provider, m.mws) + m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.mws) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } @@ -207,10 +205,9 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) idProvider := unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( + mws, _ := testutils.MiddlewareFixtures( m.T(), ids, - idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) networkCfg := testutils.NetworkConfigFixture( @@ -242,7 +239,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { idList := flow.IdentityList(append(m.ids, newId)) // needed to enable ID translation - m.provider.SetIdentities(idList) + m.providers[0].SetIdentities(idList) // unicast should fail to send because no address is known yet for the new identity con, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) @@ -314,20 +311,23 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { idProvider.SetIdentities(append(m.ids, ids...)) // create middleware - mws := testutils.MiddlewareFixtures(m.T(), + mws, providers := testutils.MiddlewareFixtures(m.T(), ids, - idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) require.Len(m.T(), ids, 1) + require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] newMw := mws[0] idList := flow.IdentityList(append(m.ids, newId)) - overlay := m.createOverlay(idProvider) + + providers[0].SetIdentities(idList) + + overlay := m.createOverlay(providers[0]) calls := atomic.NewUint64(0) ch := make(chan struct{}) @@ -350,7 +350,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) // needed to enable ID translation - m.provider.SetIdentities(idList) + m.providers[0].SetIdentities(idList) // update the addresses m.mws[0].UpdateNodeAddresses() @@ -369,13 +369,13 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream // handling of streams. for i := 0; i < 10; i++ { - _ = con0.Unicast(&libp2pmessage.TestMessage{ + err = con0.Unicast(&libp2pmessage.TestMessage{ Text: fmt.Sprintf("hello-%d", i), }, newId.NodeID) - + require.NoError(m.T(), err) } // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 1*time.Second, "could not stop rate limit test ch on time") + unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) @@ -394,8 +394,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // shutdown our middleware so that each message can be processed cancel() - unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 1*time.Second, "could not stop libp2p node on time") - unittest.RequireCloseBefore(m.T(), newMw.Done(), 1*time.Second, "could not stop middleware on time") + unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") + unittest.RequireCloseBefore(m.T(), newMw.Done(), 100*time.Millisecond, "could not stop middleware on time") // expect our rate limited peer callback to be invoked once require.True(m.T(), rateLimits.Load() > 0) @@ -450,23 +450,23 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { return nil }))) - idList := append(m.ids, ids...) - idProvider.SetIdentities(idList) + idProvider.SetIdentities(append(m.ids, ids...)) + m.providers[0].SetIdentities(append(m.ids, ids...)) // create middleware - mws := testutils.MiddlewareFixtures(m.T(), + mws, providers := testutils.MiddlewareFixtures(m.T(), ids, - idProvider, libP2PNodes, testutils.MiddlewareConfigFixture(m.T(), m.sporkId), middleware.WithUnicastRateLimiters(rateLimiters), middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) require.Len(m.T(), ids, 1) + require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] newMw := mws[0] - overlay := m.createOverlay(idProvider) - overlay.On("Receive", mockery.Anything).Return(nil) + overlay := m.createOverlay(providers[0]) + overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) newMw.SetOverlay(overlay) @@ -480,7 +480,11 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) - m.provider.SetIdentities(idList) + idList := flow.IdentityList(append(m.ids, newId)) + + // needed to enable ID translation + m.providers[0].SetIdentities(idList) + // update the addresses m.mws[0].UpdateNodeAddresses() @@ -490,7 +494,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { // peer should be removed by the peer manager. p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - // create message with about 400bytes. + // create message with about 400bytes (300 random bytes + 100bytes message info) b := make([]byte, 300) for i := range b { b[i] = byte('X') @@ -507,7 +511,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { } // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 1*time.Second, "could not stop on rate limit test ch on time") + unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index a92bd54ca96..7ed453e3e2d 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -47,7 +47,8 @@ type UnicastAuthorizationTestSuite struct { receiverNetwork network.Network // receiverID the identity on the mw sending the message receiverID *flow.Identity - provider *unittest.UpdatableIDProvider + // providers id providers generated at beginning of a test run + providers []*unittest.UpdatableIDProvider // cancel is the cancel func from the context that was used to start the middlewares in a test run cancel context.CancelFunc sporkId flow.Identifier @@ -82,15 +83,10 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer return slashingViolationsConsumer } u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) - u.provider = unittest.NewUpdatableIDProvider(ids) - mws := testutils.MiddlewareFixtures( - u.T(), - ids, - u.provider, - libP2PNodes, - cfg) - nets := testutils.NetworksFixture(u.T(), u.sporkId, ids, u.provider, mws, p2p.WithCodec(u.codec)) + mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) + nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2p.WithCodec(u.codec)) require.Len(u.T(), ids, 2) + require.Len(u.T(), providers, 2) require.Len(u.T(), mws, 2) require.Len(u.T(), nets, 2) @@ -98,6 +94,7 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer u.receiverNetwork = nets[1] u.senderID = ids[0] u.receiverID = ids[1] + u.providers = providers u.libP2PNodes = libP2PNodes } From 6f2ece46d3d05e08890f572b50be47687ea8aa0d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 12:48:14 -0400 Subject: [PATCH 737/815] removes run parallel --- network/codec/cbor/codec_test.go | 14 ------------- network/codec/cbor/decoder_test.go | 20 ------------------- .../internal/rate_limiter_map_test.go | 6 ------ network/test/blob_service_test.go | 1 - network/test/middleware_test.go | 1 - network/test/unicast_authorization_test.go | 1 - 6 files changed, 43 deletions(-) diff --git a/network/codec/cbor/codec_test.go b/network/codec/cbor/codec_test.go index 8d3c555b87d..72c91d1a817 100644 --- a/network/codec/cbor/codec_test.go +++ b/network/codec/cbor/codec_test.go @@ -12,12 +12,8 @@ import ( ) func TestCodec_Decode(t *testing.T) { - t.Parallel() - c := cbor.NewCodec() - t.Run("decodes message successfully", func(t *testing.T) { - t.Parallel() data := unittest.ProposalFixture() encoded, err := c.Encode(data) @@ -29,8 +25,6 @@ func TestCodec_Decode(t *testing.T) { }) t.Run("returns error when data is empty", func(t *testing.T) { - t.Parallel() - decoded, err := c.Decode(nil) assert.Nil(t, decoded) assert.True(t, codec.IsErrInvalidEncoding(err)) @@ -41,8 +35,6 @@ func TestCodec_Decode(t *testing.T) { }) t.Run("returns error when message code is invalid", func(t *testing.T) { - t.Parallel() - decoded, err := c.Decode([]byte{codec.CodeMin.Uint8()}) assert.Nil(t, decoded) assert.True(t, codec.IsErrUnknownMsgCode(err)) @@ -61,16 +53,12 @@ func TestCodec_Decode(t *testing.T) { }) t.Run("returns error when unmarshalling fails - empty", func(t *testing.T) { - t.Parallel() - decoded, err := c.Decode([]byte{codec.CodeBlockProposal.Uint8()}) assert.Nil(t, decoded) assert.True(t, codec.IsErrMsgUnmarshal(err)) }) t.Run("returns error when unmarshalling fails - wrong type", func(t *testing.T) { - t.Parallel() - data := unittest.ProposalFixture() encoded, err := c.Encode(data) require.NoError(t, err) @@ -83,8 +71,6 @@ func TestCodec_Decode(t *testing.T) { }) t.Run("returns error when unmarshalling fails - corrupt", func(t *testing.T) { - t.Parallel() - data := unittest.ProposalFixture() encoded, err := c.Encode(data) require.NoError(t, err) diff --git a/network/codec/cbor/decoder_test.go b/network/codec/cbor/decoder_test.go index 1f71cb5e306..27d450a455e 100644 --- a/network/codec/cbor/decoder_test.go +++ b/network/codec/cbor/decoder_test.go @@ -14,15 +14,11 @@ import ( ) func TestDecoder_Decode(t *testing.T) { - t.Parallel() - c := cbor.NewCodec() blockProposal := unittest.ProposalFixture() t.Run("decodes message successfully", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer err := c.NewEncoder(&buf).Encode(blockProposal) @@ -34,8 +30,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when data is empty", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer // empty buffer @@ -45,8 +39,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when data is empty - nil byte", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer // nil byte @@ -58,8 +50,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when data is empty - cbor nil", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer // explicit cbor encoding of nil @@ -72,8 +62,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when data is empty - cbor empty []byte", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer // explicit cbor encoding of an empty byte slice @@ -86,8 +74,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when message code is invalid", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer // the first byte is the message code, the remaining bytes are the message @@ -113,8 +99,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when unmarshalling fails - empty", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer err := cborcodec.NewCodec().NewEncoder(&buf).Encode([]byte{codec.CodeBlockProposal.Uint8()}) @@ -126,8 +110,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when unmarshalling fails - wrong type", func(t *testing.T) { - t.Parallel() - // first encode the message to bytes with an incorrect type var data bytes.Buffer _ = data.WriteByte(codec.CodeCollectionGuarantee.Uint8()) @@ -147,8 +129,6 @@ func TestDecoder_Decode(t *testing.T) { }) t.Run("returns error when unmarshalling fails - corrupt", func(t *testing.T) { - t.Parallel() - // first encode the message to bytes var data bytes.Buffer _ = data.WriteByte(codec.CodeBlockProposal.Uint8()) diff --git a/network/p2p/utils/ratelimiter/internal/rate_limiter_map_test.go b/network/p2p/utils/ratelimiter/internal/rate_limiter_map_test.go index 5c6a8a0b1d6..79bbe0fad6a 100644 --- a/network/p2p/utils/ratelimiter/internal/rate_limiter_map_test.go +++ b/network/p2p/utils/ratelimiter/internal/rate_limiter_map_test.go @@ -15,7 +15,6 @@ import ( // TestLimiterMap_get checks true is returned for stored items and false for missing items. func TestLimiterMap_get(t *testing.T) { - t.Parallel() m := internal.NewLimiterMap(time.Second, time.Second) peerID := peer.ID("id") m.Store(peerID, rate.NewLimiter(0, 0)) @@ -28,7 +27,6 @@ func TestLimiterMap_get(t *testing.T) { // TestLimiterMap_remove checks the map removes keys as expected. func TestLimiterMap_remove(t *testing.T) { - t.Parallel() m := internal.NewLimiterMap(time.Second, time.Second) peerID := peer.ID("id") m.Store(peerID, rate.NewLimiter(0, 0)) @@ -43,8 +41,6 @@ func TestLimiterMap_remove(t *testing.T) { // TestLimiterMap_cleanup checks the map removes expired keys as expected. func TestLimiterMap_cleanup(t *testing.T) { - t.Parallel() - // set fake ttl to 10 minutes ttl := 10 * time.Minute @@ -92,8 +88,6 @@ func TestLimiterMap_cleanup(t *testing.T) { // TestLimiterMap_cleanupLoopCtxCanceled checks that the Cleanup loop runs when ctx is canceled before cleanup loop exits. func TestLimiterMap_cleanupLoopCtxCanceled(t *testing.T) { - t.Parallel() - // set fake ttl to 10 minutes ttl := 10 * time.Minute diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 2f5b2d1af2c..2852f7d5c48 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -59,7 +59,6 @@ type BlobServiceTestSuite struct { } func TestBlobService(t *testing.T) { - t.Parallel() suite.Run(t, new(BlobServiceTestSuite)) } diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 8a3158df9a0..5e33e577a47 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -102,7 +102,6 @@ type MiddlewareTestSuite struct { // TestMiddlewareTestSuit runs all the test methods in this test suit func TestMiddlewareTestSuite(t *testing.T) { - t.Parallel() suite.Run(t, new(MiddlewareTestSuite)) } diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index f318b2bd327..f220695c4e3 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -57,7 +57,6 @@ type UnicastAuthorizationTestSuite struct { // TestUnicastAuthorizationTestSuite runs all the test methods in this test suit func TestUnicastAuthorizationTestSuite(t *testing.T) { - t.Parallel() suite.Run(t, new(UnicastAuthorizationTestSuite)) } From 2ad6e35647895e942892651b07d24abfdde60ad7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 13:16:30 -0400 Subject: [PATCH 738/815] wip --- network/p2p/network.go | 592 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 592 insertions(+) diff --git a/network/p2p/network.go b/network/p2p/network.go index a3225f59925..341dba5668b 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -1,13 +1,18 @@ package p2p import ( + "bufio" "errors" "fmt" + "io" "sync" "time" + ggio "github.com/gogo/protobuf/io" "github.com/ipfs/go-datastore" + libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/protocol" "github.com/rs/zerolog" @@ -20,9 +25,16 @@ import ( alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/codec" + "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p/blob" + "github.com/onflow/flow-go/network/p2p/ping" + "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/network/queue" "github.com/onflow/flow-go/network/slashing" + "github.com/onflow/flow-go/network/validator" + flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" _ "github.com/onflow/flow-go/utils/binstat" "github.com/onflow/flow-go/utils/logging" ) @@ -42,6 +54,7 @@ type Network struct { *component.ComponentManager sporkId flow.Identifier identityProvider module.IdentityProvider + identityTranslator IDTranslator logger zerolog.Logger codec network.Codec me module.Local @@ -56,6 +69,10 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager slashingViolationsConsumer network.ViolationsConsumer + + bitswapMetrics module.BitswapMetrics + libp2pNode LibP2PNode + previousProtocolStatePeers []peer.AddrInfo } var _ network.Network = &Network{} @@ -581,3 +598,578 @@ func (n *Network) Topology() flow.IdentityList { func (n *Network) ReportMisbehaviorOnChannel(channel channels.Channel, report network.MisbehaviorReport) { n.misbehaviorReportManager.HandleMisbehaviorReport(channel, report) } + +func (n *Network) NewBlobService(channel channels.Channel, ds datastore.Batching, opts ...network.BlobServiceOption) network.BlobService { + return blob.NewBlobService(n.libp2pNode.Host(), n.libp2pNode.Routing(), channel.String(), ds, n.bitswapMetrics, n.logger, opts...) +} + +func (n *Network) NewPingService(pingProtocol protocol.ID, provider network.PingInfoProvider) network.PingService { + return ping.NewPingService(n.libp2pNode.Host(), pingProtocol, n.logger, provider) +} + +func (n *Network) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { + result := make([]peer.ID, 0, len(flowIDs)) + + for _, fid := range flowIDs { + pid, err := n.identityTranslator.GetPeerID(fid) + if err != nil { + // We probably don't need to fail the entire function here, since the other + // translations may still succeed + n.logger.Err(err).Hex("node_id", logging.ID(fid)).Msg("failed to translate to peer ID") + continue + } + + result = append(result, pid) + } + + return result +} + +func (n *Network) UpdateNodeAddresses() { + n.logger.Info().Msg("Updating protocol state node addresses") + + ids := n.Identities() + newInfos, invalid := utils.PeerInfosFromIDs(ids) + + for id, err := range invalid { + n.logger. + Err(err). + Hex("node_id", logging.ID(id)). + Msg("failed to extract peer info from identity") + } + + n.Lock() + defer n.Unlock() + + // set old addresses to expire + for _, oldInfo := range n.previousProtocolStatePeers { + n.libp2pNode.Host().Peerstore().SetAddrs(oldInfo.ID, oldInfo.Addrs, peerstore.TempAddrTTL) + } + + for _, info := range newInfos { + n.libp2pNode.Host().Peerstore().SetAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) + } + + n.previousProtocolStatePeers = newInfos +} + +func (m *Middleware) SetOverlay(ov network.Overlay) { + m.ov = ov +} + +// SetSlashingViolationsConsumer sets the slashing violations consumer. +func (m *Middleware) SetSlashingViolationsConsumer(consumer network.ViolationsConsumer) { + m.slashingViolationsConsumer = consumer +} + +// authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as +// well as who this node can connect to). +// and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components +// of libp2p. +// +// Args: +// none +// Returns: +// - peer.IDSlice: a list of peer IDs that are allowed to connect to this node (and that this node can connect to). Any peer +// not in this list is assumed to be disconnected from this node (if connected) and not allowed to connect to this node. +// This is the guarantee that the underlying libp2p node implementation makes. +func (m *Middleware) authorizedPeers() peer.IDSlice { + peerIDs := make([]peer.ID, 0) + for _, id := range m.peerIDs(m.ov.Topology().NodeIDs()) { + peerAllowed := true + for _, filter := range m.peerManagerFilters { + if err := filter(id); err != nil { + m.log.Debug(). + Err(err). + Str("peer_id", id.String()). + Msg("filtering topology peer") + + peerAllowed = false + break + } + } + + if peerAllowed { + peerIDs = append(peerIDs, id) + } + } + + return peerIDs +} + +func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { + for _, pid := range m.peerIDs(notification.FlowIds) { + m.libP2PNode.OnDisallowListNotification(pid, notification.Cause) + } +} + +func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { + for _, pid := range m.peerIDs(notification.FlowIds) { + m.libP2PNode.OnAllowListNotification(pid, notification.Cause) + } +} + +// SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous +// direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized +// as the router. +// +// Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is +// a more efficient candidate. +// +// The following benign errors can be returned: +// - the peer ID for the target node ID cannot be found. +// - the msg size was too large. +// - failed to send message to peer. +// +// All errors returned from this function can be considered benign. +func (m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { + // since it is a unicast, we only need to get the first peer ID. + peerID, err := m.idTranslator.GetPeerID(msg.TargetIds()[0]) + if err != nil { + return fmt.Errorf("could not find peer id for target id: %w", err) + } + + maxMsgSize := unicastMaxMsgSize(msg.PayloadType()) + if msg.Size() > maxMsgSize { + // message size goes beyond maximum size that the serializer can handle. + // proceeding with this message results in closing the connection by the target side, and + // delivery failure. + return fmt.Errorf("message size %d exceeds configured max message size %d", msg.Size(), maxMsgSize) + } + + maxTimeout := m.unicastMaxMsgDuration(msg.PayloadType()) + + // pass in a context with timeout to make the unicast call fail fast + ctx, cancel := context.WithTimeout(m.ctx, maxTimeout) + defer cancel() + + // protect the underlying connection from being inadvertently pruned by the peer manager while the stream and + // connection creation is being attempted, and remove it from protected list once stream created. + channel, ok := channels.ChannelFromTopic(msg.Topic()) + if !ok { + return fmt.Errorf("could not find channel for topic %s", msg.Topic()) + } + tag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) + m.libP2PNode.Host().ConnManager().Protect(peerID, tag) + defer m.libP2PNode.Host().ConnManager().Unprotect(peerID, tag) + + // create new stream + // streams don't need to be reused and are fairly inexpensive to be created for each send. + // A stream creation does NOT incur an RTT as stream negotiation happens as part of the first message + // sent out the receiver + stream, err := m.libP2PNode.CreateStream(ctx, peerID) + if err != nil { + return fmt.Errorf("failed to create stream for %s: %w", msg.TargetIds()[0], err) + } + + success := false + + defer func() { + if success { + // close the stream immediately + err = stream.Close() + if err != nil { + err = fmt.Errorf("failed to close the stream for %s: %w", msg.TargetIds()[0], err) + } + } else { + resetErr := stream.Reset() + if resetErr != nil { + m.log.Err(resetErr).Msg("failed to reset stream") + } + } + }() + + deadline, _ := ctx.Deadline() + err = stream.SetWriteDeadline(deadline) + if err != nil { + return fmt.Errorf("failed to set write deadline for stream: %w", err) + } + + // create a gogo protobuf writer + bufw := bufio.NewWriter(stream) + writer := ggio.NewDelimitedWriter(bufw) + + err = writer.WriteMsg(msg.Proto()) + if err != nil { + return fmt.Errorf("failed to send message to %s: %w", msg.TargetIds()[0], err) + } + + // flush the stream + err = bufw.Flush() + if err != nil { + return fmt.Errorf("failed to flush stream for %s: %w", msg.TargetIds()[0], err) + } + + success = true + + return nil +} + +// handleIncomingStream handles an incoming stream from a remote peer +// it is a callback that gets called for each incoming stream by libp2p with a new stream object +func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { + // qualify the logger with local and remote address + log := p2putils.StreamLogger(m.log, s) + + log.Info().Msg("incoming stream received") + + success := false + + remotePeer := s.Conn().RemotePeer() + + defer func() { + if success { + err := s.Close() + if err != nil { + log.Err(err).Msg("failed to close stream") + } + } else { + err := s.Reset() + if err != nil { + log.Err(err).Msg("failed to reset stream") + } + } + }() + + // check if peer is currently rate limited before continuing to process stream. + if m.unicastRateLimiters.MessageRateLimiter.IsRateLimited(remotePeer) || m.unicastRateLimiters.BandWidthRateLimiter.IsRateLimited(remotePeer) { + log.Debug(). + Bool(logging.KeySuspicious, true). + Msg("dropping unicast stream from rate limited peer") + return + } + + // TODO: We need to allow per-topic timeouts and message size limits. + // This allows us to configure higher limits for topics on which we expect + // to receive large messages (e.g. Chunk Data Packs), and use the normal + // limits for other topics. In order to enable this, we will need to register + // a separate stream handler for each topic. + ctx, cancel := context.WithTimeout(m.ctx, LargeMsgUnicastTimeout) + defer cancel() + + deadline, _ := ctx.Deadline() + + err := s.SetReadDeadline(deadline) + if err != nil { + log.Err(err).Msg("failed to set read deadline for stream") + return + } + + // create the reader + r := ggio.NewDelimitedReader(s, LargeMsgMaxUnicastMsgSize) + for { + if ctx.Err() != nil { + return + } + + // Note: message fields must not be trusted until explicitly validated + var msg message.Message + // read the next message (blocking call) + err = r.ReadMsg(&msg) + if err != nil { + if err == io.EOF { + break + } + + m.log.Err(err).Msg("failed to read message") + return + } + + channel := channels.Channel(msg.ChannelID) + topic := channels.TopicFromChannel(channel, m.sporkId) + + // ignore messages if node does not have subscription to topic + if !m.libP2PNode.HasSubscription(topic) { + violation := &network.Violation{ + Identity: nil, PeerID: remotePeer.String(), Channel: channel, Protocol: message.ProtocolTypeUnicast, + } + + msgCode, err := codec.MessageCodeFromPayload(msg.Payload) + if err != nil { + violation.Err = err + m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + } + + // msg type is not guaranteed to be correct since it is set by the client + _, what, err := codec.InterfaceFromMessageCode(msgCode) + if err != nil { + violation.Err = err + m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + } + + violation.MsgType = what + violation.Err = ErrUnicastMsgWithoutSub + m.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(violation) + return + } + + // check if unicast messages have reached rate limit before processing next message + if !m.unicastRateLimiters.MessageAllowed(remotePeer) { + return + } + + // check if we can get a role for logging and metrics label if this is not a public channel + role := "" + if !channels.IsPublicChannel(channels.Channel(msg.ChannelID)) { + if identity, ok := m.ov.Identity(remotePeer); ok { + role = identity.Role.String() + } + } + + // check unicast bandwidth rate limiter for peer + if !m.unicastRateLimiters.BandwidthAllowed( + remotePeer, + role, + msg.Size(), + message.MessageType(msg.Payload), + channels.Topic(msg.ChannelID)) { + return + } + + m.wg.Add(1) + go func() { + defer m.wg.Done() + m.processUnicastStreamMessage(remotePeer, &msg) + }() + } + + success = true +} + +// Subscribe subscribes the middleware to a channel. +// No errors are expected during normal operation. +func (m *Middleware) Subscribe(channel channels.Channel) error { + topic := channels.TopicFromChannel(channel, m.sporkId) + + var peerFilter p2p.PeerFilter + var validators []validator.PubSubMessageValidator + if channels.IsPublicChannel(channel) { + // NOTE: for public channels the callback used to check if a node is staked will + // return true for every node. + peerFilter = p2p.AllowAllPeerFilter() + } else { + // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes + validators = append(validators, m.authorizedSenderValidator.PubSubMessageValidator(channel)) + + // NOTE: For non-public channels the libP2P node topic validator will reject + // messages from unstaked nodes. + peerFilter = m.isProtocolParticipant() + } + + topicValidator := flowpubsub.TopicValidator(m.log, peerFilter, validators...) + s, err := m.libP2PNode.Subscribe(topic, topicValidator) + if err != nil { + return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) + } + + // create a new readSubscription with the context of the middleware + rs := newReadSubscription(s, m.processPubSubMessages, m.log) + m.wg.Add(1) + + // kick off the receive loop to continuously receive messages + go func() { + defer m.wg.Done() + rs.receiveLoop(m.ctx) + }() + + // update peers to add some nodes interested in the same topic as direct peers + m.libP2PNode.RequestPeerUpdate() + + return nil +} + +// processPubSubMessages processes messages received from the pubsub subscription. +func (m *Middleware) processPubSubMessages(msg *message.Message, peerID peer.ID) { + m.processAuthenticatedMessage(msg, peerID, message.ProtocolTypePubSub) +} + +// Unsubscribe unsubscribes the middleware from a channel. +// The following benign errors are expected during normal operations from libP2P: +// - the libP2P node fails to unsubscribe to the topic created from the provided channel. +// +// All errors returned from this function can be considered benign. +func (m *Middleware) Unsubscribe(channel channels.Channel) error { + topic := channels.TopicFromChannel(channel, m.sporkId) + return m.libP2PNode.Unsubscribe(topic) +} + +// processUnicastStreamMessage will decode, perform authorized sender validation and process a message +// sent via unicast stream. This func should be invoked in a separate goroutine to avoid creating a message decoding bottleneck. +func (m *Middleware) processUnicastStreamMessage(remotePeer peer.ID, msg *message.Message) { + channel := channels.Channel(msg.ChannelID) + + // TODO: once we've implemented per topic message size limits per the TODO above, + // we can remove this check + maxSize, err := UnicastMaxMsgSizeByCode(msg.Payload) + if err != nil { + m.slashingViolationsConsumer.OnUnknownMsgTypeError(&network.Violation{ + Identity: nil, PeerID: remotePeer.String(), MsgType: "", Channel: channel, Protocol: message.ProtocolTypeUnicast, Err: err, + }) + return + } + if msg.Size() > maxSize { + // message size exceeded + m.log.Error(). + Str("peer_id", remotePeer.String()). + Str("channel", msg.ChannelID). + Int("max_size", maxSize). + Int("size", msg.Size()). + Bool(logging.KeySuspicious, true). + Msg("received message exceeded permissible message maxSize") + return + } + + // if message channel is not public perform authorized sender validation + if !channels.IsPublicChannel(channel) { + messageType, err := m.authorizedSenderValidator.Validate(remotePeer, msg.Payload, channel, message.ProtocolTypeUnicast) + if err != nil { + m.log. + Error(). + Err(err). + Str("peer_id", remotePeer.String()). + Str("type", messageType). + Str("channel", msg.ChannelID). + Msg("unicast authorized sender validation failed") + return + } + } + m.processAuthenticatedMessage(msg, remotePeer, message.ProtocolTypeUnicast) +} + +// processAuthenticatedMessage processes a message and a source (indicated by its peer ID) and eventually passes it to the overlay +// In particular, it populates the `OriginID` field of the message with a Flow ID translated from this source. +func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID peer.ID, protocol message.ProtocolType) { + originId, err := m.idTranslator.GetFlowID(peerID) + if err != nil { + // this error should never happen. by the time the message gets here, the peer should be + // authenticated which means it must be known + m.log.Error(). + Err(err). + Str("peer_id", peerID.String()). + Bool(logging.KeySuspicious, true). + Msg("dropped message from unknown peer") + return + } + + channel := channels.Channel(msg.ChannelID) + decodedMsgPayload, err := m.codec.Decode(msg.Payload) + switch { + case codec.IsErrUnknownMsgCode(err): + // slash peer if message contains unknown message code byte + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): + // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + m.slashingViolationsConsumer.OnInvalidMsgError(violation) + return + case err != nil: + // this condition should never happen and indicates there's a bug + // don't crash as a result of external inputs since that creates a DoS vector + // collect slashing data because this could potentially lead to slashing + err = fmt.Errorf("unexpected error during message validation: %w", err) + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + m.slashingViolationsConsumer.OnUnexpectedError(violation) + return + } + + scope, err := message.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) + if err != nil { + m.log.Error(). + Err(err). + Str("peer_id", peerID.String()). + Str("origin_id", originId.String()). + Msg("could not create incoming message scope") + return + } + + m.processMessage(scope) +} + +// processMessage processes a message and eventually passes it to the overlay +func (m *Middleware) processMessage(scope network.IncomingMessageScope) { + logger := m.log.With(). + Str("channel", scope.Channel().String()). + Str("type", scope.Protocol().String()). + Int("msg_size", scope.Size()). + Hex("origin_id", logging.ID(scope.OriginId())). + Logger() + + // run through all the message validators + for _, v := range m.validators { + // if any one fails, stop message propagation + if !v.Validate(scope) { + logger.Debug().Msg("new message filtered by message validators") + return + } + } + + logger.Debug().Msg("processing new message") + + // if validation passed, send the message to the overlay + err := m.ov.Receive(scope) + if err != nil { + m.log.Error().Err(err).Msg("could not deliver payload") + } +} + +// Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or +// a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best +// effort. +// The following benign errors are expected during normal operations: +// - the msg cannot be marshalled. +// - the msg size exceeds DefaultMaxPubSubMsgSize. +// - the libP2P node fails to publish the message. +// +// All errors returned from this function can be considered benign. +// TODO: DO NOT USE. Publish is ready to be removed from middleware. Use libp2pNode.Publish directly. +func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { + return m.libP2PNode.Publish(m.ctx, msg) +} + +// unicastMaxMsgSize returns the max permissible size for a unicast message +func unicastMaxMsgSize(messageType string) int { + switch messageType { + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": + return LargeMsgMaxUnicastMsgSize + default: + return DefaultMaxUnicastMsgSize + } +} + +// UnicastMaxMsgSizeByCode returns the max permissible size for a unicast message code +func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { + msgCode, err := codec.MessageCodeFromPayload(payload) + if err != nil { + return 0, err + } + _, messageType, err := codec.InterfaceFromMessageCode(msgCode) + if err != nil { + return 0, err + } + + maxSize := unicastMaxMsgSize(messageType) + return maxSize, nil +} + +// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete +func (m *Middleware) unicastMaxMsgDuration(messageType string) time.Duration { + switch messageType { + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": + if LargeMsgUnicastTimeout > m.unicastMessageTimeout { + return LargeMsgUnicastTimeout + } + return m.unicastMessageTimeout + default: + return m.unicastMessageTimeout + } +} From 21d627d30b2b9f7fb273c030ab1b3d12393d8b37 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 13:16:30 -0400 Subject: [PATCH 739/815] Revert "wip" This reverts commit 2ad6e35647895e942892651b07d24abfdde60ad7. --- network/p2p/network.go | 592 ----------------------------------------- 1 file changed, 592 deletions(-) diff --git a/network/p2p/network.go b/network/p2p/network.go index 341dba5668b..a3225f59925 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -1,18 +1,13 @@ package p2p import ( - "bufio" "errors" "fmt" - "io" "sync" "time" - ggio "github.com/gogo/protobuf/io" "github.com/ipfs/go-datastore" - libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/protocol" "github.com/rs/zerolog" @@ -25,16 +20,9 @@ import ( alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/codec" - "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/p2p/blob" - "github.com/onflow/flow-go/network/p2p/ping" - "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/network/queue" "github.com/onflow/flow-go/network/slashing" - "github.com/onflow/flow-go/network/validator" - flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" _ "github.com/onflow/flow-go/utils/binstat" "github.com/onflow/flow-go/utils/logging" ) @@ -54,7 +42,6 @@ type Network struct { *component.ComponentManager sporkId flow.Identifier identityProvider module.IdentityProvider - identityTranslator IDTranslator logger zerolog.Logger codec network.Codec me module.Local @@ -69,10 +56,6 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager slashingViolationsConsumer network.ViolationsConsumer - - bitswapMetrics module.BitswapMetrics - libp2pNode LibP2PNode - previousProtocolStatePeers []peer.AddrInfo } var _ network.Network = &Network{} @@ -598,578 +581,3 @@ func (n *Network) Topology() flow.IdentityList { func (n *Network) ReportMisbehaviorOnChannel(channel channels.Channel, report network.MisbehaviorReport) { n.misbehaviorReportManager.HandleMisbehaviorReport(channel, report) } - -func (n *Network) NewBlobService(channel channels.Channel, ds datastore.Batching, opts ...network.BlobServiceOption) network.BlobService { - return blob.NewBlobService(n.libp2pNode.Host(), n.libp2pNode.Routing(), channel.String(), ds, n.bitswapMetrics, n.logger, opts...) -} - -func (n *Network) NewPingService(pingProtocol protocol.ID, provider network.PingInfoProvider) network.PingService { - return ping.NewPingService(n.libp2pNode.Host(), pingProtocol, n.logger, provider) -} - -func (n *Network) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { - result := make([]peer.ID, 0, len(flowIDs)) - - for _, fid := range flowIDs { - pid, err := n.identityTranslator.GetPeerID(fid) - if err != nil { - // We probably don't need to fail the entire function here, since the other - // translations may still succeed - n.logger.Err(err).Hex("node_id", logging.ID(fid)).Msg("failed to translate to peer ID") - continue - } - - result = append(result, pid) - } - - return result -} - -func (n *Network) UpdateNodeAddresses() { - n.logger.Info().Msg("Updating protocol state node addresses") - - ids := n.Identities() - newInfos, invalid := utils.PeerInfosFromIDs(ids) - - for id, err := range invalid { - n.logger. - Err(err). - Hex("node_id", logging.ID(id)). - Msg("failed to extract peer info from identity") - } - - n.Lock() - defer n.Unlock() - - // set old addresses to expire - for _, oldInfo := range n.previousProtocolStatePeers { - n.libp2pNode.Host().Peerstore().SetAddrs(oldInfo.ID, oldInfo.Addrs, peerstore.TempAddrTTL) - } - - for _, info := range newInfos { - n.libp2pNode.Host().Peerstore().SetAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) - } - - n.previousProtocolStatePeers = newInfos -} - -func (m *Middleware) SetOverlay(ov network.Overlay) { - m.ov = ov -} - -// SetSlashingViolationsConsumer sets the slashing violations consumer. -func (m *Middleware) SetSlashingViolationsConsumer(consumer network.ViolationsConsumer) { - m.slashingViolationsConsumer = consumer -} - -// authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as -// well as who this node can connect to). -// and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components -// of libp2p. -// -// Args: -// none -// Returns: -// - peer.IDSlice: a list of peer IDs that are allowed to connect to this node (and that this node can connect to). Any peer -// not in this list is assumed to be disconnected from this node (if connected) and not allowed to connect to this node. -// This is the guarantee that the underlying libp2p node implementation makes. -func (m *Middleware) authorizedPeers() peer.IDSlice { - peerIDs := make([]peer.ID, 0) - for _, id := range m.peerIDs(m.ov.Topology().NodeIDs()) { - peerAllowed := true - for _, filter := range m.peerManagerFilters { - if err := filter(id); err != nil { - m.log.Debug(). - Err(err). - Str("peer_id", id.String()). - Msg("filtering topology peer") - - peerAllowed = false - break - } - } - - if peerAllowed { - peerIDs = append(peerIDs, id) - } - } - - return peerIDs -} - -func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.libP2PNode.OnDisallowListNotification(pid, notification.Cause) - } -} - -func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.libP2PNode.OnAllowListNotification(pid, notification.Cause) - } -} - -// SendDirect sends msg on a 1-1 direct connection to the target ID. It models a guaranteed delivery asynchronous -// direct one-to-one connection on the underlying network. No intermediate node on the overlay is utilized -// as the router. -// -// Dispatch should be used whenever guaranteed delivery to a specific target is required. Otherwise, Publish is -// a more efficient candidate. -// -// The following benign errors can be returned: -// - the peer ID for the target node ID cannot be found. -// - the msg size was too large. -// - failed to send message to peer. -// -// All errors returned from this function can be considered benign. -func (m *Middleware) SendDirect(msg network.OutgoingMessageScope) error { - // since it is a unicast, we only need to get the first peer ID. - peerID, err := m.idTranslator.GetPeerID(msg.TargetIds()[0]) - if err != nil { - return fmt.Errorf("could not find peer id for target id: %w", err) - } - - maxMsgSize := unicastMaxMsgSize(msg.PayloadType()) - if msg.Size() > maxMsgSize { - // message size goes beyond maximum size that the serializer can handle. - // proceeding with this message results in closing the connection by the target side, and - // delivery failure. - return fmt.Errorf("message size %d exceeds configured max message size %d", msg.Size(), maxMsgSize) - } - - maxTimeout := m.unicastMaxMsgDuration(msg.PayloadType()) - - // pass in a context with timeout to make the unicast call fail fast - ctx, cancel := context.WithTimeout(m.ctx, maxTimeout) - defer cancel() - - // protect the underlying connection from being inadvertently pruned by the peer manager while the stream and - // connection creation is being attempted, and remove it from protected list once stream created. - channel, ok := channels.ChannelFromTopic(msg.Topic()) - if !ok { - return fmt.Errorf("could not find channel for topic %s", msg.Topic()) - } - tag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) - m.libP2PNode.Host().ConnManager().Protect(peerID, tag) - defer m.libP2PNode.Host().ConnManager().Unprotect(peerID, tag) - - // create new stream - // streams don't need to be reused and are fairly inexpensive to be created for each send. - // A stream creation does NOT incur an RTT as stream negotiation happens as part of the first message - // sent out the receiver - stream, err := m.libP2PNode.CreateStream(ctx, peerID) - if err != nil { - return fmt.Errorf("failed to create stream for %s: %w", msg.TargetIds()[0], err) - } - - success := false - - defer func() { - if success { - // close the stream immediately - err = stream.Close() - if err != nil { - err = fmt.Errorf("failed to close the stream for %s: %w", msg.TargetIds()[0], err) - } - } else { - resetErr := stream.Reset() - if resetErr != nil { - m.log.Err(resetErr).Msg("failed to reset stream") - } - } - }() - - deadline, _ := ctx.Deadline() - err = stream.SetWriteDeadline(deadline) - if err != nil { - return fmt.Errorf("failed to set write deadline for stream: %w", err) - } - - // create a gogo protobuf writer - bufw := bufio.NewWriter(stream) - writer := ggio.NewDelimitedWriter(bufw) - - err = writer.WriteMsg(msg.Proto()) - if err != nil { - return fmt.Errorf("failed to send message to %s: %w", msg.TargetIds()[0], err) - } - - // flush the stream - err = bufw.Flush() - if err != nil { - return fmt.Errorf("failed to flush stream for %s: %w", msg.TargetIds()[0], err) - } - - success = true - - return nil -} - -// handleIncomingStream handles an incoming stream from a remote peer -// it is a callback that gets called for each incoming stream by libp2p with a new stream object -func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { - // qualify the logger with local and remote address - log := p2putils.StreamLogger(m.log, s) - - log.Info().Msg("incoming stream received") - - success := false - - remotePeer := s.Conn().RemotePeer() - - defer func() { - if success { - err := s.Close() - if err != nil { - log.Err(err).Msg("failed to close stream") - } - } else { - err := s.Reset() - if err != nil { - log.Err(err).Msg("failed to reset stream") - } - } - }() - - // check if peer is currently rate limited before continuing to process stream. - if m.unicastRateLimiters.MessageRateLimiter.IsRateLimited(remotePeer) || m.unicastRateLimiters.BandWidthRateLimiter.IsRateLimited(remotePeer) { - log.Debug(). - Bool(logging.KeySuspicious, true). - Msg("dropping unicast stream from rate limited peer") - return - } - - // TODO: We need to allow per-topic timeouts and message size limits. - // This allows us to configure higher limits for topics on which we expect - // to receive large messages (e.g. Chunk Data Packs), and use the normal - // limits for other topics. In order to enable this, we will need to register - // a separate stream handler for each topic. - ctx, cancel := context.WithTimeout(m.ctx, LargeMsgUnicastTimeout) - defer cancel() - - deadline, _ := ctx.Deadline() - - err := s.SetReadDeadline(deadline) - if err != nil { - log.Err(err).Msg("failed to set read deadline for stream") - return - } - - // create the reader - r := ggio.NewDelimitedReader(s, LargeMsgMaxUnicastMsgSize) - for { - if ctx.Err() != nil { - return - } - - // Note: message fields must not be trusted until explicitly validated - var msg message.Message - // read the next message (blocking call) - err = r.ReadMsg(&msg) - if err != nil { - if err == io.EOF { - break - } - - m.log.Err(err).Msg("failed to read message") - return - } - - channel := channels.Channel(msg.ChannelID) - topic := channels.TopicFromChannel(channel, m.sporkId) - - // ignore messages if node does not have subscription to topic - if !m.libP2PNode.HasSubscription(topic) { - violation := &network.Violation{ - Identity: nil, PeerID: remotePeer.String(), Channel: channel, Protocol: message.ProtocolTypeUnicast, - } - - msgCode, err := codec.MessageCodeFromPayload(msg.Payload) - if err != nil { - violation.Err = err - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - } - - // msg type is not guaranteed to be correct since it is set by the client - _, what, err := codec.InterfaceFromMessageCode(msgCode) - if err != nil { - violation.Err = err - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - } - - violation.MsgType = what - violation.Err = ErrUnicastMsgWithoutSub - m.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(violation) - return - } - - // check if unicast messages have reached rate limit before processing next message - if !m.unicastRateLimiters.MessageAllowed(remotePeer) { - return - } - - // check if we can get a role for logging and metrics label if this is not a public channel - role := "" - if !channels.IsPublicChannel(channels.Channel(msg.ChannelID)) { - if identity, ok := m.ov.Identity(remotePeer); ok { - role = identity.Role.String() - } - } - - // check unicast bandwidth rate limiter for peer - if !m.unicastRateLimiters.BandwidthAllowed( - remotePeer, - role, - msg.Size(), - message.MessageType(msg.Payload), - channels.Topic(msg.ChannelID)) { - return - } - - m.wg.Add(1) - go func() { - defer m.wg.Done() - m.processUnicastStreamMessage(remotePeer, &msg) - }() - } - - success = true -} - -// Subscribe subscribes the middleware to a channel. -// No errors are expected during normal operation. -func (m *Middleware) Subscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, m.sporkId) - - var peerFilter p2p.PeerFilter - var validators []validator.PubSubMessageValidator - if channels.IsPublicChannel(channel) { - // NOTE: for public channels the callback used to check if a node is staked will - // return true for every node. - peerFilter = p2p.AllowAllPeerFilter() - } else { - // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes - validators = append(validators, m.authorizedSenderValidator.PubSubMessageValidator(channel)) - - // NOTE: For non-public channels the libP2P node topic validator will reject - // messages from unstaked nodes. - peerFilter = m.isProtocolParticipant() - } - - topicValidator := flowpubsub.TopicValidator(m.log, peerFilter, validators...) - s, err := m.libP2PNode.Subscribe(topic, topicValidator) - if err != nil { - return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) - } - - // create a new readSubscription with the context of the middleware - rs := newReadSubscription(s, m.processPubSubMessages, m.log) - m.wg.Add(1) - - // kick off the receive loop to continuously receive messages - go func() { - defer m.wg.Done() - rs.receiveLoop(m.ctx) - }() - - // update peers to add some nodes interested in the same topic as direct peers - m.libP2PNode.RequestPeerUpdate() - - return nil -} - -// processPubSubMessages processes messages received from the pubsub subscription. -func (m *Middleware) processPubSubMessages(msg *message.Message, peerID peer.ID) { - m.processAuthenticatedMessage(msg, peerID, message.ProtocolTypePubSub) -} - -// Unsubscribe unsubscribes the middleware from a channel. -// The following benign errors are expected during normal operations from libP2P: -// - the libP2P node fails to unsubscribe to the topic created from the provided channel. -// -// All errors returned from this function can be considered benign. -func (m *Middleware) Unsubscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, m.sporkId) - return m.libP2PNode.Unsubscribe(topic) -} - -// processUnicastStreamMessage will decode, perform authorized sender validation and process a message -// sent via unicast stream. This func should be invoked in a separate goroutine to avoid creating a message decoding bottleneck. -func (m *Middleware) processUnicastStreamMessage(remotePeer peer.ID, msg *message.Message) { - channel := channels.Channel(msg.ChannelID) - - // TODO: once we've implemented per topic message size limits per the TODO above, - // we can remove this check - maxSize, err := UnicastMaxMsgSizeByCode(msg.Payload) - if err != nil { - m.slashingViolationsConsumer.OnUnknownMsgTypeError(&network.Violation{ - Identity: nil, PeerID: remotePeer.String(), MsgType: "", Channel: channel, Protocol: message.ProtocolTypeUnicast, Err: err, - }) - return - } - if msg.Size() > maxSize { - // message size exceeded - m.log.Error(). - Str("peer_id", remotePeer.String()). - Str("channel", msg.ChannelID). - Int("max_size", maxSize). - Int("size", msg.Size()). - Bool(logging.KeySuspicious, true). - Msg("received message exceeded permissible message maxSize") - return - } - - // if message channel is not public perform authorized sender validation - if !channels.IsPublicChannel(channel) { - messageType, err := m.authorizedSenderValidator.Validate(remotePeer, msg.Payload, channel, message.ProtocolTypeUnicast) - if err != nil { - m.log. - Error(). - Err(err). - Str("peer_id", remotePeer.String()). - Str("type", messageType). - Str("channel", msg.ChannelID). - Msg("unicast authorized sender validation failed") - return - } - } - m.processAuthenticatedMessage(msg, remotePeer, message.ProtocolTypeUnicast) -} - -// processAuthenticatedMessage processes a message and a source (indicated by its peer ID) and eventually passes it to the overlay -// In particular, it populates the `OriginID` field of the message with a Flow ID translated from this source. -func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID peer.ID, protocol message.ProtocolType) { - originId, err := m.idTranslator.GetFlowID(peerID) - if err != nil { - // this error should never happen. by the time the message gets here, the peer should be - // authenticated which means it must be known - m.log.Error(). - Err(err). - Str("peer_id", peerID.String()). - Bool(logging.KeySuspicious, true). - Msg("dropped message from unknown peer") - return - } - - channel := channels.Channel(msg.ChannelID) - decodedMsgPayload, err := m.codec.Decode(msg.Payload) - switch { - case codec.IsErrUnknownMsgCode(err): - // slash peer if message contains unknown message code byte - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): - // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnInvalidMsgError(violation) - return - case err != nil: - // this condition should never happen and indicates there's a bug - // don't crash as a result of external inputs since that creates a DoS vector - // collect slashing data because this could potentially lead to slashing - err = fmt.Errorf("unexpected error during message validation: %w", err) - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnUnexpectedError(violation) - return - } - - scope, err := message.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) - if err != nil { - m.log.Error(). - Err(err). - Str("peer_id", peerID.String()). - Str("origin_id", originId.String()). - Msg("could not create incoming message scope") - return - } - - m.processMessage(scope) -} - -// processMessage processes a message and eventually passes it to the overlay -func (m *Middleware) processMessage(scope network.IncomingMessageScope) { - logger := m.log.With(). - Str("channel", scope.Channel().String()). - Str("type", scope.Protocol().String()). - Int("msg_size", scope.Size()). - Hex("origin_id", logging.ID(scope.OriginId())). - Logger() - - // run through all the message validators - for _, v := range m.validators { - // if any one fails, stop message propagation - if !v.Validate(scope) { - logger.Debug().Msg("new message filtered by message validators") - return - } - } - - logger.Debug().Msg("processing new message") - - // if validation passed, send the message to the overlay - err := m.ov.Receive(scope) - if err != nil { - m.log.Error().Err(err).Msg("could not deliver payload") - } -} - -// Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or -// a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best -// effort. -// The following benign errors are expected during normal operations: -// - the msg cannot be marshalled. -// - the msg size exceeds DefaultMaxPubSubMsgSize. -// - the libP2P node fails to publish the message. -// -// All errors returned from this function can be considered benign. -// TODO: DO NOT USE. Publish is ready to be removed from middleware. Use libp2pNode.Publish directly. -func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { - return m.libP2PNode.Publish(m.ctx, msg) -} - -// unicastMaxMsgSize returns the max permissible size for a unicast message -func unicastMaxMsgSize(messageType string) int { - switch messageType { - case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": - return LargeMsgMaxUnicastMsgSize - default: - return DefaultMaxUnicastMsgSize - } -} - -// UnicastMaxMsgSizeByCode returns the max permissible size for a unicast message code -func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { - msgCode, err := codec.MessageCodeFromPayload(payload) - if err != nil { - return 0, err - } - _, messageType, err := codec.InterfaceFromMessageCode(msgCode) - if err != nil { - return 0, err - } - - maxSize := unicastMaxMsgSize(messageType) - return maxSize, nil -} - -// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete -func (m *Middleware) unicastMaxMsgDuration(messageType string) time.Duration { - switch messageType { - case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": - if LargeMsgUnicastTimeout > m.unicastMessageTimeout { - return LargeMsgUnicastTimeout - } - return m.unicastMessageTimeout - default: - return m.unicastMessageTimeout - } -} From 86d6d225e3b8b784d671894a0d28ebf1421397ac Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 13:23:12 -0400 Subject: [PATCH 740/815] quarantines flakey tests --- network/test/echoengine_test.go | 1 + network/test/meshengine_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 4d798c97f03..e51381ad359 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -180,6 +180,7 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Publish methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageDifferentChan(suite.Publish) } diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index e229be4b79d..23bc1894dc0 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -181,12 +181,14 @@ func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { // TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using // the Multicast method of conduits. func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) } // TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the // Publish method of conduits. func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) } From 141ccb888c3fe2033bac98f6f48723c5b444207b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 14:10:08 -0400 Subject: [PATCH 741/815] moves read subscription to a separate package --- network/p2p/internal/readSubscription.go | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 network/p2p/internal/readSubscription.go diff --git a/network/p2p/internal/readSubscription.go b/network/p2p/internal/readSubscription.go new file mode 100644 index 00000000000..38bfcbb0511 --- /dev/null +++ b/network/p2p/internal/readSubscription.go @@ -0,0 +1,82 @@ +package internal + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/libp2p/go-libp2p/core/peer" + + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" + validator "github.com/onflow/flow-go/network/validator/pubsub" + "github.com/onflow/flow-go/utils/logging" +) + +// ReadSubscriptionCallBackFunction the callback called when a new message is received on the read subscription +type ReadSubscriptionCallBackFunction func(msg *message.Message, peerID peer.ID) + +// ReadSubscription reads the messages coming in on the subscription and calls the given callback until +// the context of the subscription is cancelled. +type ReadSubscription struct { + log zerolog.Logger + sub p2p.Subscription + callback ReadSubscriptionCallBackFunction +} + +// NewReadSubscription reads the messages coming in on the subscription +func NewReadSubscription(sub p2p.Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *ReadSubscription { + r := ReadSubscription{ + log: log.With().Str("channel", sub.Topic()).Logger(), + sub: sub, + callback: callback, + } + + return &r +} + +// ReceiveLoop must be run in a goroutine. It continuously receives +// messages for the topic and calls the callback synchronously +func (r *ReadSubscription) ReceiveLoop(ctx context.Context) { + defer r.log.Debug().Msg("exiting receive routine") + + for { + // read the next message from libp2p's subscription (blocking call) + rawMsg, err := r.sub.Next(ctx) + + if err != nil { + // middleware may have cancelled the context + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return + } + + // subscription may have just been cancelled if node is being stopped or the topic has been unsubscribed, + // don't log error in that case + // (https://github.com/ipsn/go-ipfs/blob/master/gxlibs/github.com/libp2p/go-libp2p-pubsub/pubsub.go#L435) + if strings.Contains(err.Error(), "subscription cancelled") { + return + } + + // log any other error + r.log.Err(err).Msg("failed to read subscription message") + + return + } + + validatorData, ok := rawMsg.ValidatorData.(validator.TopicValidatorData) + if !ok { + r.log.Error(). + Str("raw_msg", rawMsg.String()). + Bool(logging.KeySuspicious, true). + Str("received_validator_data_type", fmt.Sprintf("%T", rawMsg.ValidatorData)). + Msg("[BUG] validator data missing!") + return + } + + // call the callback + r.callback(validatorData.Message, validatorData.From) + } +} From 20abebf219d8bef4fb04b117128a5b5c606f3972 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 14:10:18 -0400 Subject: [PATCH 742/815] merges middleware with network --- network/p2p/network.go | 659 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 589 insertions(+), 70 deletions(-) diff --git a/network/p2p/network.go b/network/p2p/network.go index c53b993122f..6b74071122b 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -5,14 +5,15 @@ import ( "context" "errors" "fmt" + "io" "sync" "time" ggio "github.com/gogo/protobuf/io" - libp2pnet "github.com/libp2p/go-libp2p/core/network" - "github.com/ipfs/go-datastore" + libp2pnet "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/protocol" "github.com/rs/zerolog" @@ -25,8 +26,18 @@ import ( alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/codec" + "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p/blob" + "github.com/onflow/flow-go/network/p2p/internal" + "github.com/onflow/flow-go/network/p2p/ping" + "github.com/onflow/flow-go/network/p2p/unicast/protocols" + "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" + "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/network/queue" + "github.com/onflow/flow-go/network/validator" + flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" _ "github.com/onflow/flow-go/utils/binstat" "github.com/onflow/flow-go/utils/logging" ) @@ -53,6 +64,13 @@ const ( LargeMsgUnicastTimeout = 1000 * time.Second ) +var ( + // ErrUnicastMsgWithoutSub error is provided to the slashing violations consumer in the case where + // the network receives a message via unicast but does not have a corresponding subscription for + // the channel in that message. + ErrUnicastMsgWithoutSub = errors.New("networking layer does not have subscription for the channel ID indicated in the unicast message received") +) + // NotEjectedFilter is an identity filter that, when applied to the identity // table at a given snapshot, returns all nodes that we should communicate with // over the networking layer. @@ -65,6 +83,12 @@ var NotEjectedFilter = filter.Not(filter.Ejected) // the protocols for handshakes, authentication, gossiping and heartbeats. type Network struct { sync.RWMutex + + // TODO: using a waitgroup here doesn't actually guarantee that we'll wait for all + // goroutines to exit, because new goroutines could be started after we've already + // returned from wg.Wait(). We need to solve this the right way using ComponentManager + // and worker routines. + wg sync.WaitGroup *component.ComponentManager ctx context.Context sporkId flow.Identifier @@ -73,7 +97,6 @@ type Network struct { logger zerolog.Logger codec network.Codec me module.Local - mw network.Middleware metrics module.NetworkCoreMetrics receiveCache *netcache.ReceiveCache // used to deduplicate incoming messages queue network.MessageQueue @@ -84,6 +107,17 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager unicastMessageTimeout time.Duration + + libP2PNode LibP2PNode + idTranslator IDTranslator + bitswapMetrics module.BitswapMetrics + previousProtocolStatePeers []peer.AddrInfo + slashingViolationsConsumer network.ViolationsConsumer + peerManagerFilters []PeerFilter + unicastRateLimiters *ratelimit.RateLimiters + validators []network.MessageValidator + authorizedSenderValidator *validator.AuthorizedSenderValidator + preferredUnicasts []protocols.ProtocolName } var _ network.Network = &Network{} @@ -117,20 +151,29 @@ var ErrNetworkShutdown = errors.New("network has already shutdown") // NetworkConfig is a configuration struct for the network. It contains all the // necessary components to create a new network. type NetworkConfig struct { - Logger zerolog.Logger - Codec network.Codec - Me module.Local - MiddlewareFactory func() (network.Middleware, error) - Topology network.Topology - SubscriptionManager network.SubscriptionManager - Metrics module.NetworkCoreMetrics - IdentityProvider module.IdentityProvider - IdentityTranslator IDTranslator - ReceiveCache *netcache.ReceiveCache - ConduitFactory network.ConduitFactory - AlspCfg *alspmgr.MisbehaviorReportManagerConfig - SporkId flow.Identifier - UnicastMessageTimeout time.Duration + Logger zerolog.Logger + Codec network.Codec + Me module.Local + Topology network.Topology + SubscriptionManager network.SubscriptionManager + Metrics module.NetworkCoreMetrics + IdentityProvider module.IdentityProvider + IdentityTranslator IDTranslator + ReceiveCache *netcache.ReceiveCache + ConduitFactory network.ConduitFactory + AlspCfg *alspmgr.MisbehaviorReportManagerConfig + SporkId flow.Identifier + UnicastMessageTimeout time.Duration + Libp2pNode LibP2PNode + BitSwapMetrics module.BitswapMetrics + SlashingViolationConsumerFactory func() network.ViolationsConsumer +} + +// Validate validates the configuration, and sets default values for any missing fields. +func (cfg *NetworkConfig) Validate() { + if cfg.UnicastMessageTimeout <= 0 { + cfg.UnicastMessageTimeout = DefaultUnicastTimeout + } } // NetworkConfigOption is a function that can be used to override network config parmeters. @@ -184,20 +227,12 @@ func WithAlspManager(mgr network.MisbehaviorReportManager) NetworkOption { // using the given state & cache interfaces to track volatile information. // csize determines the size of the cache dedicated to keep track of received messages func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { - mw, err := param.MiddlewareFactory() - if err != nil { - return nil, fmt.Errorf("could not create middleware: %w", err) - } - misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg, mw) - if err != nil { - return nil, fmt.Errorf("could not create misbehavior report manager: %w", err) - } + param.Validate() n := &Network{ logger: param.Logger.With().Str("component", "network").Logger(), codec: param.Codec, me: param.Me, - mw: mw, receiveCache: param.ReceiveCache, topology: param.Topology, metrics: param.Metrics, @@ -206,58 +241,102 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { conduitFactory: param.ConduitFactory, registerEngineRequests: make(chan *registerEngineRequest), registerBlobServiceRequests: make(chan *registerBlobServiceRequest), - misbehaviorReportManager: misbehaviorMngr, sporkId: param.SporkId, identityTranslator: param.IdentityTranslator, unicastMessageTimeout: param.UnicastMessageTimeout, } + misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg, n) + if err != nil { + return nil, fmt.Errorf("could not create misbehavior report manager: %w", err) + } + n.misbehaviorReportManager = misbehaviorMngr + for _, opt := range opts { opt(n) } - n.mw.SetOverlay(n) - if err := n.conduitFactory.RegisterAdapter(n); err != nil { return nil, fmt.Errorf("could not register network adapter: %w", err) } - n.ComponentManager = component.NewComponentManagerBuilder(). - AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - n.logger.Debug().Msg("starting misbehavior manager") - n.misbehaviorReportManager.Start(ctx) + builder := component.NewComponentManagerBuilder() + builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + n.logger.Debug().Msg("starting misbehavior manager") + n.misbehaviorReportManager.Start(ctx) - select { - case <-n.misbehaviorReportManager.Ready(): - n.logger.Debug().Msg("misbehavior manager is ready") - ready() - case <-ctx.Done(): - // jumps to the end of the select statement to let a graceful shutdown. - } + select { + case <-n.misbehaviorReportManager.Ready(): + n.logger.Debug().Msg("misbehavior manager is ready") + ready() + case <-ctx.Done(): + // jumps to the end of the select statement to let a graceful shutdown. + } + + <-ctx.Done() + n.logger.Debug().Msg("stopping misbehavior manager") + <-n.misbehaviorReportManager.Done() + n.logger.Debug().Msg("misbehavior manager stopped") + }) + builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + n.logger.Debug().Msg("setting up network context") + n.ctx = ctx + + ready() - <-ctx.Done() - n.logger.Debug().Msg("stopping misbehavior manager") - <-n.misbehaviorReportManager.Done() - n.logger.Debug().Msg("misbehavior manager stopped") - }). - AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - n.logger.Debug().Msg("setting up network context") - n.ctx = ctx + <-ctx.Done() + n.logger.Debug().Msg("network context is done") + }) + for _, limiter := range n.unicastRateLimiters.Limiters() { + rateLimiter := limiter + builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + rateLimiter.Start(ctx) + <-rateLimiter.Ready() ready() + <-rateLimiter.Done() + }) + } + + builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + // creation of slashing violations consumer should be postponed till here where the middleware + // is start and the overlay is set. + n.slashingViolationsConsumer = param.SlashingViolationConsumerFactory() + + n.authorizedSenderValidator = validator.NewAuthorizedSenderValidator( + n.logger, + n.slashingViolationsConsumer, + n.Identity) + + err := n.libP2PNode.WithDefaultUnicastProtocol(n.handleIncomingStream, n.preferredUnicasts) + if err != nil { + ctx.Throw(fmt.Errorf("could not register preferred unicast protocols on libp2p node: %w", err)) + } + + n.UpdateNodeAddresses() + n.libP2PNode.WithPeersProvider(n.authorizedPeers) + + ready() + + <-ctx.Done() + n.logger.Info().Str("component", "middleware").Msg("stopping subroutines, blocking on read connection loops to end") - <-ctx.Done() - n.logger.Debug().Msg("network context is done") - }). - AddWorker(n.runMiddleware). - AddWorker(n.processRegisterEngineRequests). - AddWorker(n.processRegisterBlobServiceRequests).Build() + // wait for the readConnection and readSubscription routines to stop + n.wg.Wait() + n.logger.Info().Str("component", "middleware").Msg("stopped subroutines") + }) + + builder.AddWorker(n.createInboundMessageQueue) + builder.AddWorker(n.processRegisterEngineRequests) + builder.AddWorker(n.processRegisterBlobServiceRequests) + n.ComponentManager = builder.Build() return n, nil } func (n *Network) processRegisterEngineRequests(parent irrecoverable.SignalerContext, ready component.ReadyFunc) { - <-n.mw.Ready() + <-n.libP2PNode.Ready() ready() for { @@ -281,7 +360,7 @@ func (n *Network) processRegisterEngineRequests(parent irrecoverable.SignalerCon } func (n *Network) processRegisterBlobServiceRequests(parent irrecoverable.SignalerContext, ready component.ReadyFunc) { - <-n.mw.Ready() + <-n.libP2PNode.Ready() ready() for { @@ -304,20 +383,12 @@ func (n *Network) processRegisterBlobServiceRequests(parent irrecoverable.Signal } } -func (n *Network) runMiddleware(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - // setup the message queue - // create priority queue +// createInboundMessageQueue creates the queue that will be used to process incoming messages. +func (n *Network) createInboundMessageQueue(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { n.queue = queue.NewMessageQueue(ctx, queue.GetEventPriority, n.metrics) - - // create workers to read from the queue and call queueSubmitFunc queue.CreateQueueWorkers(ctx, queue.DefaultNumWorkers, n.queue, n.queueSubmitFunc) - n.mw.Start(ctx) - <-n.mw.Ready() - ready() - - <-n.mw.Done() } func (n *Network) handleRegisterEngineRequest(parent irrecoverable.SignalerContext, channel channels.Channel, engine network.MessageProcessor) (network.Conduit, error) { @@ -344,7 +415,7 @@ func (n *Network) handleRegisterEngineRequest(parent irrecoverable.SignalerConte } func (n *Network) handleRegisterBlobServiceRequest(parent irrecoverable.SignalerContext, channel channels.Channel, ds datastore.Batching, opts []network.BlobServiceOption) (network.BlobService, error) { - bs := n.mw.NewBlobService(channel, ds, opts...) + bs := blob.NewBlobService(n.libP2PNode.Host(), n.libP2PNode.Routing(), channel.String(), ds, n.bitswapMetrics, n.logger, opts...) // start the blob service using the network's context bs.Start(parent) @@ -380,7 +451,7 @@ func (n *Network) RegisterPingService(pingProtocol protocol.ID, provider network case <-n.ComponentManager.ShutdownSignal(): return nil, ErrNetworkShutdown default: - return n.mw.NewPingService(pingProtocol, provider), nil + return ping.NewPingService(n.libP2PNode.Host(), pingProtocol, n.logger, provider), nil } } @@ -517,7 +588,7 @@ func (n *Network) UnicastOnChannel(channel channels.Channel, payload interface{} } streamProtectionTag := fmt.Sprintf("%v:%v", channel, msg.PayloadType()) - err = n.mw.OpenProtectedStream(ctx, peerID, streamProtectionTag, func(stream libp2pnet.Stream) error { + err = n.libP2PNode.OpenProtectedStream(ctx, peerID, streamProtectionTag, func(stream libp2pnet.Stream) error { bufw := bufio.NewWriter(stream) writer := ggio.NewDelimitedWriter(bufw) @@ -612,7 +683,7 @@ func (n *Network) sendOnChannel(channel channels.Channel, msg interface{}, targe // publish the message through the channel, however, the message // is only restricted to targetIDs (if they subscribed to channel). - err = n.mw.Publish(scope) + err = n.libP2PNode.Publish(n.ctx, scope) if err != nil { return fmt.Errorf("failed to send message on channel %s: %w", channel, err) } @@ -695,3 +766,451 @@ func (n *Network) unicastMaxMsgDuration(messageType string) time.Duration { return n.unicastMessageTimeout } } + +func DefaultValidators(log zerolog.Logger, flowID flow.Identifier) []network.MessageValidator { + return []network.MessageValidator{ + validator.ValidateNotSender(flowID), // validator to filter out messages sent by this node itself + validator.ValidateTarget(log, flowID), // validator to filter out messages not intended for this node + } +} + +// isProtocolParticipant returns a PeerFilter that returns true if a peer is a staked (i.e., authorized) node. +func (n *Network) isProtocolParticipant() PeerFilter { + return func(p peer.ID) error { + if _, ok := n.Identity(p); !ok { + return fmt.Errorf("failed to get identity of unknown peer with peer id %s", p.String()) + } + return nil + } +} + +func (n *Network) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { + result := make([]peer.ID, 0, len(flowIDs)) + + for _, fid := range flowIDs { + pid, err := n.idTranslator.GetPeerID(fid) + if err != nil { + // We probably don't need to fail the entire function here, since the other + // translations may still succeed + n.logger. + Err(err). + Str(logging.KeySuspicious, "true"). + Hex("node_id", logging.ID(fid)). + Msg("failed to translate to peer ID") + continue + } + + result = append(result, pid) + } + + return result +} + +func (n *Network) UpdateNodeAddresses() { + n.logger.Info().Msg("updating protocol state node addresses") + + ids := n.Identities() + newInfos, invalid := utils.PeerInfosFromIDs(ids) + + for id, err := range invalid { + n.logger. + Err(err). + Bool(logging.KeySuspicious, true). + Hex("node_id", logging.ID(id)). + Msg("failed to extract peer info from identity") + } + + n.Lock() + defer n.Unlock() + + // set old addresses to expire + for _, oldInfo := range n.previousProtocolStatePeers { + n.libP2PNode.Host().Peerstore().SetAddrs(oldInfo.ID, oldInfo.Addrs, peerstore.TempAddrTTL) + } + + for _, info := range newInfos { + n.libP2PNode.Host().Peerstore().SetAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) + } + + n.previousProtocolStatePeers = newInfos +} + +// authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as +// well as who this node can connect to). +// and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components +// of libp2p. +// +// Args: +// none +// Returns: +// - peer.IDSlice: a list of peer IDs that are allowed to connect to this node (and that this node can connect to). Any peer +// not in this list is assumed to be disconnected from this node (if connected) and not allowed to connect to this node. +// This is the guarantee that the underlying libp2p node implementation makes. +func (n *Network) authorizedPeers() peer.IDSlice { + peerIDs := make([]peer.ID, 0) + for _, id := range n.peerIDs(n.Topology().NodeIDs()) { + peerAllowed := true + for _, filter := range n.peerManagerFilters { + if err := filter(id); err != nil { + n.logger.Debug(). + Err(err). + Str("peer_id", id.String()). + Msg("filtering topology peer") + + peerAllowed = false + break + } + } + + if peerAllowed { + peerIDs = append(peerIDs, id) + } + } + + return peerIDs +} + +func (n *Network) OnDisallowListNotification(notification *network.DisallowListingUpdate) { + for _, pid := range n.peerIDs(notification.FlowIds) { + n.libP2PNode.OnDisallowListNotification(pid, notification.Cause) + } +} + +func (n *Network) OnAllowListNotification(notification *network.AllowListingUpdate) { + for _, pid := range n.peerIDs(notification.FlowIds) { + n.libP2PNode.OnAllowListNotification(pid, notification.Cause) + } +} + +// handleIncomingStream handles an incoming stream from a remote peer +// it is a callback that gets called for each incoming stream by libp2p with a new stream object. +// TODO: this should be eventually moved to libp2p node. +func (n *Network) handleIncomingStream(s libp2pnet.Stream) { + // qualify the logger with local and remote address + log := p2putils.StreamLogger(n.logger, s) + + log.Info().Msg("incoming stream received") + + success := false + + remotePeer := s.Conn().RemotePeer() + + defer func() { + if success { + err := s.Close() + if err != nil { + log.Err(err).Msg("failed to close stream") + } + } else { + err := s.Reset() + if err != nil { + log.Err(err).Msg("failed to reset stream") + } + } + }() + + // check if peer is currently rate limited before continuing to process stream. + if n.unicastRateLimiters.MessageRateLimiter.IsRateLimited(remotePeer) || n.unicastRateLimiters.BandWidthRateLimiter.IsRateLimited(remotePeer) { + log.Debug(). + Bool(logging.KeySuspicious, true). + Msg("dropping unicast stream from rate limited peer") + return + } + + // TODO: We need to allow per-topic timeouts and message size limits. + // This allows us to configure higher limits for topics on which we expect + // to receive large messages (e.g. Chunk Data Packs), and use the normal + // limits for other topics. In order to enable this, we will need to register + // a separate stream handler for each topic. + ctx, cancel := context.WithTimeout(n.ctx, LargeMsgUnicastTimeout) + defer cancel() + + deadline, _ := ctx.Deadline() + + err := s.SetReadDeadline(deadline) + if err != nil { + log.Err(err).Msg("failed to set read deadline for stream") + return + } + + // create the reader + r := ggio.NewDelimitedReader(s, LargeMsgMaxUnicastMsgSize) + for { + if ctx.Err() != nil { + return + } + + // Note: message fields must not be trusted until explicitly validated + var msg message.Message + // read the next message (blocking call) + err = r.ReadMsg(&msg) + if err != nil { + if err == io.EOF { + break + } + + n.logger.Err(err).Msg("failed to read message") + return + } + + channel := channels.Channel(msg.ChannelID) + topic := channels.TopicFromChannel(channel, n.sporkId) + + // ignore messages if node does not have subscription to topic + if !n.libP2PNode.HasSubscription(topic) { + violation := &network.Violation{ + Identity: nil, PeerID: remotePeer.String(), Channel: channel, Protocol: message.ProtocolTypeUnicast, + } + + msgCode, err := codec.MessageCodeFromPayload(msg.Payload) + if err != nil { + violation.Err = err + n.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + } + + // msg type is not guaranteed to be correct since it is set by the client + _, what, err := codec.InterfaceFromMessageCode(msgCode) + if err != nil { + violation.Err = err + n.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + } + + violation.MsgType = what + violation.Err = ErrUnicastMsgWithoutSub + n.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(violation) + return + } + + // check if unicast messages have reached rate limit before processing next message + if !n.unicastRateLimiters.MessageAllowed(remotePeer) { + return + } + + // check if we can get a role for logging and metrics label if this is not a public channel + role := "" + if !channels.IsPublicChannel(channels.Channel(msg.ChannelID)) { + if identity, ok := n.Identity(remotePeer); ok { + role = identity.Role.String() + } + } + + // check unicast bandwidth rate limiter for peer + if !n.unicastRateLimiters.BandwidthAllowed( + remotePeer, + role, + msg.Size(), + message.MessageType(msg.Payload), + channels.Topic(msg.ChannelID)) { + return + } + + n.wg.Add(1) + go func() { + defer n.wg.Done() + n.processUnicastStreamMessage(remotePeer, &msg) + }() + } + + success = true +} + +// Subscribe subscribes the middleware to a channel. +// No errors are expected during normal operation. +func (n *Network) Subscribe(channel channels.Channel) error { + topic := channels.TopicFromChannel(channel, n.sporkId) + + var peerFilter PeerFilter + var validators []validator.PubSubMessageValidator + if channels.IsPublicChannel(channel) { + // NOTE: for public channels the callback used to check if a node is staked will + // return true for every node. + peerFilter = AllowAllPeerFilter() + } else { + // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes + validators = append(validators, n.authorizedSenderValidator.PubSubMessageValidator(channel)) + + // NOTE: For non-public channels the libP2P node topic validator will reject + // messages from unstaked nodes. + peerFilter = n.isProtocolParticipant() + } + + topicValidator := flowpubsub.TopicValidator(n.logger, peerFilter, validators...) + s, err := n.libP2PNode.Subscribe(topic, topicValidator) + if err != nil { + return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) + } + + // create a new readSubscription with the context of the middleware + rs := internal.NewReadSubscription(s, n.processPubSubMessages, n.logger) + n.wg.Add(1) + + // kick off the receive loop to continuously receive messages + go func() { + defer n.wg.Done() + rs.ReceiveLoop(n.ctx) + }() + + // update peers to add some nodes interested in the same topic as direct peers + n.libP2PNode.RequestPeerUpdate() + + return nil +} + +// processPubSubMessages processes messages received from the pubsub subscription. +func (n *Network) processPubSubMessages(msg *message.Message, peerID peer.ID) { + n.processAuthenticatedMessage(msg, peerID, message.ProtocolTypePubSub) +} + +// Unsubscribe unsubscribes the middleware from a channel. +// The following benign errors are expected during normal operations from libP2P: +// - the libP2P node fails to unsubscribe to the topic created from the provided channel. +// +// All errors returned from this function can be considered benign. +func (n *Network) Unsubscribe(channel channels.Channel) error { + topic := channels.TopicFromChannel(channel, n.sporkId) + return n.libP2PNode.Unsubscribe(topic) +} + +// processUnicastStreamMessage will decode, perform authorized sender validation and process a message +// sent via unicast stream. This func should be invoked in a separate goroutine to avoid creating a message decoding bottleneck. +func (n *Network) processUnicastStreamMessage(remotePeer peer.ID, msg *message.Message) { + channel := channels.Channel(msg.ChannelID) + + // TODO: once we've implemented per topic message size limits per the TODO above, + // we can remove this check + maxSize, err := UnicastMaxMsgSizeByCode(msg.Payload) + if err != nil { + n.slashingViolationsConsumer.OnUnknownMsgTypeError(&network.Violation{ + Identity: nil, PeerID: remotePeer.String(), MsgType: "", Channel: channel, Protocol: message.ProtocolTypeUnicast, Err: err, + }) + return + } + if msg.Size() > maxSize { + // message size exceeded + n.logger.Error(). + Str("peer_id", remotePeer.String()). + Str("channel", msg.ChannelID). + Int("max_size", maxSize). + Int("size", msg.Size()). + Bool(logging.KeySuspicious, true). + Msg("received message exceeded permissible message maxSize") + return + } + + // if message channel is not public perform authorized sender validation + if !channels.IsPublicChannel(channel) { + messageType, err := n.authorizedSenderValidator.Validate(remotePeer, msg.Payload, channel, message.ProtocolTypeUnicast) + if err != nil { + n.logger. + Error(). + Err(err). + Str("peer_id", remotePeer.String()). + Str("type", messageType). + Str("channel", msg.ChannelID). + Msg("unicast authorized sender validation failed") + return + } + } + n.processAuthenticatedMessage(msg, remotePeer, message.ProtocolTypeUnicast) +} + +// processAuthenticatedMessage processes a message and a source (indicated by its peer ID) and eventually passes it to the overlay +// In particular, it populates the `OriginID` field of the message with a Flow ID translated from this source. +func (n *Network) processAuthenticatedMessage(msg *message.Message, peerID peer.ID, protocol message.ProtocolType) { + originId, err := n.idTranslator.GetFlowID(peerID) + if err != nil { + // this error should never happen. by the time the message gets here, the peer should be + // authenticated which means it must be known + n.logger.Error(). + Err(err). + Str("peer_id", peerID.String()). + Bool(logging.KeySuspicious, true). + Msg("dropped message from unknown peer") + return + } + + channel := channels.Channel(msg.ChannelID) + decodedMsgPayload, err := n.codec.Decode(msg.Payload) + switch { + case codec.IsErrUnknownMsgCode(err): + // slash peer if message contains unknown message code byte + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + n.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) + return + case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): + // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + n.slashingViolationsConsumer.OnInvalidMsgError(violation) + return + case err != nil: + // this condition should never happen and indicates there's a bug + // don't crash as a result of external inputs since that creates a DoS vector + // collect slashing data because this could potentially lead to slashing + err = fmt.Errorf("unexpected error during message validation: %w", err) + violation := &network.Violation{ + PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, + } + n.slashingViolationsConsumer.OnUnexpectedError(violation) + return + } + + scope, err := message.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) + if err != nil { + n.logger.Error(). + Err(err). + Str("peer_id", peerID.String()). + Str("origin_id", originId.String()). + Msg("could not create incoming message scope") + return + } + + n.processMessage(scope) +} + +// processMessage processes a message and eventually passes it to the overlay +func (n *Network) processMessage(scope network.IncomingMessageScope) { + logger := n.logger.With(). + Str("channel", scope.Channel().String()). + Str("type", scope.Protocol().String()). + Int("msg_size", scope.Size()). + Hex("origin_id", logging.ID(scope.OriginId())). + Logger() + + // run through all the message validators + for _, v := range n.validators { + // if any one fails, stop message propagation + if !v.Validate(scope) { + logger.Debug().Msg("new message filtered by message validators") + return + } + } + + logger.Debug().Msg("processing new message") + + // if validation passed, send the message to the overlay + err := n.Receive(scope) + if err != nil { + n.logger.Error().Err(err).Msg("could not deliver payload") + } +} + +// UnicastMaxMsgSizeByCode returns the max permissible size for a unicast message code +func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { + msgCode, err := codec.MessageCodeFromPayload(payload) + if err != nil { + return 0, err + } + _, messageType, err := codec.InterfaceFromMessageCode(msgCode) + if err != nil { + return 0, err + } + + maxSize := unicastMaxMsgSize(messageType) + return maxSize, nil +} From c64ba46617084f8492c6be14fe0ce8799140e2af Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 15:03:03 -0400 Subject: [PATCH 743/815] wires in the network parameters --- network/p2p/network.go | 43 +++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/network/p2p/network.go b/network/p2p/network.go index 6b74071122b..349adb081bb 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -107,17 +107,15 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager unicastMessageTimeout time.Duration - - libP2PNode LibP2PNode - idTranslator IDTranslator - bitswapMetrics module.BitswapMetrics - previousProtocolStatePeers []peer.AddrInfo - slashingViolationsConsumer network.ViolationsConsumer - peerManagerFilters []PeerFilter - unicastRateLimiters *ratelimit.RateLimiters - validators []network.MessageValidator - authorizedSenderValidator *validator.AuthorizedSenderValidator - preferredUnicasts []protocols.ProtocolName + libP2PNode LibP2PNode + bitswapMetrics module.BitswapMetrics + previousProtocolStatePeers []peer.AddrInfo + slashingViolationsConsumer network.ViolationsConsumer + peerManagerFilters []PeerFilter + unicastRateLimiters *ratelimit.RateLimiters + validators []network.MessageValidator + authorizedSenderValidator *validator.AuthorizedSenderValidator + preferredUnicasts []protocols.ProtocolName } var _ network.Network = &Network{} @@ -222,6 +220,22 @@ func WithAlspManager(mgr network.MisbehaviorReportManager) NetworkOption { } } +// WithPeerManagerFilters sets the peer manager filters for the network. It overrides the default +// peer manager filters that are created from the config. +func WithPeerManagerFilters(filters ...PeerFilter) NetworkOption { + return func(n *Network) { + n.peerManagerFilters = filters + } +} + +// WithUnicastRateLimiters sets the unicast rate limiters for the network. It overrides the default +// unicast rate limiters that are created from the config. +func WithUnicastRateLimiters(limiters *ratelimit.RateLimiters) NetworkOption { + return func(n *Network) { + n.unicastRateLimiters = limiters + } +} + // NewNetwork creates a new naive overlay network, using the given middleware to // communicate to direct peers, using the given codec for serialization, and // using the given state & cache interfaces to track volatile information. @@ -236,6 +250,7 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { receiveCache: param.ReceiveCache, topology: param.Topology, metrics: param.Metrics, + bitswapMetrics: param.BitSwapMetrics, subscriptionManager: param.SubscriptionManager, identityProvider: param.IdentityProvider, conduitFactory: param.ConduitFactory, @@ -244,6 +259,8 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { sporkId: param.SporkId, identityTranslator: param.IdentityTranslator, unicastMessageTimeout: param.UnicastMessageTimeout, + libP2PNode: param.Libp2pNode, + unicastRateLimiters: ratelimit.NoopRateLimiters(), } misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg, n) @@ -788,7 +805,7 @@ func (n *Network) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { result := make([]peer.ID, 0, len(flowIDs)) for _, fid := range flowIDs { - pid, err := n.idTranslator.GetPeerID(fid) + pid, err := n.identityTranslator.GetPeerID(fid) if err != nil { // We probably don't need to fail the entire function here, since the other // translations may still succeed @@ -1119,7 +1136,7 @@ func (n *Network) processUnicastStreamMessage(remotePeer peer.ID, msg *message.M // processAuthenticatedMessage processes a message and a source (indicated by its peer ID) and eventually passes it to the overlay // In particular, it populates the `OriginID` field of the message with a Flow ID translated from this source. func (n *Network) processAuthenticatedMessage(msg *message.Message, peerID peer.ID, protocol message.ProtocolType) { - originId, err := n.idTranslator.GetFlowID(peerID) + originId, err := n.identityTranslator.GetFlowID(peerID) if err != nil { // this error should never happen. by the time the message gets here, the peer should be // authenticated which means it must be known From 5db14060a8dd19c022c8c70898bf9635d46f5fc9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 15:03:17 -0400 Subject: [PATCH 744/815] removes middleware factory from observer builder --- cmd/observer/node_builder/observer_builder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 23f0d2ec6d1..a971c65fa8d 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -487,7 +487,6 @@ func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, Logger: builder.Logger, Codec: cborcodec.NewCodec(), Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, Topology: topology, SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), Metrics: networkMetrics, From b3cb31e2d596096ebf890a194c6c31d2985f91d5 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:24:19 -0700 Subject: [PATCH 745/815] [Localnet] Update metrics and logging images to latest versions --- integration/localnet/docker-compose.metrics.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/integration/localnet/docker-compose.metrics.yml b/integration/localnet/docker-compose.metrics.yml index ed19edf70c4..904e093f286 100644 --- a/integration/localnet/docker-compose.metrics.yml +++ b/integration/localnet/docker-compose.metrics.yml @@ -1,7 +1,7 @@ version: '3.7' services: tempo: - image: grafana/tempo:main-4d7e191 + image: grafana/tempo:main-afb0389 user: root command: [ "-config.file=/etc/tempo.yaml" ] volumes: @@ -11,7 +11,7 @@ services: - "14268" loki: - image: grafana/loki:main-9218e46 + image: grafana/loki:main-f1bbdc5 user: root command: [ "-config.file=/etc/loki/local-config.yaml" ] volumes: @@ -20,14 +20,14 @@ services: - "3100:3100" promtail: - image: grafana/promtail:main-9218e46 + image: grafana/promtail:main-f1bbdc5 command: -config.file=/etc/promtail/promtail-config.yaml volumes: - ./conf/promtail-config.yaml:/etc/promtail/promtail-config.yaml:z - /var/run/docker.sock:/var/run/docker.sock:z prometheus: - image: prom/prometheus:v2.36.0 + image: prom/prometheus:v2.46.0 user: root volumes: - ./conf/prometheus.yaml:/etc/prometheus/prometheus.yaml:z @@ -38,17 +38,17 @@ services: - 9090:9090 pushgateway: - image: prom/pushgateway:v1.4.3 + image: prom/pushgateway:v1.6.0 ports: - 9091:9091 exporter: - image: prom/node-exporter:v1.3.1 + image: prom/node-exporter:v1.6.1 ports: - "9100:9100" grafana: - image: grafana/grafana:9.0.0-beta3 + image: grafana/grafana:10.0.4 volumes: - ./conf/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:z - ./conf/grafana-dashboards.yaml:/etc/grafana/provisioning/dashboards/dashboards.yaml:z From 4f1916fbc50107939cedd34e0e2e5d7c18fed15e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 16:07:27 -0400 Subject: [PATCH 746/815] refactors all builders with new network --- .../node_builder/access_node_builder.go | 115 ++++++----------- cmd/node_builder.go | 2 +- cmd/observer/node_builder/observer_builder.go | 119 ++++++----------- cmd/scaffold.go | 67 ++++------ follower/follower_builder.go | 120 ++++++------------ network/alsp/manager/manager_test.go | 53 ++------ network/internal/testutils/testUtil.go | 41 ++---- network/middleware.go | 37 ------ network/p2p/network.go | 32 ++++- .../p2p/{internal => }/readSubscription.go | 7 +- .../p2p/subscription/subscriptionManager.go | 20 +-- 11 files changed, 207 insertions(+), 406 deletions(-) rename network/p2p/{internal => }/readSubscription.go (91%) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 31fc8019f49..7cbe2c31eaa 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -72,7 +72,6 @@ import ( "github.com/onflow/flow-go/network/p2p/conduit" "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/dht" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/subscription" @@ -785,43 +784,13 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { // initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network // participants and topology used to choose peers from the list of participants. The list of participants can later be // updated by calling network.SetIDs. -func (builder *FlowAccessNodeBuilder) initNetwork(nodeID module.Local, +func (builder *FlowAccessNodeBuilder) initNetwork(local module.Local, + libp2pNode p2p.LibP2PNode, networkMetrics module.NetworkCoreMetrics, - middleware network.Middleware, topology network.Topology, receiveCache *netcache.ReceiveCache, ) (*p2p.Network, error) { - // creates network instance - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, - IdentityTranslator: builder.IDTranslator, - AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ - Logger: builder.Logger, - SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, - SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, - DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, - HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, - AlspMetrics: builder.Metrics.Network, - NetworkType: network.PublicNetwork, - HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), - }, - }) - if err != nil { - return nil, fmt.Errorf("could not initialize network: %w", err) - } - return net, nil } func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { @@ -1277,11 +1246,6 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { }). Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { msgValidators := publicNetworkMsgValidators(node.Logger.With().Bool("public", true).Logger(), node.IdentityProvider, builder.NodeID) - - middleware := builder.initMiddleware(builder.NodeID, builder.PublicNetworkConfig.Metrics, publicLibp2pNode, msgValidators...) - - // topology returns empty list since peers are not known upfront - top := topology.EmptyTopology{} receiveCache := netcache.NewHeroReceiveCache(builder.FlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, builder.Logger, metrics.NetworkReceiveCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork)) @@ -1291,11 +1255,47 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - net, err := builder.initNetwork(builder.Me, builder.PublicNetworkConfig.Metrics, middleware, top, receiveCache) + net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + Logger: builder.Logger.With().Str("module", "public-network").Logger(), + Libp2pNode: publicLibp2pNode, + Codec: cborcodec.NewCodec(), + Me: builder.Me, + Topology: topology.EmptyTopology{}, // topology returns empty list since peers are not known upfront + Metrics: builder.PublicNetworkConfig.Metrics, + BitSwapMetrics: builder.Metrics.Bitswap, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, + AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ + Logger: builder.Logger, + SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, + SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, + DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, + AlspMetrics: builder.Metrics.Network, + NetworkType: network.PublicNetwork, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + }, + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, + }, p2p.WithMessageValidators(msgValidators...)) if err != nil { - return nil, err + return nil, fmt.Errorf("could not initialize network: %w", err) } + builder.Middleware = net builder.AccessNodeConfig.PublicNetworkConfig.Network = net node.Logger.Info().Msgf("network will run on address: %s", builder.PublicNetworkConfig.BindAddress) @@ -1395,38 +1395,3 @@ func (builder *FlowAccessNodeBuilder) initPublicLibp2pNode(networkKey crypto.Pri return libp2pNode, nil } - -// initMiddleware creates the network.Middleware implementation with the libp2p factory function, metrics, peer update -// interval, and validators. The network.Middleware is then passed into the initNetwork function. -func (builder *FlowAccessNodeBuilder) initMiddleware(nodeID flow.Identifier, - networkMetrics module.NetworkSecurityMetrics, - libp2pNode p2p.LibP2PNode, - validators ...network.MessageValidator, -) network.Middleware { - logger := builder.Logger.With().Bool("staked", false).Logger() - mw := middleware.NewMiddleware(&middleware.Config{ - Logger: logger, - Libp2pNode: libp2pNode, - FlowId: nodeID, - BitSwapMetrics: builder.Metrics.Bitswap, - SporkId: builder.SporkID, - UnicastMessageTimeout: middleware.DefaultUnicastTimeout, - IdTranslator: builder.IDTranslator, - Codec: builder.CodecFactory(), - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } - return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) - }, - }, - middleware.WithMessageValidators(validators...), // use default identifier provider - ) - builder.Middleware = mw - return builder.Middleware -} diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 9fb490d3f02..d68737df9de 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -194,8 +194,8 @@ type NodeConfig struct { ProtocolEvents *events.Distributor State protocol.State Resolver madns.BasicResolver - Middleware network.Middleware Network network.Network + Middleware network.Middleware ConduitFactory network.ConduitFactory PingService network.PingService MsgValidators []network.MessageValidator diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index a971c65fa8d..b2a4745ea41 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -51,14 +51,12 @@ import ( alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" "github.com/onflow/flow-go/network/channels" - cborcodec "github.com/onflow/flow-go/network/codec/cbor" "github.com/onflow/flow-go/network/converter" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/cache" "github.com/onflow/flow-go/network/p2p/conduit" p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/subscription" @@ -474,46 +472,6 @@ func (builder *ObserverServiceBuilder) extraFlags() { }) } -// initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network -// participants and topology used to choose peers from the list of participants. The list of participants can later be -// updated by calling network.SetIDs. -func (builder *ObserverServiceBuilder) initNetwork(nodeID module.Local, - networkMetrics module.NetworkCoreMetrics, - middleware network.Middleware, - topology network.Topology, - receiveCache *netcache.ReceiveCache, -) (*p2p.Network, error) { - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, - IdentityTranslator: builder.IDTranslator, - AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ - Logger: builder.Logger, - SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, - SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, - DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, - HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, - AlspMetrics: builder.Metrics.Network, - HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), - NetworkType: network.PublicNetwork, - }, - }) - if err != nil { - return nil, fmt.Errorf("could not initialize network: %w", err) - } - - return net, nil -} - func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { return []network.MessageValidator{ // filter out messages sent by this node itself @@ -831,16 +789,46 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - msgValidators := publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID) - - builder.initMiddleware(node.NodeID, publicLibp2pNode, msgValidators...) - - // topology is nil since it is automatically managed by libp2p - net, err := builder.initNetwork(builder.Me, builder.Metrics.Network, builder.Middleware, nil, receiveCache) + net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + Logger: builder.Logger.With().Str("component", "public-network").Logger(), + Codec: builder.CodecFactory(), + Me: builder.Me, + Topology: nil, // topology is nil since it is managed by libp2p; //TODO: can we set empty topology? + Metrics: builder.Metrics.Network, + BitSwapMetrics: builder.Metrics.Bitswap, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, + AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ + Logger: builder.Logger, + SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, + SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, + DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, + AlspMetrics: builder.Metrics.Network, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + NetworkType: network.PublicNetwork, + }, + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, + }, p2p.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) if err != nil { - return nil, err + return nil, fmt.Errorf("could not initialize network: %w", err) } + builder.Middleware = net builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) @@ -1024,39 +1012,6 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { }) } -// initMiddleware creates the network.Middleware implementation with the libp2p factory function, metrics, peer update -// interval, and validators. The network.Middleware is then passed into the initNetwork function. -func (builder *ObserverServiceBuilder) initMiddleware(nodeID flow.Identifier, - libp2pNode p2p.LibP2PNode, - validators ...network.MessageValidator, -) network.Middleware { - mw := middleware.NewMiddleware(&middleware.Config{ - Logger: builder.Logger, - Libp2pNode: libp2pNode, - FlowId: nodeID, - BitSwapMetrics: builder.Metrics.Bitswap, - SporkId: builder.SporkID, - UnicastMessageTimeout: middleware.DefaultUnicastTimeout, - IdTranslator: builder.IDTranslator, - Codec: builder.CodecFactory(), - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } - return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) - }, - }, - middleware.WithMessageValidators(validators...), // use default identifier provider - ) - builder.Middleware = mw - return builder.Middleware -} - func loadNetworkingKey(path string) (crypto.PrivateKey, error) { data, err := io.ReadFile(path) if err != nil { diff --git a/cmd/scaffold.go b/cmd/scaffold.go index be491f17fd2..5a635884976 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -51,11 +51,9 @@ import ( "github.com/onflow/flow-go/network/p2p/conduit" "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/dns" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/ping" - "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" "github.com/onflow/flow-go/network/p2p/utils/ratelimiter" @@ -418,51 +416,24 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( unicastRateLimiters *ratelimit.RateLimiters, peerManagerFilters []p2p.PeerFilter) (network.Network, error) { - var mwOpts []middleware.OptionFn + var networkOptions []p2p.NetworkOption if len(fnb.MsgValidators) > 0 { - mwOpts = append(mwOpts, middleware.WithMessageValidators(fnb.MsgValidators...)) + networkOptions = append(networkOptions, p2p.WithMessageValidators(fnb.MsgValidators...)) } // by default if no rate limiter configuration was provided in the CLI args the default // noop rate limiter will be used. - mwOpts = append(mwOpts, middleware.WithUnicastRateLimiters(unicastRateLimiters)) + networkOptions = append(networkOptions, p2p.WithUnicastRateLimiters(unicastRateLimiters)) - mwOpts = append(mwOpts, - middleware.WithPreferredUnicastProtocols(protocols.ToProtocolNames(fnb.FlowConfig.NetworkConfig.PreferredUnicastProtocols)), + networkOptions = append(networkOptions, + p2p.WithPreferredUnicastProtocols(protocols.ToProtocolNames(fnb.FlowConfig.NetworkConfig.PreferredUnicastProtocols)...), ) - // peerManagerFilters are used by the peerManager via the middleware to filter peers from the topology. + // peerManagerFilters are used by the peerManager via the network to filter peers from the topology. if len(peerManagerFilters) > 0 { - mwOpts = append(mwOpts, middleware.WithPeerManagerFilters(peerManagerFilters)) + networkOptions = append(networkOptions, p2p.WithPeerManagerFilters(peerManagerFilters...)) } - mw := middleware.NewMiddleware(&middleware.Config{ - Logger: fnb.Logger, - Libp2pNode: fnb.LibP2PNode, - FlowId: fnb.Me.NodeID(), - BitSwapMetrics: fnb.Metrics.Bitswap, - SporkId: fnb.SporkID, - UnicastMessageTimeout: fnb.FlowConfig.NetworkConfig.UnicastMessageTimeout, - IdTranslator: fnb.IDTranslator, - Codec: fnb.CodecFactory(), - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := fnb.Network.(network.Adapter) - if !ok { - fnb.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } - return slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network, adapter) - }, - }, - mwOpts...) - - fnb.Middleware = mw - - subscriptionManager := subscription.NewChannelSubscriptionManager(fnb.Middleware) - receiveCache := netcache.NewHeroReceiveCache(fnb.FlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, fnb.Logger, metrics.NetworkReceiveCacheMetricsFactory(fnb.HeroCacheMetricsFactory(), network.PrivateNetwork)) @@ -475,13 +446,13 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( // creates network instance net, err := p2p.NewNetwork(&p2p.NetworkConfig{ Logger: fnb.Logger, + Libp2pNode: fnb.LibP2PNode, Codec: fnb.CodecFactory(), Me: fnb.Me, SporkId: fnb.SporkID, - MiddlewareFactory: func() (network.Middleware, error) { return fnb.Middleware, nil }, Topology: topology.NewFullyConnectedTopology(), - SubscriptionManager: subscriptionManager, Metrics: fnb.Metrics.Network, + BitSwapMetrics: fnb.Metrics.Bitswap, IdentityProvider: fnb.IdentityProvider, ReceiveCache: receiveCache, ConduitFactory: cf, @@ -497,19 +468,31 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( HeroCacheMetricsFactory: fnb.HeroCacheMetricsFactory(), NetworkType: network.PrivateNetwork, }, - }) + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := fnb.Network.(network.Adapter) + if !ok { + fnb.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network, adapter) + }, + }, networkOptions...) if err != nil { return nil, fmt.Errorf("could not initialize network: %w", err) } - fnb.Network = net + fnb.Network = net // setting network as the fnb.Network for the engine-level components + fnb.Middleware = net // setting network as the fnb.Middleware for the lower-level components - // register middleware's ReadyDoneAware interface so other components can depend on it for startup + // register network ReadyDoneAware interface so other components can depend on it for startup if fnb.middlewareDependable != nil { fnb.middlewareDependable.Init(fnb.Middleware) } - idEvents := gadgets.NewIdentityDeltas(fnb.Middleware.UpdateNodeAddresses) + idEvents := gadgets.NewIdentityDeltas(net.UpdateNodeAddresses) fnb.ProtocolEvents.AddConsumer(idEvents) return net, nil diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 2c6725d5571..49ba222ddbb 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -48,7 +48,6 @@ import ( "github.com/onflow/flow-go/network/p2p/conduit" p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" "github.com/onflow/flow-go/network/p2p/subscription" @@ -361,47 +360,6 @@ func FlowConsensusFollowerService(opts ...FollowerOption) *FollowerServiceBuilde return ret } -// initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network -// participants and topology used to choose peers from the list of participants. The list of participants can later be -// updated by calling network.SetIDs. -func (builder *FollowerServiceBuilder) initNetwork(nodeID module.Local, - networkMetrics module.NetworkMetrics, - middleware network.Middleware, - topology network.Topology, - receiveCache *netcache.ReceiveCache, -) (*p2p.Network, error) { - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ - Logger: builder.Logger, - Codec: cborcodec.NewCodec(), - Me: nodeID, - MiddlewareFactory: func() (network.Middleware, error) { return builder.Middleware, nil }, - Topology: topology, - SubscriptionManager: subscription.NewChannelSubscriptionManager(middleware), - Metrics: networkMetrics, - IdentityProvider: builder.IdentityProvider, - ReceiveCache: receiveCache, - ConduitFactory: conduit.NewDefaultConduitFactory(), - SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, - IdentityTranslator: builder.IDTranslator, - AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ - Logger: builder.Logger, - SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, - SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, - DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, - HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, - AlspMetrics: builder.Metrics.Network, - HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), - NetworkType: network.PublicNetwork, - }, - }) - if err != nil { - return nil, fmt.Errorf("could not initialize network: %w", err) - } - - return net, nil -} - func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { return []network.MessageValidator{ // filter out messages sent by this node itself @@ -719,16 +677,47 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - msgValidators := publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID) - - builder.initMiddleware(node.NodeID, publicLibp2pNode, msgValidators...) - - // topology is nil since it is automatically managed by libp2p - net, err := builder.initNetwork(builder.Me, builder.Metrics.Network, builder.Middleware, nil, receiveCache) + net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + Logger: builder.Logger.With().Str("component", "public-network").Logger(), + Codec: cborcodec.NewCodec(), + Me: builder.Me, + Libp2pNode: publicLibp2pNode, + Topology: nil, // topology is nil since it is automatically managed by libp2p // TODO: can we use empty topology? + Metrics: builder.Metrics.Network, + BitSwapMetrics: builder.Metrics.Bitswap, + IdentityProvider: builder.IdentityProvider, + ReceiveCache: receiveCache, + ConduitFactory: conduit.NewDefaultConduitFactory(), + SporkId: builder.SporkID, + UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + IdentityTranslator: builder.IDTranslator, + AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ + Logger: builder.Logger, + SpamRecordCacheSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamRecordCacheSize, + SpamReportQueueSize: builder.FlowConfig.NetworkConfig.AlspConfig.SpamReportQueueSize, + DisablePenalty: builder.FlowConfig.NetworkConfig.AlspConfig.DisablePenalty, + HeartBeatInterval: builder.FlowConfig.NetworkConfig.AlspConfig.HearBeatInterval, + AlspMetrics: builder.Metrics.Network, + HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), + NetworkType: network.PublicNetwork, + }, + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + // this is temporary; we are in the process of removing the middleware; hence all this logic must be + // eventually moved to the network component. + // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. + // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. + adapter, ok := builder.Network.(network.Adapter) + if !ok { + builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") + } + return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) + }, + }, p2p.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) if err != nil { - return nil, err + return nil, fmt.Errorf("could not initialize network: %w", err) } + builder.Middleware = net builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) @@ -751,36 +740,3 @@ func (builder *FollowerServiceBuilder) enqueueConnectWithStakedAN() { return upstream.NewUpstreamConnector(builder.bootstrapIdentities, builder.LibP2PNode, builder.Logger), nil }) } - -// initMiddleware creates the network.Middleware implementation with the libp2p factory function, metrics, peer update -// interval, and validators. The network.Middleware is then passed into the initNetwork function. -func (builder *FollowerServiceBuilder) initMiddleware(nodeID flow.Identifier, - libp2pNode p2p.LibP2PNode, - validators ...network.MessageValidator, -) network.Middleware { - mw := middleware.NewMiddleware(&middleware.Config{ - Logger: builder.Logger, - Libp2pNode: libp2pNode, - FlowId: nodeID, - BitSwapMetrics: builder.Metrics.Bitswap, - SporkId: builder.SporkID, - UnicastMessageTimeout: middleware.DefaultUnicastTimeout, - IdTranslator: builder.IDTranslator, - Codec: builder.CodecFactory(), - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } - return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) - }, - }, - middleware.WithMessageValidators(validators...), - ) - builder.Middleware = mw - return builder.Middleware -} diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index e5f1e8e5824..0bbca4110cc 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -58,13 +58,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( - t, - ids, - nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) - - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0]) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0]) net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) require.NoError(t, err) @@ -122,12 +116,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( - t, - ids, - nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -225,14 +214,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(), nil)) - mws, _ := testutils.MiddlewareFixtures( - t, - ids, - nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) idProvider := id.NewFixedIdentityProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -324,9 +308,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) - mws, _ := testutils.MiddlewareFixtures(t, ids, nodes, testutils.MiddlewareConfigFixture(t, sporkId)) idProvider := unittest.NewUpdatableIDProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) @@ -484,28 +467,20 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t // Network adapter is a sub-interface of the network instance, so we can use it as a placeholder for the network instance. // This is a bit of a chicken and egg problem; you see! that is why we must get rid of the middleware soon! var adapter network.Adapter - middlewareConfig := testutils.MiddlewareConfigFixture(t, sporkId) // also a placeholder for the slashing violations consumer. var violationsConsumer network.ViolationsConsumer - middlewareConfig.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { - violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) - return violationsConsumer - } - - mws, _ := testutils.MiddlewareFixtures( - t, - ids, - nodes, - middlewareConfig, - ) networkCfg := testutils.NetworkConfigFixture( t, *ids[0], idProvider, sporkId, - mws[0], - p2p.WithAlspConfig(managerCfgFixture(t))) + nodes[0], + p2p.WithAlspConfig(managerCfgFixture(t)), + p2p.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { + violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) + return violationsConsumer + })) victimNetwork, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) adapter = victimNetwork @@ -594,12 +569,8 @@ func TestMisbehaviorReportMetrics(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - mws, _ := testutils.MiddlewareFixtures( - t, - ids, - nodes, - testutils.MiddlewareConfigFixture(t, sporkId)) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, mws[0], p2p.WithAlspConfig(cfg)) + + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) net, err := p2p.NewNetwork(networkCfg) require.NoError(t, err) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 28824628b4d..fd76490e9e7 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -33,7 +33,6 @@ import ( "github.com/onflow/flow-go/network/p2p/conduit" "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/middleware" - "github.com/onflow/flow-go/network/p2p/subscription" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/utils/unittest" @@ -154,7 +153,6 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware.Config { return &middleware.Config{ Logger: unittest.Logger(), - BitSwapMetrics: metrics.NewNoopCollector(), SporkId: sporkId, UnicastMessageTimeout: middleware.DefaultUnicastTimeout, Codec: unittest.NetworkCodec(), @@ -178,35 +176,14 @@ func MiddlewareFixtures( libP2PNodes []p2p.LibP2PNode, cfg *middleware.Config, opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { - - require.Equal(t, len(identities), len(libP2PNodes)) - - mws := make([]network.Middleware, len(identities)) - idProviders := make([]*unittest.UpdatableIDProvider, len(identities)) - - if cfg.SlashingViolationConsumerFactory == nil { - // use a mock slashing violation consumer factory if not provided - cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { - return mocknetwork.NewViolationsConsumer(t) - } - } - - for i := 0; i < len(identities); i++ { - i := i - cfg.Libp2pNode = libP2PNodes[i] - cfg.FlowId = identities[i].NodeID - idProviders[i] = unittest.NewUpdatableIDProvider(identities) - cfg.IdTranslator = translator.NewIdentityProviderIDTranslator(idProviders[i]) - mws[i] = middleware.NewMiddleware(cfg, opts...) - } - return mws, idProviders + return nil, nil } // NetworksFixture generates the network for the given middlewares func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, - mws []network.Middleware, + libp2pNodes []p2p.LibP2PNode, configOpts ...func(*p2p.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { count := len(ids) @@ -214,8 +191,10 @@ func NetworksFixture(t *testing.T, idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { + i := i // capture loop variable + idProvider := unittest.NewUpdatableIDProvider(ids) - params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, mws[i]) + params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, libp2pNodes[i]) for _, opt := range configOpts { opt(params) @@ -236,7 +215,7 @@ func NetworkConfigFixture( myId flow.Identity, idProvider module.IdentityProvider, sporkId flow.Identifier, - mw network.Middleware, + libp2pNode p2p.LibP2PNode, opts ...p2p.NetworkConfigOption) *p2p.NetworkConfig { me := mock.NewLocal(t) @@ -251,14 +230,13 @@ func NetworkConfigFixture( defaultFlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, unittest.Logger(), metrics.NewNoopCollector()) - subMgr := subscription.NewChannelSubscriptionManager(mw) params := &p2p.NetworkConfig{ Logger: unittest.Logger(), Codec: unittest.NetworkCodec(), + Libp2pNode: libp2pNode, Me: me, - MiddlewareFactory: func() (network.Middleware, error) { return mw, nil }, + BitSwapMetrics: metrics.NewNoopCollector(), Topology: unittest.NetworkTopology(), - SubscriptionManager: subMgr, Metrics: metrics.NewNoopCollector(), IdentityProvider: idProvider, ReceiveCache: receiveCache, @@ -274,6 +252,9 @@ func NetworkConfigFixture( AlspMetrics: metrics.NewNoopCollector(), HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), }, + SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + return mocknetwork.NewViolationsConsumer(t) + }, } for _, opt := range opts { diff --git a/network/middleware.go b/network/middleware.go index 74833b02b18..e4bcdf19b5c 100644 --- a/network/middleware.go +++ b/network/middleware.go @@ -3,12 +3,7 @@ package network import ( - "context" - - "github.com/ipfs/go-datastore" - libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/protocol" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/component" @@ -22,32 +17,6 @@ type Middleware interface { component.Component DisallowListNotificationConsumer - // SetOverlay sets the overlay used by the middleware. This must be called before the middleware can be Started. - SetOverlay(Overlay) - - // OpenProtectedStream acts as a short-circuit method that delegates the opening of a protected stream to the underlying - // libP2PNode. This method is intended to be temporary and is going to be removed in the long term. Users should plan - // to interact with the libP2P node directly in the future. - // - // Parameters: - // ctx: The context used to control the stream's lifecycle. - // peerID: The ID of the peer to open the stream to. - // protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive. - // writingLogic: A callback function that contains the logic for writing to the stream. - // - // Returns: - // error: An error, if any occurred during the process. All returned errors are benign. - // - // Note: This method is subject to removal in future versions and direct use of the libp2p node is encouraged. - // TODO: Remove this method in the future. - OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) error - - // Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or - // a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best - // effort. - // All errors returned from this function can be considered benign. - Publish(msg OutgoingMessageScope) error - // Subscribe subscribes the middleware to a channel. // No errors are expected during normal operation. Subscribe(channel channels.Channel) error @@ -59,12 +28,6 @@ type Middleware interface { // UpdateNodeAddresses fetches and updates the addresses of all the authorized participants // in the Flow protocol. UpdateNodeAddresses() - - // NewBlobService creates a new BlobService for the given channel. - NewBlobService(channel channels.Channel, store datastore.Batching, opts ...BlobServiceOption) BlobService - - // NewPingService creates a new PingService for the given ping protocol ID. - NewPingService(pingProtocol protocol.ID, provider PingInfoProvider) PingService } // Overlay represents the interface that middleware uses to interact with the diff --git a/network/p2p/network.go b/network/p2p/network.go index 349adb081bb..599f1cc98c8 100644 --- a/network/p2p/network.go +++ b/network/p2p/network.go @@ -30,8 +30,8 @@ import ( "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p/blob" - "github.com/onflow/flow-go/network/p2p/internal" "github.com/onflow/flow-go/network/p2p/ping" + "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" "github.com/onflow/flow-go/network/p2p/utils" @@ -120,6 +120,7 @@ type Network struct { var _ network.Network = &Network{} var _ network.Overlay = &Network{} +var _ network.Middleware = &Network{} type registerEngineRequest struct { channel channels.Channel @@ -153,7 +154,6 @@ type NetworkConfig struct { Codec network.Codec Me module.Local Topology network.Topology - SubscriptionManager network.SubscriptionManager Metrics module.NetworkCoreMetrics IdentityProvider module.IdentityProvider IdentityTranslator IDTranslator @@ -197,6 +197,12 @@ func WithCodec(codec network.Codec) NetworkConfigOption { } } +func WithSlashingViolationConsumerFactory(factory func() network.ViolationsConsumer) NetworkConfigOption { + return func(params *NetworkConfig) { + params.SlashingViolationConsumerFactory = factory + } +} + // NetworkOption is a function that can be used to override network attributes. // It is mostly used for testing purposes. // Note: do not override network attributes in production unless you know what you are doing. @@ -236,6 +242,22 @@ func WithUnicastRateLimiters(limiters *ratelimit.RateLimiters) NetworkOption { } } +// WithPreferredUnicastProtocols sets the preferred unicast protocols for the network. It overrides the default +// preferred unicast. +func WithPreferredUnicastProtocols(protocols ...protocols.ProtocolName) NetworkOption { + return func(n *Network) { + n.preferredUnicasts = protocols + } +} + +// WithMessageValidators sets the message validators for the network. It overrides the default +// message validators. +func WithMessageValidators(validators ...network.MessageValidator) NetworkOption { + return func(n *Network) { + n.validators = validators + } +} + // NewNetwork creates a new naive overlay network, using the given middleware to // communicate to direct peers, using the given codec for serialization, and // using the given state & cache interfaces to track volatile information. @@ -251,7 +273,6 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { topology: param.Topology, metrics: param.Metrics, bitswapMetrics: param.BitSwapMetrics, - subscriptionManager: param.SubscriptionManager, identityProvider: param.IdentityProvider, conduitFactory: param.ConduitFactory, registerEngineRequests: make(chan *registerEngineRequest), @@ -261,8 +282,11 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { unicastMessageTimeout: param.UnicastMessageTimeout, libP2PNode: param.Libp2pNode, unicastRateLimiters: ratelimit.NoopRateLimiters(), + validators: DefaultValidators(param.Logger.With().Str("component", "network-validators").Logger(), param.Me.NodeID()), } + n.subscriptionManager = subscription.NewChannelSubscriptionManager(n) + misbehaviorMngr, err := alspmgr.NewMisbehaviorReportManager(param.AlspCfg, n) if err != nil { return nil, fmt.Errorf("could not create misbehavior report manager: %w", err) @@ -1060,7 +1084,7 @@ func (n *Network) Subscribe(channel channels.Channel) error { } // create a new readSubscription with the context of the middleware - rs := internal.NewReadSubscription(s, n.processPubSubMessages, n.logger) + rs := NewReadSubscription(s, n.processPubSubMessages, n.logger) n.wg.Add(1) // kick off the receive loop to continuously receive messages diff --git a/network/p2p/internal/readSubscription.go b/network/p2p/readSubscription.go similarity index 91% rename from network/p2p/internal/readSubscription.go rename to network/p2p/readSubscription.go index 38bfcbb0511..ca57f2eadd9 100644 --- a/network/p2p/internal/readSubscription.go +++ b/network/p2p/readSubscription.go @@ -1,4 +1,4 @@ -package internal +package p2p import ( "context" @@ -11,7 +11,6 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/p2p" validator "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/logging" ) @@ -23,12 +22,12 @@ type ReadSubscriptionCallBackFunction func(msg *message.Message, peerID peer.ID) // the context of the subscription is cancelled. type ReadSubscription struct { log zerolog.Logger - sub p2p.Subscription + sub Subscription callback ReadSubscriptionCallBackFunction } // NewReadSubscription reads the messages coming in on the subscription -func NewReadSubscription(sub p2p.Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *ReadSubscription { +func NewReadSubscription(sub Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *ReadSubscription { r := ReadSubscription{ log: log.With().Str("channel", sub.Topic()).Logger(), sub: sub, diff --git a/network/p2p/subscription/subscriptionManager.go b/network/p2p/subscription/subscriptionManager.go index 391468f58bc..d73350129d7 100644 --- a/network/p2p/subscription/subscriptionManager.go +++ b/network/p2p/subscription/subscriptionManager.go @@ -11,15 +11,18 @@ import ( // ChannelSubscriptionManager manages subscriptions of engines running on the node to channels. // Each channel should be taken by at most a single engine. type ChannelSubscriptionManager struct { - mu sync.RWMutex - engines map[channels.Channel]network.MessageProcessor - mw network.Middleware + mu sync.RWMutex + engines map[channels.Channel]network.MessageProcessor + middleware network.Middleware } -func NewChannelSubscriptionManager(mw network.Middleware) *ChannelSubscriptionManager { +// NewChannelSubscriptionManager creates a new subscription manager. +// Args: +// - subscribeFunction: a function that subscribes to a channel. +func NewChannelSubscriptionManager(middleware network.Middleware) *ChannelSubscriptionManager { return &ChannelSubscriptionManager{ - engines: make(map[channels.Channel]network.MessageProcessor), - mw: mw, + engines: make(map[channels.Channel]network.MessageProcessor), + middleware: middleware, } } @@ -35,7 +38,8 @@ func (sm *ChannelSubscriptionManager) Register(channel channels.Channel, engine } // registers the channel with the middleware to let middleware start receiving messages - err := sm.mw.Subscribe(channel) + // TODO: subscribe function should be replaced by a better abstraction of the network. + err := sm.middleware.Subscribe(channel) if err != nil { return fmt.Errorf("subscriptionManager: failed to subscribe to channel %s: %w", channel, err) } @@ -58,7 +62,7 @@ func (sm *ChannelSubscriptionManager) Unregister(channel channels.Channel) error return nil } - err := sm.mw.Unsubscribe(channel) + err := sm.middleware.Unsubscribe(channel) if err != nil { return fmt.Errorf("subscriptionManager: failed to unregister from channel %s: %w", channel, err) } From b9d3c379ed79eccc5d3183981427142d1970abc8 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 16:21:41 -0400 Subject: [PATCH 747/815] moves network to p2pnet --- .../node_builder/access_node_builder.go | 11 ++++---- cmd/observer/node_builder/observer_builder.go | 7 +++--- cmd/scaffold.go | 15 +++++------ follower/follower_builder.go | 7 +++--- network/alsp/manager/manager_test.go | 25 ++++++++++--------- network/internal/testutils/testUtil.go | 11 ++++---- .../p2p/cache/node_blocklist_wrapper_test.go | 4 +-- network/p2p/mock/network_config_option.go | 3 ++- network/p2p/mock/network_opt_function.go | 3 ++- network/p2p/mock/network_option.go | 3 ++- network/p2p/mock/network_param_option.go | 3 ++- network/p2p/{ => p2pnet}/network.go | 21 ++++++++-------- network/p2p/{ => p2pnet}/readSubscription.go | 7 +++--- network/test/middleware_test.go | 3 ++- network/test/unicast_authorization_test.go | 3 ++- 15 files changed, 70 insertions(+), 56 deletions(-) rename network/p2p/{ => p2pnet}/network.go (98%) rename network/p2p/{ => p2pnet}/readSubscription.go (91%) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 7cbe2c31eaa..e0c10f2ed04 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -74,6 +74,7 @@ import ( "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/network/p2p/translator" @@ -789,7 +790,7 @@ func (builder *FlowAccessNodeBuilder) initNetwork(local module.Local, networkMetrics module.NetworkCoreMetrics, topology network.Topology, receiveCache *netcache.ReceiveCache, -) (*p2p.Network, error) { +) (*p2pnet.Network, error) { } @@ -838,7 +839,7 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { filter.And( filter.HasRole(flow.RoleConsensus), filter.Not(filter.HasNodeID(node.Me.NodeID())), - p2p.NotEjectedFilter, + p2pnet.NotEjectedFilter, ), builder.IdentityProvider, ) @@ -1255,7 +1256,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + net, err := p2pnet.NewNetwork(&p2pnet.NetworkConfig{ Logger: builder.Logger.With().Str("module", "public-network").Logger(), Libp2pNode: publicLibp2pNode, Codec: cborcodec.NewCodec(), @@ -1267,7 +1268,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + UnicastMessageTimeout: p2pnet.DefaultUnicastTimeout, IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, @@ -1290,7 +1291,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { } return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, - }, p2p.WithMessageValidators(msgValidators...)) + }, p2pnet.WithMessageValidators(msgValidators...)) if err != nil { return nil, fmt.Errorf("could not initialize network: %w", err) } diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index b2a4745ea41..7e3488c674f 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -59,6 +59,7 @@ import ( "github.com/onflow/flow-go/network/p2p/keyutils" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/network/p2p/translator" @@ -789,7 +790,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + net, err := p2pnet.NewNetwork(&p2pnet.NetworkConfig{ Logger: builder.Logger.With().Str("component", "public-network").Logger(), Codec: builder.CodecFactory(), Me: builder.Me, @@ -800,7 +801,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + UnicastMessageTimeout: p2pnet.DefaultUnicastTimeout, IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, @@ -823,7 +824,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { } return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, - }, p2p.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) + }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) if err != nil { return nil, fmt.Errorf("could not initialize network: %w", err) } diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 5a635884976..ac7aebe66e3 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -53,6 +53,7 @@ import ( "github.com/onflow/flow-go/network/p2p/dns" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/unicast/protocols" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" @@ -416,22 +417,22 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( unicastRateLimiters *ratelimit.RateLimiters, peerManagerFilters []p2p.PeerFilter) (network.Network, error) { - var networkOptions []p2p.NetworkOption + var networkOptions []p2pnet.NetworkOption if len(fnb.MsgValidators) > 0 { - networkOptions = append(networkOptions, p2p.WithMessageValidators(fnb.MsgValidators...)) + networkOptions = append(networkOptions, p2pnet.WithMessageValidators(fnb.MsgValidators...)) } // by default if no rate limiter configuration was provided in the CLI args the default // noop rate limiter will be used. - networkOptions = append(networkOptions, p2p.WithUnicastRateLimiters(unicastRateLimiters)) + networkOptions = append(networkOptions, p2pnet.WithUnicastRateLimiters(unicastRateLimiters)) networkOptions = append(networkOptions, - p2p.WithPreferredUnicastProtocols(protocols.ToProtocolNames(fnb.FlowConfig.NetworkConfig.PreferredUnicastProtocols)...), + p2pnet.WithPreferredUnicastProtocols(protocols.ToProtocolNames(fnb.FlowConfig.NetworkConfig.PreferredUnicastProtocols)...), ) // peerManagerFilters are used by the peerManager via the network to filter peers from the topology. if len(peerManagerFilters) > 0 { - networkOptions = append(networkOptions, p2p.WithPeerManagerFilters(peerManagerFilters...)) + networkOptions = append(networkOptions, p2pnet.WithPeerManagerFilters(peerManagerFilters...)) } receiveCache := netcache.NewHeroReceiveCache(fnb.FlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, @@ -444,7 +445,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( } // creates network instance - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + net, err := p2pnet.NewNetwork(&p2pnet.NetworkConfig{ Logger: fnb.Logger, Libp2pNode: fnb.LibP2PNode, Codec: fnb.CodecFactory(), @@ -1033,7 +1034,7 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { filter.And( filter.HasRole(flow.RoleConsensus), filter.Not(filter.HasNodeID(node.Me.NodeID())), - p2p.NotEjectedFilter, + p2pnet.NotEjectedFilter, ), node.IdentityProvider, ) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 49ba222ddbb..306d7391e04 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -50,6 +50,7 @@ import ( "github.com/onflow/flow-go/network/p2p/keyutils" "github.com/onflow/flow-go/network/p2p/p2pbuilder" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/tracer" "github.com/onflow/flow-go/network/p2p/translator" @@ -677,7 +678,7 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not register networking receive cache metric: %w", err) } - net, err := p2p.NewNetwork(&p2p.NetworkConfig{ + net, err := p2pnet.NewNetwork(&p2pnet.NetworkConfig{ Logger: builder.Logger.With().Str("component", "public-network").Logger(), Codec: cborcodec.NewCodec(), Me: builder.Me, @@ -689,7 +690,7 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), SporkId: builder.SporkID, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + UnicastMessageTimeout: p2pnet.DefaultUnicastTimeout, IdentityTranslator: builder.IDTranslator, AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: builder.Logger, @@ -712,7 +713,7 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { } return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, - }, p2p.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) + }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) if err != nil { return nil, fmt.Errorf("could not initialize network: %w", err) } diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 0bbca4110cc..a3baa989656 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -30,6 +30,7 @@ import ( "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pnet" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/utils/unittest" @@ -59,7 +60,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0]) - net, err := p2p.NewNetwork(networkCfg, p2p.WithAlspManager(misbehaviorReportManger)) + net, err := p2pnet.NewNetwork(networkCfg, p2pnet.WithAlspManager(misbehaviorReportManger)) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -116,8 +117,8 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) - net, err := p2p.NewNetwork(networkCfg) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) + net, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) @@ -216,8 +217,8 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(), nil)) idProvider := id.NewFixedIdentityProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) - victimNetwork, err := p2p.NewNetwork(networkCfg) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) + victimNetwork, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) // index of the victim node in the nodes slice. @@ -309,9 +310,9 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) idProvider := unittest.NewUpdatableIDProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) - victimNetwork, err := p2p.NewNetwork(networkCfg) + victimNetwork, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) // index of the victim node in the nodes slice. @@ -476,12 +477,12 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t idProvider, sporkId, nodes[0], - p2p.WithAlspConfig(managerCfgFixture(t)), - p2p.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { + p2pnet.WithAlspConfig(managerCfgFixture(t)), + p2pnet.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) return violationsConsumer })) - victimNetwork, err := p2p.NewNetwork(networkCfg) + victimNetwork, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) adapter = victimNetwork @@ -570,8 +571,8 @@ func TestMisbehaviorReportMetrics(t *testing.T) { ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) - networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2p.WithAlspConfig(cfg)) - net, err := p2p.NewNetwork(networkCfg) + networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) + net, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index fd76490e9e7..868d8f7b59d 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -33,6 +33,7 @@ import ( "github.com/onflow/flow-go/network/p2p/conduit" "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network/p2p/p2pnet" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/utils/unittest" @@ -184,7 +185,7 @@ func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, libp2pNodes []p2p.LibP2PNode, - configOpts ...func(*p2p.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { + configOpts ...func(*p2pnet.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { count := len(ids) nets := make([]network.Network, 0) @@ -200,7 +201,7 @@ func NetworksFixture(t *testing.T, opt(params) } - net, err := p2p.NewNetwork(params) + net, err := p2pnet.NewNetwork(params) require.NoError(t, err) nets = append(nets, net) @@ -216,7 +217,7 @@ func NetworkConfigFixture( idProvider module.IdentityProvider, sporkId flow.Identifier, libp2pNode p2p.LibP2PNode, - opts ...p2p.NetworkConfigOption) *p2p.NetworkConfig { + opts ...p2pnet.NetworkConfigOption) *p2pnet.NetworkConfig { me := mock.NewLocal(t) me.On("NodeID").Return(myId.NodeID).Maybe() @@ -230,7 +231,7 @@ func NetworkConfigFixture( defaultFlowConfig.NetworkConfig.NetworkReceivedMessageCacheSize, unittest.Logger(), metrics.NewNoopCollector()) - params := &p2p.NetworkConfig{ + params := &p2pnet.NetworkConfig{ Logger: unittest.Logger(), Codec: unittest.NetworkCodec(), Libp2pNode: libp2pNode, @@ -242,7 +243,7 @@ func NetworkConfigFixture( ReceiveCache: receiveCache, ConduitFactory: conduit.NewDefaultConduitFactory(), SporkId: sporkId, - UnicastMessageTimeout: p2p.DefaultUnicastTimeout, + UnicastMessageTimeout: p2pnet.DefaultUnicastTimeout, IdentityTranslator: translator.NewIdentityProviderIDTranslator(idProvider), AlspCfg: &alspmgr.MisbehaviorReportManagerConfig{ Logger: unittest.Logger(), diff --git a/network/p2p/cache/node_blocklist_wrapper_test.go b/network/p2p/cache/node_blocklist_wrapper_test.go index 929be0b066a..bdeb50ffd27 100644 --- a/network/p2p/cache/node_blocklist_wrapper_test.go +++ b/network/p2p/cache/node_blocklist_wrapper_test.go @@ -16,8 +16,8 @@ import ( mocks "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/mocknetwork" - "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/cache" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/utils/unittest" ) @@ -177,7 +177,7 @@ func (s *NodeDisallowListWrapperTestSuite) TestDisallowListNode() { s.provider.On("Identities", mock.Anything).Return(combinedIdentities) - identities := s.wrapper.Identities(p2p.NotEjectedFilter) + identities := s.wrapper.Identities(p2pnet.NotEjectedFilter) require.Equal(s.T(), len(honestIdentities), len(identities)) // expected only honest nodes to be returned for _, i := range identities { diff --git a/network/p2p/mock/network_config_option.go b/network/p2p/mock/network_config_option.go index 89fc2bc5b78..8474d84d9a9 100644 --- a/network/p2p/mock/network_config_option.go +++ b/network/p2p/mock/network_config_option.go @@ -3,7 +3,8 @@ package mockp2p import ( - p2p "github.com/onflow/flow-go/network/p2p" + p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + mock "github.com/stretchr/testify/mock" ) diff --git a/network/p2p/mock/network_opt_function.go b/network/p2p/mock/network_opt_function.go index 50048811456..260368afe50 100644 --- a/network/p2p/mock/network_opt_function.go +++ b/network/p2p/mock/network_opt_function.go @@ -3,7 +3,8 @@ package mockp2p import ( - p2p "github.com/onflow/flow-go/network/p2p" + p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + mock "github.com/stretchr/testify/mock" ) diff --git a/network/p2p/mock/network_option.go b/network/p2p/mock/network_option.go index 470eb615c23..6b36ba7399f 100644 --- a/network/p2p/mock/network_option.go +++ b/network/p2p/mock/network_option.go @@ -3,7 +3,8 @@ package mockp2p import ( - p2p "github.com/onflow/flow-go/network/p2p" + p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + mock "github.com/stretchr/testify/mock" ) diff --git a/network/p2p/mock/network_param_option.go b/network/p2p/mock/network_param_option.go index aa84df68497..28f3c79b367 100644 --- a/network/p2p/mock/network_param_option.go +++ b/network/p2p/mock/network_param_option.go @@ -3,7 +3,8 @@ package mockp2p import ( - p2p "github.com/onflow/flow-go/network/p2p" + p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + mock "github.com/stretchr/testify/mock" ) diff --git a/network/p2p/network.go b/network/p2p/p2pnet/network.go similarity index 98% rename from network/p2p/network.go rename to network/p2p/p2pnet/network.go index 599f1cc98c8..1bd978cefa5 100644 --- a/network/p2p/network.go +++ b/network/p2p/p2pnet/network.go @@ -1,4 +1,4 @@ -package p2p +package p2pnet import ( "bufio" @@ -29,6 +29,7 @@ import ( "github.com/onflow/flow-go/network/codec" "github.com/onflow/flow-go/network/internal/p2putils" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/blob" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/subscription" @@ -93,7 +94,7 @@ type Network struct { ctx context.Context sporkId flow.Identifier identityProvider module.IdentityProvider - identityTranslator IDTranslator + identityTranslator p2p.IDTranslator logger zerolog.Logger codec network.Codec me module.Local @@ -107,11 +108,11 @@ type Network struct { registerBlobServiceRequests chan *registerBlobServiceRequest misbehaviorReportManager network.MisbehaviorReportManager unicastMessageTimeout time.Duration - libP2PNode LibP2PNode + libP2PNode p2p.LibP2PNode bitswapMetrics module.BitswapMetrics previousProtocolStatePeers []peer.AddrInfo slashingViolationsConsumer network.ViolationsConsumer - peerManagerFilters []PeerFilter + peerManagerFilters []p2p.PeerFilter unicastRateLimiters *ratelimit.RateLimiters validators []network.MessageValidator authorizedSenderValidator *validator.AuthorizedSenderValidator @@ -156,13 +157,13 @@ type NetworkConfig struct { Topology network.Topology Metrics module.NetworkCoreMetrics IdentityProvider module.IdentityProvider - IdentityTranslator IDTranslator + IdentityTranslator p2p.IDTranslator ReceiveCache *netcache.ReceiveCache ConduitFactory network.ConduitFactory AlspCfg *alspmgr.MisbehaviorReportManagerConfig SporkId flow.Identifier UnicastMessageTimeout time.Duration - Libp2pNode LibP2PNode + Libp2pNode p2p.LibP2PNode BitSwapMetrics module.BitswapMetrics SlashingViolationConsumerFactory func() network.ViolationsConsumer } @@ -228,7 +229,7 @@ func WithAlspManager(mgr network.MisbehaviorReportManager) NetworkOption { // WithPeerManagerFilters sets the peer manager filters for the network. It overrides the default // peer manager filters that are created from the config. -func WithPeerManagerFilters(filters ...PeerFilter) NetworkOption { +func WithPeerManagerFilters(filters ...p2p.PeerFilter) NetworkOption { return func(n *Network) { n.peerManagerFilters = filters } @@ -816,7 +817,7 @@ func DefaultValidators(log zerolog.Logger, flowID flow.Identifier) []network.Mes } // isProtocolParticipant returns a PeerFilter that returns true if a peer is a staked (i.e., authorized) node. -func (n *Network) isProtocolParticipant() PeerFilter { +func (n *Network) isProtocolParticipant() p2p.PeerFilter { return func(p peer.ID) error { if _, ok := n.Identity(p); !ok { return fmt.Errorf("failed to get identity of unknown peer with peer id %s", p.String()) @@ -1062,12 +1063,12 @@ func (n *Network) handleIncomingStream(s libp2pnet.Stream) { func (n *Network) Subscribe(channel channels.Channel) error { topic := channels.TopicFromChannel(channel, n.sporkId) - var peerFilter PeerFilter + var peerFilter p2p.PeerFilter var validators []validator.PubSubMessageValidator if channels.IsPublicChannel(channel) { // NOTE: for public channels the callback used to check if a node is staked will // return true for every node. - peerFilter = AllowAllPeerFilter() + peerFilter = p2p.AllowAllPeerFilter() } else { // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes validators = append(validators, n.authorizedSenderValidator.PubSubMessageValidator(channel)) diff --git a/network/p2p/readSubscription.go b/network/p2p/p2pnet/readSubscription.go similarity index 91% rename from network/p2p/readSubscription.go rename to network/p2p/p2pnet/readSubscription.go index ca57f2eadd9..c0565dab924 100644 --- a/network/p2p/readSubscription.go +++ b/network/p2p/p2pnet/readSubscription.go @@ -1,4 +1,4 @@ -package p2p +package p2pnet import ( "context" @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/p2p" validator "github.com/onflow/flow-go/network/validator/pubsub" "github.com/onflow/flow-go/utils/logging" ) @@ -22,12 +23,12 @@ type ReadSubscriptionCallBackFunction func(msg *message.Message, peerID peer.ID) // the context of the subscription is cancelled. type ReadSubscription struct { log zerolog.Logger - sub Subscription + sub p2p.Subscription callback ReadSubscriptionCallBackFunction } // NewReadSubscription reads the messages coming in on the subscription -func NewReadSubscription(sub Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *ReadSubscription { +func NewReadSubscription(sub p2p.Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *ReadSubscription { r := ReadSubscription{ log: log.With().Str("channel", sub.Topic()).Logger(), sub: sub, diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 190f26026e7..97efd83698f 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -34,6 +34,7 @@ import ( "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/p2pnode" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" @@ -216,7 +217,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { idProvider, m.sporkId, mws[0]) - newNet, err := p2p.NewNetwork(networkCfg) + newNet, err := p2pnet.NewNetwork(networkCfg) require.NoError(m.T(), err) require.Len(m.T(), ids, 1) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 7ed453e3e2d..23da48c02a7 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -24,6 +24,7 @@ import ( "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/validator" "github.com/onflow/flow-go/utils/unittest" ) @@ -84,7 +85,7 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer } u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) - nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2p.WithCodec(u.codec)) + nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2pnet.WithCodec(u.codec)) require.Len(u.T(), ids, 2) require.Len(u.T(), providers, 2) require.Len(u.T(), mws, 2) From 26631adbc557080823b23c0d3433862c9ff96276 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 16:25:18 -0400 Subject: [PATCH 748/815] fixes all manager tests --- network/p2p/p2pnet/network.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index 1bd978cefa5..d05c9fcc2e0 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -378,9 +378,13 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { } func (n *Network) processRegisterEngineRequests(parent irrecoverable.SignalerContext, ready component.ReadyFunc) { - <-n.libP2PNode.Ready() ready() + // we need to wait for the libp2p node to be ready before we can register engines + n.logger.Debug().Msg("waiting for libp2p node to be ready") + <-n.libP2PNode.Ready() + n.logger.Debug().Msg("libp2p node is ready") + for { select { case req := <-n.registerEngineRequests: @@ -402,9 +406,12 @@ func (n *Network) processRegisterEngineRequests(parent irrecoverable.SignalerCon } func (n *Network) processRegisterBlobServiceRequests(parent irrecoverable.SignalerContext, ready component.ReadyFunc) { - <-n.libP2PNode.Ready() ready() + n.logger.Debug().Msg("waiting for libp2p node to be ready") + <-n.libP2PNode.Ready() + n.logger.Debug().Msg("libp2p node is ready") + for { select { case req := <-n.registerBlobServiceRequests: From a962b70aa96e3a66e46ff8acad4d80fc5f6ea378 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 17:11:40 -0400 Subject: [PATCH 749/815] wip --- network/internal/testutils/testUtil.go | 4 +- network/test/blob_service_test.go | 488 ++++---- network/test/echoengine_test.go | 1197 ++++++++++---------- network/test/epochtransition_test.go | 935 +++++++-------- network/test/meshengine_test.go | 1049 +++++++++-------- network/test/middleware_test.go | 311 +++-- network/test/unicast_authorization_test.go | 1111 +++++++++--------- 7 files changed, 2533 insertions(+), 2562 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 868d8f7b59d..0850e708a6b 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -185,10 +185,10 @@ func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, libp2pNodes []p2p.LibP2PNode, - configOpts ...func(*p2pnet.NetworkConfig)) ([]network.Network, []*unittest.UpdatableIDProvider) { + configOpts ...func(*p2pnet.NetworkConfig)) ([]*p2pnet.Network, []*unittest.UpdatableIDProvider) { count := len(ids) - nets := make([]network.Network, 0) + nets := make([]*p2pnet.Network, 0) idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 64b9b52e60a..338ef9d1d5d 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -1,248 +1,244 @@ package test -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/sync" - blockstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "go.uber.org/atomic" - - "github.com/onflow/flow-go/network/p2p/connection" - "github.com/onflow/flow-go/network/p2p/dht" - p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/utils/unittest" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/blobs" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/module/util" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/internal/testutils" -) - -// conditionalTopology is a topology that behaves like the underlying topology when the condition is true, -// otherwise returns an empty identity list. -type conditionalTopology struct { - top network.Topology - condition func() bool -} - -var _ network.Topology = (*conditionalTopology)(nil) - -func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList { - if t.condition() { - return t.top.Fanout(ids) - } else { - return flow.IdentityList{} - } -} - -type BlobServiceTestSuite struct { - suite.Suite - - cancel context.CancelFunc - networks []network.Network - blobServices []network.BlobService - datastores []datastore.Batching - blobCids []cid.Cid - numNodes int -} - -func TestBlobService(t *testing.T) { - t.Parallel() - suite.Run(t, new(BlobServiceTestSuite)) -} - -func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob)) -} - -func (suite *BlobServiceTestSuite) SetupTest() { - suite.numNodes = 3 - - // Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without - // race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen - // on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other. - // See https://github.com/ipfs/go-bitswap/issues/525 for more details. - topologyActive := atomic.NewBool(false) - - ctx, cancel := context.WithCancel(context.Background()) - suite.cancel = cancel - - signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - - sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), - sporkId, - suite.numNodes, - p2ptest.WithDHTOptions(dht.AsServer()), - p2ptest.WithPeerManagerEnabled(&p2pconfig.PeerManagerConfig{ - UpdateInterval: 1 * time.Second, - ConnectionPruning: true, - ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), - }, nil)) - mws, _ := testutils.MiddlewareFixtures( - suite.T(), - ids, - nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) - - blobExchangeChannel := channels.Channel("blob-exchange") - - for i, net := range suite.networks { - ds := sync.MutexWrap(datastore.NewMapDatastore()) - suite.datastores = append(suite.datastores, ds) - blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i))) - suite.blobCids = append(suite.blobCids, blob.Cid()) - suite.putBlob(ds, blob) - blobService, err := net.RegisterBlobService(blobExchangeChannel, ds) - suite.Require().NoError(err) - unittest.RequireCloseBefore(suite.T(), blobService.Ready(), 100*time.Millisecond, "blob service not ready") - suite.blobServices = append(suite.blobServices, blobService) - } - - // let nodes connect to each other only after they are all listening on Bitswap - topologyActive.Store(true) - suite.Require().Eventually(func() bool { - for i, libp2pNode := range nodes { - for j := i + 1; j < suite.numNodes; j++ { - connected, err := libp2pNode.IsConnected(nodes[j].ID()) - require.NoError(suite.T(), err) - if !connected { - return false - } - } - } - return true - }, 3*time.Second, 100*time.Millisecond) -} - -func (suite *BlobServiceTestSuite) TearDownTest() { - suite.cancel() - - netDoneChans := make([]<-chan struct{}, len(suite.networks)) - for i, net := range suite.networks { - netDoneChans[i] = net.Done() - } - <-util.AllClosed(netDoneChans...) - - suite.networks = nil - suite.cancel = nil - suite.blobServices = nil - suite.datastores = nil - suite.blobCids = nil -} - -func (suite *BlobServiceTestSuite) TestGetBlobs() { - for i, bex := range suite.blobServices { - // check that we can get all other blobs - var blobsToGet []cid.Cid - unreceivedBlobs := make(map[cid.Cid]struct{}) - for j, blobCid := range suite.blobCids { - if j != i { - blobsToGet = append(blobsToGet, blobCid) - unreceivedBlobs[blobCid] = struct{}{} - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - blobs := bex.GetBlobs(ctx, blobsToGet) - - for blob := range blobs { - delete(unreceivedBlobs, blob.Cid()) - } - - for c := range unreceivedBlobs { - suite.T().Errorf("Blob %v not received by node %v", c, i) - } - } -} - -func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() { - for i, bex := range suite.blobServices { - // check that we can get all other blobs in a single session - blobsToGet := make(map[cid.Cid]struct{}) - for j, blobCid := range suite.blobCids { - if j != i { - blobsToGet[blobCid] = struct{}{} - } - } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - session := bex.GetSession(ctx) - for blobCid := range blobsToGet { - _, err := session.GetBlob(ctx, blobCid) - suite.Assert().NoError(err) - } - } -} - -func (suite *BlobServiceTestSuite) TestHas() { - var blobChans []<-chan blobs.Blob - unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices)) - - for i, bex := range suite.blobServices { - unreceivedBlobs[i] = make(map[cid.Cid]struct{}) - // check that peers are notified when we have a new blob - var blobsToGet []cid.Cid - for j := 0; j < suite.numNodes; j++ { - if j != i { - blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))) - blobsToGet = append(blobsToGet, blob.Cid()) - unreceivedBlobs[i][blob.Cid()] = struct{}{} - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - blobs := bex.GetBlobs(ctx, blobsToGet) - blobChans = append(blobChans, blobs) - } - - // check that blobs are not received until Has is called by the server - suite.Require().Never(func() bool { - for _, blobChan := range blobChans { - select { - case _, ok := <-blobChan: - if ok { - return true - } - default: - } - } - return false - }, time.Second, 100*time.Millisecond) - - for i, bex := range suite.blobServices { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))) - suite.Require().NoError(err) - } - - for i, blobs := range blobChans { - for blob := range blobs { - delete(unreceivedBlobs[i], blob.Cid()) - } - for c := range unreceivedBlobs[i] { - suite.T().Errorf("blob %v not received by node %v", c, i) - } - } -} +// +//import ( +// "context" +// "fmt" +// "testing" +// "time" +// +// "github.com/ipfs/go-cid" +// "github.com/ipfs/go-datastore" +// "github.com/ipfs/go-datastore/sync" +// blockstore "github.com/ipfs/go-ipfs-blockstore" +// "github.com/stretchr/testify/require" +// "github.com/stretchr/testify/suite" +// "go.uber.org/atomic" +// +// "github.com/onflow/flow-go/network/p2p/connection" +// "github.com/onflow/flow-go/network/p2p/dht" +// p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" +// p2ptest "github.com/onflow/flow-go/network/p2p/test" +// "github.com/onflow/flow-go/utils/unittest" +// +// "github.com/onflow/flow-go/model/flow" +// "github.com/onflow/flow-go/module/blobs" +// "github.com/onflow/flow-go/module/irrecoverable" +// "github.com/onflow/flow-go/module/util" +// "github.com/onflow/flow-go/network" +// "github.com/onflow/flow-go/network/channels" +// "github.com/onflow/flow-go/network/internal/testutils" +//) +// +//// conditionalTopology is a topology that behaves like the underlying topology when the condition is true, +//// otherwise returns an empty identity list. +//type conditionalTopology struct { +// top network.Topology +// condition func() bool +//} +// +//var _ network.Topology = (*conditionalTopology)(nil) +// +//func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList { +// if t.condition() { +// return t.top.Fanout(ids) +// } else { +// return flow.IdentityList{} +// } +//} +// +//type BlobServiceTestSuite struct { +// suite.Suite +// +// cancel context.CancelFunc +// networks []network.Network +// blobServices []network.BlobService +// datastores []datastore.Batching +// blobCids []cid.Cid +// numNodes int +//} +// +//func TestBlobService(t *testing.T) { +// t.Parallel() +// suite.Run(t, new(BlobServiceTestSuite)) +//} +// +//func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) { +// ctx, cancel := context.WithTimeout(context.Background(), time.Second) +// defer cancel() +// suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob)) +//} +// +//func (suite *BlobServiceTestSuite) SetupTest() { +// suite.numNodes = 3 +// +// // Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without +// // race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen +// // on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other. +// // See https://github.com/ipfs/go-bitswap/issues/525 for more details. +// topologyActive := atomic.NewBool(false) +// +// ctx, cancel := context.WithCancel(context.Background()) +// suite.cancel = cancel +// +// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) +// +// sporkId := unittest.IdentifierFixture() +// ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), +// sporkId, +// suite.numNodes, +// p2ptest.WithDHTOptions(dht.AsServer()), +// p2ptest.WithPeerManagerEnabled(&p2pconfig.PeerManagerConfig{ +// UpdateInterval: 1 * time.Second, +// ConnectionPruning: true, +// ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), +// }, nil)) +// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) +// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) +// +// blobExchangeChannel := channels.Channel("blob-exchange") +// +// for i, net := range suite.networks { +// ds := sync.MutexWrap(datastore.NewMapDatastore()) +// suite.datastores = append(suite.datastores, ds) +// blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i))) +// suite.blobCids = append(suite.blobCids, blob.Cid()) +// suite.putBlob(ds, blob) +// blobService, err := net.RegisterBlobService(blobExchangeChannel, ds) +// suite.Require().NoError(err) +// unittest.RequireCloseBefore(suite.T(), blobService.Ready(), 100*time.Millisecond, "blob service not ready") +// suite.blobServices = append(suite.blobServices, blobService) +// } +// +// // let nodes connect to each other only after they are all listening on Bitswap +// topologyActive.Store(true) +// suite.Require().Eventually(func() bool { +// for i, libp2pNode := range nodes { +// for j := i + 1; j < suite.numNodes; j++ { +// connected, err := libp2pNode.IsConnected(nodes[j].ID()) +// require.NoError(suite.T(), err) +// if !connected { +// return false +// } +// } +// } +// return true +// }, 3*time.Second, 100*time.Millisecond) +//} +// +//func (suite *BlobServiceTestSuite) TearDownTest() { +// suite.cancel() +// +// netDoneChans := make([]<-chan struct{}, len(suite.networks)) +// for i, net := range suite.networks { +// netDoneChans[i] = net.Done() +// } +// <-util.AllClosed(netDoneChans...) +// +// suite.networks = nil +// suite.cancel = nil +// suite.blobServices = nil +// suite.datastores = nil +// suite.blobCids = nil +//} +// +//func (suite *BlobServiceTestSuite) TestGetBlobs() { +// for i, bex := range suite.blobServices { +// // check that we can get all other blobs +// var blobsToGet []cid.Cid +// unreceivedBlobs := make(map[cid.Cid]struct{}) +// for j, blobCid := range suite.blobCids { +// if j != i { +// blobsToGet = append(blobsToGet, blobCid) +// unreceivedBlobs[blobCid] = struct{}{} +// } +// } +// +// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +// defer cancel() +// +// blobs := bex.GetBlobs(ctx, blobsToGet) +// +// for blob := range blobs { +// delete(unreceivedBlobs, blob.Cid()) +// } +// +// for c := range unreceivedBlobs { +// suite.T().Errorf("Blob %v not received by node %v", c, i) +// } +// } +//} +// +//func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() { +// for i, bex := range suite.blobServices { +// // check that we can get all other blobs in a single session +// blobsToGet := make(map[cid.Cid]struct{}) +// for j, blobCid := range suite.blobCids { +// if j != i { +// blobsToGet[blobCid] = struct{}{} +// } +// } +// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +// defer cancel() +// session := bex.GetSession(ctx) +// for blobCid := range blobsToGet { +// _, err := session.GetBlob(ctx, blobCid) +// suite.Assert().NoError(err) +// } +// } +//} +// +//func (suite *BlobServiceTestSuite) TestHas() { +// var blobChans []<-chan blobs.Blob +// unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices)) +// +// for i, bex := range suite.blobServices { +// unreceivedBlobs[i] = make(map[cid.Cid]struct{}) +// // check that peers are notified when we have a new blob +// var blobsToGet []cid.Cid +// for j := 0; j < suite.numNodes; j++ { +// if j != i { +// blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))) +// blobsToGet = append(blobsToGet, blob.Cid()) +// unreceivedBlobs[i][blob.Cid()] = struct{}{} +// } +// } +// +// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +// defer cancel() +// +// blobs := bex.GetBlobs(ctx, blobsToGet) +// blobChans = append(blobChans, blobs) +// } +// +// // check that blobs are not received until Has is called by the server +// suite.Require().Never(func() bool { +// for _, blobChan := range blobChans { +// select { +// case _, ok := <-blobChan: +// if ok { +// return true +// } +// default: +// } +// } +// return false +// }, time.Second, 100*time.Millisecond) +// +// for i, bex := range suite.blobServices { +// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +// defer cancel() +// +// err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))) +// suite.Require().NoError(err) +// } +// +// for i, blobs := range blobChans { +// for blob := range blobs { +// delete(unreceivedBlobs[i], blob.Cid()) +// } +// for c := range unreceivedBlobs[i] { +// suite.T().Errorf("blob %v not received by node %v", c, i) +// } +// } +//} diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 5087981624d..aca74e5234a 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -1,601 +1,600 @@ package test -import ( - "context" - "fmt" - "strings" - "sync" - "testing" - "time" - - "github.com/ipfs/go-log" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/utils/unittest" -) - -// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p -// protocol stack. It creates two instances of a stubengine, connects them through network, and sends a -// single message from one engine to the other one through different scenarios. -type EchoEngineTestSuite struct { - suite.Suite - testutils.ConduitWrapper // used as a wrapper around conduit methods - nets []network.Network // used to keep track of the networks - mws []network.Middleware // used to keep track of the middlewares - ids flow.IdentityList // used to keep track of the identifiers associated with networks - cancel context.CancelFunc -} - -// Some tests are skipped in short mode to speedup the build. - -// TestStubEngineTestSuite runs all the test methods in this test suit -func TestStubEngineTestSuite(t *testing.T) { - suite.Run(t, new(EchoEngineTestSuite)) -} - -func (suite *EchoEngineTestSuite) SetupTest() { - const count = 2 - log.SetAllLoggers(log.LevelError) - - ctx, cancel := context.WithCancel(context.Background()) - suite.cancel = cancel - - signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - - // both nodes should be of the same role to get connected on epidemic dissemination - var nodes []p2p.LibP2PNode - sporkId := unittest.IdentifierFixture() - suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - suite.mws, _ = testutils.MiddlewareFixtures( - suite.T(), - suite.ids, - nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets, 100*time.Millisecond) -} - -// TearDownTest closes the networks within a specified timeout -func (suite *EchoEngineTestSuite) TearDownTest() { - suite.cancel() - testutils.StopComponents(suite.T(), suite.nets, 3*time.Second) - testutils.StopComponents(suite.T(), suite.mws, 3*time.Second) -} - -// TestUnknownChannel evaluates that registering an engine with an unknown channel returns an error. -// All channels should be registered as topics in engine.topicMap. -func (suite *EchoEngineTestSuite) TestUnknownChannel() { - e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast) - _, err := suite.nets[0].Register("unknown-channel-id", e) - require.Error(suite.T(), err) -} - -// TestClusterChannel evaluates that registering a cluster channel is done without any error. -func (suite *EchoEngineTestSuite) TestClusterChannel() { - e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast) - // creates a cluster channel - clusterChannel := channels.SyncCluster(flow.Testnet) - // registers engine with cluster channel - _, err := suite.nets[0].Register(clusterChannel, e) - // registering cluster channel should not cause an error - require.NoError(suite.T(), err) -} - -// TestDuplicateChannel evaluates that registering an engine with duplicate channel returns an error. -func (suite *EchoEngineTestSuite) TestDuplicateChannel() { - // creates an echo engine, which registers it on test network channel - e := NewEchoEngine(suite.T(), suite.nets[0], 1, channels.TestNetworkChannel, false, suite.Unicast) - - // attempts to register the same engine again on test network channel which - // should cause an error - _, err := suite.nets[0].Register(channels.TestNetworkChannel, e) - require.Error(suite.T(), err) -} - -// TestEchoMultiMsgAsync_Publish tests sending multiple messages from sender to receiver -// using the Publish method of their Conduit. -// It also evaluates the correct reception of an echo message back for each send. -// Sender and receiver are not synchronized -func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Publish() { - // set to true for an echo expectation - suite.multiMessageAsync(true, 10, suite.Publish) -} - -// TestEchoMultiMsgAsync_Unicast tests sending multiple messages from sender to receiver -// using the Unicast method of their Conduit. -// It also evaluates the correct reception of an echo message back for each send. -// Sender and receiver are not synchronized -func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { - // set to true for an echo expectation - suite.multiMessageAsync(true, 10, suite.Unicast) -} - -// TestEchoMultiMsgAsync_Multicast tests sending multiple messages from sender to receiver -// using the Multicast method of their Conduit. -// It also evaluates the correct reception of an echo message back for each send. -// Sender and receiver are not synchronized -func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { - // set to true for an echo expectation - suite.multiMessageAsync(true, 10, suite.Multicast) -} - -// TestDuplicateMessageSequential_Publish evaluates the correctness of network layer on deduplicating -// the received messages over Publish method of nodes' Conduits. -// Messages are delivered to the receiver in a sequential manner. -func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Publish() { - unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Publish") - suite.duplicateMessageSequential(suite.Publish) -} - -// TestDuplicateMessageSequential_Unicast evaluates the correctness of network layer on deduplicating -// the received messages over Unicast method of nodes' Conduits. -// Messages are delivered to the receiver in a sequential manner. -func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Unicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Unicast") - suite.duplicateMessageSequential(suite.Unicast) -} - -// TestDuplicateMessageSequential_Multicast evaluates the correctness of network layer on deduplicating -// the received messages over Multicast method of nodes' Conduits. -// Messages are delivered to the receiver in a sequential manner. -func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast") - suite.duplicateMessageSequential(suite.Multicast) -} - -// TestDuplicateMessageParallel_Publish evaluates the correctness of network layer -// on deduplicating the received messages via Publish method of nodes' Conduits. -// Messages are delivered to the receiver in parallel via the Publish method of Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() { - suite.duplicateMessageParallel(suite.Publish) -} - -// TestDuplicateMessageParallel_Unicast evaluates the correctness of network layer -// on deduplicating the received messages via Unicast method of nodes' Conduits. -// Messages are delivered to the receiver in parallel via the Unicast method of Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { - suite.duplicateMessageParallel(suite.Unicast) -} - -// TestDuplicateMessageParallel_Multicast evaluates the correctness of network layer -// on deduplicating the received messages via Multicast method of nodes' Conduits. -// Messages are delivered to the receiver in parallel via the Multicast method of Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { - suite.duplicateMessageParallel(suite.Multicast) -} - -// TestDuplicateMessageDifferentChan_Publish evaluates the correctness of network layer -// on deduplicating the received messages against different engine ids. In specific, the -// desire behavior is that the deduplication should happen based on both eventID and channel. -// Messages are sent via the Publish methods of the Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { - suite.duplicateMessageDifferentChan(suite.Publish) -} - -// TestDuplicateMessageDifferentChan_Unicast evaluates the correctness of network layer -// on deduplicating the received messages against different engine ids. In specific, the -// desire behavior is that the deduplication should happen based on both eventID and channel. -// Messages are sent via the Unicast methods of the Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { - suite.duplicateMessageDifferentChan(suite.Unicast) -} - -// TestDuplicateMessageDifferentChan_Multicast evaluates the correctness of network layer -// on deduplicating the received messages against different engine ids. In specific, the -// desire behavior is that the deduplication should happen based on both eventID and channel. -// Messages are sent via the Multicast methods of the Conduits. -func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { - suite.duplicateMessageDifferentChan(suite.Multicast) -} - -// duplicateMessageSequential is a helper function that sends duplicate messages sequentially -// from a receiver to the sender via the injected send wrapper function of conduit. -func (suite *EchoEngineTestSuite) duplicateMessageSequential(send testutils.ConduitSendWrapperFunc) { - sndID := 0 - rcvID := 1 - // registers engines in the network - // sender's engine - sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, false, send) - - // receiver's engine - receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, false, send) - - // allow nodes to heartbeat and discover each other if using PubSub - testutils.OptionalSleep(send) - - // Sends a message from sender to receiver - event := &message.TestMessage{ - Text: "hello", - } - - // sends the same message 10 times - for i := 0; i < 10; i++ { - require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) - } - - time.Sleep(1 * time.Second) - - // receiver should only see the message once, and the rest should be dropped due to - // duplication - receiver.RLock() - require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) - require.Len(suite.Suite.T(), receiver.seen, 1) - receiver.RUnlock() -} - -// duplicateMessageParallel is a helper function that sends duplicate messages concurrent;u -// from a receiver to the sender via the injected send wrapper function of conduit. -func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.ConduitSendWrapperFunc) { - sndID := 0 - rcvID := 1 - // registers engines in the network - // sender's engine - sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, false, send) - - // receiver's engine - receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, false, send) - - // allow nodes to heartbeat and discover each other - testutils.OptionalSleep(send) - - // Sends a message from sender to receiver - event := &message.TestMessage{ - Text: "hello", - } - - // sends the same message 10 times - wg := sync.WaitGroup{} - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) - wg.Done() - }() - } - unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not send message on time") - - require.Eventually(suite.T(), func() bool { - return len(receiver.seen) > 0 - }, 3*time.Second, 500*time.Millisecond) - - // receiver should only see the message once, and the rest should be dropped due to - // duplication - receiver.RLock() - require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) - require.Len(suite.Suite.T(), receiver.seen, 1) - receiver.RUnlock() -} - -// duplicateMessageDifferentChan is a helper function that sends the same message from two distinct -// sender engines to the two distinct receiver engines via the send wrapper function of Conduits. -func (suite *EchoEngineTestSuite) duplicateMessageDifferentChan(send testutils.ConduitSendWrapperFunc) { - const ( - sndNode = iota - rcvNode - ) - const ( - channel1 = channels.TestNetworkChannel - channel2 = channels.TestMetricsChannel - ) - // registers engines in the network - // first type - // sender'suite engine - sender1 := NewEchoEngine(suite.Suite.T(), suite.nets[sndNode], 10, channel1, false, send) - - // receiver's engine - receiver1 := NewEchoEngine(suite.Suite.T(), suite.nets[rcvNode], 10, channel1, false, send) - - // second type - // registers engines in the network - // sender'suite engine - sender2 := NewEchoEngine(suite.Suite.T(), suite.nets[sndNode], 10, channel2, false, send) - - // receiver's engine - receiver2 := NewEchoEngine(suite.Suite.T(), suite.nets[rcvNode], 10, channel2, false, send) - - // allow nodes to heartbeat and discover each other - testutils.OptionalSleep(send) - - // Sends a message from sender to receiver - event := &message.TestMessage{ - Text: "hello", - } - - // sends the same message 10 times on both channels - wg := sync.WaitGroup{} - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - // sender1 to receiver1 on channel1 - require.NoError(suite.Suite.T(), send(event, sender1.con, suite.ids[rcvNode].NodeID)) - - // sender2 to receiver2 on channel2 - require.NoError(suite.Suite.T(), send(event, sender2.con, suite.ids[rcvNode].NodeID)) - }() - } - unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not handle sending unicasts on time") - time.Sleep(1 * time.Second) - - // each receiver should only see the message once, and the rest should be dropped due to - // duplication - receiver1.RLock() - receiver2.RLock() - require.Equal(suite.Suite.T(), 1, receiver1.seen[event.Text]) - require.Equal(suite.Suite.T(), 1, receiver2.seen[event.Text]) - - require.Len(suite.Suite.T(), receiver1.seen, 1) - require.Len(suite.Suite.T(), receiver2.seen, 1) - receiver1.RUnlock() - receiver2.RUnlock() -} - -// singleMessage sends a single message from one network instance to the other one -// it evaluates the correctness of implementation against correct delivery of the message. -// in case echo is true, it also evaluates correct reception of the echo message from the receiver side -func (suite *EchoEngineTestSuite) singleMessage(echo bool, send testutils.ConduitSendWrapperFunc) { - sndID := 0 - rcvID := 1 - - // registers engines in the network - // sender's engine - sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send) - - // receiver's engine - receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send) - - // allow nodes to heartbeat and discover each other - testutils.OptionalSleep(send) - - // Sends a message from sender to receiver - event := &message.TestMessage{ - Text: "hello", - } - require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) - - // evaluates reception of echo request - select { - case <-receiver.received: - // evaluates reception of message at the other side - // does not evaluate the content - receiver.RLock() - require.NotNil(suite.Suite.T(), receiver.originID) - require.NotNil(suite.Suite.T(), receiver.event) - assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) - receiver.RUnlock() - - assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) - - case <-time.After(10 * time.Second): - assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") - } - - // evaluates echo back - if echo { - // evaluates reception of echo response - select { - case <-sender.received: - // evaluates reception of message at the other side - // does not evaluate the content - sender.RLock() - require.NotNil(suite.Suite.T(), sender.originID) - require.NotNil(suite.Suite.T(), sender.event) - assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) - sender.RUnlock() - - echoEvent := &message.TestMessage{ - Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), - } - assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) - - case <-time.After(10 * time.Second): - assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") - } - } -} - -// multiMessageSync sends a multiple messages from one network instance to the other one -// it evaluates the correctness of implementation against correct delivery of the messages. -// sender and receiver are sync over reception, i.e., sender sends one message at a time and -// waits for its reception -// count defines number of messages -func (suite *EchoEngineTestSuite) multiMessageSync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { - sndID := 0 - rcvID := 1 - // registers engines in the network - // sender's engine - sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send) - - // receiver's engine - receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send) - - // allow nodes to heartbeat and discover each other - testutils.OptionalSleep(send) - - for i := 0; i < count; i++ { - // Send the message to receiver - event := &message.TestMessage{ - Text: fmt.Sprintf("hello%d", i), - } - // sends a message from sender to receiver using send wrapper function - require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) - - select { - case <-receiver.received: - // evaluates reception of message at the other side - // does not evaluate the content - receiver.RLock() - require.NotNil(suite.Suite.T(), receiver.originID) - require.NotNil(suite.Suite.T(), receiver.event) - assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) - receiver.RUnlock() - - assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) - - case <-time.After(2 * time.Second): - assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") - } - - // evaluates echo back - if echo { - // evaluates reception of echo response - select { - case <-sender.received: - // evaluates reception of message at the other side - // does not evaluate the content - sender.RLock() - require.NotNil(suite.Suite.T(), sender.originID) - require.NotNil(suite.Suite.T(), sender.event) - assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) - - receiver.RLock() - echoEvent := &message.TestMessage{ - Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), - } - assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) - receiver.RUnlock() - sender.RUnlock() - - case <-time.After(10 * time.Second): - assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") - } - } - - } - -} - -// multiMessageAsync sends a multiple messages from one network instance to the other one -// it evaluates the correctness of implementation against correct delivery of the messages. -// sender and receiver are async, i.e., sender sends all its message at blast -// count defines number of messages -func (suite *EchoEngineTestSuite) multiMessageAsync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { - sndID := 0 - rcvID := 1 - - // registers engines in the network - // sender's engine - sender := NewEchoEngine(suite.Suite.T(), suite.nets[sndID], 10, channels.TestNetworkChannel, echo, send) - - // receiver's engine - receiver := NewEchoEngine(suite.Suite.T(), suite.nets[rcvID], 10, channels.TestNetworkChannel, echo, send) - - // allow nodes to heartbeat and discover each other - testutils.OptionalSleep(send) - - // keeps track of async received messages at receiver side - received := make(map[string]struct{}) - - // keeps track of async received echo messages at sender side - // echorcv := make(map[string]struct{}) - - for i := 0; i < count; i++ { - // Send the message to node 2 using the conduit of node 1 - event := &message.TestMessage{ - Text: fmt.Sprintf("hello%d", i), - } - require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[1].NodeID)) - } - - for i := 0; i < count; i++ { - select { - case <-receiver.received: - // evaluates reception of message at the other side - // does not evaluate the content - receiver.RLock() - require.NotNil(suite.Suite.T(), receiver.originID) - require.NotNil(suite.Suite.T(), receiver.event) - assert.Equal(suite.Suite.T(), suite.ids[0].NodeID, receiver.originID) - receiver.RUnlock() - - // wrap blocking channel reads with a timeout - unittest.AssertReturnsBefore(suite.T(), func() { - // evaluates proper reception of event - // casts the received event at the receiver side - rcvEvent, ok := (<-receiver.event).(*message.TestMessage) - // evaluates correctness of casting - require.True(suite.T(), ok) - - // evaluates content of received message - // the content should not yet received and be unique - _, rcv := received[rcvEvent.Text] - assert.False(suite.T(), rcv) - // marking event as received - received[rcvEvent.Text] = struct{}{} - - // evaluates channel that message was received on - assert.Equal(suite.T(), channels.TestNetworkChannel, <-receiver.channel) - }, 100*time.Millisecond) - - case <-time.After(2 * time.Second): - assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") - } - } - - for i := 0; i < count; i++ { - // evaluates echo back - if echo { - // evaluates reception of echo response - select { - case <-sender.received: - // evaluates reception of message at the other side - // does not evaluate the content - sender.RLock() - require.NotNil(suite.Suite.T(), sender.originID) - require.NotNil(suite.Suite.T(), sender.event) - assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) - sender.RUnlock() - - // wrap blocking channel reads with a timeout - unittest.AssertReturnsBefore(suite.T(), func() { - // evaluates proper reception of event - // casts the received event at the receiver side - rcvEvent, ok := (<-sender.event).(*message.TestMessage) - // evaluates correctness of casting - require.True(suite.T(), ok) - // evaluates content of received echo message - // the content should not yet received and be unique - _, rcv := received[rcvEvent.Text] - assert.False(suite.T(), rcv) - // echo messages should start with prefix msg of receiver that echos back - assert.True(suite.T(), strings.HasPrefix(rcvEvent.Text, receiver.echomsg)) - // marking echo event as received - received[rcvEvent.Text] = struct{}{} - - // evaluates channel that message was received on - assert.Equal(suite.T(), channels.TestNetworkChannel, <-sender.channel) - }, 100*time.Millisecond) - - case <-time.After(10 * time.Second): - assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") - } - } - } -} - -// assertMessageReceived asserts that the given message was received on the given channel -// for the given engine -func assertMessageReceived(t *testing.T, e *EchoEngine, m *message.TestMessage, c channels.Channel) { - // wrap blocking channel reads with a timeout - unittest.AssertReturnsBefore(t, func() { - // evaluates proper reception of event - // casts the received event at the receiver side - rcvEvent, ok := (<-e.event).(*message.TestMessage) - // evaluates correctness of casting - require.True(t, ok) - // evaluates content of received message - assert.Equal(t, m, rcvEvent) - - // evaluates channel that message was received on - assert.Equal(t, c, <-e.channel) - }, 100*time.Millisecond) -} +// +//import ( +// "context" +// "fmt" +// "strings" +// "sync" +// "testing" +// "time" +// +// "github.com/ipfs/go-log" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/require" +// "github.com/stretchr/testify/suite" +// +// "github.com/onflow/flow-go/model/flow" +// "github.com/onflow/flow-go/model/libp2p/message" +// "github.com/onflow/flow-go/module/irrecoverable" +// "github.com/onflow/flow-go/network" +// "github.com/onflow/flow-go/network/channels" +// "github.com/onflow/flow-go/network/internal/testutils" +// "github.com/onflow/flow-go/network/p2p" +// "github.com/onflow/flow-go/network/p2p/p2pnet" +// "github.com/onflow/flow-go/utils/unittest" +//) +// +//// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p +//// protocol stack. It creates two instances of a stubengine, connects them through network, and sends a +//// single message from one engine to the other one through different scenarios. +//type EchoEngineTestSuite struct { +// suite.Suite +// testutils.ConduitWrapper // used as a wrapper around conduit methods +// networks []*p2pnet.Network // used to keep track of the networks +// libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes +// ids flow.IdentityList // used to keep track of the identifiers associated with networks +// cancel context.CancelFunc +//} +// +//// Some tests are skipped in short mode to speedup the build. +// +//// TestStubEngineTestSuite runs all the test methods in this test suit +//func TestStubEngineTestSuite(t *testing.T) { +// suite.Run(t, new(EchoEngineTestSuite)) +//} +// +//func (suite *EchoEngineTestSuite) SetupTest() { +// const count = 2 +// log.SetAllLoggers(log.LevelError) +// +// ctx, cancel := context.WithCancel(context.Background()) +// suite.cancel = cancel +// +// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) +// +// // both nodes should be of the same role to get connected on epidemic dissemination +// var nodes []p2p.LibP2PNode +// sporkId := unittest.IdentifierFixture() +// +// suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) +// suite.libp2pNodes = nodes +// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, nodes) +// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 3*time.Second) +//} +// +//// TearDownTest closes the networks within a specified timeout +//func (suite *EchoEngineTestSuite) TearDownTest() { +// suite.cancel() +// testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) +// testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) +//} +// +//// TestUnknownChannel evaluates that registering an engine with an unknown channel returns an error. +//// All channels should be registered as topics in engine.topicMap. +//func (suite *EchoEngineTestSuite) TestUnknownChannel() { +// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) +// _, err := suite.networks[0].Register("unknown-channel-id", e) +// require.Error(suite.T(), err) +//} +// +//// TestClusterChannel evaluates that registering a cluster channel is done without any error. +//func (suite *EchoEngineTestSuite) TestClusterChannel() { +// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) +// // creates a cluster channel +// clusterChannel := channels.SyncCluster(flow.Testnet) +// // registers engine with cluster channel +// _, err := suite.networks[0].Register(clusterChannel, e) +// // registering cluster channel should not cause an error +// require.NoError(suite.T(), err) +//} +// +//// TestDuplicateChannel evaluates that registering an engine with duplicate channel returns an error. +//func (suite *EchoEngineTestSuite) TestDuplicateChannel() { +// // creates an echo engine, which registers it on test network channel +// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) +// +// // attempts to register the same engine again on test network channel which +// // should cause an error +// _, err := suite.networks[0].Register(channels.TestNetworkChannel, e) +// require.Error(suite.T(), err) +//} +// +//// TestEchoMultiMsgAsync_Publish tests sending multiple messages from sender to receiver +//// using the Publish method of their Conduit. +//// It also evaluates the correct reception of an echo message back for each send. +//// Sender and receiver are not synchronized +//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Publish() { +// // set to true for an echo expectation +// suite.multiMessageAsync(true, 10, suite.Publish) +//} +// +//// TestEchoMultiMsgAsync_Unicast tests sending multiple messages from sender to receiver +//// using the Unicast method of their Conduit. +//// It also evaluates the correct reception of an echo message back for each send. +//// Sender and receiver are not synchronized +//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { +// // set to true for an echo expectation +// suite.multiMessageAsync(true, 10, suite.Unicast) +//} +// +//// TestEchoMultiMsgAsync_Multicast tests sending multiple messages from sender to receiver +//// using the Multicast method of their Conduit. +//// It also evaluates the correct reception of an echo message back for each send. +//// Sender and receiver are not synchronized +//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { +// // set to true for an echo expectation +// suite.multiMessageAsync(true, 10, suite.Multicast) +//} +// +//// TestDuplicateMessageSequential_Publish evaluates the correctness of network layer on deduplicating +//// the received messages over Publish method of nodes' Conduits. +//// Messages are delivered to the receiver in a sequential manner. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Publish() { +// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Publish") +// suite.duplicateMessageSequential(suite.Publish) +//} +// +//// TestDuplicateMessageSequential_Unicast evaluates the correctness of network layer on deduplicating +//// the received messages over Unicast method of nodes' Conduits. +//// Messages are delivered to the receiver in a sequential manner. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Unicast() { +// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Unicast") +// suite.duplicateMessageSequential(suite.Unicast) +//} +// +//// TestDuplicateMessageSequential_Multicast evaluates the correctness of network layer on deduplicating +//// the received messages over Multicast method of nodes' Conduits. +//// Messages are delivered to the receiver in a sequential manner. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() { +// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast") +// suite.duplicateMessageSequential(suite.Multicast) +//} +// +//// TestDuplicateMessageParallel_Publish evaluates the correctness of network layer +//// on deduplicating the received messages via Publish method of nodes' Conduits. +//// Messages are delivered to the receiver in parallel via the Publish method of Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() { +// suite.duplicateMessageParallel(suite.Publish) +//} +// +//// TestDuplicateMessageParallel_Unicast evaluates the correctness of network layer +//// on deduplicating the received messages via Unicast method of nodes' Conduits. +//// Messages are delivered to the receiver in parallel via the Unicast method of Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { +// suite.duplicateMessageParallel(suite.Unicast) +//} +// +//// TestDuplicateMessageParallel_Multicast evaluates the correctness of network layer +//// on deduplicating the received messages via Multicast method of nodes' Conduits. +//// Messages are delivered to the receiver in parallel via the Multicast method of Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { +// suite.duplicateMessageParallel(suite.Multicast) +//} +// +//// TestDuplicateMessageDifferentChan_Publish evaluates the correctness of network layer +//// on deduplicating the received messages against different engine ids. In specific, the +//// desire behavior is that the deduplication should happen based on both eventID and channel. +//// Messages are sent via the Publish methods of the Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { +// suite.duplicateMessageDifferentChan(suite.Publish) +//} +// +//// TestDuplicateMessageDifferentChan_Unicast evaluates the correctness of network layer +//// on deduplicating the received messages against different engine ids. In specific, the +//// desire behavior is that the deduplication should happen based on both eventID and channel. +//// Messages are sent via the Unicast methods of the Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { +// suite.duplicateMessageDifferentChan(suite.Unicast) +//} +// +//// TestDuplicateMessageDifferentChan_Multicast evaluates the correctness of network layer +//// on deduplicating the received messages against different engine ids. In specific, the +//// desire behavior is that the deduplication should happen based on both eventID and channel. +//// Messages are sent via the Multicast methods of the Conduits. +//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { +// suite.duplicateMessageDifferentChan(suite.Multicast) +//} +// +//// duplicateMessageSequential is a helper function that sends duplicate messages sequentially +//// from a receiver to the sender via the injected send wrapper function of conduit. +//func (suite *EchoEngineTestSuite) duplicateMessageSequential(send testutils.ConduitSendWrapperFunc) { +// sndID := 0 +// rcvID := 1 +// // registers engines in the network +// // sender's engine +// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) +// +// // receiver's engine +// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) +// +// // allow nodes to heartbeat and discover each other if using PubSub +// testutils.OptionalSleep(send) +// +// // Sends a message from sender to receiver +// event := &message.TestMessage{ +// Text: "hello", +// } +// +// // sends the same message 10 times +// for i := 0; i < 10; i++ { +// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) +// } +// +// time.Sleep(1 * time.Second) +// +// // receiver should only see the message once, and the rest should be dropped due to +// // duplication +// receiver.RLock() +// require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) +// require.Len(suite.Suite.T(), receiver.seen, 1) +// receiver.RUnlock() +//} +// +//// duplicateMessageParallel is a helper function that sends duplicate messages concurrent;u +//// from a receiver to the sender via the injected send wrapper function of conduit. +//func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.ConduitSendWrapperFunc) { +// sndID := 0 +// rcvID := 1 +// // registers engines in the network +// // sender's engine +// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) +// +// // receiver's engine +// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) +// +// // allow nodes to heartbeat and discover each other +// testutils.OptionalSleep(send) +// +// // Sends a message from sender to receiver +// event := &message.TestMessage{ +// Text: "hello", +// } +// +// // sends the same message 10 times +// wg := sync.WaitGroup{} +// for i := 0; i < 10; i++ { +// wg.Add(1) +// go func() { +// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) +// wg.Done() +// }() +// } +// unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not send message on time") +// +// require.Eventually(suite.T(), func() bool { +// return len(receiver.seen) > 0 +// }, 3*time.Second, 500*time.Millisecond) +// +// // receiver should only see the message once, and the rest should be dropped due to +// // duplication +// receiver.RLock() +// require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) +// require.Len(suite.Suite.T(), receiver.seen, 1) +// receiver.RUnlock() +//} +// +//// duplicateMessageDifferentChan is a helper function that sends the same message from two distinct +//// sender engines to the two distinct receiver engines via the send wrapper function of Conduits. +//func (suite *EchoEngineTestSuite) duplicateMessageDifferentChan(send testutils.ConduitSendWrapperFunc) { +// const ( +// sndNode = iota +// rcvNode +// ) +// const ( +// channel1 = channels.TestNetworkChannel +// channel2 = channels.TestMetricsChannel +// ) +// // registers engines in the network +// // first type +// // sender'suite engine +// sender1 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel1, false, send) +// +// // receiver's engine +// receiver1 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel1, false, send) +// +// // second type +// // registers engines in the network +// // sender'suite engine +// sender2 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel2, false, send) +// +// // receiver's engine +// receiver2 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel2, false, send) +// +// // allow nodes to heartbeat and discover each other +// testutils.OptionalSleep(send) +// +// // Sends a message from sender to receiver +// event := &message.TestMessage{ +// Text: "hello", +// } +// +// // sends the same message 10 times on both channels +// wg := sync.WaitGroup{} +// for i := 0; i < 10; i++ { +// wg.Add(1) +// go func() { +// defer wg.Done() +// // sender1 to receiver1 on channel1 +// require.NoError(suite.Suite.T(), send(event, sender1.con, suite.ids[rcvNode].NodeID)) +// +// // sender2 to receiver2 on channel2 +// require.NoError(suite.Suite.T(), send(event, sender2.con, suite.ids[rcvNode].NodeID)) +// }() +// } +// unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not handle sending unicasts on time") +// time.Sleep(1 * time.Second) +// +// // each receiver should only see the message once, and the rest should be dropped due to +// // duplication +// receiver1.RLock() +// receiver2.RLock() +// require.Equal(suite.Suite.T(), 1, receiver1.seen[event.Text]) +// require.Equal(suite.Suite.T(), 1, receiver2.seen[event.Text]) +// +// require.Len(suite.Suite.T(), receiver1.seen, 1) +// require.Len(suite.Suite.T(), receiver2.seen, 1) +// receiver1.RUnlock() +// receiver2.RUnlock() +//} +// +//// singleMessage sends a single message from one network instance to the other one +//// it evaluates the correctness of implementation against correct delivery of the message. +//// in case echo is true, it also evaluates correct reception of the echo message from the receiver side +//func (suite *EchoEngineTestSuite) singleMessage(echo bool, send testutils.ConduitSendWrapperFunc) { +// sndID := 0 +// rcvID := 1 +// +// // registers engines in the network +// // sender's engine +// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) +// +// // receiver's engine +// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) +// +// // allow nodes to heartbeat and discover each other +// testutils.OptionalSleep(send) +// +// // Sends a message from sender to receiver +// event := &message.TestMessage{ +// Text: "hello", +// } +// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) +// +// // evaluates reception of echo request +// select { +// case <-receiver.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// receiver.RLock() +// require.NotNil(suite.Suite.T(), receiver.originID) +// require.NotNil(suite.Suite.T(), receiver.event) +// assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) +// receiver.RUnlock() +// +// assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) +// +// case <-time.After(10 * time.Second): +// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") +// } +// +// // evaluates echo back +// if echo { +// // evaluates reception of echo response +// select { +// case <-sender.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// sender.RLock() +// require.NotNil(suite.Suite.T(), sender.originID) +// require.NotNil(suite.Suite.T(), sender.event) +// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) +// sender.RUnlock() +// +// echoEvent := &message.TestMessage{ +// Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), +// } +// assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) +// +// case <-time.After(10 * time.Second): +// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") +// } +// } +//} +// +//// multiMessageSync sends a multiple messages from one network instance to the other one +//// it evaluates the correctness of implementation against correct delivery of the messages. +//// sender and receiver are sync over reception, i.e., sender sends one message at a time and +//// waits for its reception +//// count defines number of messages +//func (suite *EchoEngineTestSuite) multiMessageSync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { +// sndID := 0 +// rcvID := 1 +// // registers engines in the network +// // sender's engine +// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) +// +// // receiver's engine +// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) +// +// // allow nodes to heartbeat and discover each other +// testutils.OptionalSleep(send) +// +// for i := 0; i < count; i++ { +// // Send the message to receiver +// event := &message.TestMessage{ +// Text: fmt.Sprintf("hello%d", i), +// } +// // sends a message from sender to receiver using send wrapper function +// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) +// +// select { +// case <-receiver.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// receiver.RLock() +// require.NotNil(suite.Suite.T(), receiver.originID) +// require.NotNil(suite.Suite.T(), receiver.event) +// assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) +// receiver.RUnlock() +// +// assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) +// +// case <-time.After(2 * time.Second): +// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") +// } +// +// // evaluates echo back +// if echo { +// // evaluates reception of echo response +// select { +// case <-sender.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// sender.RLock() +// require.NotNil(suite.Suite.T(), sender.originID) +// require.NotNil(suite.Suite.T(), sender.event) +// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) +// +// receiver.RLock() +// echoEvent := &message.TestMessage{ +// Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), +// } +// assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) +// receiver.RUnlock() +// sender.RUnlock() +// +// case <-time.After(10 * time.Second): +// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") +// } +// } +// +// } +// +//} +// +//// multiMessageAsync sends a multiple messages from one network instance to the other one +//// it evaluates the correctness of implementation against correct delivery of the messages. +//// sender and receiver are async, i.e., sender sends all its message at blast +//// count defines number of messages +//func (suite *EchoEngineTestSuite) multiMessageAsync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { +// sndID := 0 +// rcvID := 1 +// +// // registers engines in the network +// // sender's engine +// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) +// +// // receiver's engine +// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) +// +// // allow nodes to heartbeat and discover each other +// testutils.OptionalSleep(send) +// +// // keeps track of async received messages at receiver side +// received := make(map[string]struct{}) +// +// // keeps track of async received echo messages at sender side +// // echorcv := make(map[string]struct{}) +// +// for i := 0; i < count; i++ { +// // Send the message to node 2 using the conduit of node 1 +// event := &message.TestMessage{ +// Text: fmt.Sprintf("hello%d", i), +// } +// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[1].NodeID)) +// } +// +// for i := 0; i < count; i++ { +// select { +// case <-receiver.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// receiver.RLock() +// require.NotNil(suite.Suite.T(), receiver.originID) +// require.NotNil(suite.Suite.T(), receiver.event) +// assert.Equal(suite.Suite.T(), suite.ids[0].NodeID, receiver.originID) +// receiver.RUnlock() +// +// // wrap blocking channel reads with a timeout +// unittest.AssertReturnsBefore(suite.T(), func() { +// // evaluates proper reception of event +// // casts the received event at the receiver side +// rcvEvent, ok := (<-receiver.event).(*message.TestMessage) +// // evaluates correctness of casting +// require.True(suite.T(), ok) +// +// // evaluates content of received message +// // the content should not yet received and be unique +// _, rcv := received[rcvEvent.Text] +// assert.False(suite.T(), rcv) +// // marking event as received +// received[rcvEvent.Text] = struct{}{} +// +// // evaluates channel that message was received on +// assert.Equal(suite.T(), channels.TestNetworkChannel, <-receiver.channel) +// }, 100*time.Millisecond) +// +// case <-time.After(2 * time.Second): +// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") +// } +// } +// +// for i := 0; i < count; i++ { +// // evaluates echo back +// if echo { +// // evaluates reception of echo response +// select { +// case <-sender.received: +// // evaluates reception of message at the other side +// // does not evaluate the content +// sender.RLock() +// require.NotNil(suite.Suite.T(), sender.originID) +// require.NotNil(suite.Suite.T(), sender.event) +// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) +// sender.RUnlock() +// +// // wrap blocking channel reads with a timeout +// unittest.AssertReturnsBefore(suite.T(), func() { +// // evaluates proper reception of event +// // casts the received event at the receiver side +// rcvEvent, ok := (<-sender.event).(*message.TestMessage) +// // evaluates correctness of casting +// require.True(suite.T(), ok) +// // evaluates content of received echo message +// // the content should not yet received and be unique +// _, rcv := received[rcvEvent.Text] +// assert.False(suite.T(), rcv) +// // echo messages should start with prefix msg of receiver that echos back +// assert.True(suite.T(), strings.HasPrefix(rcvEvent.Text, receiver.echomsg)) +// // marking echo event as received +// received[rcvEvent.Text] = struct{}{} +// +// // evaluates channel that message was received on +// assert.Equal(suite.T(), channels.TestNetworkChannel, <-sender.channel) +// }, 100*time.Millisecond) +// +// case <-time.After(10 * time.Second): +// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") +// } +// } +// } +//} +// +//// assertMessageReceived asserts that the given message was received on the given channel +//// for the given engine +//func assertMessageReceived(t *testing.T, e *EchoEngine, m *message.TestMessage, c channels.Channel) { +// // wrap blocking channel reads with a timeout +// unittest.AssertReturnsBefore(t, func() { +// // evaluates proper reception of event +// // casts the received event at the receiver side +// rcvEvent, ok := (<-e.event).(*message.TestMessage) +// // evaluates correctness of casting +// require.True(t, ok) +// // evaluates content of received message +// assert.Equal(t, m, rcvEvent) +// +// // evaluates channel that message was received on +// assert.Equal(t, c, <-e.channel) +// }, 100*time.Millisecond) +//} diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index a40e958e8db..535f5be2021 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -1,469 +1,470 @@ package test -import ( - "context" - "fmt" - "math/rand" - "os" - "reflect" - "runtime" - "sync" - "testing" - "time" - - "github.com/ipfs/go-log" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/p2p" - mockprotocol "github.com/onflow/flow-go/state/protocol/mock" - "github.com/onflow/flow-go/utils/unittest" -) - -// MutableIdentityTableSuite tests that the networking layer responds correctly -// to changes to the identity table. When nodes are added, we should update our -// topology and accept connections from these new nodes. When nodes are removed -// or ejected we should update our topology and restrict connections from these -// nodes. -type MutableIdentityTableSuite struct { - suite.Suite - testutils.ConduitWrapper - testNodes testNodeList - removedTestNodes testNodeList // test nodes which might have been removed from the mesh - state *mockprotocol.State - snapshot *mockprotocol.Snapshot - logger zerolog.Logger - cancels []context.CancelFunc -} - -// testNode encapsulates the node state which includes its identity, middleware, network, -// mesh engine and the id refresher -type testNode struct { - id *flow.Identity - libp2pNode p2p.LibP2PNode - mw network.Middleware - net network.Network - engine *testutils.MeshEngine -} - -// testNodeList encapsulates a list of test node and -// has functions to retrieve the different elements of the test nodes in a concurrency safe manner -type testNodeList struct { - sync.RWMutex - nodes []testNode -} - -func newTestNodeList() testNodeList { - return testNodeList{} -} - -func (t *testNodeList) append(node testNode) { - t.Lock() - defer t.Unlock() - t.nodes = append(t.nodes, node) -} - -func (t *testNodeList) remove() testNode { - t.Lock() - defer t.Unlock() - // choose a random node to remove - i := rand.Intn(len(t.nodes)) - removedNode := t.nodes[i] - t.nodes = append(t.nodes[:i], t.nodes[i+1:]...) - return removedNode -} - -func (t *testNodeList) ids() flow.IdentityList { - t.RLock() - defer t.RUnlock() - ids := make(flow.IdentityList, len(t.nodes)) - for i, node := range t.nodes { - ids[i] = node.id - } - return ids -} - -func (t *testNodeList) lastAdded() (testNode, error) { - t.RLock() - defer t.RUnlock() - if len(t.nodes) > 0 { - return t.nodes[len(t.nodes)-1], nil - } - return testNode{}, fmt.Errorf("node list empty") -} - -func (t *testNodeList) engines() []*testutils.MeshEngine { - t.RLock() - defer t.RUnlock() - engs := make([]*testutils.MeshEngine, len(t.nodes)) - for i, node := range t.nodes { - engs[i] = node.engine - } - return engs -} - -func (t *testNodeList) networks() []network.Network { - t.RLock() - defer t.RUnlock() - nets := make([]network.Network, len(t.nodes)) - for i, node := range t.nodes { - nets[i] = node.net - } - return nets -} - -func (t *testNodeList) libp2pNodes() []p2p.LibP2PNode { - t.RLock() - defer t.RUnlock() - nodes := make([]p2p.LibP2PNode, len(t.nodes)) - for i, node := range t.nodes { - nodes[i] = node.libp2pNode - } - return nodes -} - -func TestMutableIdentityTable(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_TODO, "broken test") - suite.Run(t, new(MutableIdentityTableSuite)) -} - -// signalIdentityChanged update IDs for all the current set of nodes (simulating an epoch) -func (suite *MutableIdentityTableSuite) signalIdentityChanged() { - for _, n := range suite.testNodes.nodes { - n.mw.UpdateNodeAddresses() - } -} - -func (suite *MutableIdentityTableSuite) SetupTest() { - suite.testNodes = newTestNodeList() - suite.removedTestNodes = newTestNodeList() - - nodeCount := 10 - suite.logger = zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) - log.SetAllLoggers(log.LevelError) - - suite.setupStateMock() - suite.addNodes(nodeCount) - - // simulate a start of an epoch by signaling a change in the identity table - suite.signalIdentityChanged() - - // wait for two lip2p heatbeats for the nodes to discover each other and form the mesh - time.Sleep(2 * time.Second) -} - -// TearDownTest closes all the networks within a specified timeout -func (suite *MutableIdentityTableSuite) TearDownTest() { - for _, cancel := range suite.cancels { - cancel() - } - networks := append(suite.testNodes.networks(), suite.removedTestNodes.networks()...) - testutils.StopComponents(suite.T(), networks, 3*time.Second) -} - -// setupStateMock setup state related mocks (all networks share the same state mock) -func (suite *MutableIdentityTableSuite) setupStateMock() { - final := unittest.BlockHeaderFixture() - suite.state = new(mockprotocol.State) - suite.snapshot = new(mockprotocol.Snapshot) - suite.snapshot.On("Head").Return(&final, nil) - suite.snapshot.On("Phase").Return(flow.EpochPhaseCommitted, nil) - // return all the current list of ids for the state.Final.Identities call made by the network - suite.snapshot.On("Identities", mock.Anything).Return( - func(flow.IdentityFilter) flow.IdentityList { - return suite.testNodes.ids() - }, - func(flow.IdentityFilter) error { return nil }) - suite.state.On("Final").Return(suite.snapshot, nil) -} - -// addNodes creates count many new nodes and appends them to the suite state variables -func (suite *MutableIdentityTableSuite) addNodes(count int) { - ctx, cancel := context.WithCancel(context.Background()) - signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - - // create the ids, middlewares and networks - sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - mws, _ := testutils.MiddlewareFixtures( - suite.T(), - ids, - nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) - suite.cancels = append(suite.cancels, cancel) - - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) - - // create the engines for the new nodes - engines := testutils.GenerateEngines(suite.T(), nets) - - // create the test engines - for i := 0; i < count; i++ { - node := testNode{ - id: ids[i], - libp2pNode: nodes[i], - mw: mws[i], - net: nets[i], - engine: engines[i], - } - suite.testNodes.append(node) - } -} - -// removeNode removes a randomly chosen test node from suite.testNodes and adds it to suite.removedTestNodes -func (suite *MutableIdentityTableSuite) removeNode() testNode { - removedNode := suite.testNodes.remove() - suite.removedTestNodes.append(removedNode) - return removedNode -} - -// TestNewNodeAdded tests that when a new node is added to the identity list e.g. on an epoch, -// then it can connect to the network. -func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { - - // add a new node the current list of nodes - suite.addNodes(1) - - newNode, err := suite.testNodes.lastAdded() - require.NoError(suite.T(), err) - newID := newNode.id - - suite.logger.Debug(). - Str("new_node", newID.NodeID.String()). - Msg("added one node") - - // update IDs for all the networks (simulating an epoch) - suite.signalIdentityChanged() - - ids := suite.testNodes.ids() - engs := suite.testNodes.engines() - - // check if the new node has sufficient connections with the existing nodes - // if it does, then it has been inducted successfully in the network - suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) - - // check that all the engines on this new epoch can talk to each other using any of the three networking primitives - suite.assertNetworkPrimitives(ids, engs, nil, nil) -} - -// TestNodeRemoved tests that when an existing node is removed from the identity -// list (ie. as a result of an ejection or transition into an epoch where that node -// has un-staked) then it cannot connect to the network. -func (suite *MutableIdentityTableSuite) TestNodeRemoved() { - // removed a node - removedNode := suite.removeNode() - removedID := removedNode.id - removedEngine := removedNode.engine - - // update IDs for all the remaining nodes - // the removed node continues with the old identity list as we don't want to rely on it updating its ids list - suite.signalIdentityChanged() - - remainingIDs := suite.testNodes.ids() - remainingEngs := suite.testNodes.engines() - - // assert that the removed node has no connections with any of the other nodes - suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) - - // check that all remaining engines can still talk to each other while the ones removed can't - // using any of the three networking primitives - removedIDs := []*flow.Identity{removedID} - removedEngines := []*testutils.MeshEngine{removedEngine} - - // assert that all three network primitives still work - suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) -} - -// TestNodesAddedAndRemoved tests that: -// a. a newly added node can exchange messages with the existing nodes -// b. a node that has has been removed cannot exchange messages with the existing nodes -func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { - - // remove a node - removedNode := suite.removeNode() - removedID := removedNode.id - removedEngine := removedNode.engine - - // add a node - suite.addNodes(1) - newNode, err := suite.testNodes.lastAdded() - require.NoError(suite.T(), err) - - // update all current nodes - suite.signalIdentityChanged() - - remainingIDs := suite.testNodes.ids() - remainingEngs := suite.testNodes.engines() - - // check if the new node has sufficient connections with the existing nodes - suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) - - // assert that the removed node has no connections with any of the other nodes - suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) - - // check that all remaining engines can still talk to each other while the ones removed can't - // using any of the three networking primitives - removedIDs := []*flow.Identity{removedID} - removedEngines := []*testutils.MeshEngine{removedEngine} - - // assert that all three network primitives still work - suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) -} - -// assertConnected checks that the middleware of a node is directly connected -// to at least half of the other nodes. -func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { - t := suite.T() - threshold := len(allNodes) / 2 - require.Eventuallyf(t, func() bool { - connections := 0 - for _, node := range allNodes { - if node == thisNode { - // we don't want to check if a node is connected to itself - continue - } - connected, err := thisNode.IsConnected(node.ID()) - require.NoError(t, err) - if connected { - connections++ - } - } - suite.logger.Debug(). - Int("threshold", threshold). - Int("connections", connections). - Msg("current connection count") - return connections >= threshold - }, 5*time.Second, 100*time.Millisecond, "node is not connected to enough nodes") -} - -// assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the -// ids list -func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { - t := suite.T() - require.Eventuallyf(t, func() bool { - for _, node := range allNodes { - connected, err := thisNode.IsConnected(node.ID()) - require.NoError(t, err) - if connected { - return false - } - } - return true - }, 5*time.Second, 100*time.Millisecond, "node is still connected") -} - -// assertNetworkPrimitives asserts that allowed engines can exchange messages between themselves but not with the -// disallowed engines using each of the three network primitives -func (suite *MutableIdentityTableSuite) assertNetworkPrimitives( - allowedIDs flow.IdentityList, - allowedEngs []*testutils.MeshEngine, - disallowedIDs flow.IdentityList, - disallowedEngs []*testutils.MeshEngine) { - suite.Run("Publish", func() { - suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Publish, false) - }) - suite.Run("Multicast", func() { - suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Multicast, false) - }) - suite.Run("Unicast", func() { - // unicast send from or to a node that has been evicted should fail with an error - suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Unicast, true) - }) -} - -// exchangeMessages verifies that allowed engines can successfully exchange messages between them while disallowed -// engines can't using the ConduitSendWrapperFunc network primitive -func (suite *MutableIdentityTableSuite) exchangeMessages( - allowedIDs flow.IdentityList, - allowedEngs []*testutils.MeshEngine, - disallowedIDs flow.IdentityList, - disallowedEngs []*testutils.MeshEngine, - send testutils.ConduitSendWrapperFunc, - expectSendErrorForDisallowedIDs bool) { - - // send a message from each of the allowed engine to the other allowed engines - for i, allowedEng := range allowedEngs { - - fromID := allowedIDs[i].NodeID - targetIDs := allowedIDs.Filter(filter.Not(filter.HasNodeID(allowedIDs[i].NodeID))) - - err := suite.sendMessage(fromID, allowedEng, targetIDs, send) - require.NoError(suite.T(), err) - } - - // send a message from each of the allowed engine to all of the disallowed engines - if len(disallowedEngs) > 0 { - for i, fromEng := range allowedEngs { - - fromID := allowedIDs[i].NodeID - targetIDs := disallowedIDs - - err := suite.sendMessage(fromID, fromEng, targetIDs, send) - if expectSendErrorForDisallowedIDs { - require.Error(suite.T(), err) - } - } - } - - // send a message from each of the disallowed engine to each of the allowed engines - for i, fromEng := range disallowedEngs { - - fromID := disallowedIDs[i].NodeID - targetIDs := allowedIDs - - err := suite.sendMessage(fromID, fromEng, targetIDs, send) - if expectSendErrorForDisallowedIDs { - require.Error(suite.T(), err) - } - } - - count := len(allowedEngs) - expectedMsgCnt := count - 1 - wg := sync.WaitGroup{} - // fires a goroutine for each of the allowed engine to listen for incoming messages - for i := range allowedEngs { - wg.Add(expectedMsgCnt) - go func(e *testutils.MeshEngine) { - for x := 0; x < expectedMsgCnt; x++ { - <-e.Received - wg.Done() - } - }(allowedEngs[i]) - } - - // assert that all allowed engines received expectedMsgCnt number of messages - unittest.AssertReturnsBefore(suite.T(), wg.Wait, 5*time.Second) - // assert that all allowed engines received no other messages - for i := range allowedEngs { - assert.Empty(suite.T(), allowedEngs[i].Received) - } - - // assert that the disallowed engines didn't receive any message - for i, eng := range disallowedEngs { - unittest.RequireNeverClosedWithin(suite.T(), eng.Received, time.Millisecond, - fmt.Sprintf("%s engine should not have recevied message", disallowedIDs[i])) - } -} - -func (suite *MutableIdentityTableSuite) sendMessage(fromID flow.Identifier, - fromEngine *testutils.MeshEngine, - toIDs flow.IdentityList, - send testutils.ConduitSendWrapperFunc) error { - - primitive := runtime.FuncForPC(reflect.ValueOf(send).Pointer()).Name() - event := &message.TestMessage{ - Text: fmt.Sprintf("hello from node %s using %s", fromID.String(), primitive), - } - - return send(event, fromEngine.Con, toIDs.NodeIDs()...) -} +// +//import ( +// "context" +// "fmt" +// "math/rand" +// "os" +// "reflect" +// "runtime" +// "sync" +// "testing" +// "time" +// +// "github.com/ipfs/go-log" +// "github.com/rs/zerolog" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/mock" +// "github.com/stretchr/testify/require" +// "github.com/stretchr/testify/suite" +// +// "github.com/onflow/flow-go/model/flow" +// "github.com/onflow/flow-go/model/flow/filter" +// "github.com/onflow/flow-go/model/libp2p/message" +// "github.com/onflow/flow-go/module/irrecoverable" +// "github.com/onflow/flow-go/network" +// "github.com/onflow/flow-go/network/internal/testutils" +// "github.com/onflow/flow-go/network/p2p" +// mockprotocol "github.com/onflow/flow-go/state/protocol/mock" +// "github.com/onflow/flow-go/utils/unittest" +//) +// +//// MutableIdentityTableSuite tests that the networking layer responds correctly +//// to changes to the identity table. When nodes are added, we should update our +//// topology and accept connections from these new nodes. When nodes are removed +//// or ejected we should update our topology and restrict connections from these +//// nodes. +//type MutableIdentityTableSuite struct { +// suite.Suite +// testutils.ConduitWrapper +// testNodes testNodeList +// removedTestNodes testNodeList // test nodes which might have been removed from the mesh +// state *mockprotocol.State +// snapshot *mockprotocol.Snapshot +// logger zerolog.Logger +// cancels []context.CancelFunc +//} +// +//// testNode encapsulates the node state which includes its identity, middleware, network, +//// mesh engine and the id refresher +//type testNode struct { +// id *flow.Identity +// libp2pNode p2p.LibP2PNode +// mw network.Middleware +// net network.Network +// engine *testutils.MeshEngine +//} +// +//// testNodeList encapsulates a list of test node and +//// has functions to retrieve the different elements of the test nodes in a concurrency safe manner +//type testNodeList struct { +// sync.RWMutex +// nodes []testNode +//} +// +//func newTestNodeList() testNodeList { +// return testNodeList{} +//} +// +//func (t *testNodeList) append(node testNode) { +// t.Lock() +// defer t.Unlock() +// t.nodes = append(t.nodes, node) +//} +// +//func (t *testNodeList) remove() testNode { +// t.Lock() +// defer t.Unlock() +// // choose a random node to remove +// i := rand.Intn(len(t.nodes)) +// removedNode := t.nodes[i] +// t.nodes = append(t.nodes[:i], t.nodes[i+1:]...) +// return removedNode +//} +// +//func (t *testNodeList) ids() flow.IdentityList { +// t.RLock() +// defer t.RUnlock() +// ids := make(flow.IdentityList, len(t.nodes)) +// for i, node := range t.nodes { +// ids[i] = node.id +// } +// return ids +//} +// +//func (t *testNodeList) lastAdded() (testNode, error) { +// t.RLock() +// defer t.RUnlock() +// if len(t.nodes) > 0 { +// return t.nodes[len(t.nodes)-1], nil +// } +// return testNode{}, fmt.Errorf("node list empty") +//} +// +//func (t *testNodeList) engines() []*testutils.MeshEngine { +// t.RLock() +// defer t.RUnlock() +// engs := make([]*testutils.MeshEngine, len(t.nodes)) +// for i, node := range t.nodes { +// engs[i] = node.engine +// } +// return engs +//} +// +//func (t *testNodeList) networks() []network.Network { +// t.RLock() +// defer t.RUnlock() +// nets := make([]network.Network, len(t.nodes)) +// for i, node := range t.nodes { +// nets[i] = node.net +// } +// return nets +//} +// +//func (t *testNodeList) libp2pNodes() []p2p.LibP2PNode { +// t.RLock() +// defer t.RUnlock() +// nodes := make([]p2p.LibP2PNode, len(t.nodes)) +// for i, node := range t.nodes { +// nodes[i] = node.libp2pNode +// } +// return nodes +//} +// +//func TestMutableIdentityTable(t *testing.T) { +// unittest.SkipUnless(t, unittest.TEST_TODO, "broken test") +// suite.Run(t, new(MutableIdentityTableSuite)) +//} +// +//// signalIdentityChanged update IDs for all the current set of nodes (simulating an epoch) +//func (suite *MutableIdentityTableSuite) signalIdentityChanged() { +// for _, n := range suite.testNodes.nodes { +// n.mw.UpdateNodeAddresses() +// } +//} +// +//func (suite *MutableIdentityTableSuite) SetupTest() { +// suite.testNodes = newTestNodeList() +// suite.removedTestNodes = newTestNodeList() +// +// nodeCount := 10 +// suite.logger = zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) +// log.SetAllLoggers(log.LevelError) +// +// suite.setupStateMock() +// suite.addNodes(nodeCount) +// +// // simulate a start of an epoch by signaling a change in the identity table +// suite.signalIdentityChanged() +// +// // wait for two lip2p heatbeats for the nodes to discover each other and form the mesh +// time.Sleep(2 * time.Second) +//} +// +//// TearDownTest closes all the networks within a specified timeout +//func (suite *MutableIdentityTableSuite) TearDownTest() { +// for _, cancel := range suite.cancels { +// cancel() +// } +// networks := append(suite.testNodes.networks(), suite.removedTestNodes.networks()...) +// testutils.StopComponents(suite.T(), networks, 3*time.Second) +//} +// +//// setupStateMock setup state related mocks (all networks share the same state mock) +//func (suite *MutableIdentityTableSuite) setupStateMock() { +// final := unittest.BlockHeaderFixture() +// suite.state = new(mockprotocol.State) +// suite.snapshot = new(mockprotocol.Snapshot) +// suite.snapshot.On("Head").Return(&final, nil) +// suite.snapshot.On("Phase").Return(flow.EpochPhaseCommitted, nil) +// // return all the current list of ids for the state.Final.Identities call made by the network +// suite.snapshot.On("Identities", mock.Anything).Return( +// func(flow.IdentityFilter) flow.IdentityList { +// return suite.testNodes.ids() +// }, +// func(flow.IdentityFilter) error { return nil }) +// suite.state.On("Final").Return(suite.snapshot, nil) +//} +// +//// addNodes creates count many new nodes and appends them to the suite state variables +//func (suite *MutableIdentityTableSuite) addNodes(count int) { +// ctx, cancel := context.WithCancel(context.Background()) +// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) +// +// // create the ids, middlewares and networks +// sporkId := unittest.IdentifierFixture() +// ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) +// mws, _ := testutils.MiddlewareFixtures( +// suite.T(), +// ids, +// nodes, +// testutils.MiddlewareConfigFixture(suite.T(), sporkId)) +// nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) +// suite.cancels = append(suite.cancels, cancel) +// +// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) +// +// // create the engines for the new nodes +// engines := testutils.GenerateEngines(suite.T(), nets) +// +// // create the test engines +// for i := 0; i < count; i++ { +// node := testNode{ +// id: ids[i], +// libp2pNode: nodes[i], +// mw: mws[i], +// net: nets[i], +// engine: engines[i], +// } +// suite.testNodes.append(node) +// } +//} +// +//// removeNode removes a randomly chosen test node from suite.testNodes and adds it to suite.removedTestNodes +//func (suite *MutableIdentityTableSuite) removeNode() testNode { +// removedNode := suite.testNodes.remove() +// suite.removedTestNodes.append(removedNode) +// return removedNode +//} +// +//// TestNewNodeAdded tests that when a new node is added to the identity list e.g. on an epoch, +//// then it can connect to the network. +//func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { +// +// // add a new node the current list of nodes +// suite.addNodes(1) +// +// newNode, err := suite.testNodes.lastAdded() +// require.NoError(suite.T(), err) +// newID := newNode.id +// +// suite.logger.Debug(). +// Str("new_node", newID.NodeID.String()). +// Msg("added one node") +// +// // update IDs for all the networks (simulating an epoch) +// suite.signalIdentityChanged() +// +// ids := suite.testNodes.ids() +// engs := suite.testNodes.engines() +// +// // check if the new node has sufficient connections with the existing nodes +// // if it does, then it has been inducted successfully in the network +// suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) +// +// // check that all the engines on this new epoch can talk to each other using any of the three networking primitives +// suite.assertNetworkPrimitives(ids, engs, nil, nil) +//} +// +//// TestNodeRemoved tests that when an existing node is removed from the identity +//// list (ie. as a result of an ejection or transition into an epoch where that node +//// has un-staked) then it cannot connect to the network. +//func (suite *MutableIdentityTableSuite) TestNodeRemoved() { +// // removed a node +// removedNode := suite.removeNode() +// removedID := removedNode.id +// removedEngine := removedNode.engine +// +// // update IDs for all the remaining nodes +// // the removed node continues with the old identity list as we don't want to rely on it updating its ids list +// suite.signalIdentityChanged() +// +// remainingIDs := suite.testNodes.ids() +// remainingEngs := suite.testNodes.engines() +// +// // assert that the removed node has no connections with any of the other nodes +// suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) +// +// // check that all remaining engines can still talk to each other while the ones removed can't +// // using any of the three networking primitives +// removedIDs := []*flow.Identity{removedID} +// removedEngines := []*testutils.MeshEngine{removedEngine} +// +// // assert that all three network primitives still work +// suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) +//} +// +//// TestNodesAddedAndRemoved tests that: +//// a. a newly added node can exchange messages with the existing nodes +//// b. a node that has has been removed cannot exchange messages with the existing nodes +//func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { +// +// // remove a node +// removedNode := suite.removeNode() +// removedID := removedNode.id +// removedEngine := removedNode.engine +// +// // add a node +// suite.addNodes(1) +// newNode, err := suite.testNodes.lastAdded() +// require.NoError(suite.T(), err) +// +// // update all current nodes +// suite.signalIdentityChanged() +// +// remainingIDs := suite.testNodes.ids() +// remainingEngs := suite.testNodes.engines() +// +// // check if the new node has sufficient connections with the existing nodes +// suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) +// +// // assert that the removed node has no connections with any of the other nodes +// suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) +// +// // check that all remaining engines can still talk to each other while the ones removed can't +// // using any of the three networking primitives +// removedIDs := []*flow.Identity{removedID} +// removedEngines := []*testutils.MeshEngine{removedEngine} +// +// // assert that all three network primitives still work +// suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) +//} +// +//// assertConnected checks that the middleware of a node is directly connected +//// to at least half of the other nodes. +//func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { +// t := suite.T() +// threshold := len(allNodes) / 2 +// require.Eventuallyf(t, func() bool { +// connections := 0 +// for _, node := range allNodes { +// if node == thisNode { +// // we don't want to check if a node is connected to itself +// continue +// } +// connected, err := thisNode.IsConnected(node.ID()) +// require.NoError(t, err) +// if connected { +// connections++ +// } +// } +// suite.logger.Debug(). +// Int("threshold", threshold). +// Int("connections", connections). +// Msg("current connection count") +// return connections >= threshold +// }, 5*time.Second, 100*time.Millisecond, "node is not connected to enough nodes") +//} +// +//// assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the +//// ids list +//func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { +// t := suite.T() +// require.Eventuallyf(t, func() bool { +// for _, node := range allNodes { +// connected, err := thisNode.IsConnected(node.ID()) +// require.NoError(t, err) +// if connected { +// return false +// } +// } +// return true +// }, 5*time.Second, 100*time.Millisecond, "node is still connected") +//} +// +//// assertNetworkPrimitives asserts that allowed engines can exchange messages between themselves but not with the +//// disallowed engines using each of the three network primitives +//func (suite *MutableIdentityTableSuite) assertNetworkPrimitives( +// allowedIDs flow.IdentityList, +// allowedEngs []*testutils.MeshEngine, +// disallowedIDs flow.IdentityList, +// disallowedEngs []*testutils.MeshEngine) { +// suite.Run("Publish", func() { +// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Publish, false) +// }) +// suite.Run("Multicast", func() { +// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Multicast, false) +// }) +// suite.Run("Unicast", func() { +// // unicast send from or to a node that has been evicted should fail with an error +// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Unicast, true) +// }) +//} +// +//// exchangeMessages verifies that allowed engines can successfully exchange messages between them while disallowed +//// engines can't using the ConduitSendWrapperFunc network primitive +//func (suite *MutableIdentityTableSuite) exchangeMessages( +// allowedIDs flow.IdentityList, +// allowedEngs []*testutils.MeshEngine, +// disallowedIDs flow.IdentityList, +// disallowedEngs []*testutils.MeshEngine, +// send testutils.ConduitSendWrapperFunc, +// expectSendErrorForDisallowedIDs bool) { +// +// // send a message from each of the allowed engine to the other allowed engines +// for i, allowedEng := range allowedEngs { +// +// fromID := allowedIDs[i].NodeID +// targetIDs := allowedIDs.Filter(filter.Not(filter.HasNodeID(allowedIDs[i].NodeID))) +// +// err := suite.sendMessage(fromID, allowedEng, targetIDs, send) +// require.NoError(suite.T(), err) +// } +// +// // send a message from each of the allowed engine to all of the disallowed engines +// if len(disallowedEngs) > 0 { +// for i, fromEng := range allowedEngs { +// +// fromID := allowedIDs[i].NodeID +// targetIDs := disallowedIDs +// +// err := suite.sendMessage(fromID, fromEng, targetIDs, send) +// if expectSendErrorForDisallowedIDs { +// require.Error(suite.T(), err) +// } +// } +// } +// +// // send a message from each of the disallowed engine to each of the allowed engines +// for i, fromEng := range disallowedEngs { +// +// fromID := disallowedIDs[i].NodeID +// targetIDs := allowedIDs +// +// err := suite.sendMessage(fromID, fromEng, targetIDs, send) +// if expectSendErrorForDisallowedIDs { +// require.Error(suite.T(), err) +// } +// } +// +// count := len(allowedEngs) +// expectedMsgCnt := count - 1 +// wg := sync.WaitGroup{} +// // fires a goroutine for each of the allowed engine to listen for incoming messages +// for i := range allowedEngs { +// wg.Add(expectedMsgCnt) +// go func(e *testutils.MeshEngine) { +// for x := 0; x < expectedMsgCnt; x++ { +// <-e.Received +// wg.Done() +// } +// }(allowedEngs[i]) +// } +// +// // assert that all allowed engines received expectedMsgCnt number of messages +// unittest.AssertReturnsBefore(suite.T(), wg.Wait, 5*time.Second) +// // assert that all allowed engines received no other messages +// for i := range allowedEngs { +// assert.Empty(suite.T(), allowedEngs[i].Received) +// } +// +// // assert that the disallowed engines didn't receive any message +// for i, eng := range disallowedEngs { +// unittest.RequireNeverClosedWithin(suite.T(), eng.Received, time.Millisecond, +// fmt.Sprintf("%s engine should not have recevied message", disallowedIDs[i])) +// } +//} +// +//func (suite *MutableIdentityTableSuite) sendMessage(fromID flow.Identifier, +// fromEngine *testutils.MeshEngine, +// toIDs flow.IdentityList, +// send testutils.ConduitSendWrapperFunc) error { +// +// primitive := runtime.FuncForPC(reflect.ValueOf(send).Pointer()).Name() +// event := &message.TestMessage{ +// Text: fmt.Sprintf("hello from node %s using %s", fromID.String(), primitive), +// } +// +// return send(event, fromEngine.Con, toIDs.NodeIDs()...) +//} diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 84b5f9a5556..0803e132dbd 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -1,528 +1,525 @@ package test -import ( - "context" - "fmt" - "math/rand" - "os" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/ipfs/go-log" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/onflow/flow-go/config" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/module/observable" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/middleware" - "github.com/onflow/flow-go/network/p2p/p2pnode" - p2ptest "github.com/onflow/flow-go/network/p2p/test" - "github.com/onflow/flow-go/utils/unittest" -) - -// MeshEngineTestSuite evaluates the message delivery functionality for the overlay -// of engines over a complete graph -type MeshEngineTestSuite struct { - suite.Suite - testutils.ConduitWrapper // used as a wrapper around conduit methods - nets []network.Network // used to keep track of the networks - mws []network.Middleware // used to keep track of the middlewares - ids flow.IdentityList // used to keep track of the identifiers associated with networks - obs chan string // used to keep track of Protect events tagged by pubsub messages - cancel context.CancelFunc -} - -// TestMeshNetTestSuite runs all tests in this test suit -func TestMeshNetTestSuite(t *testing.T) { - suite.Run(t, new(MeshEngineTestSuite)) -} - -// SetupTest is executed prior to each test in this test suite. It creates and initializes -// a set of network instances, sets up connection managers, nodes, identities, observables, etc. -// This setup ensures that all necessary configurations are in place before running the tests. -func (suite *MeshEngineTestSuite) SetupTest() { - // defines total number of nodes in our network (minimum 3 needed to use 1-k messaging) - const count = 10 - logger := zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) - log.SetAllLoggers(log.LevelError) - - // set up a channel to receive pubsub tags from connManagers of the nodes - peerChannel := make(chan string) - - // Tag Observables Usage Explanation: - // The tagsObserver is used to observe connections tagged by pubsub messages. This is instrumental in understanding - // the connectivity between different peers and verifying the formation of the mesh within this test suite. - // Issues: - // - Deviation from Production Code: The usage of tag observables here may not reflect the behavior in the production environment. - // - Mask Issues in the Production Environment: The observables tied to testing might lead to behaviors or errors that are - // masked or not evident within the actual production code. - // TODO: Evaluate the necessity of tag observables in this test and consider addressing the deviation from production - // code and potential mask issues. Evaluate the possibility of removing this part eventually. - ob := tagsObserver{ - tags: peerChannel, - log: logger, - } - - ctx, cancel := context.WithCancel(context.Background()) - suite.cancel = cancel - - signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - - sporkId := unittest.IdentifierFixture() - libP2PNodes := make([]p2p.LibP2PNode, 0) - identities := make(flow.IdentityList, 0) - tagObservables := make([]observable.Observable, 0) - idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) - defaultFlowConfig, err := config.DefaultConfig() - require.NoError(suite.T(), err) - opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} - - for i := 0; i < count; i++ { - connManager, err := testutils.NewTagWatchingConnManager( - unittest.Logger(), - metrics.NewNoopCollector(), - &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) - require.NoError(suite.T(), err) - - opts = append(opts, p2ptest.WithConnectionManager(connManager)) - node, nodeId := p2ptest.NodeFixture(suite.T(), - sporkId, - suite.T().Name(), - idProvider, - opts...) - libP2PNodes = append(libP2PNodes, node) - identities = append(identities, &nodeId) - tagObservables = append(tagObservables, connManager) - } - idProvider.SetIdentities(identities) - - suite.ids = identities - suite.mws, _ = testutils.MiddlewareFixtures( - suite.T(), - suite.ids, - libP2PNodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId)) - suite.nets, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets, 100*time.Millisecond) - - for _, observableConnMgr := range tagObservables { - observableConnMgr.Subscribe(&ob) - } - suite.obs = peerChannel -} - -// TearDownTest closes the networks within a specified timeout -func (suite *MeshEngineTestSuite) TearDownTest() { - suite.cancel() - testutils.StopComponents(suite.T(), suite.nets, 3*time.Second) - testutils.StopComponents(suite.T(), suite.mws, 3*time.Second) -} - -// TestAllToAll_Publish evaluates the network of mesh engines against allToAllScenario scenario. -// Network instances during this test use their Publish method to disseminate messages. -func (suite *MeshEngineTestSuite) TestAllToAll_Publish() { - suite.allToAllScenario(suite.Publish) -} - -// TestAllToAll_Multicast evaluates the network of mesh engines against allToAllScenario scenario. -// Network instances during this test use their Multicast method to disseminate messages. -func (suite *MeshEngineTestSuite) TestAllToAll_Multicast() { - suite.allToAllScenario(suite.Multicast) -} - -// TestAllToAll_Unicast evaluates the network of mesh engines against allToAllScenario scenario. -// Network instances during this test use their Unicast method to disseminate messages. -func (suite *MeshEngineTestSuite) TestAllToAll_Unicast() { - suite.allToAllScenario(suite.Unicast) -} - -// TestTargetedValidators_Unicast tests if only the intended recipients in a 1-k messaging actually receive the message. -// The messages are disseminated through the Unicast method of conduits. -func (suite *MeshEngineTestSuite) TestTargetedValidators_Unicast() { - suite.targetValidatorScenario(suite.Unicast) -} - -// TestTargetedValidators_Multicast tests if only the intended recipients in a 1-k messaging actually receive the -// message. -// The messages are disseminated through the Multicast method of conduits. -func (suite *MeshEngineTestSuite) TestTargetedValidators_Multicast() { - suite.targetValidatorScenario(suite.Multicast) -} - -// TestTargetedValidators_Publish tests if only the intended recipients in a 1-k messaging actually receive the message. -// The messages are disseminated through the Multicast method of conduits. -func (suite *MeshEngineTestSuite) TestTargetedValidators_Publish() { - suite.targetValidatorScenario(suite.Publish) -} - -// TestMaxMessageSize_Unicast evaluates the messageSizeScenario scenario using -// the Unicast method of conduits. -func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { - suite.messageSizeScenario(suite.Unicast, middleware.DefaultMaxUnicastMsgSize) -} - -// TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using -// the Multicast method of conduits. -func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { - suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) -} - -// TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the -// Publish method of conduits. -func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { - suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) -} - -// TestUnregister_Publish tests that an engine cannot send any message using Publish -// or receive any messages after the conduit is closed -func (suite *MeshEngineTestSuite) TestUnregister_Publish() { - suite.conduitCloseScenario(suite.Publish) -} - -// TestUnregister_Publish tests that an engine cannot send any message using Multicast -// or receive any messages after the conduit is closed -func (suite *MeshEngineTestSuite) TestUnregister_Multicast() { - suite.conduitCloseScenario(suite.Multicast) -} - -// TestUnregister_Publish tests that an engine cannot send any message using Unicast -// or receive any messages after the conduit is closed -func (suite *MeshEngineTestSuite) TestUnregister_Unicast() { - suite.conduitCloseScenario(suite.Unicast) -} - -// allToAllScenario creates a complete mesh of the engines, where each engine x sends a -// "hello from node x" to other engines. It then evaluates the correctness of message -// delivery as well as the content of the messages. This scenario tests the capability of -// the engines to communicate in a fully connected graph, ensuring both the reachability -// of messages and the integrity of their contents. -func (suite *MeshEngineTestSuite) allToAllScenario(send testutils.ConduitSendWrapperFunc) { - // allows nodes to find each other in case of Mulitcast and Publish - testutils.OptionalSleep(send) - - // creating engines - count := len(suite.nets) - engs := make([]*testutils.MeshEngine, 0) - wg := sync.WaitGroup{} - - // logs[i][j] keeps the message that node i sends to node j - logs := make(map[int][]string) - for i := range suite.nets { - eng := testutils.NewMeshEngine(suite.Suite.T(), suite.nets[i], count-1, channels.TestNetworkChannel) - engs = append(engs, eng) - logs[i] = make([]string, 0) - } - - // allow nodes to heartbeat and discover each other - // each node will register ~D protect messages, where D is the default out-degree - for i := 0; i < pubsub.GossipSubD*count; i++ { - select { - case <-suite.obs: - case <-time.After(8 * time.Second): - assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") - } - } - - // Each node broadcasting a message to all others - for i := range suite.nets { - event := &message.TestMessage{ - Text: fmt.Sprintf("hello from node %v", i), - } - - // others keeps the identifier of all nodes except ith node - others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID))).NodeIDs() - require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) - wg.Add(count - 1) - } - - // fires a goroutine for each engine that listens to incoming messages - for i := range suite.nets { - go func(e *testutils.MeshEngine) { - for x := 0; x < count-1; x++ { - <-e.Received - wg.Done() - } - }(engs[i]) - } - - unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) - - // evaluates that all messages are received - for index, e := range engs { - // confirms the number of received messages at each node - if len(e.Event) != (count - 1) { - assert.Fail(suite.Suite.T(), - fmt.Sprintf("Message reception mismatch at node %v. Expected: %v, Got: %v", index, count-1, len(e.Event))) - } - - for i := 0; i < count-1; i++ { - assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) - } - - // extracts failed messages - receivedIndices, err := extractSenderID(count, e.Event, "hello from node") - require.NoError(suite.Suite.T(), err) - - for j := 0; j < count; j++ { - // evaluates self-gossip - if j == index { - assert.False(suite.Suite.T(), (receivedIndices)[index], fmt.Sprintf("self gossiped for node %v detected", index)) - } - // evaluates content - if !(receivedIndices)[j] { - assert.False(suite.Suite.T(), (receivedIndices)[index], - fmt.Sprintf("Message not found in node #%v's messages. Expected: Message from node %v. Got: No message", index, j)) - } - } - } -} - -// targetValidatorScenario sends a single message from last node to the first half of the nodes -// based on identifiers list. -// It then verifies that only the intended recipients receive the message. -// Message dissemination is done using the send wrapper of conduit. -func (suite *MeshEngineTestSuite) targetValidatorScenario(send testutils.ConduitSendWrapperFunc) { - // creating engines - count := len(suite.nets) - engs := make([]*testutils.MeshEngine, 0) - wg := sync.WaitGroup{} - - for i := range suite.nets { - eng := testutils.NewMeshEngine(suite.Suite.T(), suite.nets[i], count-1, channels.TestNetworkChannel) - engs = append(engs, eng) - } - - // allow nodes to heartbeat and discover each other - // each node will register ~D protect messages, where D is the default out-degree - for i := 0; i < pubsub.GossipSubD*count; i++ { - select { - case <-suite.obs: - case <-time.After(2 * time.Second): - assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") - } - } - - // choose half of the nodes as target - allIds := suite.ids.NodeIDs() - var targets []flow.Identifier - // create a target list of half of the nodes - for i := 0; i < len(allIds)/2; i++ { - targets = append(targets, allIds[i]) - } - - // node 0 broadcasting a message to all targets - event := &message.TestMessage{ - Text: "hello from node 0", - } - require.NoError(suite.Suite.T(), send(event, engs[len(engs)-1].Con, targets...)) - - // fires a goroutine for all engines to listens for the incoming message - for i := 0; i < len(allIds)/2; i++ { - wg.Add(1) - go func(e *testutils.MeshEngine) { - <-e.Received - wg.Done() - }(engs[i]) - } - - unittest.AssertReturnsBefore(suite.T(), wg.Wait, 10*time.Second) - - // evaluates that all messages are received - for index, e := range engs { - if index < len(engs)/2 { - assert.Len(suite.Suite.T(), e.Event, 1, fmt.Sprintf("message not received %v", index)) - assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) - } else { - assert.Len(suite.Suite.T(), e.Event, 0, fmt.Sprintf("message received when none was expected %v", index)) - } - } -} - -// messageSizeScenario provides a scenario to check if a message of maximum permissible size can be sent -// successfully. -// It broadcasts a message from the first node to all the nodes in the identifiers list using send wrapper function. -func (suite *MeshEngineTestSuite) messageSizeScenario(send testutils.ConduitSendWrapperFunc, size uint) { - // creating engines - count := len(suite.nets) - engs := make([]*testutils.MeshEngine, 0) - wg := sync.WaitGroup{} - - for i := range suite.nets { - eng := testutils.NewMeshEngine(suite.Suite.T(), suite.nets[i], count-1, channels.TestNetworkChannel) - engs = append(engs, eng) - } - - // allow nodes to heartbeat and discover each other - // each node will register ~D protect messages per mesh setup, where D is the default out-degree - for i := 0; i < pubsub.GossipSubD*count; i++ { - select { - case <-suite.obs: - case <-time.After(8 * time.Second): - assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") - } - } - // others keeps the identifier of all nodes except node that is sender. - others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[0].NodeID))).NodeIDs() - - // generates and sends an event of custom size to the network - payload := testutils.NetworkPayloadFixture(suite.T(), size) - event := &message.TestMessage{ - Text: string(payload), - } - - require.NoError(suite.T(), send(event, engs[0].Con, others...)) - - // fires a goroutine for all engines (except sender) to listen for the incoming message - for _, eng := range engs[1:] { - wg.Add(1) - go func(e *testutils.MeshEngine) { - <-e.Received - wg.Done() - }(eng) - } - - unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) - - // evaluates that all messages are received - for index, e := range engs[1:] { - assert.Len(suite.Suite.T(), e.Event, 1, "message not received by engine %d", index+1) - assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) - } -} - -// conduitCloseScenario tests after a Conduit is closed, an engine cannot send or receive a message for that channel. -func (suite *MeshEngineTestSuite) conduitCloseScenario(send testutils.ConduitSendWrapperFunc) { - - testutils.OptionalSleep(send) - - // creating engines - count := len(suite.nets) - engs := make([]*testutils.MeshEngine, 0) - wg := sync.WaitGroup{} - - for i := range suite.nets { - eng := testutils.NewMeshEngine(suite.Suite.T(), suite.nets[i], count-1, channels.TestNetworkChannel) - engs = append(engs, eng) - } - - // allow nodes to heartbeat and discover each other - // each node will register ~D protect messages, where D is the default out-degree - for i := 0; i < pubsub.GossipSubD*count; i++ { - select { - case <-suite.obs: - case <-time.After(2 * time.Second): - assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") - } - } - - // unregister a random engine from the test topic by calling close on it's conduit - unregisterIndex := rand.Intn(count) - err := engs[unregisterIndex].Con.Close() - assert.NoError(suite.T(), err) - - // waits enough for peer manager to unsubscribe the node from the topic - // while libp2p is unsubscribing the node, the topology gets unstable - // and connections to the node may be refused (although very unlikely). - time.Sleep(2 * time.Second) - - // each node attempts to broadcast a message to all others - for i := range suite.nets { - event := &message.TestMessage{ - Text: fmt.Sprintf("hello from node %v", i), - } - - // others keeps the identifier of all nodes except ith node and the node that unregistered from the topic. - // nodes without valid topic registration for a channel will reject messages on that channel via unicast. - others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID, suite.ids[unregisterIndex].NodeID))).NodeIDs() - - if i == unregisterIndex { - // assert that unsubscribed engine cannot publish on that topic - require.Error(suite.Suite.T(), send(event, engs[i].Con, others...)) - continue - } - - require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) - } - - // fire a goroutine to listen for incoming messages for each engine except for the one which unregistered - for i := range suite.nets { - if i == unregisterIndex { - continue - } - wg.Add(1) - go func(e *testutils.MeshEngine) { - expectedMsgCnt := count - 2 // count less self and unsubscribed engine - for x := 0; x < expectedMsgCnt; x++ { - <-e.Received - } - wg.Done() - }(engs[i]) - } - - // assert every one except the unsubscribed engine received the message - unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 2*time.Second) - - // assert that the unregistered engine did not receive the message - unregisteredEng := engs[unregisterIndex] - assert.Emptyf(suite.T(), unregisteredEng.Received, "unregistered engine received the topic message") -} - -// assertChannelReceived asserts that the given channel was received on the given engine -func assertChannelReceived(t *testing.T, e *testutils.MeshEngine, channel channels.Channel) { - unittest.AssertReturnsBefore(t, func() { - assert.Equal(t, channel, <-e.Channel) - }, 100*time.Millisecond) -} - -// extractSenderID returns a bool array with the index i true if there is a message from node i in the provided messages. -// enginesNum is the number of engines -// events is the channel of received events -// expectedMsgTxt is the common prefix among all the messages that we expect to receive, for example -// we expect to receive "hello from node x" in this test, and then expectedMsgTxt is "hello form node" -func extractSenderID(enginesNum int, events chan interface{}, expectedMsgTxt string) ([]bool, error) { - indices := make([]bool, enginesNum) - expectedMsgSize := len(expectedMsgTxt) - for i := 0; i < enginesNum-1; i++ { - var event interface{} - select { - case event = <-events: - default: - continue - } - echo := event.(*message.TestMessage) - msg := echo.Text - if len(msg) < expectedMsgSize { - return nil, fmt.Errorf("invalid message format") - } - senderIndex := msg[expectedMsgSize:] - senderIndex = strings.TrimLeft(senderIndex, " ") - nodeID, err := strconv.Atoi(senderIndex) - if err != nil { - return nil, fmt.Errorf("could not extract the node id from: %v", msg) - } - - if indices[nodeID] { - return nil, fmt.Errorf("duplicate message reception: %v", msg) - } - - if msg == fmt.Sprintf("%s %v", expectedMsgTxt, nodeID) { - indices[nodeID] = true - } - } - return indices, nil -} +// +//import ( +// "context" +// "fmt" +// "math/rand" +// "os" +// "strconv" +// "strings" +// "sync" +// "testing" +// "time" +// +// "github.com/ipfs/go-log" +// pubsub "github.com/libp2p/go-libp2p-pubsub" +// "github.com/rs/zerolog" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/require" +// "github.com/stretchr/testify/suite" +// +// "github.com/onflow/flow-go/config" +// "github.com/onflow/flow-go/model/flow" +// "github.com/onflow/flow-go/model/flow/filter" +// "github.com/onflow/flow-go/model/libp2p/message" +// "github.com/onflow/flow-go/module/irrecoverable" +// "github.com/onflow/flow-go/module/metrics" +// "github.com/onflow/flow-go/module/observable" +// "github.com/onflow/flow-go/network" +// "github.com/onflow/flow-go/network/channels" +// "github.com/onflow/flow-go/network/internal/testutils" +// "github.com/onflow/flow-go/network/p2p" +// "github.com/onflow/flow-go/network/p2p/middleware" +// "github.com/onflow/flow-go/network/p2p/p2pnode" +// p2ptest "github.com/onflow/flow-go/network/p2p/test" +// "github.com/onflow/flow-go/utils/unittest" +//) +// +//// MeshEngineTestSuite evaluates the message delivery functionality for the overlay +//// of engines over a complete graph +//type MeshEngineTestSuite struct { +// suite.Suite +// testutils.ConduitWrapper // used as a wrapper around conduit methods +// networks []network.Network // used to keep track of the networks +// libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes +// ids flow.IdentityList // used to keep track of the identifiers associated with networks +// obs chan string // used to keep track of Protect events tagged by pubsub messages +// cancel context.CancelFunc +//} +// +//// TestMeshNetTestSuite runs all tests in this test suit +//func TestMeshNetTestSuite(t *testing.T) { +// suite.Run(t, new(MeshEngineTestSuite)) +//} +// +//// SetupTest is executed prior to each test in this test suite. It creates and initializes +//// a set of network instances, sets up connection managers, nodes, identities, observables, etc. +//// This setup ensures that all necessary configurations are in place before running the tests. +//func (suite *MeshEngineTestSuite) SetupTest() { +// // defines total number of nodes in our network (minimum 3 needed to use 1-k messaging) +// const count = 10 +// logger := zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) +// log.SetAllLoggers(log.LevelError) +// +// // set up a channel to receive pubsub tags from connManagers of the nodes +// peerChannel := make(chan string) +// +// // Tag Observables Usage Explanation: +// // The tagsObserver is used to observe connections tagged by pubsub messages. This is instrumental in understanding +// // the connectivity between different peers and verifying the formation of the mesh within this test suite. +// // Issues: +// // - Deviation from Production Code: The usage of tag observables here may not reflect the behavior in the production environment. +// // - Mask Issues in the Production Environment: The observables tied to testing might lead to behaviors or errors that are +// // masked or not evident within the actual production code. +// // TODO: Evaluate the necessity of tag observables in this test and consider addressing the deviation from production +// // code and potential mask issues. Evaluate the possibility of removing this part eventually. +// ob := tagsObserver{ +// tags: peerChannel, +// log: logger, +// } +// +// ctx, cancel := context.WithCancel(context.Background()) +// suite.cancel = cancel +// +// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) +// +// sporkId := unittest.IdentifierFixture() +// libP2PNodes := make([]p2p.LibP2PNode, 0) +// identities := make(flow.IdentityList, 0) +// tagObservables := make([]observable.Observable, 0) +// idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) +// defaultFlowConfig, err := config.DefaultConfig() +// require.NoError(suite.T(), err) +// opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} +// +// for i := 0; i < count; i++ { +// connManager, err := testutils.NewTagWatchingConnManager( +// unittest.Logger(), +// metrics.NewNoopCollector(), +// &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) +// require.NoError(suite.T(), err) +// +// opts = append(opts, p2ptest.WithConnectionManager(connManager)) +// node, nodeId := p2ptest.NodeFixture(suite.T(), +// sporkId, +// suite.T().Name(), +// idProvider, +// opts...) +// libP2PNodes = append(libP2PNodes, node) +// identities = append(identities, &nodeId) +// tagObservables = append(tagObservables, connManager) +// } +// idProvider.SetIdentities(identities) +// +// suite.libp2pNodes = libP2PNodes +// suite.ids = identities +// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.libp2pNodes) +// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.networks, 3*time.Second) +// +// for _, observableConnMgr := range tagObservables { +// observableConnMgr.Subscribe(&ob) +// } +// suite.obs = peerChannel +//} +// +//// TearDownTest closes the networks within a specified timeout +//func (suite *MeshEngineTestSuite) TearDownTest() { +// suite.cancel() +// testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) +// testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) +//} +// +//// TestAllToAll_Publish evaluates the network of mesh engines against allToAllScenario scenario. +//// Network instances during this test use their Publish method to disseminate messages. +//func (suite *MeshEngineTestSuite) TestAllToAll_Publish() { +// suite.allToAllScenario(suite.Publish) +//} +// +//// TestAllToAll_Multicast evaluates the network of mesh engines against allToAllScenario scenario. +//// Network instances during this test use their Multicast method to disseminate messages. +//func (suite *MeshEngineTestSuite) TestAllToAll_Multicast() { +// suite.allToAllScenario(suite.Multicast) +//} +// +//// TestAllToAll_Unicast evaluates the network of mesh engines against allToAllScenario scenario. +//// Network instances during this test use their Unicast method to disseminate messages. +//func (suite *MeshEngineTestSuite) TestAllToAll_Unicast() { +// suite.allToAllScenario(suite.Unicast) +//} +// +//// TestTargetedValidators_Unicast tests if only the intended recipients in a 1-k messaging actually receive the message. +//// The messages are disseminated through the Unicast method of conduits. +//func (suite *MeshEngineTestSuite) TestTargetedValidators_Unicast() { +// suite.targetValidatorScenario(suite.Unicast) +//} +// +//// TestTargetedValidators_Multicast tests if only the intended recipients in a 1-k messaging actually receive the +//// message. +//// The messages are disseminated through the Multicast method of conduits. +//func (suite *MeshEngineTestSuite) TestTargetedValidators_Multicast() { +// suite.targetValidatorScenario(suite.Multicast) +//} +// +//// TestTargetedValidators_Publish tests if only the intended recipients in a 1-k messaging actually receive the message. +//// The messages are disseminated through the Multicast method of conduits. +//func (suite *MeshEngineTestSuite) TestTargetedValidators_Publish() { +// suite.targetValidatorScenario(suite.Publish) +//} +// +//// TestMaxMessageSize_Unicast evaluates the messageSizeScenario scenario using +//// the Unicast method of conduits. +//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { +// suite.messageSizeScenario(suite.Unicast, middleware.DefaultMaxUnicastMsgSize) +//} +// +//// TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using +//// the Multicast method of conduits. +//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { +// suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) +//} +// +//// TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the +//// Publish method of conduits. +//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { +// suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) +//} +// +//// TestUnregister_Publish tests that an engine cannot send any message using Publish +//// or receive any messages after the conduit is closed +//func (suite *MeshEngineTestSuite) TestUnregister_Publish() { +// suite.conduitCloseScenario(suite.Publish) +//} +// +//// TestUnregister_Publish tests that an engine cannot send any message using Multicast +//// or receive any messages after the conduit is closed +//func (suite *MeshEngineTestSuite) TestUnregister_Multicast() { +// suite.conduitCloseScenario(suite.Multicast) +//} +// +//// TestUnregister_Publish tests that an engine cannot send any message using Unicast +//// or receive any messages after the conduit is closed +//func (suite *MeshEngineTestSuite) TestUnregister_Unicast() { +// suite.conduitCloseScenario(suite.Unicast) +//} +// +//// allToAllScenario creates a complete mesh of the engines, where each engine x sends a +//// "hello from node x" to other engines. It then evaluates the correctness of message +//// delivery as well as the content of the messages. This scenario tests the capability of +//// the engines to communicate in a fully connected graph, ensuring both the reachability +//// of messages and the integrity of their contents. +//func (suite *MeshEngineTestSuite) allToAllScenario(send testutils.ConduitSendWrapperFunc) { +// // allows nodes to find each other in case of Mulitcast and Publish +// testutils.OptionalSleep(send) +// +// // creating engines +// count := len(suite.networks) +// engs := make([]*testutils.MeshEngine, 0) +// wg := sync.WaitGroup{} +// +// // logs[i][j] keeps the message that node i sends to node j +// logs := make(map[int][]string) +// for i := range suite.networks { +// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) +// engs = append(engs, eng) +// logs[i] = make([]string, 0) +// } +// +// // allow nodes to heartbeat and discover each other +// // each node will register ~D protect messages, where D is the default out-degree +// for i := 0; i < pubsub.GossipSubD*count; i++ { +// select { +// case <-suite.obs: +// case <-time.After(8 * time.Second): +// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") +// } +// } +// +// // Each node broadcasting a message to all others +// for i := range suite.networks { +// event := &message.TestMessage{ +// Text: fmt.Sprintf("hello from node %v", i), +// } +// +// // others keeps the identifier of all nodes except ith node +// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID))).NodeIDs() +// require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) +// wg.Add(count - 1) +// } +// +// // fires a goroutine for each engine that listens to incoming messages +// for i := range suite.networks { +// go func(e *testutils.MeshEngine) { +// for x := 0; x < count-1; x++ { +// <-e.Received +// wg.Done() +// } +// }(engs[i]) +// } +// +// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) +// +// // evaluates that all messages are received +// for index, e := range engs { +// // confirms the number of received messages at each node +// if len(e.Event) != (count - 1) { +// assert.Fail(suite.Suite.T(), +// fmt.Sprintf("Message reception mismatch at node %v. Expected: %v, Got: %v", index, count-1, len(e.Event))) +// } +// +// for i := 0; i < count-1; i++ { +// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) +// } +// +// // extracts failed messages +// receivedIndices, err := extractSenderID(count, e.Event, "hello from node") +// require.NoError(suite.Suite.T(), err) +// +// for j := 0; j < count; j++ { +// // evaluates self-gossip +// if j == index { +// assert.False(suite.Suite.T(), (receivedIndices)[index], fmt.Sprintf("self gossiped for node %v detected", index)) +// } +// // evaluates content +// if !(receivedIndices)[j] { +// assert.False(suite.Suite.T(), (receivedIndices)[index], +// fmt.Sprintf("Message not found in node #%v's messages. Expected: Message from node %v. Got: No message", index, j)) +// } +// } +// } +//} +// +//// targetValidatorScenario sends a single message from last node to the first half of the nodes +//// based on identifiers list. +//// It then verifies that only the intended recipients receive the message. +//// Message dissemination is done using the send wrapper of conduit. +//func (suite *MeshEngineTestSuite) targetValidatorScenario(send testutils.ConduitSendWrapperFunc) { +// // creating engines +// count := len(suite.networks) +// engs := make([]*testutils.MeshEngine, 0) +// wg := sync.WaitGroup{} +// +// for i := range suite.networks { +// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) +// engs = append(engs, eng) +// } +// +// // allow nodes to heartbeat and discover each other +// // each node will register ~D protect messages, where D is the default out-degree +// for i := 0; i < pubsub.GossipSubD*count; i++ { +// select { +// case <-suite.obs: +// case <-time.After(2 * time.Second): +// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") +// } +// } +// +// // choose half of the nodes as target +// allIds := suite.ids.NodeIDs() +// var targets []flow.Identifier +// // create a target list of half of the nodes +// for i := 0; i < len(allIds)/2; i++ { +// targets = append(targets, allIds[i]) +// } +// +// // node 0 broadcasting a message to all targets +// event := &message.TestMessage{ +// Text: "hello from node 0", +// } +// require.NoError(suite.Suite.T(), send(event, engs[len(engs)-1].Con, targets...)) +// +// // fires a goroutine for all engines to listens for the incoming message +// for i := 0; i < len(allIds)/2; i++ { +// wg.Add(1) +// go func(e *testutils.MeshEngine) { +// <-e.Received +// wg.Done() +// }(engs[i]) +// } +// +// unittest.AssertReturnsBefore(suite.T(), wg.Wait, 10*time.Second) +// +// // evaluates that all messages are received +// for index, e := range engs { +// if index < len(engs)/2 { +// assert.Len(suite.Suite.T(), e.Event, 1, fmt.Sprintf("message not received %v", index)) +// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) +// } else { +// assert.Len(suite.Suite.T(), e.Event, 0, fmt.Sprintf("message received when none was expected %v", index)) +// } +// } +//} +// +//// messageSizeScenario provides a scenario to check if a message of maximum permissible size can be sent +//// successfully. +//// It broadcasts a message from the first node to all the nodes in the identifiers list using send wrapper function. +//func (suite *MeshEngineTestSuite) messageSizeScenario(send testutils.ConduitSendWrapperFunc, size uint) { +// // creating engines +// count := len(suite.networks) +// engs := make([]*testutils.MeshEngine, 0) +// wg := sync.WaitGroup{} +// +// for i := range suite.networks { +// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) +// engs = append(engs, eng) +// } +// +// // allow nodes to heartbeat and discover each other +// // each node will register ~D protect messages per mesh setup, where D is the default out-degree +// for i := 0; i < pubsub.GossipSubD*count; i++ { +// select { +// case <-suite.obs: +// case <-time.After(8 * time.Second): +// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") +// } +// } +// // others keeps the identifier of all nodes except node that is sender. +// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[0].NodeID))).NodeIDs() +// +// // generates and sends an event of custom size to the network +// payload := testutils.NetworkPayloadFixture(suite.T(), size) +// event := &message.TestMessage{ +// Text: string(payload), +// } +// +// require.NoError(suite.T(), send(event, engs[0].Con, others...)) +// +// // fires a goroutine for all engines (except sender) to listen for the incoming message +// for _, eng := range engs[1:] { +// wg.Add(1) +// go func(e *testutils.MeshEngine) { +// <-e.Received +// wg.Done() +// }(eng) +// } +// +// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) +// +// // evaluates that all messages are received +// for index, e := range engs[1:] { +// assert.Len(suite.Suite.T(), e.Event, 1, "message not received by engine %d", index+1) +// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) +// } +//} +// +//// conduitCloseScenario tests after a Conduit is closed, an engine cannot send or receive a message for that channel. +//func (suite *MeshEngineTestSuite) conduitCloseScenario(send testutils.ConduitSendWrapperFunc) { +// +// testutils.OptionalSleep(send) +// +// // creating engines +// count := len(suite.networks) +// engs := make([]*testutils.MeshEngine, 0) +// wg := sync.WaitGroup{} +// +// for i := range suite.networks { +// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) +// engs = append(engs, eng) +// } +// +// // allow nodes to heartbeat and discover each other +// // each node will register ~D protect messages, where D is the default out-degree +// for i := 0; i < pubsub.GossipSubD*count; i++ { +// select { +// case <-suite.obs: +// case <-time.After(2 * time.Second): +// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") +// } +// } +// +// // unregister a random engine from the test topic by calling close on it's conduit +// unregisterIndex := rand.Intn(count) +// err := engs[unregisterIndex].Con.Close() +// assert.NoError(suite.T(), err) +// +// // waits enough for peer manager to unsubscribe the node from the topic +// // while libp2p is unsubscribing the node, the topology gets unstable +// // and connections to the node may be refused (although very unlikely). +// time.Sleep(2 * time.Second) +// +// // each node attempts to broadcast a message to all others +// for i := range suite.networks { +// event := &message.TestMessage{ +// Text: fmt.Sprintf("hello from node %v", i), +// } +// +// // others keeps the identifier of all nodes except ith node and the node that unregistered from the topic. +// // nodes without valid topic registration for a channel will reject messages on that channel via unicast. +// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID, suite.ids[unregisterIndex].NodeID))).NodeIDs() +// +// if i == unregisterIndex { +// // assert that unsubscribed engine cannot publish on that topic +// require.Error(suite.Suite.T(), send(event, engs[i].Con, others...)) +// continue +// } +// +// require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) +// } +// +// // fire a goroutine to listen for incoming messages for each engine except for the one which unregistered +// for i := range suite.networks { +// if i == unregisterIndex { +// continue +// } +// wg.Add(1) +// go func(e *testutils.MeshEngine) { +// expectedMsgCnt := count - 2 // count less self and unsubscribed engine +// for x := 0; x < expectedMsgCnt; x++ { +// <-e.Received +// } +// wg.Done() +// }(engs[i]) +// } +// +// // assert every one except the unsubscribed engine received the message +// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 2*time.Second) +// +// // assert that the unregistered engine did not receive the message +// unregisteredEng := engs[unregisterIndex] +// assert.Emptyf(suite.T(), unregisteredEng.Received, "unregistered engine received the topic message") +//} +// +//// assertChannelReceived asserts that the given channel was received on the given engine +//func assertChannelReceived(t *testing.T, e *testutils.MeshEngine, channel channels.Channel) { +// unittest.AssertReturnsBefore(t, func() { +// assert.Equal(t, channel, <-e.Channel) +// }, 100*time.Millisecond) +//} +// +//// extractSenderID returns a bool array with the index i true if there is a message from node i in the provided messages. +//// enginesNum is the number of engines +//// events is the channel of received events +//// expectedMsgTxt is the common prefix among all the messages that we expect to receive, for example +//// we expect to receive "hello from node x" in this test, and then expectedMsgTxt is "hello form node" +//func extractSenderID(enginesNum int, events chan interface{}, expectedMsgTxt string) ([]bool, error) { +// indices := make([]bool, enginesNum) +// expectedMsgSize := len(expectedMsgTxt) +// for i := 0; i < enginesNum-1; i++ { +// var event interface{} +// select { +// case event = <-events: +// default: +// continue +// } +// echo := event.(*message.TestMessage) +// msg := echo.Text +// if len(msg) < expectedMsgSize { +// return nil, fmt.Errorf("invalid message format") +// } +// senderIndex := msg[expectedMsgSize:] +// senderIndex = strings.TrimLeft(senderIndex, " ") +// nodeID, err := strconv.Atoi(senderIndex) +// if err != nil { +// return nil, fmt.Errorf("could not extract the node id from: %v", msg) +// } +// +// if indices[nodeID] { +// return nil, fmt.Errorf("duplicate message reception: %v", msg) +// } +// +// if msg == fmt.Sprintf("%s %v", expectedMsgTxt, nodeID) { +// indices[nodeID] = true +// } +// } +// return indices, nil +//} diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 97efd83698f..a8ef06c7f62 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -88,9 +88,8 @@ type MiddlewareTestSuite struct { sync.RWMutex size int // used to determine number of middlewares under test libP2PNodes []p2p.LibP2PNode - networks []network.Network - mws []network.Middleware // used to keep track of middlewares under test - obs chan string // used to keep track of Protect events tagged by pubsub messages + networks []*p2pnet.Network + obs chan string // used to keep track of Protect events tagged by pubsub messages ids []*flow.Identity metrics *metrics.NoopCollector // no-op performance monitoring simulation logger zerolog.Logger @@ -155,13 +154,7 @@ func (m *MiddlewareTestSuite) SetupTest() { m.ids = identities m.libP2PNodes = libP2PNodes - m.mws, _ = testutils.MiddlewareFixtures( - m.T(), - m.ids, - m.libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) - - m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.mws) + m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.libP2PNodes) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } @@ -169,7 +162,6 @@ func (m *MiddlewareTestSuite) SetupTest() { require.Len(m.Suite.T(), tagObservables, m.size) require.Len(m.Suite.T(), m.ids, m.size) - require.Len(m.Suite.T(), m.mws, m.size) ctx, cancel := context.WithCancel(context.Background()) m.mwCancel = cancel @@ -190,8 +182,6 @@ func (m *MiddlewareTestSuite) TearDownTest() { testutils.StopComponents(m.T(), m.networks, 1*time.Second) testutils.StopComponents(m.T(), m.libP2PNodes, 1*time.Second) - - m.mws = nil m.libP2PNodes = nil m.ids = nil m.size = 0 @@ -206,36 +196,24 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { // create a new staked identity ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) idProvider := unittest.NewUpdatableIDProvider(ids) - mws, _ := testutils.MiddlewareFixtures( - m.T(), - ids, - libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId)) networkCfg := testutils.NetworkConfigFixture( m.T(), *ids[0], idProvider, m.sporkId, - mws[0]) + libP2PNodes[0]) newNet, err := p2pnet.NewNetwork(networkCfg) require.NoError(m.T(), err) - require.Len(m.T(), ids, 1) - require.Len(m.T(), mws, 1) newId := ids[0] - newMw := mws[0] - - overlay := m.createOverlay(idProvider) - overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) - newMw.SetOverlay(overlay) // start up nodes and peer managers testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) newNet.Start(irrecoverableCtx) - defer testutils.StopComponents(m.T(), []network.Middleware{newMw}, 1*time.Second) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) + defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) idList := flow.IdentityList(append(m.ids, newId)) @@ -251,7 +229,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { require.True(m.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) // update the addresses - m.mws[0].UpdateNodeAddresses() + m.networks[0].UpdateNodeAddresses() // now the message should send successfully err = con.Unicast(&libp2pmessage.TestMessage{ @@ -260,7 +238,7 @@ func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { require.NoError(m.T(), err) cancel() - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) } func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { @@ -270,8 +248,8 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // burst per interval burst := 5 - for _, mw := range m.mws { - require.NoError(m.T(), mw.Subscribe(channels.TestNetworkChannel)) + for _, net := range m.networks { + require.NoError(m.T(), net.Subscribe(channels.TestNetworkChannel)) } messageRateLimiter := ratelimiter.NewRateLimiter(limit, burst, 3*time.Second) @@ -323,7 +301,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.Len(m.T(), providers, 1) require.Len(m.T(), mws, 1) newId := ids[0] - newMw := mws[0] idList := flow.IdentityList(append(m.ids, newId)) providers[0].SetIdentities(idList) @@ -402,140 +379,140 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.True(m.T(), rateLimits.Load() > 0) } -func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { - //limiter limit will be set up to 1000 bytes/sec - limit := rate.Limit(1000) - - //burst per interval - burst := 1000 - - // we only expect messages from the first middleware on the test suite - expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) - require.NoError(m.T(), err) - - // setup bandwidth rate limiter - bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) - - // the onRateLimit call back we will use to keep track of how many times a rate limit happens - // after 5 rate limits we will close ch. - ch := make(chan struct{}) - rateLimits := atomic.NewUint64(0) - onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { - require.Equal(m.T(), reason, ratelimit.ReasonBandwidth.String()) - - // we only expect messages from the first middleware on the test suite - require.NoError(m.T(), err) - require.Equal(m.T(), expectedPID, peerID) - // update hook calls - rateLimits.Inc() - close(ch) - } - - consumer := testutils.NewRateLimiterConsumer(onRateLimit) - distributor := ratelimit.NewUnicastRateLimiterDistributor() - distributor.AddConsumer(consumer) - opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} - rateLimiters := ratelimit.NewRateLimiters(opts...) - - idProvider := unittest.NewUpdatableIDProvider(m.ids) - // create a new staked identity - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), - m.sporkId, - 1, - p2ptest.WithUnicastRateLimitDistributor(distributor), - p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { - // create connection gater, connection gater will refuse connections from rate limited nodes - if bandwidthRateLimiter.IsRateLimited(pid) { - return fmt.Errorf("rate-limited peer") - } - - return nil - }))) - idProvider.SetIdentities(append(m.ids, ids...)) - m.providers[0].SetIdentities(append(m.ids, ids...)) - - // create middleware - mws, providers := testutils.MiddlewareFixtures(m.T(), - ids, - libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - middleware.WithUnicastRateLimiters(rateLimiters), - middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) - require.Len(m.T(), ids, 1) - require.Len(m.T(), providers, 1) - require.Len(m.T(), mws, 1) - newId := ids[0] - newMw := mws[0] - overlay := m.createOverlay(providers[0]) - overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) - - newMw.SetOverlay(overlay) - - ctx, cancel := context.WithCancel(m.mwCtx) - irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) - defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - - newMw.Start(irrecoverableCtx) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) - require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) - - idList := flow.IdentityList(append(m.ids, newId)) - - // needed to enable ID translation - m.providers[0].SetIdentities(idList) - - // update the addresses - m.mws[0].UpdateNodeAddresses() - - // add our sender node as a direct peer to our receiving node, this allows us to ensure - // that connections to peers that are rate limited are completely prune. IsConnected will - // return true only if the node is a direct peer of the other, after rate limiting this direct - // peer should be removed by the peer manager. - p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - - // create message with about 400bytes (300 random bytes + 100bytes message info) - b := make([]byte, 300) - for i := range b { - b[i] = byte('X') - } - // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited - // as it is more than our allowed bandwidth of 1000 bytes. - con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) - for i := 0; i < 3; i++ { - err = con0.Unicast(&libp2pmessage.TestMessage{ - Text: string(b), - }, newId.NodeID) - require.NoError(m.T(), err) - } - - // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") - - // sleep for 1 seconds to allow connection pruner to prune connections - time.Sleep(1 * time.Second) - - // ensure connection to rate limited peer is pruned - p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) - - // eventually the rate limited node should be able to reconnect and send messages - require.Eventually(m.T(), func() bool { - err = con0.Unicast(&libp2pmessage.TestMessage{ - Text: "", - }, newId.NodeID) - return err == nil - }, 3*time.Second, 100*time.Millisecond) - - // shutdown our middleware so that each message can be processed - cancel() - unittest.RequireComponentsDoneBefore(m.T(), 100*time.Millisecond, newMw) - - // expect our rate limited peer callback to be invoked once - require.Equal(m.T(), uint64(1), rateLimits.Load()) -} +//func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { +// //limiter limit will be set up to 1000 bytes/sec +// limit := rate.Limit(1000) +// +// //burst per interval +// burst := 1000 +// +// // we only expect messages from the first middleware on the test suite +// expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) +// require.NoError(m.T(), err) +// +// // setup bandwidth rate limiter +// bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) +// +// // the onRateLimit call back we will use to keep track of how many times a rate limit happens +// // after 5 rate limits we will close ch. +// ch := make(chan struct{}) +// rateLimits := atomic.NewUint64(0) +// onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { +// require.Equal(m.T(), reason, ratelimit.ReasonBandwidth.String()) +// +// // we only expect messages from the first middleware on the test suite +// require.NoError(m.T(), err) +// require.Equal(m.T(), expectedPID, peerID) +// // update hook calls +// rateLimits.Inc() +// close(ch) +// } +// +// consumer := testutils.NewRateLimiterConsumer(onRateLimit) +// distributor := ratelimit.NewUnicastRateLimiterDistributor() +// distributor.AddConsumer(consumer) +// opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} +// rateLimiters := ratelimit.NewRateLimiters(opts...) +// +// idProvider := unittest.NewUpdatableIDProvider(m.ids) +// // create a new staked identity +// ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), +// m.sporkId, +// 1, +// p2ptest.WithUnicastRateLimitDistributor(distributor), +// p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { +// // create connection gater, connection gater will refuse connections from rate limited nodes +// if bandwidthRateLimiter.IsRateLimited(pid) { +// return fmt.Errorf("rate-limited peer") +// } +// +// return nil +// }))) +// idProvider.SetIdentities(append(m.ids, ids...)) +// m.providers[0].SetIdentities(append(m.ids, ids...)) +// +// // create middleware +// mws, providers := testutils.MiddlewareFixtures(m.T(), +// ids, +// libP2PNodes, +// testutils.MiddlewareConfigFixture(m.T(), m.sporkId), +// middleware.WithUnicastRateLimiters(rateLimiters), +// middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) +// require.Len(m.T(), ids, 1) +// require.Len(m.T(), providers, 1) +// require.Len(m.T(), mws, 1) +// newId := ids[0] +// newMw := mws[0] +// overlay := m.createOverlay(providers[0]) +// overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) +// +// newMw.SetOverlay(overlay) +// +// ctx, cancel := context.WithCancel(m.mwCtx) +// irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) +// +// testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) +// defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) +// +// newMw.Start(irrecoverableCtx) +// unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) +// require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) +// +// idList := flow.IdentityList(append(m.ids, newId)) +// +// // needed to enable ID translation +// m.providers[0].SetIdentities(idList) +// +// // update the addresses +// m.mws[0].UpdateNodeAddresses() +// +// // add our sender node as a direct peer to our receiving node, this allows us to ensure +// // that connections to peers that are rate limited are completely prune. IsConnected will +// // return true only if the node is a direct peer of the other, after rate limiting this direct +// // peer should be removed by the peer manager. +// p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) +// +// // create message with about 400bytes (300 random bytes + 100bytes message info) +// b := make([]byte, 300) +// for i := range b { +// b[i] = byte('X') +// } +// // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited +// // as it is more than our allowed bandwidth of 1000 bytes. +// con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(m.T(), err) +// for i := 0; i < 3; i++ { +// err = con0.Unicast(&libp2pmessage.TestMessage{ +// Text: string(b), +// }, newId.NodeID) +// require.NoError(m.T(), err) +// } +// +// // wait for all rate limits before shutting down middleware +// unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") +// +// // sleep for 1 seconds to allow connection pruner to prune connections +// time.Sleep(1 * time.Second) +// +// // ensure connection to rate limited peer is pruned +// p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) +// p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) +// +// // eventually the rate limited node should be able to reconnect and send messages +// require.Eventually(m.T(), func() bool { +// err = con0.Unicast(&libp2pmessage.TestMessage{ +// Text: "", +// }, newId.NodeID) +// return err == nil +// }, 3*time.Second, 100*time.Millisecond) +// +// // shutdown our middleware so that each message can be processed +// cancel() +// unittest.RequireComponentsDoneBefore(m.T(), 100*time.Millisecond, newMw) +// +// // expect our rate limited peer callback to be invoked once +// require.Equal(m.T(), uint64(1), rateLimits.Load()) +//} func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvider) *mocknetwork.Overlay { overlay := &mocknetwork.Overlay{} diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 23da48c02a7..16d91d9d7b7 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -1,557 +1,558 @@ package test -import ( - "context" - "io" - "reflect" - "testing" - "time" - - "github.com/rs/zerolog" - mockery "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/onflow/flow-go/model/flow" - libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" - "github.com/onflow/flow-go/model/messages" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/codec" - "github.com/onflow/flow-go/network/internal/testutils" - "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/mocknetwork" - "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/middleware" - "github.com/onflow/flow-go/network/p2p/p2pnet" - "github.com/onflow/flow-go/network/validator" - "github.com/onflow/flow-go/utils/unittest" -) - -// UnicastAuthorizationTestSuite tests that messages sent via unicast that are unauthenticated or unauthorized are correctly rejected. Each test on the test suite -// uses 2 middlewares, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Middleware and the cancel func -// are set during each test run inside the test and remove after each test run in the TearDownTest callback. -type UnicastAuthorizationTestSuite struct { - suite.Suite - channelCloseDuration time.Duration - logger zerolog.Logger - - codec *overridableMessageEncoder - - libP2PNodes []p2p.LibP2PNode - // senderNetwork is the networking layer instance that will be used to send the message. - senderNetwork network.Network - // senderID the identity on the mw sending the message - senderID *flow.Identity - // receiverNetwork is the networking layer instance that will be used to receive the message. - receiverNetwork network.Network - // receiverID the identity on the mw sending the message - receiverID *flow.Identity - // providers id providers generated at beginning of a test run - providers []*unittest.UpdatableIDProvider - // cancel is the cancel func from the context that was used to start the middlewares in a test run - cancel context.CancelFunc - sporkId flow.Identifier - // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing - //violation's consumer before making mock assertions and cleaning up resources - waitCh chan struct{} -} - -// TestUnicastAuthorizationTestSuite runs all the test methods in this test suit -func TestUnicastAuthorizationTestSuite(t *testing.T) { - t.Parallel() - suite.Run(t, new(UnicastAuthorizationTestSuite)) -} - -func (u *UnicastAuthorizationTestSuite) SetupTest() { - u.logger = unittest.Logger() - u.channelCloseDuration = 100 * time.Millisecond - // this ch will allow us to wait until the expected method call happens before shutting down middleware - u.waitCh = make(chan struct{}) -} - -func (u *UnicastAuthorizationTestSuite) TearDownTest() { - u.stopMiddlewares() -} - -// setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. -func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { - u.sporkId = unittest.IdentifierFixture() - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) - cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) - cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { - return slashingViolationsConsumer - } - u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) - mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) - nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2pnet.WithCodec(u.codec)) - require.Len(u.T(), ids, 2) - require.Len(u.T(), providers, 2) - require.Len(u.T(), mws, 2) - require.Len(u.T(), nets, 2) - - u.senderNetwork = nets[0] - u.receiverNetwork = nets[1] - u.senderID = ids[0] - u.receiverID = ids[1] - u.providers = providers - u.libP2PNodes = libP2PNodes -} - -// startMiddlewares will start both sender and receiver middlewares with an irrecoverable signaler context and set the context cancel func. -func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Overlay) { - ctx, cancel := context.WithCancel(context.Background()) - sigCtx, _ := irrecoverable.WithSignaler(ctx) - - testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) - testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) - unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) - - u.cancel = cancel -} - -// stopMiddlewares will stop all middlewares. -func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { - u.cancel() - - testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) - unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) -} - -// TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - var nilID *flow.Identity - expectedViolation := &network.Violation{ - Identity: nilID, // because the peer will be unverified this identity will be nil - PeerID: expectedSenderPeerID.String(), - MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type - Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID - Protocol: message.ProtocolTypeUnicast, - Err: validator.ErrIdentityUnverified, - } - slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation).Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - // overriding the identity provide of the receiver node to return an empty identity list so that the - // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message - // from the sender node - u.providers[1].SetIdentities(nil) - - _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_EjectedPeer tests that messages sent via unicast by an ejected peer is correctly rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - //NOTE: setup ejected identity - u.senderID.Ejected = true - - // overriding the identity provide of the receiver node to return the ejected identity so that the - // sender node looks ejected to its networking layer and hence it sends a SenderEjectedError upon receiving a message - // from the sender node - u.providers[1].SetIdentities(flow.IdentityList{u.senderID}) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - expectedViolation := &network.Violation{ - Identity: u.senderID, // we expect this method to be called with the ejected identity - OriginID: u.senderID.NodeID, - PeerID: expectedSenderPeerID.String(), - MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type - Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID - Protocol: message.ProtocolTypeUnicast, - Err: validator.ErrSenderEjected, - } - slashingViolationsConsumer.On("OnSenderEjectedError", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_UnauthorizedPeer tests that messages sent via unicast by an unauthorized peer is correctly rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - expectedViolation := &network.Violation{ - Identity: u.senderID, - OriginID: u.senderID.NodeID, - PeerID: expectedSenderPeerID.String(), - MsgType: "*message.TestMessage", - Channel: channels.ConsensusCommittee, - Protocol: message.ProtocolTypeUnicast, - Err: message.ErrUnauthorizedMessageOnChannel, - } - - slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast; a test message must only be unicasted on the TestNetworkChannel, not on the ConsensusCommittee channel - // so we expect an unauthorized sender error - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_UnknownMsgCode tests that messages sent via unicast with an unknown message code is correctly rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - invalidMessageCode := codec.MessageCode(byte('X')) - // register a custom encoder that encodes the message with an invalid message code when encoding a string. - u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { - e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ - Text: v.(string), - }) - require.NoError(u.T(), err) - // manipulate message code byte - invalidMessageCode := codec.MessageCode(byte('X')) - e[0] = invalidMessageCode.Uint8() - return e, nil - }) - - var nilID *flow.Identity - expectedViolation := &network.Violation{ - Identity: nilID, - PeerID: expectedSenderPeerID.String(), - MsgType: "", - Channel: channels.TestNetworkChannel, - Protocol: message.ProtocolTypeUnicast, - Err: codec.NewUnknownMsgCodeErr(invalidMessageCode), - } - - slashingViolationsConsumer.On("OnUnknownMsgTypeError", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast("hello!", u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_WrongMsgCode tests that messages sent via unicast with a message code that does not match the underlying message type are correctly rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - modifiedMessageCode := codec.CodeDKGMessage - // register a custom encoder that overrides the message code when encoding a TestMessage. - u.codec.RegisterEncoder(reflect.TypeOf(&libp2pmessage.TestMessage{}), func(v interface{}) ([]byte, error) { - e, err := unittest.NetworkCodec().Encode(v) - require.NoError(u.T(), err) - e[0] = modifiedMessageCode.Uint8() - return e, nil - }) - - expectedViolation := &network.Violation{ - Identity: u.senderID, - OriginID: u.senderID.NodeID, - PeerID: expectedSenderPeerID.String(), - MsgType: "*messages.DKGMessage", - Channel: channels.TestNetworkChannel, - Protocol: message.ProtocolTypeUnicast, - Err: message.ErrUnauthorizedMessageOnChannel, - } - - slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_PublicChannel tests that messages sent via unicast on a public channel are not rejected for any reason. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - u.startMiddlewares(nil) - - msg := &libp2pmessage.TestMessage{ - Text: string("hello"), - } - - // mock a message processor that will receive the message. - receiverEngine := &mocknetwork.MessageProcessor{} - receiverEngine.On("Process", channels.PublicPushBlocks, u.senderID.NodeID, msg).Run( - func(args mockery.Arguments) { - close(u.waitCh) - }).Return(nil).Once() - _, err := u.receiverNetwork.Register(channels.PublicPushBlocks, receiverEngine) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.PublicPushBlocks, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_UnauthorizedUnicastOnChannel tests that messages sent via unicast that are not authorized for unicast are rejected. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - // set sender id role to RoleConsensus to avoid unauthorized sender validation error - u.senderID.Role = flow.RoleConsensus - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - expectedViolation := &network.Violation{ - Identity: u.senderID, - OriginID: u.senderID.NodeID, - PeerID: expectedSenderPeerID.String(), - MsgType: "*messages.BlockProposal", - Channel: channels.ConsensusCommittee, - Protocol: message.ProtocolTypeUnicast, - Err: message.ErrUnauthorizedUnicastOnChannel, - } - - slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel - payload := unittest.ProposalFixture() - // send message via unicast - err = senderCon.Unicast(payload, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_ReceiverHasNoSubscription tests that messages sent via unicast are rejected on the receiver end if the receiver does not have a subscription -// to the channel of the message. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - - expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) - require.NoError(u.T(), err) - - expectedViolation := &network.Violation{ - Identity: nil, - PeerID: expectedSenderPeerID.String(), - MsgType: "*message.TestMessage", - Channel: channels.TestNetworkChannel, - Protocol: message.ProtocolTypeUnicast, - Err: middleware.ErrUnicastMsgWithoutSub, - } - - slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). - Return(nil).Once().Run(func(args mockery.Arguments) { - close(u.waitCh) - }) - - u.startMiddlewares(nil) - - senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(&libp2pmessage.TestMessage{ - Text: string("hello"), - }, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// TestUnicastAuthorization_ReceiverHasSubscription tests that messages sent via unicast are processed on the receiver end if the receiver does have a subscription -// to the channel of the message. -func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { - // setup mock slashing violations consumer and middlewares - slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) - u.setupNetworks(slashingViolationsConsumer) - u.startMiddlewares(nil) - - msg := &messages.EntityRequest{ - EntityIDs: unittest.IdentifierListFixture(10), - } - - // both sender and receiver must have an authorized role to send and receive messages on the ConsensusCommittee channel. - u.senderID.Role = flow.RoleConsensus - u.receiverID.Role = flow.RoleExecution - - receiverEngine := &mocknetwork.MessageProcessor{} - receiverEngine.On("Process", channels.RequestReceiptsByBlockID, u.senderID.NodeID, msg).Run( - func(args mockery.Arguments) { - close(u.waitCh) - }).Return(nil).Once() - _, err := u.receiverNetwork.Register(channels.RequestReceiptsByBlockID, receiverEngine) - require.NoError(u.T(), err) - - senderCon, err := u.senderNetwork.Register(channels.RequestReceiptsByBlockID, &mocknetwork.MessageProcessor{}) - require.NoError(u.T(), err) - - // send message via unicast - err = senderCon.Unicast(msg, u.receiverID.NodeID) - require.NoError(u.T(), err) - - // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens - unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -} - -// overridableMessageEncoder is a codec that allows to override the encoder for a specific type only for sake of testing. -// We specifically use this to override the encoder for the TestMessage type to encode it with an invalid message code. -type overridableMessageEncoder struct { - codec network.Codec - specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) -} - -var _ network.Codec = (*overridableMessageEncoder)(nil) - -func newOverridableMessageEncoder(codec network.Codec) *overridableMessageEncoder { - return &overridableMessageEncoder{ - codec: codec, - specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), - } -} - -// RegisterEncoder registers an encoder for a specific type, overriding the default encoder for that type. -func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { - u.specificEncoder[t] = encoder -} - -// NewEncoder creates a new encoder. -func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { - return u.codec.NewEncoder(w) -} - -// NewDecoder creates a new decoder. -func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { - return u.codec.NewDecoder(r) -} - -// Encode encodes a value into a byte slice. If a specific encoder is registered for the type of the value, it will be used. -// Otherwise, the default encoder will be used. -func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { - if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { - return encoder(v) - } - return u.codec.Encode(v) -} - -// Decode decodes a byte slice into a value. It uses the default decoder. -func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { - return u.codec.Decode(data) -} +// +//import ( +// "context" +// "io" +// "reflect" +// "testing" +// "time" +// +// "github.com/rs/zerolog" +// mockery "github.com/stretchr/testify/mock" +// "github.com/stretchr/testify/require" +// "github.com/stretchr/testify/suite" +// +// "github.com/onflow/flow-go/model/flow" +// libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" +// "github.com/onflow/flow-go/model/messages" +// "github.com/onflow/flow-go/module/irrecoverable" +// "github.com/onflow/flow-go/network" +// "github.com/onflow/flow-go/network/channels" +// "github.com/onflow/flow-go/network/codec" +// "github.com/onflow/flow-go/network/internal/testutils" +// "github.com/onflow/flow-go/network/message" +// "github.com/onflow/flow-go/network/mocknetwork" +// "github.com/onflow/flow-go/network/p2p" +// "github.com/onflow/flow-go/network/p2p/middleware" +// "github.com/onflow/flow-go/network/p2p/p2pnet" +// "github.com/onflow/flow-go/network/validator" +// "github.com/onflow/flow-go/utils/unittest" +//) +// +//// UnicastAuthorizationTestSuite tests that messages sent via unicast that are unauthenticated or unauthorized are correctly rejected. Each test on the test suite +//// uses 2 middlewares, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Middleware and the cancel func +//// are set during each test run inside the test and remove after each test run in the TearDownTest callback. +//type UnicastAuthorizationTestSuite struct { +// suite.Suite +// channelCloseDuration time.Duration +// logger zerolog.Logger +// +// codec *overridableMessageEncoder +// +// libP2PNodes []p2p.LibP2PNode +// // senderNetwork is the networking layer instance that will be used to send the message. +// senderNetwork network.Network +// // senderID the identity on the mw sending the message +// senderID *flow.Identity +// // receiverNetwork is the networking layer instance that will be used to receive the message. +// receiverNetwork network.Network +// // receiverID the identity on the mw sending the message +// receiverID *flow.Identity +// // providers id providers generated at beginning of a test run +// providers []*unittest.UpdatableIDProvider +// // cancel is the cancel func from the context that was used to start the middlewares in a test run +// cancel context.CancelFunc +// sporkId flow.Identifier +// // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing +// //violation's consumer before making mock assertions and cleaning up resources +// waitCh chan struct{} +//} +// +//// TestUnicastAuthorizationTestSuite runs all the test methods in this test suit +//func TestUnicastAuthorizationTestSuite(t *testing.T) { +// t.Parallel() +// suite.Run(t, new(UnicastAuthorizationTestSuite)) +//} +// +//func (u *UnicastAuthorizationTestSuite) SetupTest() { +// u.logger = unittest.Logger() +// u.channelCloseDuration = 100 * time.Millisecond +// // this ch will allow us to wait until the expected method call happens before shutting down middleware +// u.waitCh = make(chan struct{}) +//} +// +//func (u *UnicastAuthorizationTestSuite) TearDownTest() { +// u.stopMiddlewares() +//} +// +//// setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. +//func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { +// u.sporkId = unittest.IdentifierFixture() +// ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) +// cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) +// cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { +// return slashingViolationsConsumer +// } +// u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) +// mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) +// nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2pnet.WithCodec(u.codec)) +// require.Len(u.T(), ids, 2) +// require.Len(u.T(), providers, 2) +// require.Len(u.T(), mws, 2) +// require.Len(u.T(), nets, 2) +// +// u.senderNetwork = nets[0] +// u.receiverNetwork = nets[1] +// u.senderID = ids[0] +// u.receiverID = ids[1] +// u.providers = providers +// u.libP2PNodes = libP2PNodes +//} +// +//// startMiddlewares will start both sender and receiver middlewares with an irrecoverable signaler context and set the context cancel func. +//func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Overlay) { +// ctx, cancel := context.WithCancel(context.Background()) +// sigCtx, _ := irrecoverable.WithSignaler(ctx) +// +// testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) +// testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) +// unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) +// +// u.cancel = cancel +//} +// +//// stopMiddlewares will stop all middlewares. +//func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { +// u.cancel() +// +// testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) +// unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) +//} +// +//// TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// var nilID *flow.Identity +// expectedViolation := &network.Violation{ +// Identity: nilID, // because the peer will be unverified this identity will be nil +// PeerID: expectedSenderPeerID.String(), +// MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type +// Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID +// Protocol: message.ProtocolTypeUnicast, +// Err: validator.ErrIdentityUnverified, +// } +// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation).Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// // overriding the identity provide of the receiver node to return an empty identity list so that the +// // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message +// // from the sender node +// u.providers[1].SetIdentities(nil) +// +// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_EjectedPeer tests that messages sent via unicast by an ejected peer is correctly rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// //NOTE: setup ejected identity +// u.senderID.Ejected = true +// +// // overriding the identity provide of the receiver node to return the ejected identity so that the +// // sender node looks ejected to its networking layer and hence it sends a SenderEjectedError upon receiving a message +// // from the sender node +// u.providers[1].SetIdentities(flow.IdentityList{u.senderID}) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// expectedViolation := &network.Violation{ +// Identity: u.senderID, // we expect this method to be called with the ejected identity +// OriginID: u.senderID.NodeID, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type +// Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID +// Protocol: message.ProtocolTypeUnicast, +// Err: validator.ErrSenderEjected, +// } +// slashingViolationsConsumer.On("OnSenderEjectedError", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_UnauthorizedPeer tests that messages sent via unicast by an unauthorized peer is correctly rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// expectedViolation := &network.Violation{ +// Identity: u.senderID, +// OriginID: u.senderID.NodeID, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "*message.TestMessage", +// Channel: channels.ConsensusCommittee, +// Protocol: message.ProtocolTypeUnicast, +// Err: message.ErrUnauthorizedMessageOnChannel, +// } +// +// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast; a test message must only be unicasted on the TestNetworkChannel, not on the ConsensusCommittee channel +// // so we expect an unauthorized sender error +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_UnknownMsgCode tests that messages sent via unicast with an unknown message code is correctly rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// invalidMessageCode := codec.MessageCode(byte('X')) +// // register a custom encoder that encodes the message with an invalid message code when encoding a string. +// u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { +// e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ +// Text: v.(string), +// }) +// require.NoError(u.T(), err) +// // manipulate message code byte +// invalidMessageCode := codec.MessageCode(byte('X')) +// e[0] = invalidMessageCode.Uint8() +// return e, nil +// }) +// +// var nilID *flow.Identity +// expectedViolation := &network.Violation{ +// Identity: nilID, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "", +// Channel: channels.TestNetworkChannel, +// Protocol: message.ProtocolTypeUnicast, +// Err: codec.NewUnknownMsgCodeErr(invalidMessageCode), +// } +// +// slashingViolationsConsumer.On("OnUnknownMsgTypeError", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast("hello!", u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_WrongMsgCode tests that messages sent via unicast with a message code that does not match the underlying message type are correctly rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// modifiedMessageCode := codec.CodeDKGMessage +// // register a custom encoder that overrides the message code when encoding a TestMessage. +// u.codec.RegisterEncoder(reflect.TypeOf(&libp2pmessage.TestMessage{}), func(v interface{}) ([]byte, error) { +// e, err := unittest.NetworkCodec().Encode(v) +// require.NoError(u.T(), err) +// e[0] = modifiedMessageCode.Uint8() +// return e, nil +// }) +// +// expectedViolation := &network.Violation{ +// Identity: u.senderID, +// OriginID: u.senderID.NodeID, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "*messages.DKGMessage", +// Channel: channels.TestNetworkChannel, +// Protocol: message.ProtocolTypeUnicast, +// Err: message.ErrUnauthorizedMessageOnChannel, +// } +// +// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_PublicChannel tests that messages sent via unicast on a public channel are not rejected for any reason. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// u.startMiddlewares(nil) +// +// msg := &libp2pmessage.TestMessage{ +// Text: string("hello"), +// } +// +// // mock a message processor that will receive the message. +// receiverEngine := &mocknetwork.MessageProcessor{} +// receiverEngine.On("Process", channels.PublicPushBlocks, u.senderID.NodeID, msg).Run( +// func(args mockery.Arguments) { +// close(u.waitCh) +// }).Return(nil).Once() +// _, err := u.receiverNetwork.Register(channels.PublicPushBlocks, receiverEngine) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.PublicPushBlocks, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_UnauthorizedUnicastOnChannel tests that messages sent via unicast that are not authorized for unicast are rejected. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// // set sender id role to RoleConsensus to avoid unauthorized sender validation error +// u.senderID.Role = flow.RoleConsensus +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// expectedViolation := &network.Violation{ +// Identity: u.senderID, +// OriginID: u.senderID.NodeID, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "*messages.BlockProposal", +// Channel: channels.ConsensusCommittee, +// Protocol: message.ProtocolTypeUnicast, +// Err: message.ErrUnauthorizedUnicastOnChannel, +// } +// +// slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel +// payload := unittest.ProposalFixture() +// // send message via unicast +// err = senderCon.Unicast(payload, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_ReceiverHasNoSubscription tests that messages sent via unicast are rejected on the receiver end if the receiver does not have a subscription +//// to the channel of the message. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// +// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) +// require.NoError(u.T(), err) +// +// expectedViolation := &network.Violation{ +// Identity: nil, +// PeerID: expectedSenderPeerID.String(), +// MsgType: "*message.TestMessage", +// Channel: channels.TestNetworkChannel, +// Protocol: message.ProtocolTypeUnicast, +// Err: middleware.ErrUnicastMsgWithoutSub, +// } +// +// slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). +// Return(nil).Once().Run(func(args mockery.Arguments) { +// close(u.waitCh) +// }) +// +// u.startMiddlewares(nil) +// +// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(&libp2pmessage.TestMessage{ +// Text: string("hello"), +// }, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// TestUnicastAuthorization_ReceiverHasSubscription tests that messages sent via unicast are processed on the receiver end if the receiver does have a subscription +//// to the channel of the message. +//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { +// // setup mock slashing violations consumer and middlewares +// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) +// u.setupNetworks(slashingViolationsConsumer) +// u.startMiddlewares(nil) +// +// msg := &messages.EntityRequest{ +// EntityIDs: unittest.IdentifierListFixture(10), +// } +// +// // both sender and receiver must have an authorized role to send and receive messages on the ConsensusCommittee channel. +// u.senderID.Role = flow.RoleConsensus +// u.receiverID.Role = flow.RoleExecution +// +// receiverEngine := &mocknetwork.MessageProcessor{} +// receiverEngine.On("Process", channels.RequestReceiptsByBlockID, u.senderID.NodeID, msg).Run( +// func(args mockery.Arguments) { +// close(u.waitCh) +// }).Return(nil).Once() +// _, err := u.receiverNetwork.Register(channels.RequestReceiptsByBlockID, receiverEngine) +// require.NoError(u.T(), err) +// +// senderCon, err := u.senderNetwork.Register(channels.RequestReceiptsByBlockID, &mocknetwork.MessageProcessor{}) +// require.NoError(u.T(), err) +// +// // send message via unicast +// err = senderCon.Unicast(msg, u.receiverID.NodeID) +// require.NoError(u.T(), err) +// +// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens +// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +//} +// +//// overridableMessageEncoder is a codec that allows to override the encoder for a specific type only for sake of testing. +//// We specifically use this to override the encoder for the TestMessage type to encode it with an invalid message code. +//type overridableMessageEncoder struct { +// codec network.Codec +// specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) +//} +// +//var _ network.Codec = (*overridableMessageEncoder)(nil) +// +//func newOverridableMessageEncoder(codec network.Codec) *overridableMessageEncoder { +// return &overridableMessageEncoder{ +// codec: codec, +// specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), +// } +//} +// +//// RegisterEncoder registers an encoder for a specific type, overriding the default encoder for that type. +//func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { +// u.specificEncoder[t] = encoder +//} +// +//// NewEncoder creates a new encoder. +//func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { +// return u.codec.NewEncoder(w) +//} +// +//// NewDecoder creates a new decoder. +//func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { +// return u.codec.NewDecoder(r) +//} +// +//// Encode encodes a value into a byte slice. If a specific encoder is registered for the type of the value, it will be used. +//// Otherwise, the default encoder will be used. +//func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { +// if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { +// return encoder(v) +// } +// return u.codec.Encode(v) +//} +// +//// Decode decodes a byte slice into a value. It uses the default decoder. +//func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { +// return u.codec.Decode(data) +//} From 2d837eb05a3917b435758522a7c133eca49c9fa1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 17:20:38 -0400 Subject: [PATCH 750/815] quarantines more tests --- network/test/echoengine_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index e51381ad359..ae975b45886 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -38,8 +38,8 @@ type EchoEngineTestSuite struct { // Some tests are skipped in short mode to speedup the build. -// TestStubEngineTestSuite runs all the test methods in this test suit -func TestStubEngineTestSuite(t *testing.T) { +// TestEchoEngineTestSuite runs all the test methods in this test suit +func TestEchoEngineTestSuite(t *testing.T) { suite.Run(t, new(EchoEngineTestSuite)) } @@ -158,6 +158,7 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() { // on deduplicating the received messages via Publish method of nodes' Conduits. // Messages are delivered to the receiver in parallel via the Publish method of Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() { + unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast") suite.duplicateMessageParallel(suite.Publish) } @@ -197,6 +198,7 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Multicast methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageDifferentChan(suite.Multicast) } From cf577b61fae41b9a9a661b892a9cf4ea4300edaa Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 17:44:11 -0400 Subject: [PATCH 751/815] fixes TestUnicastRateLimit_Messages --- network/test/middleware_test.go | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index a8ef06c7f62..19e86f8fdb5 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -290,48 +290,48 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { idProvider.SetIdentities(append(m.ids, ids...)) // create middleware - mws, providers := testutils.MiddlewareFixtures(m.T(), - ids, - libP2PNodes, - testutils.MiddlewareConfigFixture(m.T(), m.sporkId), - middleware.WithUnicastRateLimiters(rateLimiters), - middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(messageRateLimiter)})) + netCfg := testutils.NetworkConfigFixture( + m.T(), + *ids[0], + idProvider, + m.sporkId, + libP2PNodes[0]) + newNet, err := p2pnet.NewNetwork( + netCfg, + p2pnet.WithUnicastRateLimiters(rateLimiters), + p2pnet.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(messageRateLimiter))) + require.NoError(m.T(), err) require.Len(m.T(), ids, 1) - require.Len(m.T(), providers, 1) - require.Len(m.T(), mws, 1) newId := ids[0] idList := flow.IdentityList(append(m.ids, newId)) - providers[0].SetIdentities(idList) + m.providers[0].SetIdentities(idList) - overlay := m.createOverlay(providers[0]) + ctx, cancel := context.WithCancel(m.mwCtx) + irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) + testutils.StartNetworks(irrecoverableCtx, m.T(), []network.Network{newNet}, 1*time.Second) calls := atomic.NewUint64(0) ch := make(chan struct{}) - overlay.On("Receive", mockery.AnythingOfType("*message.IncomingMessageScope")).Return(nil).Run(func(args mockery.Arguments) { + // registers an engine on the new network + newEngine := &mocknetwork.MessageProcessor{} + _, err = newNet.Register(channels.TestNetworkChannel, newEngine) + require.NoError(m.T(), err) + newEngine.On("Process", channels.TestNetworkChannel, m.ids[0].NodeID, mockery.Anything).Run(func(args mockery.Arguments) { calls.Inc() if calls.Load() >= 5 { close(ch) } - }) - newMw.SetOverlay(overlay) - - ctx, cancel := context.WithCancel(m.mwCtx) - irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) - defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - - newMw.Start(irrecoverableCtx) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) - require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) + }).Return(nil) // needed to enable ID translation m.providers[0].SetIdentities(idList) // update the addresses - m.mws[0].UpdateNodeAddresses() + m.networks[0].UpdateNodeAddresses() // add our sender node as a direct peer to our receiving node, this allows us to ensure // that connections to peers that are rate limited are completely prune. IsConnected will @@ -373,7 +373,7 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { // shutdown our middleware so that each message can be processed cancel() unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") - unittest.RequireCloseBefore(m.T(), newMw.Done(), 100*time.Millisecond, "could not stop middleware on time") + unittest.RequireCloseBefore(m.T(), newNet.Done(), 100*time.Millisecond, "could not stop middleware on time") // expect our rate limited peer callback to be invoked once require.True(m.T(), rateLimits.Load() > 0) From 52a8ab857fd2f24c6b576c5c3ce313e29583d5e4 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 17:46:14 -0400 Subject: [PATCH 752/815] quarantines more tests --- network/test/echoengine_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index ae975b45886..a832bf34baa 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -127,6 +127,7 @@ func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { // Sender and receiver are not synchronized func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { // set to true for an echo expectation + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.multiMessageAsync(true, 10, suite.Multicast) } @@ -173,6 +174,7 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { // on deduplicating the received messages via Multicast method of nodes' Conduits. // Messages are delivered to the receiver in parallel via the Multicast method of Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageParallel(suite.Multicast) } From edd55198d517d9417195af732d92ddb63dcdac52 Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 30 Aug 2023 22:19:14 -0400 Subject: [PATCH 753/815] Update connection_gater_test.go --- network/p2p/connection/connection_gater_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 7cec31f3d79..0830a267984 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -143,6 +143,13 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { node2Metrics.On("InboundConnections", mock.Anything).Return() node2Metrics.On("OutboundConnections", mock.Anything).Return() + // Libp2p control message validation metrics, these may or may not be called depending on the machine the test is running on and how long + // the nodes in the test run for. + node2Metrics.On("BlockingPreProcessingStarted", mock.Anything, mock.Anything) + node2Metrics.On("BlockingPreProcessingFinished", mock.Anything, mock.Anything, mock.Anything) + node2Metrics.On("AsyncProcessingStarted", mock.Anything) + node2Metrics.On("AsyncProcessingFinished", mock.Anything, mock.Anything) + // we create node2 with a connection gater that allows all connections and the mocked metrics collector. node2, node2Id := p2ptest.NodeFixture( t, From 4cb2d49851df3693aabe40decd606672b090276b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 22:47:56 -0400 Subject: [PATCH 754/815] fixes mock with connection gater --- network/p2p/connection/connection_gater_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 0830a267984..ce3e4358d28 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -145,10 +145,10 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { // Libp2p control message validation metrics, these may or may not be called depending on the machine the test is running on and how long // the nodes in the test run for. - node2Metrics.On("BlockingPreProcessingStarted", mock.Anything, mock.Anything) - node2Metrics.On("BlockingPreProcessingFinished", mock.Anything, mock.Anything, mock.Anything) - node2Metrics.On("AsyncProcessingStarted", mock.Anything) - node2Metrics.On("AsyncProcessingFinished", mock.Anything, mock.Anything) + node2Metrics.On("BlockingPreProcessingStarted", mock.Anything, mock.Anything).Maybe() + node2Metrics.On("BlockingPreProcessingFinished", mock.Anything, mock.Anything, mock.Anything).Maybe() + node2Metrics.On("AsyncProcessingStarted", mock.Anything).Maybe() + node2Metrics.On("AsyncProcessingFinished", mock.Anything, mock.Anything).Maybe() // we create node2 with a connection gater that allows all connections and the mocked metrics collector. node2, node2Id := p2ptest.NodeFixture( From 2b82487be5a6932c4ada31e6aef90cf2efccce29 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 30 Aug 2023 22:52:49 -0400 Subject: [PATCH 755/815] fixes build issue with access node --- cmd/access/node_builder/access_node_builder.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index e0c10f2ed04..f2690174a54 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -782,18 +782,6 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { }) } -// initNetwork creates the network.Network implementation with the given metrics, middleware, initial list of network -// participants and topology used to choose peers from the list of participants. The list of participants can later be -// updated by calling network.SetIDs. -func (builder *FlowAccessNodeBuilder) initNetwork(local module.Local, - libp2pNode p2p.LibP2PNode, - networkMetrics module.NetworkCoreMetrics, - topology network.Topology, - receiveCache *netcache.ReceiveCache, -) (*p2pnet.Network, error) { - -} - func publicNetworkMsgValidators(log zerolog.Logger, idProvider module.IdentityProvider, selfID flow.Identifier) []network.MessageValidator { return []network.MessageValidator{ // filter out messages sent by this node itself From f7580cf03c80a8a4feb27a474a3cad079052f1db Mon Sep 17 00:00:00 2001 From: Khalil Claybon Date: Wed, 30 Aug 2023 22:53:32 -0400 Subject: [PATCH 756/815] Update connection_gater_test.go --- network/p2p/connection/connection_gater_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/p2p/connection/connection_gater_test.go b/network/p2p/connection/connection_gater_test.go index 0830a267984..ce3e4358d28 100644 --- a/network/p2p/connection/connection_gater_test.go +++ b/network/p2p/connection/connection_gater_test.go @@ -145,10 +145,10 @@ func TestConnectionGating_ResourceAllocation_AllowListing(t *testing.T) { // Libp2p control message validation metrics, these may or may not be called depending on the machine the test is running on and how long // the nodes in the test run for. - node2Metrics.On("BlockingPreProcessingStarted", mock.Anything, mock.Anything) - node2Metrics.On("BlockingPreProcessingFinished", mock.Anything, mock.Anything, mock.Anything) - node2Metrics.On("AsyncProcessingStarted", mock.Anything) - node2Metrics.On("AsyncProcessingFinished", mock.Anything, mock.Anything) + node2Metrics.On("BlockingPreProcessingStarted", mock.Anything, mock.Anything).Maybe() + node2Metrics.On("BlockingPreProcessingFinished", mock.Anything, mock.Anything, mock.Anything).Maybe() + node2Metrics.On("AsyncProcessingStarted", mock.Anything).Maybe() + node2Metrics.On("AsyncProcessingFinished", mock.Anything, mock.Anything).Maybe() // we create node2 with a connection gater that allows all connections and the mocked metrics collector. node2, node2Id := p2ptest.NodeFixture( From f369aa623d057a465915f9e9aa9da14522596d44 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Thu, 31 Aug 2023 13:13:37 +0100 Subject: [PATCH 757/815] Fix compilation errors --- network/p2p/libp2pNode.go | 5 +++-- network/p2p/p2pnode/libp2pNode.go | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 3803e6fd741..0f64e61fe62 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" "github.com/onflow/flow-go/network" + flownet "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/p2p/unicast/protocols" ) @@ -76,9 +77,9 @@ type PubSub interface { // Subscribe subscribes the node to the given topic and returns the subscription Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) // UnSubscribe cancels the subscriber and closes the topic. - UnSubscribe(topic channels.Topic) error + Unsubscribe(topic channels.Topic) error // Publish publishes the given payload on the topic. - Publish(ctx context.Context, topic channels.Topic, data []byte) error + Publish(ctx context.Context, messageScope flownet.OutgoingMessageScope) error // SetPubSub sets the node's pubsub implementation. // SetPubSub may be called at most once. SetPubSub(ps PubSubAdapter) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 9fcbcc9a466..dd639f5a319 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -90,8 +90,6 @@ func NewNode( } } -var _ p2p.LibP2PNode = (*Node)(nil) - func (n *Node) Start(ctx irrecoverable.SignalerContext) { n.Component.Start(ctx) } From 26f70c3e7cae735eb0d850609cff9440db80540e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:13:59 -0400 Subject: [PATCH 758/815] skips entire echo engine test suite --- network/test/echoengine_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index a832bf34baa..6eafb894846 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -40,6 +40,7 @@ type EchoEngineTestSuite struct { // TestEchoEngineTestSuite runs all the test methods in this test suit func TestEchoEngineTestSuite(t *testing.T) { + unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(EchoEngineTestSuite)) } @@ -127,7 +128,6 @@ func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { // Sender and receiver are not synchronized func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { // set to true for an echo expectation - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.multiMessageAsync(true, 10, suite.Multicast) } @@ -174,7 +174,6 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { // on deduplicating the received messages via Multicast method of nodes' Conduits. // Messages are delivered to the receiver in parallel via the Multicast method of Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageParallel(suite.Multicast) } @@ -183,7 +182,6 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Publish methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageDifferentChan(suite.Publish) } @@ -200,7 +198,6 @@ func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { // desire behavior is that the deduplication should happen based on both eventID and channel. // Messages are sent via the Multicast methods of the Conduits. func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.duplicateMessageDifferentChan(suite.Multicast) } From e1dd46c0405b17d44e3d8c0ba8e07e90b497d1fa Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:16:50 -0400 Subject: [PATCH 759/815] re-generates mocks --- network/mocknetwork/middleware.go | 85 +------------------------------ 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go index d67d6bf102d..3816ee996f5 100644 --- a/network/mocknetwork/middleware.go +++ b/network/mocknetwork/middleware.go @@ -3,23 +3,12 @@ package mocknetwork import ( - context "context" - - channels "github.com/onflow/flow-go/network/channels" - - corenetwork "github.com/libp2p/go-libp2p/core/network" - - datastore "github.com/ipfs/go-datastore" - irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + channels "github.com/onflow/flow-go/network/channels" mock "github.com/stretchr/testify/mock" network "github.com/onflow/flow-go/network" - - peer "github.com/libp2p/go-libp2p/core/peer" - - protocol "github.com/libp2p/go-libp2p/core/protocol" ) // Middleware is an autogenerated mock type for the Middleware type @@ -43,45 +32,6 @@ func (_m *Middleware) Done() <-chan struct{} { return r0 } -// NewBlobService provides a mock function with given fields: channel, store, opts -func (_m *Middleware) NewBlobService(channel channels.Channel, store datastore.Batching, opts ...network.BlobServiceOption) network.BlobService { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, channel, store) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 network.BlobService - if rf, ok := ret.Get(0).(func(channels.Channel, datastore.Batching, ...network.BlobServiceOption) network.BlobService); ok { - r0 = rf(channel, store, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(network.BlobService) - } - } - - return r0 -} - -// NewPingService provides a mock function with given fields: pingProtocol, provider -func (_m *Middleware) NewPingService(pingProtocol protocol.ID, provider network.PingInfoProvider) network.PingService { - ret := _m.Called(pingProtocol, provider) - - var r0 network.PingService - if rf, ok := ret.Get(0).(func(protocol.ID, network.PingInfoProvider) network.PingService); ok { - r0 = rf(pingProtocol, provider) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(network.PingService) - } - } - - return r0 -} - // OnAllowListNotification provides a mock function with given fields: _a0 func (_m *Middleware) OnAllowListNotification(_a0 *network.AllowListingUpdate) { _m.Called(_a0) @@ -92,34 +42,6 @@ func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpd _m.Called(_a0) } -// OpenProtectedStream provides a mock function with given fields: ctx, peerID, protectionTag, writingLogic -func (_m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(corenetwork.Stream) error) error { - ret := _m.Called(ctx, peerID, protectionTag, writingLogic) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(corenetwork.Stream) error) error); ok { - r0 = rf(ctx, peerID, protectionTag, writingLogic) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Publish provides a mock function with given fields: msg -func (_m *Middleware) Publish(msg network.OutgoingMessageScope) error { - ret := _m.Called(msg) - - var r0 error - if rf, ok := ret.Get(0).(func(network.OutgoingMessageScope) error); ok { - r0 = rf(msg) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // Ready provides a mock function with given fields: func (_m *Middleware) Ready() <-chan struct{} { ret := _m.Called() @@ -136,11 +58,6 @@ func (_m *Middleware) Ready() <-chan struct{} { return r0 } -// SetOverlay provides a mock function with given fields: _a0 -func (_m *Middleware) SetOverlay(_a0 network.Overlay) { - _m.Called(_a0) -} - // Start provides a mock function with given fields: _a0 func (_m *Middleware) Start(_a0 irrecoverable.SignalerContext) { _m.Called(_a0) From 8abea8b9bf19442c705e4b042595de53f9b1b140 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:26:12 -0400 Subject: [PATCH 760/815] fixes all middleware tests --- network/test/middleware_test.go | 271 ++++++++++++++++---------------- 1 file changed, 136 insertions(+), 135 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 19e86f8fdb5..b219fce8dc8 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -289,7 +289,6 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { }))) idProvider.SetIdentities(append(m.ids, ids...)) - // create middleware netCfg := testutils.NetworkConfigFixture( m.T(), *ids[0], @@ -379,140 +378,142 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { require.True(m.T(), rateLimits.Load() > 0) } -//func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { -// //limiter limit will be set up to 1000 bytes/sec -// limit := rate.Limit(1000) -// -// //burst per interval -// burst := 1000 -// -// // we only expect messages from the first middleware on the test suite -// expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) -// require.NoError(m.T(), err) -// -// // setup bandwidth rate limiter -// bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) -// -// // the onRateLimit call back we will use to keep track of how many times a rate limit happens -// // after 5 rate limits we will close ch. -// ch := make(chan struct{}) -// rateLimits := atomic.NewUint64(0) -// onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { -// require.Equal(m.T(), reason, ratelimit.ReasonBandwidth.String()) -// -// // we only expect messages from the first middleware on the test suite -// require.NoError(m.T(), err) -// require.Equal(m.T(), expectedPID, peerID) -// // update hook calls -// rateLimits.Inc() -// close(ch) -// } -// -// consumer := testutils.NewRateLimiterConsumer(onRateLimit) -// distributor := ratelimit.NewUnicastRateLimiterDistributor() -// distributor.AddConsumer(consumer) -// opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} -// rateLimiters := ratelimit.NewRateLimiters(opts...) -// -// idProvider := unittest.NewUpdatableIDProvider(m.ids) -// // create a new staked identity -// ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), -// m.sporkId, -// 1, -// p2ptest.WithUnicastRateLimitDistributor(distributor), -// p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { -// // create connection gater, connection gater will refuse connections from rate limited nodes -// if bandwidthRateLimiter.IsRateLimited(pid) { -// return fmt.Errorf("rate-limited peer") -// } -// -// return nil -// }))) -// idProvider.SetIdentities(append(m.ids, ids...)) -// m.providers[0].SetIdentities(append(m.ids, ids...)) -// -// // create middleware -// mws, providers := testutils.MiddlewareFixtures(m.T(), -// ids, -// libP2PNodes, -// testutils.MiddlewareConfigFixture(m.T(), m.sporkId), -// middleware.WithUnicastRateLimiters(rateLimiters), -// middleware.WithPeerManagerFilters([]p2p.PeerFilter{testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter)})) -// require.Len(m.T(), ids, 1) -// require.Len(m.T(), providers, 1) -// require.Len(m.T(), mws, 1) -// newId := ids[0] -// newMw := mws[0] -// overlay := m.createOverlay(providers[0]) -// overlay.On("Receive", m.ids[0].NodeID, mockery.AnythingOfType("*message.Message")).Return(nil) -// -// newMw.SetOverlay(overlay) -// -// ctx, cancel := context.WithCancel(m.mwCtx) -// irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) -// -// testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) -// defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) -// -// newMw.Start(irrecoverableCtx) -// unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newMw) -// require.NoError(m.T(), newMw.Subscribe(channels.TestNetworkChannel)) -// -// idList := flow.IdentityList(append(m.ids, newId)) -// -// // needed to enable ID translation -// m.providers[0].SetIdentities(idList) -// -// // update the addresses -// m.mws[0].UpdateNodeAddresses() -// -// // add our sender node as a direct peer to our receiving node, this allows us to ensure -// // that connections to peers that are rate limited are completely prune. IsConnected will -// // return true only if the node is a direct peer of the other, after rate limiting this direct -// // peer should be removed by the peer manager. -// p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) -// -// // create message with about 400bytes (300 random bytes + 100bytes message info) -// b := make([]byte, 300) -// for i := range b { -// b[i] = byte('X') -// } -// // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited -// // as it is more than our allowed bandwidth of 1000 bytes. -// con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(m.T(), err) -// for i := 0; i < 3; i++ { -// err = con0.Unicast(&libp2pmessage.TestMessage{ -// Text: string(b), -// }, newId.NodeID) -// require.NoError(m.T(), err) -// } -// -// // wait for all rate limits before shutting down middleware -// unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") -// -// // sleep for 1 seconds to allow connection pruner to prune connections -// time.Sleep(1 * time.Second) -// -// // ensure connection to rate limited peer is pruned -// p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) -// p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) -// -// // eventually the rate limited node should be able to reconnect and send messages -// require.Eventually(m.T(), func() bool { -// err = con0.Unicast(&libp2pmessage.TestMessage{ -// Text: "", -// }, newId.NodeID) -// return err == nil -// }, 3*time.Second, 100*time.Millisecond) -// -// // shutdown our middleware so that each message can be processed -// cancel() -// unittest.RequireComponentsDoneBefore(m.T(), 100*time.Millisecond, newMw) -// -// // expect our rate limited peer callback to be invoked once -// require.Equal(m.T(), uint64(1), rateLimits.Load()) -//} +func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { + //limiter limit will be set up to 1000 bytes/sec + limit := rate.Limit(1000) + + //burst per interval + burst := 1000 + + // we only expect messages from the first middleware on the test suite + expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) + require.NoError(m.T(), err) + + // setup bandwidth rate limiter + bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) + + // the onRateLimit call back we will use to keep track of how many times a rate limit happens + // after 5 rate limits we will close ch. + ch := make(chan struct{}) + rateLimits := atomic.NewUint64(0) + onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { + require.Equal(m.T(), reason, ratelimit.ReasonBandwidth.String()) + + // we only expect messages from the first middleware on the test suite + require.NoError(m.T(), err) + require.Equal(m.T(), expectedPID, peerID) + // update hook calls + rateLimits.Inc() + close(ch) + } + + consumer := testutils.NewRateLimiterConsumer(onRateLimit) + distributor := ratelimit.NewUnicastRateLimiterDistributor() + distributor.AddConsumer(consumer) + opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} + rateLimiters := ratelimit.NewRateLimiters(opts...) + + idProvider := unittest.NewUpdatableIDProvider(m.ids) + // create a new staked identity + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), + m.sporkId, + 1, + p2ptest.WithUnicastRateLimitDistributor(distributor), + p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { + // create connection gater, connection gater will refuse connections from rate limited nodes + if bandwidthRateLimiter.IsRateLimited(pid) { + return fmt.Errorf("rate-limited peer") + } + + return nil + }))) + idProvider.SetIdentities(append(m.ids, ids...)) + m.providers[0].SetIdentities(append(m.ids, ids...)) + + netCfg := testutils.NetworkConfigFixture( + m.T(), + *ids[0], + idProvider, + m.sporkId, + libP2PNodes[0]) + newNet, err := p2pnet.NewNetwork( + netCfg, + p2pnet.WithUnicastRateLimiters(rateLimiters), + p2pnet.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter))) + require.NoError(m.T(), err) + require.Len(m.T(), ids, 1) + newId := ids[0] + + ctx, cancel := context.WithCancel(m.mwCtx) + irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) + + testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) + + testutils.StartNetworks(irrecoverableCtx, m.T(), []network.Network{newNet}, 1*time.Second) + unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + + // registers an engine on the new network so that it can receive messages on the TestNetworkChannel + newEngine := &mocknetwork.MessageProcessor{} + _, err = newNet.Register(channels.TestNetworkChannel, newEngine) + require.NoError(m.T(), err) + newEngine.On("Process", channels.TestNetworkChannel, m.ids[0].NodeID, mockery.Anything).Return(nil) + + idList := flow.IdentityList(append(m.ids, newId)) + + // needed to enable ID translation + m.providers[0].SetIdentities(idList) + + // update the addresses + m.networks[0].UpdateNodeAddresses() + + // add our sender node as a direct peer to our receiving node, this allows us to ensure + // that connections to peers that are rate limited are completely prune. IsConnected will + // return true only if the node is a direct peer of the other, after rate limiting this direct + // peer should be removed by the peer manager. + p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) + + // create message with about 400bytes (300 random bytes + 100bytes message info) + b := make([]byte, 300) + for i := range b { + b[i] = byte('X') + } + // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited + // as it is more than our allowed bandwidth of 1000 bytes. + con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(m.T(), err) + for i := 0; i < 3; i++ { + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: string(b), + }, newId.NodeID) + require.NoError(m.T(), err) + } + + // wait for all rate limits before shutting down middleware + unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") + + // sleep for 1 seconds to allow connection pruner to prune connections + time.Sleep(1 * time.Second) + + // ensure connection to rate limited peer is pruned + p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + + // eventually the rate limited node should be able to reconnect and send messages + require.Eventually(m.T(), func() bool { + err = con0.Unicast(&libp2pmessage.TestMessage{ + Text: "", + }, newId.NodeID) + return err == nil + }, 3*time.Second, 100*time.Millisecond) + + // shutdown our middleware so that each message can be processed + cancel() + unittest.RequireComponentsDoneBefore(m.T(), 100*time.Millisecond, newNet) + + // expect our rate limited peer callback to be invoked once + require.Equal(m.T(), uint64(1), rateLimits.Load()) +} func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvider) *mocknetwork.Overlay { overlay := &mocknetwork.Overlay{} From c4f4e4d7733316b0396cfa81f5898b85a4efedba Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:47:11 -0400 Subject: [PATCH 761/815] fixes BlobServiceTestSuite --- network/test/blob_service_test.go | 489 +++++++++++++++--------------- 1 file changed, 247 insertions(+), 242 deletions(-) diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 338ef9d1d5d..ee3ea530219 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -1,244 +1,249 @@ package test -// -//import ( -// "context" -// "fmt" -// "testing" -// "time" -// -// "github.com/ipfs/go-cid" -// "github.com/ipfs/go-datastore" -// "github.com/ipfs/go-datastore/sync" -// blockstore "github.com/ipfs/go-ipfs-blockstore" -// "github.com/stretchr/testify/require" -// "github.com/stretchr/testify/suite" -// "go.uber.org/atomic" -// -// "github.com/onflow/flow-go/network/p2p/connection" -// "github.com/onflow/flow-go/network/p2p/dht" -// p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" -// p2ptest "github.com/onflow/flow-go/network/p2p/test" -// "github.com/onflow/flow-go/utils/unittest" -// -// "github.com/onflow/flow-go/model/flow" -// "github.com/onflow/flow-go/module/blobs" -// "github.com/onflow/flow-go/module/irrecoverable" -// "github.com/onflow/flow-go/module/util" -// "github.com/onflow/flow-go/network" -// "github.com/onflow/flow-go/network/channels" -// "github.com/onflow/flow-go/network/internal/testutils" -//) -// -//// conditionalTopology is a topology that behaves like the underlying topology when the condition is true, -//// otherwise returns an empty identity list. -//type conditionalTopology struct { -// top network.Topology -// condition func() bool -//} -// -//var _ network.Topology = (*conditionalTopology)(nil) -// -//func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList { -// if t.condition() { -// return t.top.Fanout(ids) -// } else { -// return flow.IdentityList{} -// } -//} -// -//type BlobServiceTestSuite struct { -// suite.Suite -// -// cancel context.CancelFunc -// networks []network.Network -// blobServices []network.BlobService -// datastores []datastore.Batching -// blobCids []cid.Cid -// numNodes int -//} -// -//func TestBlobService(t *testing.T) { -// t.Parallel() -// suite.Run(t, new(BlobServiceTestSuite)) -//} -// -//func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) { -// ctx, cancel := context.WithTimeout(context.Background(), time.Second) -// defer cancel() -// suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob)) -//} -// -//func (suite *BlobServiceTestSuite) SetupTest() { -// suite.numNodes = 3 -// -// // Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without -// // race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen -// // on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other. -// // See https://github.com/ipfs/go-bitswap/issues/525 for more details. -// topologyActive := atomic.NewBool(false) -// -// ctx, cancel := context.WithCancel(context.Background()) -// suite.cancel = cancel -// -// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) -// -// sporkId := unittest.IdentifierFixture() -// ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), -// sporkId, -// suite.numNodes, -// p2ptest.WithDHTOptions(dht.AsServer()), -// p2ptest.WithPeerManagerEnabled(&p2pconfig.PeerManagerConfig{ -// UpdateInterval: 1 * time.Second, -// ConnectionPruning: true, -// ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), -// }, nil)) -// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) -// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 100*time.Millisecond) -// -// blobExchangeChannel := channels.Channel("blob-exchange") -// -// for i, net := range suite.networks { -// ds := sync.MutexWrap(datastore.NewMapDatastore()) -// suite.datastores = append(suite.datastores, ds) -// blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i))) -// suite.blobCids = append(suite.blobCids, blob.Cid()) -// suite.putBlob(ds, blob) -// blobService, err := net.RegisterBlobService(blobExchangeChannel, ds) -// suite.Require().NoError(err) -// unittest.RequireCloseBefore(suite.T(), blobService.Ready(), 100*time.Millisecond, "blob service not ready") -// suite.blobServices = append(suite.blobServices, blobService) -// } -// -// // let nodes connect to each other only after they are all listening on Bitswap -// topologyActive.Store(true) -// suite.Require().Eventually(func() bool { -// for i, libp2pNode := range nodes { -// for j := i + 1; j < suite.numNodes; j++ { -// connected, err := libp2pNode.IsConnected(nodes[j].ID()) -// require.NoError(suite.T(), err) -// if !connected { -// return false -// } -// } -// } -// return true -// }, 3*time.Second, 100*time.Millisecond) -//} -// -//func (suite *BlobServiceTestSuite) TearDownTest() { -// suite.cancel() -// -// netDoneChans := make([]<-chan struct{}, len(suite.networks)) -// for i, net := range suite.networks { -// netDoneChans[i] = net.Done() -// } -// <-util.AllClosed(netDoneChans...) -// -// suite.networks = nil -// suite.cancel = nil -// suite.blobServices = nil -// suite.datastores = nil -// suite.blobCids = nil -//} -// -//func (suite *BlobServiceTestSuite) TestGetBlobs() { -// for i, bex := range suite.blobServices { -// // check that we can get all other blobs -// var blobsToGet []cid.Cid -// unreceivedBlobs := make(map[cid.Cid]struct{}) -// for j, blobCid := range suite.blobCids { -// if j != i { -// blobsToGet = append(blobsToGet, blobCid) -// unreceivedBlobs[blobCid] = struct{}{} -// } -// } -// -// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -// defer cancel() -// -// blobs := bex.GetBlobs(ctx, blobsToGet) -// -// for blob := range blobs { -// delete(unreceivedBlobs, blob.Cid()) -// } -// -// for c := range unreceivedBlobs { -// suite.T().Errorf("Blob %v not received by node %v", c, i) -// } -// } -//} -// -//func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() { -// for i, bex := range suite.blobServices { -// // check that we can get all other blobs in a single session -// blobsToGet := make(map[cid.Cid]struct{}) -// for j, blobCid := range suite.blobCids { -// if j != i { -// blobsToGet[blobCid] = struct{}{} -// } -// } -// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -// defer cancel() -// session := bex.GetSession(ctx) -// for blobCid := range blobsToGet { -// _, err := session.GetBlob(ctx, blobCid) -// suite.Assert().NoError(err) -// } -// } -//} -// -//func (suite *BlobServiceTestSuite) TestHas() { -// var blobChans []<-chan blobs.Blob -// unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices)) -// -// for i, bex := range suite.blobServices { -// unreceivedBlobs[i] = make(map[cid.Cid]struct{}) -// // check that peers are notified when we have a new blob -// var blobsToGet []cid.Cid -// for j := 0; j < suite.numNodes; j++ { -// if j != i { -// blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))) -// blobsToGet = append(blobsToGet, blob.Cid()) -// unreceivedBlobs[i][blob.Cid()] = struct{}{} -// } -// } -// -// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -// defer cancel() -// -// blobs := bex.GetBlobs(ctx, blobsToGet) -// blobChans = append(blobChans, blobs) -// } -// -// // check that blobs are not received until Has is called by the server -// suite.Require().Never(func() bool { -// for _, blobChan := range blobChans { -// select { -// case _, ok := <-blobChan: -// if ok { -// return true -// } -// default: -// } -// } -// return false -// }, time.Second, 100*time.Millisecond) -// -// for i, bex := range suite.blobServices { -// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -// defer cancel() -// -// err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))) -// suite.Require().NoError(err) -// } -// -// for i, blobs := range blobChans { -// for blob := range blobs { -// delete(unreceivedBlobs[i], blob.Cid()) -// } -// for c := range unreceivedBlobs[i] { -// suite.T().Errorf("blob %v not received by node %v", c, i) -// } -// } -//} +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/sync" + blockstore "github.com/ipfs/go-ipfs-blockstore" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "go.uber.org/atomic" + + "github.com/onflow/flow-go/network/p2p/connection" + "github.com/onflow/flow-go/network/p2p/dht" + p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" + p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/blobs" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/util" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/testutils" +) + +// conditionalTopology is a topology that behaves like the underlying topology when the condition is true, +// otherwise returns an empty identity list. +type conditionalTopology struct { + top network.Topology + condition func() bool +} + +var _ network.Topology = (*conditionalTopology)(nil) + +func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList { + if t.condition() { + return t.top.Fanout(ids) + } else { + return flow.IdentityList{} + } +} + +type BlobServiceTestSuite struct { + suite.Suite + + cancel context.CancelFunc + networks []*p2p.Network + blobServices []network.BlobService + datastores []datastore.Batching + blobCids []cid.Cid + numNodes int +} + +func TestBlobService(t *testing.T) { + t.Parallel() + suite.Run(t, new(BlobServiceTestSuite)) +} + +func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob)) +} + +func (suite *BlobServiceTestSuite) SetupTest() { + suite.numNodes = 3 + + // Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without + // race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen + // on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other. + // See https://github.com/ipfs/go-bitswap/issues/525 for more details. + topologyActive := atomic.NewBool(false) + + ctx, cancel := context.WithCancel(context.Background()) + suite.cancel = cancel + + signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + + sporkId := unittest.IdentifierFixture() + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), + sporkId, + suite.numNodes, + p2ptest.WithDHTOptions(dht.AsServer()), + p2ptest.WithPeerManagerEnabled(&p2pconfig.PeerManagerConfig{ + UpdateInterval: 1 * time.Second, + ConnectionPruning: true, + ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), + }, nil)) + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) + // starts the nodes and networks + testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + for _, net := range suite.networks { + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) + } + + blobExchangeChannel := channels.Channel("blob-exchange") + + for i, net := range suite.networks { + ds := sync.MutexWrap(datastore.NewMapDatastore()) + suite.datastores = append(suite.datastores, ds) + blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i))) + suite.blobCids = append(suite.blobCids, blob.Cid()) + suite.putBlob(ds, blob) + blobService, err := net.RegisterBlobService(blobExchangeChannel, ds) + suite.Require().NoError(err) + unittest.RequireCloseBefore(suite.T(), blobService.Ready(), 100*time.Millisecond, "blob service not ready") + suite.blobServices = append(suite.blobServices, blobService) + } + + // let nodes connect to each other only after they are all listening on Bitswap + topologyActive.Store(true) + suite.Require().Eventually(func() bool { + for i, libp2pNode := range nodes { + for j := i + 1; j < suite.numNodes; j++ { + connected, err := libp2pNode.IsConnected(nodes[j].ID()) + require.NoError(suite.T(), err) + if !connected { + return false + } + } + } + return true + }, 3*time.Second, 100*time.Millisecond) +} + +func (suite *BlobServiceTestSuite) TearDownTest() { + suite.cancel() + + netDoneChans := make([]<-chan struct{}, len(suite.networks)) + for i, net := range suite.networks { + netDoneChans[i] = net.Done() + } + <-util.AllClosed(netDoneChans...) + + suite.networks = nil + suite.cancel = nil + suite.blobServices = nil + suite.datastores = nil + suite.blobCids = nil +} + +func (suite *BlobServiceTestSuite) TestGetBlobs() { + for i, bex := range suite.blobServices { + // check that we can get all other blobs + var blobsToGet []cid.Cid + unreceivedBlobs := make(map[cid.Cid]struct{}) + for j, blobCid := range suite.blobCids { + if j != i { + blobsToGet = append(blobsToGet, blobCid) + unreceivedBlobs[blobCid] = struct{}{} + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + blobs := bex.GetBlobs(ctx, blobsToGet) + + for blob := range blobs { + delete(unreceivedBlobs, blob.Cid()) + } + + for c := range unreceivedBlobs { + suite.T().Errorf("Blob %v not received by node %v", c, i) + } + } +} + +func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() { + for i, bex := range suite.blobServices { + // check that we can get all other blobs in a single session + blobsToGet := make(map[cid.Cid]struct{}) + for j, blobCid := range suite.blobCids { + if j != i { + blobsToGet[blobCid] = struct{}{} + } + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + session := bex.GetSession(ctx) + for blobCid := range blobsToGet { + _, err := session.GetBlob(ctx, blobCid) + suite.Assert().NoError(err) + } + } +} + +func (suite *BlobServiceTestSuite) TestHas() { + var blobChans []<-chan blobs.Blob + unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices)) + + for i, bex := range suite.blobServices { + unreceivedBlobs[i] = make(map[cid.Cid]struct{}) + // check that peers are notified when we have a new blob + var blobsToGet []cid.Cid + for j := 0; j < suite.numNodes; j++ { + if j != i { + blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))) + blobsToGet = append(blobsToGet, blob.Cid()) + unreceivedBlobs[i][blob.Cid()] = struct{}{} + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + blobs := bex.GetBlobs(ctx, blobsToGet) + blobChans = append(blobChans, blobs) + } + + // check that blobs are not received until Has is called by the server + suite.Require().Never(func() bool { + for _, blobChan := range blobChans { + select { + case _, ok := <-blobChan: + if ok { + return true + } + default: + } + } + return false + }, time.Second, 100*time.Millisecond) + + for i, bex := range suite.blobServices { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))) + suite.Require().NoError(err) + } + + for i, blobs := range blobChans { + for blob := range blobs { + delete(unreceivedBlobs[i], blob.Cid()) + } + for c := range unreceivedBlobs[i] { + suite.T().Errorf("blob %v not received by node %v", c, i) + } + } +} From 573dea1bc5dedbe54507e90754ff7ba045ddb9b2 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:48:15 -0400 Subject: [PATCH 762/815] skips entire meshnet engine test suite --- network/test/meshengine_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 23bc1894dc0..6c31e21c422 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -50,6 +50,7 @@ type MeshEngineTestSuite struct { // TestMeshNetTestSuite runs all tests in this test suit func TestMeshNetTestSuite(t *testing.T) { + unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(MeshEngineTestSuite)) } @@ -181,14 +182,12 @@ func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { // TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using // the Multicast method of conduits. func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) } // TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the // Publish method of conduits. func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { - unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) } From d64cdeb390efbe01ad21a1398a2b1a7d15d28f46 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 10:53:30 -0400 Subject: [PATCH 763/815] fixes all mesh engine tests --- network/internal/testutils/testUtil.go | 67 +- network/test/blob_service_test.go | 4 +- network/test/meshengine_test.go | 1051 ++++++++++++------------ 3 files changed, 563 insertions(+), 559 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 0850e708a6b..940156c14f7 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -32,7 +32,6 @@ import ( "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/conduit" "github.com/onflow/flow-go/network/p2p/connection" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pnet" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/network/p2p/translator" @@ -146,39 +145,39 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int return identities, libP2PNodes } -// MiddlewareConfigFixture is a test helper that generates a middleware config for testing. -// Args: -// - t: the test instance. -// Returns: -// - a middleware config. -func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware.Config { - return &middleware.Config{ - Logger: unittest.Logger(), - SporkId: sporkId, - UnicastMessageTimeout: middleware.DefaultUnicastTimeout, - Codec: unittest.NetworkCodec(), - } -} - -// MiddlewareFixtures is a test helper that generates middlewares with the given identities and libp2p nodes. -// It also generates a list of UpdatableIDProvider that can be used to update the identities of the middlewares. -// The number of identities and libp2p nodes must be the same. -// Args: -// - identities: a list of flow identities that correspond to the libp2p nodes. -// - libP2PNodes: a list of libp2p nodes that correspond to the identities. -// - cfg: the middleware config. -// - opts: a list of middleware option functions. -// Returns: -// - a list of middlewares - one for each identity. -// - a list of UpdatableIDProvider - one for each identity. -func MiddlewareFixtures( - t *testing.T, - identities flow.IdentityList, - libP2PNodes []p2p.LibP2PNode, - cfg *middleware.Config, - opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { - return nil, nil -} +//// MiddlewareConfigFixture is a test helper that generates a middleware config for testing. +//// Args: +//// - t: the test instance. +//// Returns: +//// - a middleware config. +//func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware.Config { +// return &middleware.Config{ +// Logger: unittest.Logger(), +// SporkId: sporkId, +// UnicastMessageTimeout: middleware.DefaultUnicastTimeout, +// Codec: unittest.NetworkCodec(), +// } +//} + +//// MiddlewareFixtures is a test helper that generates middlewares with the given identities and libp2p nodes. +//// It also generates a list of UpdatableIDProvider that can be used to update the identities of the middlewares. +//// The number of identities and libp2p nodes must be the same. +//// Args: +//// - identities: a list of flow identities that correspond to the libp2p nodes. +//// - libP2PNodes: a list of libp2p nodes that correspond to the identities. +//// - cfg: the middleware config. +//// - opts: a list of middleware option functions. +//// Returns: +//// - a list of middlewares - one for each identity. +//// - a list of UpdatableIDProvider - one for each identity. +//func MiddlewareFixtures( +// t *testing.T, +// identities flow.IdentityList, +// libP2PNodes []p2p.LibP2PNode, +// cfg *middleware.Config, +// opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { +// return nil, nil +//} // NetworksFixture generates the network for the given middlewares func NetworksFixture(t *testing.T, diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index ee3ea530219..0c054d57075 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -17,7 +17,7 @@ import ( "github.com/onflow/flow-go/network/p2p/connection" "github.com/onflow/flow-go/network/p2p/dht" p2pconfig "github.com/onflow/flow-go/network/p2p/p2pbuilder/config" - p2p "github.com/onflow/flow-go/network/p2p/p2pnet" + "github.com/onflow/flow-go/network/p2p/p2pnet" p2ptest "github.com/onflow/flow-go/network/p2p/test" "github.com/onflow/flow-go/utils/unittest" @@ -51,7 +51,7 @@ type BlobServiceTestSuite struct { suite.Suite cancel context.CancelFunc - networks []*p2p.Network + networks []*p2pnet.Network blobServices []network.BlobService datastores []datastore.Batching blobCids []cid.Cid diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 0803e132dbd..4d24ae8b062 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -1,525 +1,530 @@ package test -// -//import ( -// "context" -// "fmt" -// "math/rand" -// "os" -// "strconv" -// "strings" -// "sync" -// "testing" -// "time" -// -// "github.com/ipfs/go-log" -// pubsub "github.com/libp2p/go-libp2p-pubsub" -// "github.com/rs/zerolog" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/require" -// "github.com/stretchr/testify/suite" -// -// "github.com/onflow/flow-go/config" -// "github.com/onflow/flow-go/model/flow" -// "github.com/onflow/flow-go/model/flow/filter" -// "github.com/onflow/flow-go/model/libp2p/message" -// "github.com/onflow/flow-go/module/irrecoverable" -// "github.com/onflow/flow-go/module/metrics" -// "github.com/onflow/flow-go/module/observable" -// "github.com/onflow/flow-go/network" -// "github.com/onflow/flow-go/network/channels" -// "github.com/onflow/flow-go/network/internal/testutils" -// "github.com/onflow/flow-go/network/p2p" -// "github.com/onflow/flow-go/network/p2p/middleware" -// "github.com/onflow/flow-go/network/p2p/p2pnode" -// p2ptest "github.com/onflow/flow-go/network/p2p/test" -// "github.com/onflow/flow-go/utils/unittest" -//) -// -//// MeshEngineTestSuite evaluates the message delivery functionality for the overlay -//// of engines over a complete graph -//type MeshEngineTestSuite struct { -// suite.Suite -// testutils.ConduitWrapper // used as a wrapper around conduit methods -// networks []network.Network // used to keep track of the networks -// libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes -// ids flow.IdentityList // used to keep track of the identifiers associated with networks -// obs chan string // used to keep track of Protect events tagged by pubsub messages -// cancel context.CancelFunc -//} -// -//// TestMeshNetTestSuite runs all tests in this test suit -//func TestMeshNetTestSuite(t *testing.T) { -// suite.Run(t, new(MeshEngineTestSuite)) -//} -// -//// SetupTest is executed prior to each test in this test suite. It creates and initializes -//// a set of network instances, sets up connection managers, nodes, identities, observables, etc. -//// This setup ensures that all necessary configurations are in place before running the tests. -//func (suite *MeshEngineTestSuite) SetupTest() { -// // defines total number of nodes in our network (minimum 3 needed to use 1-k messaging) -// const count = 10 -// logger := zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) -// log.SetAllLoggers(log.LevelError) -// -// // set up a channel to receive pubsub tags from connManagers of the nodes -// peerChannel := make(chan string) -// -// // Tag Observables Usage Explanation: -// // The tagsObserver is used to observe connections tagged by pubsub messages. This is instrumental in understanding -// // the connectivity between different peers and verifying the formation of the mesh within this test suite. -// // Issues: -// // - Deviation from Production Code: The usage of tag observables here may not reflect the behavior in the production environment. -// // - Mask Issues in the Production Environment: The observables tied to testing might lead to behaviors or errors that are -// // masked or not evident within the actual production code. -// // TODO: Evaluate the necessity of tag observables in this test and consider addressing the deviation from production -// // code and potential mask issues. Evaluate the possibility of removing this part eventually. -// ob := tagsObserver{ -// tags: peerChannel, -// log: logger, -// } -// -// ctx, cancel := context.WithCancel(context.Background()) -// suite.cancel = cancel -// -// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) -// -// sporkId := unittest.IdentifierFixture() -// libP2PNodes := make([]p2p.LibP2PNode, 0) -// identities := make(flow.IdentityList, 0) -// tagObservables := make([]observable.Observable, 0) -// idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) -// defaultFlowConfig, err := config.DefaultConfig() -// require.NoError(suite.T(), err) -// opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} -// -// for i := 0; i < count; i++ { -// connManager, err := testutils.NewTagWatchingConnManager( -// unittest.Logger(), -// metrics.NewNoopCollector(), -// &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) -// require.NoError(suite.T(), err) -// -// opts = append(opts, p2ptest.WithConnectionManager(connManager)) -// node, nodeId := p2ptest.NodeFixture(suite.T(), -// sporkId, -// suite.T().Name(), -// idProvider, -// opts...) -// libP2PNodes = append(libP2PNodes, node) -// identities = append(identities, &nodeId) -// tagObservables = append(tagObservables, connManager) -// } -// idProvider.SetIdentities(identities) -// -// suite.libp2pNodes = libP2PNodes -// suite.ids = identities -// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.libp2pNodes) -// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.networks, 3*time.Second) -// -// for _, observableConnMgr := range tagObservables { -// observableConnMgr.Subscribe(&ob) -// } -// suite.obs = peerChannel -//} -// -//// TearDownTest closes the networks within a specified timeout -//func (suite *MeshEngineTestSuite) TearDownTest() { -// suite.cancel() -// testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) -// testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) -//} -// -//// TestAllToAll_Publish evaluates the network of mesh engines against allToAllScenario scenario. -//// Network instances during this test use their Publish method to disseminate messages. -//func (suite *MeshEngineTestSuite) TestAllToAll_Publish() { -// suite.allToAllScenario(suite.Publish) -//} -// -//// TestAllToAll_Multicast evaluates the network of mesh engines against allToAllScenario scenario. -//// Network instances during this test use their Multicast method to disseminate messages. -//func (suite *MeshEngineTestSuite) TestAllToAll_Multicast() { -// suite.allToAllScenario(suite.Multicast) -//} -// -//// TestAllToAll_Unicast evaluates the network of mesh engines against allToAllScenario scenario. -//// Network instances during this test use their Unicast method to disseminate messages. -//func (suite *MeshEngineTestSuite) TestAllToAll_Unicast() { -// suite.allToAllScenario(suite.Unicast) -//} -// -//// TestTargetedValidators_Unicast tests if only the intended recipients in a 1-k messaging actually receive the message. -//// The messages are disseminated through the Unicast method of conduits. -//func (suite *MeshEngineTestSuite) TestTargetedValidators_Unicast() { -// suite.targetValidatorScenario(suite.Unicast) -//} -// -//// TestTargetedValidators_Multicast tests if only the intended recipients in a 1-k messaging actually receive the -//// message. -//// The messages are disseminated through the Multicast method of conduits. -//func (suite *MeshEngineTestSuite) TestTargetedValidators_Multicast() { -// suite.targetValidatorScenario(suite.Multicast) -//} -// -//// TestTargetedValidators_Publish tests if only the intended recipients in a 1-k messaging actually receive the message. -//// The messages are disseminated through the Multicast method of conduits. -//func (suite *MeshEngineTestSuite) TestTargetedValidators_Publish() { -// suite.targetValidatorScenario(suite.Publish) -//} -// -//// TestMaxMessageSize_Unicast evaluates the messageSizeScenario scenario using -//// the Unicast method of conduits. -//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { -// suite.messageSizeScenario(suite.Unicast, middleware.DefaultMaxUnicastMsgSize) -//} -// -//// TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using -//// the Multicast method of conduits. -//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { -// suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) -//} -// -//// TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the -//// Publish method of conduits. -//func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { -// suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) -//} -// -//// TestUnregister_Publish tests that an engine cannot send any message using Publish -//// or receive any messages after the conduit is closed -//func (suite *MeshEngineTestSuite) TestUnregister_Publish() { -// suite.conduitCloseScenario(suite.Publish) -//} -// -//// TestUnregister_Publish tests that an engine cannot send any message using Multicast -//// or receive any messages after the conduit is closed -//func (suite *MeshEngineTestSuite) TestUnregister_Multicast() { -// suite.conduitCloseScenario(suite.Multicast) -//} -// -//// TestUnregister_Publish tests that an engine cannot send any message using Unicast -//// or receive any messages after the conduit is closed -//func (suite *MeshEngineTestSuite) TestUnregister_Unicast() { -// suite.conduitCloseScenario(suite.Unicast) -//} -// -//// allToAllScenario creates a complete mesh of the engines, where each engine x sends a -//// "hello from node x" to other engines. It then evaluates the correctness of message -//// delivery as well as the content of the messages. This scenario tests the capability of -//// the engines to communicate in a fully connected graph, ensuring both the reachability -//// of messages and the integrity of their contents. -//func (suite *MeshEngineTestSuite) allToAllScenario(send testutils.ConduitSendWrapperFunc) { -// // allows nodes to find each other in case of Mulitcast and Publish -// testutils.OptionalSleep(send) -// -// // creating engines -// count := len(suite.networks) -// engs := make([]*testutils.MeshEngine, 0) -// wg := sync.WaitGroup{} -// -// // logs[i][j] keeps the message that node i sends to node j -// logs := make(map[int][]string) -// for i := range suite.networks { -// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) -// engs = append(engs, eng) -// logs[i] = make([]string, 0) -// } -// -// // allow nodes to heartbeat and discover each other -// // each node will register ~D protect messages, where D is the default out-degree -// for i := 0; i < pubsub.GossipSubD*count; i++ { -// select { -// case <-suite.obs: -// case <-time.After(8 * time.Second): -// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") -// } -// } -// -// // Each node broadcasting a message to all others -// for i := range suite.networks { -// event := &message.TestMessage{ -// Text: fmt.Sprintf("hello from node %v", i), -// } -// -// // others keeps the identifier of all nodes except ith node -// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID))).NodeIDs() -// require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) -// wg.Add(count - 1) -// } -// -// // fires a goroutine for each engine that listens to incoming messages -// for i := range suite.networks { -// go func(e *testutils.MeshEngine) { -// for x := 0; x < count-1; x++ { -// <-e.Received -// wg.Done() -// } -// }(engs[i]) -// } -// -// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) -// -// // evaluates that all messages are received -// for index, e := range engs { -// // confirms the number of received messages at each node -// if len(e.Event) != (count - 1) { -// assert.Fail(suite.Suite.T(), -// fmt.Sprintf("Message reception mismatch at node %v. Expected: %v, Got: %v", index, count-1, len(e.Event))) -// } -// -// for i := 0; i < count-1; i++ { -// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) -// } -// -// // extracts failed messages -// receivedIndices, err := extractSenderID(count, e.Event, "hello from node") -// require.NoError(suite.Suite.T(), err) -// -// for j := 0; j < count; j++ { -// // evaluates self-gossip -// if j == index { -// assert.False(suite.Suite.T(), (receivedIndices)[index], fmt.Sprintf("self gossiped for node %v detected", index)) -// } -// // evaluates content -// if !(receivedIndices)[j] { -// assert.False(suite.Suite.T(), (receivedIndices)[index], -// fmt.Sprintf("Message not found in node #%v's messages. Expected: Message from node %v. Got: No message", index, j)) -// } -// } -// } -//} -// -//// targetValidatorScenario sends a single message from last node to the first half of the nodes -//// based on identifiers list. -//// It then verifies that only the intended recipients receive the message. -//// Message dissemination is done using the send wrapper of conduit. -//func (suite *MeshEngineTestSuite) targetValidatorScenario(send testutils.ConduitSendWrapperFunc) { -// // creating engines -// count := len(suite.networks) -// engs := make([]*testutils.MeshEngine, 0) -// wg := sync.WaitGroup{} -// -// for i := range suite.networks { -// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) -// engs = append(engs, eng) -// } -// -// // allow nodes to heartbeat and discover each other -// // each node will register ~D protect messages, where D is the default out-degree -// for i := 0; i < pubsub.GossipSubD*count; i++ { -// select { -// case <-suite.obs: -// case <-time.After(2 * time.Second): -// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") -// } -// } -// -// // choose half of the nodes as target -// allIds := suite.ids.NodeIDs() -// var targets []flow.Identifier -// // create a target list of half of the nodes -// for i := 0; i < len(allIds)/2; i++ { -// targets = append(targets, allIds[i]) -// } -// -// // node 0 broadcasting a message to all targets -// event := &message.TestMessage{ -// Text: "hello from node 0", -// } -// require.NoError(suite.Suite.T(), send(event, engs[len(engs)-1].Con, targets...)) -// -// // fires a goroutine for all engines to listens for the incoming message -// for i := 0; i < len(allIds)/2; i++ { -// wg.Add(1) -// go func(e *testutils.MeshEngine) { -// <-e.Received -// wg.Done() -// }(engs[i]) -// } -// -// unittest.AssertReturnsBefore(suite.T(), wg.Wait, 10*time.Second) -// -// // evaluates that all messages are received -// for index, e := range engs { -// if index < len(engs)/2 { -// assert.Len(suite.Suite.T(), e.Event, 1, fmt.Sprintf("message not received %v", index)) -// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) -// } else { -// assert.Len(suite.Suite.T(), e.Event, 0, fmt.Sprintf("message received when none was expected %v", index)) -// } -// } -//} -// -//// messageSizeScenario provides a scenario to check if a message of maximum permissible size can be sent -//// successfully. -//// It broadcasts a message from the first node to all the nodes in the identifiers list using send wrapper function. -//func (suite *MeshEngineTestSuite) messageSizeScenario(send testutils.ConduitSendWrapperFunc, size uint) { -// // creating engines -// count := len(suite.networks) -// engs := make([]*testutils.MeshEngine, 0) -// wg := sync.WaitGroup{} -// -// for i := range suite.networks { -// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) -// engs = append(engs, eng) -// } -// -// // allow nodes to heartbeat and discover each other -// // each node will register ~D protect messages per mesh setup, where D is the default out-degree -// for i := 0; i < pubsub.GossipSubD*count; i++ { -// select { -// case <-suite.obs: -// case <-time.After(8 * time.Second): -// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") -// } -// } -// // others keeps the identifier of all nodes except node that is sender. -// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[0].NodeID))).NodeIDs() -// -// // generates and sends an event of custom size to the network -// payload := testutils.NetworkPayloadFixture(suite.T(), size) -// event := &message.TestMessage{ -// Text: string(payload), -// } -// -// require.NoError(suite.T(), send(event, engs[0].Con, others...)) -// -// // fires a goroutine for all engines (except sender) to listen for the incoming message -// for _, eng := range engs[1:] { -// wg.Add(1) -// go func(e *testutils.MeshEngine) { -// <-e.Received -// wg.Done() -// }(eng) -// } -// -// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) -// -// // evaluates that all messages are received -// for index, e := range engs[1:] { -// assert.Len(suite.Suite.T(), e.Event, 1, "message not received by engine %d", index+1) -// assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) -// } -//} -// -//// conduitCloseScenario tests after a Conduit is closed, an engine cannot send or receive a message for that channel. -//func (suite *MeshEngineTestSuite) conduitCloseScenario(send testutils.ConduitSendWrapperFunc) { -// -// testutils.OptionalSleep(send) -// -// // creating engines -// count := len(suite.networks) -// engs := make([]*testutils.MeshEngine, 0) -// wg := sync.WaitGroup{} -// -// for i := range suite.networks { -// eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) -// engs = append(engs, eng) -// } -// -// // allow nodes to heartbeat and discover each other -// // each node will register ~D protect messages, where D is the default out-degree -// for i := 0; i < pubsub.GossipSubD*count; i++ { -// select { -// case <-suite.obs: -// case <-time.After(2 * time.Second): -// assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") -// } -// } -// -// // unregister a random engine from the test topic by calling close on it's conduit -// unregisterIndex := rand.Intn(count) -// err := engs[unregisterIndex].Con.Close() -// assert.NoError(suite.T(), err) -// -// // waits enough for peer manager to unsubscribe the node from the topic -// // while libp2p is unsubscribing the node, the topology gets unstable -// // and connections to the node may be refused (although very unlikely). -// time.Sleep(2 * time.Second) -// -// // each node attempts to broadcast a message to all others -// for i := range suite.networks { -// event := &message.TestMessage{ -// Text: fmt.Sprintf("hello from node %v", i), -// } -// -// // others keeps the identifier of all nodes except ith node and the node that unregistered from the topic. -// // nodes without valid topic registration for a channel will reject messages on that channel via unicast. -// others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID, suite.ids[unregisterIndex].NodeID))).NodeIDs() -// -// if i == unregisterIndex { -// // assert that unsubscribed engine cannot publish on that topic -// require.Error(suite.Suite.T(), send(event, engs[i].Con, others...)) -// continue -// } -// -// require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) -// } -// -// // fire a goroutine to listen for incoming messages for each engine except for the one which unregistered -// for i := range suite.networks { -// if i == unregisterIndex { -// continue -// } -// wg.Add(1) -// go func(e *testutils.MeshEngine) { -// expectedMsgCnt := count - 2 // count less self and unsubscribed engine -// for x := 0; x < expectedMsgCnt; x++ { -// <-e.Received -// } -// wg.Done() -// }(engs[i]) -// } -// -// // assert every one except the unsubscribed engine received the message -// unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 2*time.Second) -// -// // assert that the unregistered engine did not receive the message -// unregisteredEng := engs[unregisterIndex] -// assert.Emptyf(suite.T(), unregisteredEng.Received, "unregistered engine received the topic message") -//} -// -//// assertChannelReceived asserts that the given channel was received on the given engine -//func assertChannelReceived(t *testing.T, e *testutils.MeshEngine, channel channels.Channel) { -// unittest.AssertReturnsBefore(t, func() { -// assert.Equal(t, channel, <-e.Channel) -// }, 100*time.Millisecond) -//} -// -//// extractSenderID returns a bool array with the index i true if there is a message from node i in the provided messages. -//// enginesNum is the number of engines -//// events is the channel of received events -//// expectedMsgTxt is the common prefix among all the messages that we expect to receive, for example -//// we expect to receive "hello from node x" in this test, and then expectedMsgTxt is "hello form node" -//func extractSenderID(enginesNum int, events chan interface{}, expectedMsgTxt string) ([]bool, error) { -// indices := make([]bool, enginesNum) -// expectedMsgSize := len(expectedMsgTxt) -// for i := 0; i < enginesNum-1; i++ { -// var event interface{} -// select { -// case event = <-events: -// default: -// continue -// } -// echo := event.(*message.TestMessage) -// msg := echo.Text -// if len(msg) < expectedMsgSize { -// return nil, fmt.Errorf("invalid message format") -// } -// senderIndex := msg[expectedMsgSize:] -// senderIndex = strings.TrimLeft(senderIndex, " ") -// nodeID, err := strconv.Atoi(senderIndex) -// if err != nil { -// return nil, fmt.Errorf("could not extract the node id from: %v", msg) -// } -// -// if indices[nodeID] { -// return nil, fmt.Errorf("duplicate message reception: %v", msg) -// } -// -// if msg == fmt.Sprintf("%s %v", expectedMsgTxt, nodeID) { -// indices[nodeID] = true -// } -// } -// return indices, nil -//} +import ( + "context" + "fmt" + "math/rand" + "os" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/ipfs/go-log" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/config" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/module/observable" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/middleware" + "github.com/onflow/flow-go/network/p2p/p2pnet" + "github.com/onflow/flow-go/network/p2p/p2pnode" + p2ptest "github.com/onflow/flow-go/network/p2p/test" + "github.com/onflow/flow-go/utils/unittest" +) + +// MeshEngineTestSuite evaluates the message delivery functionality for the overlay +// of engines over a complete graph +type MeshEngineTestSuite struct { + suite.Suite + testutils.ConduitWrapper // used as a wrapper around conduit methods + networks []*p2pnet.Network // used to keep track of the networks + libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes + ids flow.IdentityList // used to keep track of the identifiers associated with networks + obs chan string // used to keep track of Protect events tagged by pubsub messages + cancel context.CancelFunc +} + +// TestMeshNetTestSuite runs all tests in this test suit +func TestMeshNetTestSuite(t *testing.T) { + suite.Run(t, new(MeshEngineTestSuite)) +} + +// SetupTest is executed prior to each test in this test suite. It creates and initializes +// a set of network instances, sets up connection managers, nodes, identities, observables, etc. +// This setup ensures that all necessary configurations are in place before running the tests. +func (suite *MeshEngineTestSuite) SetupTest() { + // defines total number of nodes in our network (minimum 3 needed to use 1-k messaging) + const count = 10 + logger := zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) + log.SetAllLoggers(log.LevelError) + + // set up a channel to receive pubsub tags from connManagers of the nodes + peerChannel := make(chan string) + + // Tag Observables Usage Explanation: + // The tagsObserver is used to observe connections tagged by pubsub messages. This is instrumental in understanding + // the connectivity between different peers and verifying the formation of the mesh within this test suite. + // Issues: + // - Deviation from Production Code: The usage of tag observables here may not reflect the behavior in the production environment. + // - Mask Issues in the Production Environment: The observables tied to testing might lead to behaviors or errors that are + // masked or not evident within the actual production code. + // TODO: Evaluate the necessity of tag observables in this test and consider addressing the deviation from production + // code and potential mask issues. Evaluate the possibility of removing this part eventually. + ob := tagsObserver{ + tags: peerChannel, + log: logger, + } + + ctx, cancel := context.WithCancel(context.Background()) + suite.cancel = cancel + + signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + + sporkId := unittest.IdentifierFixture() + libP2PNodes := make([]p2p.LibP2PNode, 0) + identities := make(flow.IdentityList, 0) + tagObservables := make([]observable.Observable, 0) + idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) + defaultFlowConfig, err := config.DefaultConfig() + require.NoError(suite.T(), err) + opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} + + for i := 0; i < count; i++ { + connManager, err := testutils.NewTagWatchingConnManager( + unittest.Logger(), + metrics.NewNoopCollector(), + &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) + require.NoError(suite.T(), err) + + opts = append(opts, p2ptest.WithConnectionManager(connManager)) + node, nodeId := p2ptest.NodeFixture(suite.T(), + sporkId, + suite.T().Name(), + idProvider, + opts...) + libP2PNodes = append(libP2PNodes, node) + identities = append(identities, &nodeId) + tagObservables = append(tagObservables, connManager) + } + idProvider.SetIdentities(identities) + + suite.libp2pNodes = libP2PNodes + suite.ids = identities + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.libp2pNodes) + // starts the nodes and networks + testutils.StartNodes(signalerCtx, suite.T(), suite.libp2pNodes, 1*time.Second) + for _, net := range suite.networks { + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) + } + + for _, observableConnMgr := range tagObservables { + observableConnMgr.Subscribe(&ob) + } + suite.obs = peerChannel +} + +// TearDownTest closes the networks within a specified timeout +func (suite *MeshEngineTestSuite) TearDownTest() { + suite.cancel() + testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) + testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) +} + +// TestAllToAll_Publish evaluates the network of mesh engines against allToAllScenario scenario. +// Network instances during this test use their Publish method to disseminate messages. +func (suite *MeshEngineTestSuite) TestAllToAll_Publish() { + suite.allToAllScenario(suite.Publish) +} + +// TestAllToAll_Multicast evaluates the network of mesh engines against allToAllScenario scenario. +// Network instances during this test use their Multicast method to disseminate messages. +func (suite *MeshEngineTestSuite) TestAllToAll_Multicast() { + suite.allToAllScenario(suite.Multicast) +} + +// TestAllToAll_Unicast evaluates the network of mesh engines against allToAllScenario scenario. +// Network instances during this test use their Unicast method to disseminate messages. +func (suite *MeshEngineTestSuite) TestAllToAll_Unicast() { + suite.allToAllScenario(suite.Unicast) +} + +// TestTargetedValidators_Unicast tests if only the intended recipients in a 1-k messaging actually receive the message. +// The messages are disseminated through the Unicast method of conduits. +func (suite *MeshEngineTestSuite) TestTargetedValidators_Unicast() { + suite.targetValidatorScenario(suite.Unicast) +} + +// TestTargetedValidators_Multicast tests if only the intended recipients in a 1-k messaging actually receive the +// message. +// The messages are disseminated through the Multicast method of conduits. +func (suite *MeshEngineTestSuite) TestTargetedValidators_Multicast() { + suite.targetValidatorScenario(suite.Multicast) +} + +// TestTargetedValidators_Publish tests if only the intended recipients in a 1-k messaging actually receive the message. +// The messages are disseminated through the Multicast method of conduits. +func (suite *MeshEngineTestSuite) TestTargetedValidators_Publish() { + suite.targetValidatorScenario(suite.Publish) +} + +// TestMaxMessageSize_Unicast evaluates the messageSizeScenario scenario using +// the Unicast method of conduits. +func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { + suite.messageSizeScenario(suite.Unicast, middleware.DefaultMaxUnicastMsgSize) +} + +// TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using +// the Multicast method of conduits. +func (suite *MeshEngineTestSuite) TestMaxMessageSize_Multicast() { + suite.messageSizeScenario(suite.Multicast, p2pnode.DefaultMaxPubSubMsgSize) +} + +// TestMaxMessageSize_Publish evaluates the messageSizeScenario scenario using the +// Publish method of conduits. +func (suite *MeshEngineTestSuite) TestMaxMessageSize_Publish() { + suite.messageSizeScenario(suite.Publish, p2pnode.DefaultMaxPubSubMsgSize) +} + +// TestUnregister_Publish tests that an engine cannot send any message using Publish +// or receive any messages after the conduit is closed +func (suite *MeshEngineTestSuite) TestUnregister_Publish() { + suite.conduitCloseScenario(suite.Publish) +} + +// TestUnregister_Publish tests that an engine cannot send any message using Multicast +// or receive any messages after the conduit is closed +func (suite *MeshEngineTestSuite) TestUnregister_Multicast() { + suite.conduitCloseScenario(suite.Multicast) +} + +// TestUnregister_Publish tests that an engine cannot send any message using Unicast +// or receive any messages after the conduit is closed +func (suite *MeshEngineTestSuite) TestUnregister_Unicast() { + suite.conduitCloseScenario(suite.Unicast) +} + +// allToAllScenario creates a complete mesh of the engines, where each engine x sends a +// "hello from node x" to other engines. It then evaluates the correctness of message +// delivery as well as the content of the messages. This scenario tests the capability of +// the engines to communicate in a fully connected graph, ensuring both the reachability +// of messages and the integrity of their contents. +func (suite *MeshEngineTestSuite) allToAllScenario(send testutils.ConduitSendWrapperFunc) { + // allows nodes to find each other in case of Mulitcast and Publish + testutils.OptionalSleep(send) + + // creating engines + count := len(suite.networks) + engs := make([]*testutils.MeshEngine, 0) + wg := sync.WaitGroup{} + + // logs[i][j] keeps the message that node i sends to node j + logs := make(map[int][]string) + for i := range suite.networks { + eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) + engs = append(engs, eng) + logs[i] = make([]string, 0) + } + + // allow nodes to heartbeat and discover each other + // each node will register ~D protect messages, where D is the default out-degree + for i := 0; i < pubsub.GossipSubD*count; i++ { + select { + case <-suite.obs: + case <-time.After(8 * time.Second): + assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") + } + } + + // Each node broadcasting a message to all others + for i := range suite.networks { + event := &message.TestMessage{ + Text: fmt.Sprintf("hello from node %v", i), + } + + // others keeps the identifier of all nodes except ith node + others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID))).NodeIDs() + require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) + wg.Add(count - 1) + } + + // fires a goroutine for each engine that listens to incoming messages + for i := range suite.networks { + go func(e *testutils.MeshEngine) { + for x := 0; x < count-1; x++ { + <-e.Received + wg.Done() + } + }(engs[i]) + } + + unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) + + // evaluates that all messages are received + for index, e := range engs { + // confirms the number of received messages at each node + if len(e.Event) != (count - 1) { + assert.Fail(suite.Suite.T(), + fmt.Sprintf("Message reception mismatch at node %v. Expected: %v, Got: %v", index, count-1, len(e.Event))) + } + + for i := 0; i < count-1; i++ { + assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) + } + + // extracts failed messages + receivedIndices, err := extractSenderID(count, e.Event, "hello from node") + require.NoError(suite.Suite.T(), err) + + for j := 0; j < count; j++ { + // evaluates self-gossip + if j == index { + assert.False(suite.Suite.T(), (receivedIndices)[index], fmt.Sprintf("self gossiped for node %v detected", index)) + } + // evaluates content + if !(receivedIndices)[j] { + assert.False(suite.Suite.T(), (receivedIndices)[index], + fmt.Sprintf("Message not found in node #%v's messages. Expected: Message from node %v. Got: No message", index, j)) + } + } + } +} + +// targetValidatorScenario sends a single message from last node to the first half of the nodes +// based on identifiers list. +// It then verifies that only the intended recipients receive the message. +// Message dissemination is done using the send wrapper of conduit. +func (suite *MeshEngineTestSuite) targetValidatorScenario(send testutils.ConduitSendWrapperFunc) { + // creating engines + count := len(suite.networks) + engs := make([]*testutils.MeshEngine, 0) + wg := sync.WaitGroup{} + + for i := range suite.networks { + eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) + engs = append(engs, eng) + } + + // allow nodes to heartbeat and discover each other + // each node will register ~D protect messages, where D is the default out-degree + for i := 0; i < pubsub.GossipSubD*count; i++ { + select { + case <-suite.obs: + case <-time.After(2 * time.Second): + assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") + } + } + + // choose half of the nodes as target + allIds := suite.ids.NodeIDs() + var targets []flow.Identifier + // create a target list of half of the nodes + for i := 0; i < len(allIds)/2; i++ { + targets = append(targets, allIds[i]) + } + + // node 0 broadcasting a message to all targets + event := &message.TestMessage{ + Text: "hello from node 0", + } + require.NoError(suite.Suite.T(), send(event, engs[len(engs)-1].Con, targets...)) + + // fires a goroutine for all engines to listens for the incoming message + for i := 0; i < len(allIds)/2; i++ { + wg.Add(1) + go func(e *testutils.MeshEngine) { + <-e.Received + wg.Done() + }(engs[i]) + } + + unittest.AssertReturnsBefore(suite.T(), wg.Wait, 10*time.Second) + + // evaluates that all messages are received + for index, e := range engs { + if index < len(engs)/2 { + assert.Len(suite.Suite.T(), e.Event, 1, fmt.Sprintf("message not received %v", index)) + assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) + } else { + assert.Len(suite.Suite.T(), e.Event, 0, fmt.Sprintf("message received when none was expected %v", index)) + } + } +} + +// messageSizeScenario provides a scenario to check if a message of maximum permissible size can be sent +// successfully. +// It broadcasts a message from the first node to all the nodes in the identifiers list using send wrapper function. +func (suite *MeshEngineTestSuite) messageSizeScenario(send testutils.ConduitSendWrapperFunc, size uint) { + // creating engines + count := len(suite.networks) + engs := make([]*testutils.MeshEngine, 0) + wg := sync.WaitGroup{} + + for i := range suite.networks { + eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) + engs = append(engs, eng) + } + + // allow nodes to heartbeat and discover each other + // each node will register ~D protect messages per mesh setup, where D is the default out-degree + for i := 0; i < pubsub.GossipSubD*count; i++ { + select { + case <-suite.obs: + case <-time.After(8 * time.Second): + assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") + } + } + // others keeps the identifier of all nodes except node that is sender. + others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[0].NodeID))).NodeIDs() + + // generates and sends an event of custom size to the network + payload := testutils.NetworkPayloadFixture(suite.T(), size) + event := &message.TestMessage{ + Text: string(payload), + } + + require.NoError(suite.T(), send(event, engs[0].Con, others...)) + + // fires a goroutine for all engines (except sender) to listen for the incoming message + for _, eng := range engs[1:] { + wg.Add(1) + go func(e *testutils.MeshEngine) { + <-e.Received + wg.Done() + }(eng) + } + + unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 30*time.Second) + + // evaluates that all messages are received + for index, e := range engs[1:] { + assert.Len(suite.Suite.T(), e.Event, 1, "message not received by engine %d", index+1) + assertChannelReceived(suite.T(), e, channels.TestNetworkChannel) + } +} + +// conduitCloseScenario tests after a Conduit is closed, an engine cannot send or receive a message for that channel. +func (suite *MeshEngineTestSuite) conduitCloseScenario(send testutils.ConduitSendWrapperFunc) { + + testutils.OptionalSleep(send) + + // creating engines + count := len(suite.networks) + engs := make([]*testutils.MeshEngine, 0) + wg := sync.WaitGroup{} + + for i := range suite.networks { + eng := testutils.NewMeshEngine(suite.Suite.T(), suite.networks[i], count-1, channels.TestNetworkChannel) + engs = append(engs, eng) + } + + // allow nodes to heartbeat and discover each other + // each node will register ~D protect messages, where D is the default out-degree + for i := 0; i < pubsub.GossipSubD*count; i++ { + select { + case <-suite.obs: + case <-time.After(2 * time.Second): + assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") + } + } + + // unregister a random engine from the test topic by calling close on it's conduit + unregisterIndex := rand.Intn(count) + err := engs[unregisterIndex].Con.Close() + assert.NoError(suite.T(), err) + + // waits enough for peer manager to unsubscribe the node from the topic + // while libp2p is unsubscribing the node, the topology gets unstable + // and connections to the node may be refused (although very unlikely). + time.Sleep(2 * time.Second) + + // each node attempts to broadcast a message to all others + for i := range suite.networks { + event := &message.TestMessage{ + Text: fmt.Sprintf("hello from node %v", i), + } + + // others keeps the identifier of all nodes except ith node and the node that unregistered from the topic. + // nodes without valid topic registration for a channel will reject messages on that channel via unicast. + others := suite.ids.Filter(filter.Not(filter.HasNodeID(suite.ids[i].NodeID, suite.ids[unregisterIndex].NodeID))).NodeIDs() + + if i == unregisterIndex { + // assert that unsubscribed engine cannot publish on that topic + require.Error(suite.Suite.T(), send(event, engs[i].Con, others...)) + continue + } + + require.NoError(suite.Suite.T(), send(event, engs[i].Con, others...)) + } + + // fire a goroutine to listen for incoming messages for each engine except for the one which unregistered + for i := range suite.networks { + if i == unregisterIndex { + continue + } + wg.Add(1) + go func(e *testutils.MeshEngine) { + expectedMsgCnt := count - 2 // count less self and unsubscribed engine + for x := 0; x < expectedMsgCnt; x++ { + <-e.Received + } + wg.Done() + }(engs[i]) + } + + // assert every one except the unsubscribed engine received the message + unittest.AssertReturnsBefore(suite.Suite.T(), wg.Wait, 2*time.Second) + + // assert that the unregistered engine did not receive the message + unregisteredEng := engs[unregisterIndex] + assert.Emptyf(suite.T(), unregisteredEng.Received, "unregistered engine received the topic message") +} + +// assertChannelReceived asserts that the given channel was received on the given engine +func assertChannelReceived(t *testing.T, e *testutils.MeshEngine, channel channels.Channel) { + unittest.AssertReturnsBefore(t, func() { + assert.Equal(t, channel, <-e.Channel) + }, 100*time.Millisecond) +} + +// extractSenderID returns a bool array with the index i true if there is a message from node i in the provided messages. +// enginesNum is the number of engines +// events is the channel of received events +// expectedMsgTxt is the common prefix among all the messages that we expect to receive, for example +// we expect to receive "hello from node x" in this test, and then expectedMsgTxt is "hello form node" +func extractSenderID(enginesNum int, events chan interface{}, expectedMsgTxt string) ([]bool, error) { + indices := make([]bool, enginesNum) + expectedMsgSize := len(expectedMsgTxt) + for i := 0; i < enginesNum-1; i++ { + var event interface{} + select { + case event = <-events: + default: + continue + } + echo := event.(*message.TestMessage) + msg := echo.Text + if len(msg) < expectedMsgSize { + return nil, fmt.Errorf("invalid message format") + } + senderIndex := msg[expectedMsgSize:] + senderIndex = strings.TrimLeft(senderIndex, " ") + nodeID, err := strconv.Atoi(senderIndex) + if err != nil { + return nil, fmt.Errorf("could not extract the node id from: %v", msg) + } + + if indices[nodeID] { + return nil, fmt.Errorf("duplicate message reception: %v", msg) + } + + if msg == fmt.Sprintf("%s %v", expectedMsgTxt, nodeID) { + indices[nodeID] = true + } + } + return indices, nil +} From 36f203f943b74f80929a8542dae8e1e1859b3c16 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:00:50 -0400 Subject: [PATCH 764/815] fixes all echo engine tests --- network/internal/testutils/testUtil.go | 12 - network/test/echoengine_test.go | 1200 ++++++++++++------------ network/test/epochtransition_test.go | 939 +++++++++--------- 3 files changed, 1073 insertions(+), 1078 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 940156c14f7..83604d9272a 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -25,7 +25,6 @@ import ( "github.com/onflow/flow-go/network" alspmgr "github.com/onflow/flow-go/network/alsp/manager" netcache "github.com/onflow/flow-go/network/cache" - "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/codec/cbor" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/netconf" @@ -264,17 +263,6 @@ func NetworkConfigFixture( return params } -// GenerateEngines generates MeshEngines for the given networks -func GenerateEngines(t *testing.T, nets []network.Network) []*MeshEngine { - count := len(nets) - engs := make([]*MeshEngine, count) - for i, n := range nets { - eng := NewMeshEngine(t, n, 100, channels.TestNetworkChannel) - engs[i] = eng - } - return engs -} - // StartNodesAndNetworks starts the provided networks and libp2p nodes, returning the irrecoverable error channel. // Arguments: // - ctx: the irrecoverable context to use for starting the nodes and networks. diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index aca74e5234a..3730ab25e99 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -1,600 +1,604 @@ package test -// -//import ( -// "context" -// "fmt" -// "strings" -// "sync" -// "testing" -// "time" -// -// "github.com/ipfs/go-log" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/require" -// "github.com/stretchr/testify/suite" -// -// "github.com/onflow/flow-go/model/flow" -// "github.com/onflow/flow-go/model/libp2p/message" -// "github.com/onflow/flow-go/module/irrecoverable" -// "github.com/onflow/flow-go/network" -// "github.com/onflow/flow-go/network/channels" -// "github.com/onflow/flow-go/network/internal/testutils" -// "github.com/onflow/flow-go/network/p2p" -// "github.com/onflow/flow-go/network/p2p/p2pnet" -// "github.com/onflow/flow-go/utils/unittest" -//) -// -//// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p -//// protocol stack. It creates two instances of a stubengine, connects them through network, and sends a -//// single message from one engine to the other one through different scenarios. -//type EchoEngineTestSuite struct { -// suite.Suite -// testutils.ConduitWrapper // used as a wrapper around conduit methods -// networks []*p2pnet.Network // used to keep track of the networks -// libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes -// ids flow.IdentityList // used to keep track of the identifiers associated with networks -// cancel context.CancelFunc -//} -// -//// Some tests are skipped in short mode to speedup the build. -// -//// TestStubEngineTestSuite runs all the test methods in this test suit -//func TestStubEngineTestSuite(t *testing.T) { -// suite.Run(t, new(EchoEngineTestSuite)) -//} -// -//func (suite *EchoEngineTestSuite) SetupTest() { -// const count = 2 -// log.SetAllLoggers(log.LevelError) -// -// ctx, cancel := context.WithCancel(context.Background()) -// suite.cancel = cancel -// -// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) -// -// // both nodes should be of the same role to get connected on epidemic dissemination -// var nodes []p2p.LibP2PNode -// sporkId := unittest.IdentifierFixture() -// -// suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) -// suite.libp2pNodes = nodes -// suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, nodes) -// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks, 3*time.Second) -//} -// -//// TearDownTest closes the networks within a specified timeout -//func (suite *EchoEngineTestSuite) TearDownTest() { -// suite.cancel() -// testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) -// testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) -//} -// -//// TestUnknownChannel evaluates that registering an engine with an unknown channel returns an error. -//// All channels should be registered as topics in engine.topicMap. -//func (suite *EchoEngineTestSuite) TestUnknownChannel() { -// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) -// _, err := suite.networks[0].Register("unknown-channel-id", e) -// require.Error(suite.T(), err) -//} -// -//// TestClusterChannel evaluates that registering a cluster channel is done without any error. -//func (suite *EchoEngineTestSuite) TestClusterChannel() { -// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) -// // creates a cluster channel -// clusterChannel := channels.SyncCluster(flow.Testnet) -// // registers engine with cluster channel -// _, err := suite.networks[0].Register(clusterChannel, e) -// // registering cluster channel should not cause an error -// require.NoError(suite.T(), err) -//} -// -//// TestDuplicateChannel evaluates that registering an engine with duplicate channel returns an error. -//func (suite *EchoEngineTestSuite) TestDuplicateChannel() { -// // creates an echo engine, which registers it on test network channel -// e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) -// -// // attempts to register the same engine again on test network channel which -// // should cause an error -// _, err := suite.networks[0].Register(channels.TestNetworkChannel, e) -// require.Error(suite.T(), err) -//} -// -//// TestEchoMultiMsgAsync_Publish tests sending multiple messages from sender to receiver -//// using the Publish method of their Conduit. -//// It also evaluates the correct reception of an echo message back for each send. -//// Sender and receiver are not synchronized -//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Publish() { -// // set to true for an echo expectation -// suite.multiMessageAsync(true, 10, suite.Publish) -//} -// -//// TestEchoMultiMsgAsync_Unicast tests sending multiple messages from sender to receiver -//// using the Unicast method of their Conduit. -//// It also evaluates the correct reception of an echo message back for each send. -//// Sender and receiver are not synchronized -//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { -// // set to true for an echo expectation -// suite.multiMessageAsync(true, 10, suite.Unicast) -//} -// -//// TestEchoMultiMsgAsync_Multicast tests sending multiple messages from sender to receiver -//// using the Multicast method of their Conduit. -//// It also evaluates the correct reception of an echo message back for each send. -//// Sender and receiver are not synchronized -//func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { -// // set to true for an echo expectation -// suite.multiMessageAsync(true, 10, suite.Multicast) -//} -// -//// TestDuplicateMessageSequential_Publish evaluates the correctness of network layer on deduplicating -//// the received messages over Publish method of nodes' Conduits. -//// Messages are delivered to the receiver in a sequential manner. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Publish() { -// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Publish") -// suite.duplicateMessageSequential(suite.Publish) -//} -// -//// TestDuplicateMessageSequential_Unicast evaluates the correctness of network layer on deduplicating -//// the received messages over Unicast method of nodes' Conduits. -//// Messages are delivered to the receiver in a sequential manner. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Unicast() { -// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Unicast") -// suite.duplicateMessageSequential(suite.Unicast) -//} -// -//// TestDuplicateMessageSequential_Multicast evaluates the correctness of network layer on deduplicating -//// the received messages over Multicast method of nodes' Conduits. -//// Messages are delivered to the receiver in a sequential manner. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() { -// unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast") -// suite.duplicateMessageSequential(suite.Multicast) -//} -// -//// TestDuplicateMessageParallel_Publish evaluates the correctness of network layer -//// on deduplicating the received messages via Publish method of nodes' Conduits. -//// Messages are delivered to the receiver in parallel via the Publish method of Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() { -// suite.duplicateMessageParallel(suite.Publish) -//} -// -//// TestDuplicateMessageParallel_Unicast evaluates the correctness of network layer -//// on deduplicating the received messages via Unicast method of nodes' Conduits. -//// Messages are delivered to the receiver in parallel via the Unicast method of Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { -// suite.duplicateMessageParallel(suite.Unicast) -//} -// -//// TestDuplicateMessageParallel_Multicast evaluates the correctness of network layer -//// on deduplicating the received messages via Multicast method of nodes' Conduits. -//// Messages are delivered to the receiver in parallel via the Multicast method of Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { -// suite.duplicateMessageParallel(suite.Multicast) -//} -// -//// TestDuplicateMessageDifferentChan_Publish evaluates the correctness of network layer -//// on deduplicating the received messages against different engine ids. In specific, the -//// desire behavior is that the deduplication should happen based on both eventID and channel. -//// Messages are sent via the Publish methods of the Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { -// suite.duplicateMessageDifferentChan(suite.Publish) -//} -// -//// TestDuplicateMessageDifferentChan_Unicast evaluates the correctness of network layer -//// on deduplicating the received messages against different engine ids. In specific, the -//// desire behavior is that the deduplication should happen based on both eventID and channel. -//// Messages are sent via the Unicast methods of the Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { -// suite.duplicateMessageDifferentChan(suite.Unicast) -//} -// -//// TestDuplicateMessageDifferentChan_Multicast evaluates the correctness of network layer -//// on deduplicating the received messages against different engine ids. In specific, the -//// desire behavior is that the deduplication should happen based on both eventID and channel. -//// Messages are sent via the Multicast methods of the Conduits. -//func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { -// suite.duplicateMessageDifferentChan(suite.Multicast) -//} -// -//// duplicateMessageSequential is a helper function that sends duplicate messages sequentially -//// from a receiver to the sender via the injected send wrapper function of conduit. -//func (suite *EchoEngineTestSuite) duplicateMessageSequential(send testutils.ConduitSendWrapperFunc) { -// sndID := 0 -// rcvID := 1 -// // registers engines in the network -// // sender's engine -// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) -// -// // receiver's engine -// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) -// -// // allow nodes to heartbeat and discover each other if using PubSub -// testutils.OptionalSleep(send) -// -// // Sends a message from sender to receiver -// event := &message.TestMessage{ -// Text: "hello", -// } -// -// // sends the same message 10 times -// for i := 0; i < 10; i++ { -// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) -// } -// -// time.Sleep(1 * time.Second) -// -// // receiver should only see the message once, and the rest should be dropped due to -// // duplication -// receiver.RLock() -// require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) -// require.Len(suite.Suite.T(), receiver.seen, 1) -// receiver.RUnlock() -//} -// -//// duplicateMessageParallel is a helper function that sends duplicate messages concurrent;u -//// from a receiver to the sender via the injected send wrapper function of conduit. -//func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.ConduitSendWrapperFunc) { -// sndID := 0 -// rcvID := 1 -// // registers engines in the network -// // sender's engine -// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) -// -// // receiver's engine -// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) -// -// // allow nodes to heartbeat and discover each other -// testutils.OptionalSleep(send) -// -// // Sends a message from sender to receiver -// event := &message.TestMessage{ -// Text: "hello", -// } -// -// // sends the same message 10 times -// wg := sync.WaitGroup{} -// for i := 0; i < 10; i++ { -// wg.Add(1) -// go func() { -// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) -// wg.Done() -// }() -// } -// unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not send message on time") -// -// require.Eventually(suite.T(), func() bool { -// return len(receiver.seen) > 0 -// }, 3*time.Second, 500*time.Millisecond) -// -// // receiver should only see the message once, and the rest should be dropped due to -// // duplication -// receiver.RLock() -// require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) -// require.Len(suite.Suite.T(), receiver.seen, 1) -// receiver.RUnlock() -//} -// -//// duplicateMessageDifferentChan is a helper function that sends the same message from two distinct -//// sender engines to the two distinct receiver engines via the send wrapper function of Conduits. -//func (suite *EchoEngineTestSuite) duplicateMessageDifferentChan(send testutils.ConduitSendWrapperFunc) { -// const ( -// sndNode = iota -// rcvNode -// ) -// const ( -// channel1 = channels.TestNetworkChannel -// channel2 = channels.TestMetricsChannel -// ) -// // registers engines in the network -// // first type -// // sender'suite engine -// sender1 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel1, false, send) -// -// // receiver's engine -// receiver1 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel1, false, send) -// -// // second type -// // registers engines in the network -// // sender'suite engine -// sender2 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel2, false, send) -// -// // receiver's engine -// receiver2 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel2, false, send) -// -// // allow nodes to heartbeat and discover each other -// testutils.OptionalSleep(send) -// -// // Sends a message from sender to receiver -// event := &message.TestMessage{ -// Text: "hello", -// } -// -// // sends the same message 10 times on both channels -// wg := sync.WaitGroup{} -// for i := 0; i < 10; i++ { -// wg.Add(1) -// go func() { -// defer wg.Done() -// // sender1 to receiver1 on channel1 -// require.NoError(suite.Suite.T(), send(event, sender1.con, suite.ids[rcvNode].NodeID)) -// -// // sender2 to receiver2 on channel2 -// require.NoError(suite.Suite.T(), send(event, sender2.con, suite.ids[rcvNode].NodeID)) -// }() -// } -// unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not handle sending unicasts on time") -// time.Sleep(1 * time.Second) -// -// // each receiver should only see the message once, and the rest should be dropped due to -// // duplication -// receiver1.RLock() -// receiver2.RLock() -// require.Equal(suite.Suite.T(), 1, receiver1.seen[event.Text]) -// require.Equal(suite.Suite.T(), 1, receiver2.seen[event.Text]) -// -// require.Len(suite.Suite.T(), receiver1.seen, 1) -// require.Len(suite.Suite.T(), receiver2.seen, 1) -// receiver1.RUnlock() -// receiver2.RUnlock() -//} -// -//// singleMessage sends a single message from one network instance to the other one -//// it evaluates the correctness of implementation against correct delivery of the message. -//// in case echo is true, it also evaluates correct reception of the echo message from the receiver side -//func (suite *EchoEngineTestSuite) singleMessage(echo bool, send testutils.ConduitSendWrapperFunc) { -// sndID := 0 -// rcvID := 1 -// -// // registers engines in the network -// // sender's engine -// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) -// -// // receiver's engine -// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) -// -// // allow nodes to heartbeat and discover each other -// testutils.OptionalSleep(send) -// -// // Sends a message from sender to receiver -// event := &message.TestMessage{ -// Text: "hello", -// } -// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) -// -// // evaluates reception of echo request -// select { -// case <-receiver.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// receiver.RLock() -// require.NotNil(suite.Suite.T(), receiver.originID) -// require.NotNil(suite.Suite.T(), receiver.event) -// assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) -// receiver.RUnlock() -// -// assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) -// -// case <-time.After(10 * time.Second): -// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") -// } -// -// // evaluates echo back -// if echo { -// // evaluates reception of echo response -// select { -// case <-sender.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// sender.RLock() -// require.NotNil(suite.Suite.T(), sender.originID) -// require.NotNil(suite.Suite.T(), sender.event) -// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) -// sender.RUnlock() -// -// echoEvent := &message.TestMessage{ -// Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), -// } -// assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) -// -// case <-time.After(10 * time.Second): -// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") -// } -// } -//} -// -//// multiMessageSync sends a multiple messages from one network instance to the other one -//// it evaluates the correctness of implementation against correct delivery of the messages. -//// sender and receiver are sync over reception, i.e., sender sends one message at a time and -//// waits for its reception -//// count defines number of messages -//func (suite *EchoEngineTestSuite) multiMessageSync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { -// sndID := 0 -// rcvID := 1 -// // registers engines in the network -// // sender's engine -// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) -// -// // receiver's engine -// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) -// -// // allow nodes to heartbeat and discover each other -// testutils.OptionalSleep(send) -// -// for i := 0; i < count; i++ { -// // Send the message to receiver -// event := &message.TestMessage{ -// Text: fmt.Sprintf("hello%d", i), -// } -// // sends a message from sender to receiver using send wrapper function -// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) -// -// select { -// case <-receiver.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// receiver.RLock() -// require.NotNil(suite.Suite.T(), receiver.originID) -// require.NotNil(suite.Suite.T(), receiver.event) -// assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) -// receiver.RUnlock() -// -// assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) -// -// case <-time.After(2 * time.Second): -// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") -// } -// -// // evaluates echo back -// if echo { -// // evaluates reception of echo response -// select { -// case <-sender.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// sender.RLock() -// require.NotNil(suite.Suite.T(), sender.originID) -// require.NotNil(suite.Suite.T(), sender.event) -// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) -// -// receiver.RLock() -// echoEvent := &message.TestMessage{ -// Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), -// } -// assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) -// receiver.RUnlock() -// sender.RUnlock() -// -// case <-time.After(10 * time.Second): -// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") -// } -// } -// -// } -// -//} -// -//// multiMessageAsync sends a multiple messages from one network instance to the other one -//// it evaluates the correctness of implementation against correct delivery of the messages. -//// sender and receiver are async, i.e., sender sends all its message at blast -//// count defines number of messages -//func (suite *EchoEngineTestSuite) multiMessageAsync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { -// sndID := 0 -// rcvID := 1 -// -// // registers engines in the network -// // sender's engine -// sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) -// -// // receiver's engine -// receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) -// -// // allow nodes to heartbeat and discover each other -// testutils.OptionalSleep(send) -// -// // keeps track of async received messages at receiver side -// received := make(map[string]struct{}) -// -// // keeps track of async received echo messages at sender side -// // echorcv := make(map[string]struct{}) -// -// for i := 0; i < count; i++ { -// // Send the message to node 2 using the conduit of node 1 -// event := &message.TestMessage{ -// Text: fmt.Sprintf("hello%d", i), -// } -// require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[1].NodeID)) -// } -// -// for i := 0; i < count; i++ { -// select { -// case <-receiver.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// receiver.RLock() -// require.NotNil(suite.Suite.T(), receiver.originID) -// require.NotNil(suite.Suite.T(), receiver.event) -// assert.Equal(suite.Suite.T(), suite.ids[0].NodeID, receiver.originID) -// receiver.RUnlock() -// -// // wrap blocking channel reads with a timeout -// unittest.AssertReturnsBefore(suite.T(), func() { -// // evaluates proper reception of event -// // casts the received event at the receiver side -// rcvEvent, ok := (<-receiver.event).(*message.TestMessage) -// // evaluates correctness of casting -// require.True(suite.T(), ok) -// -// // evaluates content of received message -// // the content should not yet received and be unique -// _, rcv := received[rcvEvent.Text] -// assert.False(suite.T(), rcv) -// // marking event as received -// received[rcvEvent.Text] = struct{}{} -// -// // evaluates channel that message was received on -// assert.Equal(suite.T(), channels.TestNetworkChannel, <-receiver.channel) -// }, 100*time.Millisecond) -// -// case <-time.After(2 * time.Second): -// assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") -// } -// } -// -// for i := 0; i < count; i++ { -// // evaluates echo back -// if echo { -// // evaluates reception of echo response -// select { -// case <-sender.received: -// // evaluates reception of message at the other side -// // does not evaluate the content -// sender.RLock() -// require.NotNil(suite.Suite.T(), sender.originID) -// require.NotNil(suite.Suite.T(), sender.event) -// assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) -// sender.RUnlock() -// -// // wrap blocking channel reads with a timeout -// unittest.AssertReturnsBefore(suite.T(), func() { -// // evaluates proper reception of event -// // casts the received event at the receiver side -// rcvEvent, ok := (<-sender.event).(*message.TestMessage) -// // evaluates correctness of casting -// require.True(suite.T(), ok) -// // evaluates content of received echo message -// // the content should not yet received and be unique -// _, rcv := received[rcvEvent.Text] -// assert.False(suite.T(), rcv) -// // echo messages should start with prefix msg of receiver that echos back -// assert.True(suite.T(), strings.HasPrefix(rcvEvent.Text, receiver.echomsg)) -// // marking echo event as received -// received[rcvEvent.Text] = struct{}{} -// -// // evaluates channel that message was received on -// assert.Equal(suite.T(), channels.TestNetworkChannel, <-sender.channel) -// }, 100*time.Millisecond) -// -// case <-time.After(10 * time.Second): -// assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") -// } -// } -// } -//} -// -//// assertMessageReceived asserts that the given message was received on the given channel -//// for the given engine -//func assertMessageReceived(t *testing.T, e *EchoEngine, m *message.TestMessage, c channels.Channel) { -// // wrap blocking channel reads with a timeout -// unittest.AssertReturnsBefore(t, func() { -// // evaluates proper reception of event -// // casts the received event at the receiver side -// rcvEvent, ok := (<-e.event).(*message.TestMessage) -// // evaluates correctness of casting -// require.True(t, ok) -// // evaluates content of received message -// assert.Equal(t, m, rcvEvent) -// -// // evaluates channel that message was received on -// assert.Equal(t, c, <-e.channel) -// }, 100*time.Millisecond) -//} +import ( + "context" + "fmt" + "strings" + "sync" + "testing" + "time" + + "github.com/ipfs/go-log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pnet" + "github.com/onflow/flow-go/utils/unittest" +) + +// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p +// protocol stack. It creates two instances of a stubengine, connects them through network, and sends a +// single message from one engine to the other one through different scenarios. +type EchoEngineTestSuite struct { + suite.Suite + testutils.ConduitWrapper // used as a wrapper around conduit methods + networks []*p2pnet.Network // used to keep track of the networks + libp2pNodes []p2p.LibP2PNode // used to keep track of the libp2p nodes + ids flow.IdentityList // used to keep track of the identifiers associated with networks + cancel context.CancelFunc +} + +// Some tests are skipped in short mode to speedup the build. + +// TestStubEngineTestSuite runs all the test methods in this test suit +func TestStubEngineTestSuite(t *testing.T) { + suite.Run(t, new(EchoEngineTestSuite)) +} + +func (suite *EchoEngineTestSuite) SetupTest() { + const count = 2 + log.SetAllLoggers(log.LevelError) + + ctx, cancel := context.WithCancel(context.Background()) + suite.cancel = cancel + + signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + + // both nodes should be of the same role to get connected on epidemic dissemination + var nodes []p2p.LibP2PNode + sporkId := unittest.IdentifierFixture() + + suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + suite.libp2pNodes = nodes + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, nodes) + // starts the nodes and networks + testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + for _, net := range suite.networks { + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) + } +} + +// TearDownTest closes the networks within a specified timeout +func (suite *EchoEngineTestSuite) TearDownTest() { + suite.cancel() + testutils.StopComponents(suite.T(), suite.networks, 3*time.Second) + testutils.StopComponents(suite.T(), suite.libp2pNodes, 3*time.Second) +} + +// TestUnknownChannel evaluates that registering an engine with an unknown channel returns an error. +// All channels should be registered as topics in engine.topicMap. +func (suite *EchoEngineTestSuite) TestUnknownChannel() { + e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) + _, err := suite.networks[0].Register("unknown-channel-id", e) + require.Error(suite.T(), err) +} + +// TestClusterChannel evaluates that registering a cluster channel is done without any error. +func (suite *EchoEngineTestSuite) TestClusterChannel() { + e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) + // creates a cluster channel + clusterChannel := channels.SyncCluster(flow.Testnet) + // registers engine with cluster channel + _, err := suite.networks[0].Register(clusterChannel, e) + // registering cluster channel should not cause an error + require.NoError(suite.T(), err) +} + +// TestDuplicateChannel evaluates that registering an engine with duplicate channel returns an error. +func (suite *EchoEngineTestSuite) TestDuplicateChannel() { + // creates an echo engine, which registers it on test network channel + e := NewEchoEngine(suite.T(), suite.networks[0], 1, channels.TestNetworkChannel, false, suite.Unicast) + + // attempts to register the same engine again on test network channel which + // should cause an error + _, err := suite.networks[0].Register(channels.TestNetworkChannel, e) + require.Error(suite.T(), err) +} + +// TestEchoMultiMsgAsync_Publish tests sending multiple messages from sender to receiver +// using the Publish method of their Conduit. +// It also evaluates the correct reception of an echo message back for each send. +// Sender and receiver are not synchronized +func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Publish() { + // set to true for an echo expectation + suite.multiMessageAsync(true, 10, suite.Publish) +} + +// TestEchoMultiMsgAsync_Unicast tests sending multiple messages from sender to receiver +// using the Unicast method of their Conduit. +// It also evaluates the correct reception of an echo message back for each send. +// Sender and receiver are not synchronized +func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Unicast() { + // set to true for an echo expectation + suite.multiMessageAsync(true, 10, suite.Unicast) +} + +// TestEchoMultiMsgAsync_Multicast tests sending multiple messages from sender to receiver +// using the Multicast method of their Conduit. +// It also evaluates the correct reception of an echo message back for each send. +// Sender and receiver are not synchronized +func (suite *EchoEngineTestSuite) TestEchoMultiMsgAsync_Multicast() { + // set to true for an echo expectation + suite.multiMessageAsync(true, 10, suite.Multicast) +} + +// TestDuplicateMessageSequential_Publish evaluates the correctness of network layer on deduplicating +// the received messages over Publish method of nodes' Conduits. +// Messages are delivered to the receiver in a sequential manner. +func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Publish() { + unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Publish") + suite.duplicateMessageSequential(suite.Publish) +} + +// TestDuplicateMessageSequential_Unicast evaluates the correctness of network layer on deduplicating +// the received messages over Unicast method of nodes' Conduits. +// Messages are delivered to the receiver in a sequential manner. +func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Unicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Unicast") + suite.duplicateMessageSequential(suite.Unicast) +} + +// TestDuplicateMessageSequential_Multicast evaluates the correctness of network layer on deduplicating +// the received messages over Multicast method of nodes' Conduits. +// Messages are delivered to the receiver in a sequential manner. +func (suite *EchoEngineTestSuite) TestDuplicateMessageSequential_Multicast() { + unittest.SkipUnless(suite.T(), unittest.TEST_LONG_RUNNING, "covered by TestDuplicateMessageParallel_Multicast") + suite.duplicateMessageSequential(suite.Multicast) +} + +// TestDuplicateMessageParallel_Publish evaluates the correctness of network layer +// on deduplicating the received messages via Publish method of nodes' Conduits. +// Messages are delivered to the receiver in parallel via the Publish method of Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Publish() { + suite.duplicateMessageParallel(suite.Publish) +} + +// TestDuplicateMessageParallel_Unicast evaluates the correctness of network layer +// on deduplicating the received messages via Unicast method of nodes' Conduits. +// Messages are delivered to the receiver in parallel via the Unicast method of Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Unicast() { + suite.duplicateMessageParallel(suite.Unicast) +} + +// TestDuplicateMessageParallel_Multicast evaluates the correctness of network layer +// on deduplicating the received messages via Multicast method of nodes' Conduits. +// Messages are delivered to the receiver in parallel via the Multicast method of Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageParallel_Multicast() { + suite.duplicateMessageParallel(suite.Multicast) +} + +// TestDuplicateMessageDifferentChan_Publish evaluates the correctness of network layer +// on deduplicating the received messages against different engine ids. In specific, the +// desire behavior is that the deduplication should happen based on both eventID and channel. +// Messages are sent via the Publish methods of the Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Publish() { + suite.duplicateMessageDifferentChan(suite.Publish) +} + +// TestDuplicateMessageDifferentChan_Unicast evaluates the correctness of network layer +// on deduplicating the received messages against different engine ids. In specific, the +// desire behavior is that the deduplication should happen based on both eventID and channel. +// Messages are sent via the Unicast methods of the Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Unicast() { + suite.duplicateMessageDifferentChan(suite.Unicast) +} + +// TestDuplicateMessageDifferentChan_Multicast evaluates the correctness of network layer +// on deduplicating the received messages against different engine ids. In specific, the +// desire behavior is that the deduplication should happen based on both eventID and channel. +// Messages are sent via the Multicast methods of the Conduits. +func (suite *EchoEngineTestSuite) TestDuplicateMessageDifferentChan_Multicast() { + suite.duplicateMessageDifferentChan(suite.Multicast) +} + +// duplicateMessageSequential is a helper function that sends duplicate messages sequentially +// from a receiver to the sender via the injected send wrapper function of conduit. +func (suite *EchoEngineTestSuite) duplicateMessageSequential(send testutils.ConduitSendWrapperFunc) { + sndID := 0 + rcvID := 1 + // registers engines in the network + // sender's engine + sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) + + // receiver's engine + receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) + + // allow nodes to heartbeat and discover each other if using PubSub + testutils.OptionalSleep(send) + + // Sends a message from sender to receiver + event := &message.TestMessage{ + Text: "hello", + } + + // sends the same message 10 times + for i := 0; i < 10; i++ { + require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) + } + + time.Sleep(1 * time.Second) + + // receiver should only see the message once, and the rest should be dropped due to + // duplication + receiver.RLock() + require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) + require.Len(suite.Suite.T(), receiver.seen, 1) + receiver.RUnlock() +} + +// duplicateMessageParallel is a helper function that sends duplicate messages concurrent;u +// from a receiver to the sender via the injected send wrapper function of conduit. +func (suite *EchoEngineTestSuite) duplicateMessageParallel(send testutils.ConduitSendWrapperFunc) { + sndID := 0 + rcvID := 1 + // registers engines in the network + // sender's engine + sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, false, send) + + // receiver's engine + receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, false, send) + + // allow nodes to heartbeat and discover each other + testutils.OptionalSleep(send) + + // Sends a message from sender to receiver + event := &message.TestMessage{ + Text: "hello", + } + + // sends the same message 10 times + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) + wg.Done() + }() + } + unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not send message on time") + + require.Eventually(suite.T(), func() bool { + return len(receiver.seen) > 0 + }, 3*time.Second, 500*time.Millisecond) + + // receiver should only see the message once, and the rest should be dropped due to + // duplication + receiver.RLock() + require.Equal(suite.Suite.T(), 1, receiver.seen[event.Text]) + require.Len(suite.Suite.T(), receiver.seen, 1) + receiver.RUnlock() +} + +// duplicateMessageDifferentChan is a helper function that sends the same message from two distinct +// sender engines to the two distinct receiver engines via the send wrapper function of Conduits. +func (suite *EchoEngineTestSuite) duplicateMessageDifferentChan(send testutils.ConduitSendWrapperFunc) { + const ( + sndNode = iota + rcvNode + ) + const ( + channel1 = channels.TestNetworkChannel + channel2 = channels.TestMetricsChannel + ) + // registers engines in the network + // first type + // sender'suite engine + sender1 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel1, false, send) + + // receiver's engine + receiver1 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel1, false, send) + + // second type + // registers engines in the network + // sender'suite engine + sender2 := NewEchoEngine(suite.Suite.T(), suite.networks[sndNode], 10, channel2, false, send) + + // receiver's engine + receiver2 := NewEchoEngine(suite.Suite.T(), suite.networks[rcvNode], 10, channel2, false, send) + + // allow nodes to heartbeat and discover each other + testutils.OptionalSleep(send) + + // Sends a message from sender to receiver + event := &message.TestMessage{ + Text: "hello", + } + + // sends the same message 10 times on both channels + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // sender1 to receiver1 on channel1 + require.NoError(suite.Suite.T(), send(event, sender1.con, suite.ids[rcvNode].NodeID)) + + // sender2 to receiver2 on channel2 + require.NoError(suite.Suite.T(), send(event, sender2.con, suite.ids[rcvNode].NodeID)) + }() + } + unittest.RequireReturnsBefore(suite.T(), wg.Wait, 3*time.Second, "could not handle sending unicasts on time") + time.Sleep(1 * time.Second) + + // each receiver should only see the message once, and the rest should be dropped due to + // duplication + receiver1.RLock() + receiver2.RLock() + require.Equal(suite.Suite.T(), 1, receiver1.seen[event.Text]) + require.Equal(suite.Suite.T(), 1, receiver2.seen[event.Text]) + + require.Len(suite.Suite.T(), receiver1.seen, 1) + require.Len(suite.Suite.T(), receiver2.seen, 1) + receiver1.RUnlock() + receiver2.RUnlock() +} + +// singleMessage sends a single message from one network instance to the other one +// it evaluates the correctness of implementation against correct delivery of the message. +// in case echo is true, it also evaluates correct reception of the echo message from the receiver side +func (suite *EchoEngineTestSuite) singleMessage(echo bool, send testutils.ConduitSendWrapperFunc) { + sndID := 0 + rcvID := 1 + + // registers engines in the network + // sender's engine + sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) + + // receiver's engine + receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) + + // allow nodes to heartbeat and discover each other + testutils.OptionalSleep(send) + + // Sends a message from sender to receiver + event := &message.TestMessage{ + Text: "hello", + } + require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) + + // evaluates reception of echo request + select { + case <-receiver.received: + // evaluates reception of message at the other side + // does not evaluate the content + receiver.RLock() + require.NotNil(suite.Suite.T(), receiver.originID) + require.NotNil(suite.Suite.T(), receiver.event) + assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) + receiver.RUnlock() + + assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) + + case <-time.After(10 * time.Second): + assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") + } + + // evaluates echo back + if echo { + // evaluates reception of echo response + select { + case <-sender.received: + // evaluates reception of message at the other side + // does not evaluate the content + sender.RLock() + require.NotNil(suite.Suite.T(), sender.originID) + require.NotNil(suite.Suite.T(), sender.event) + assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) + sender.RUnlock() + + echoEvent := &message.TestMessage{ + Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), + } + assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) + + case <-time.After(10 * time.Second): + assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") + } + } +} + +// multiMessageSync sends a multiple messages from one network instance to the other one +// it evaluates the correctness of implementation against correct delivery of the messages. +// sender and receiver are sync over reception, i.e., sender sends one message at a time and +// waits for its reception +// count defines number of messages +func (suite *EchoEngineTestSuite) multiMessageSync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { + sndID := 0 + rcvID := 1 + // registers engines in the network + // sender's engine + sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) + + // receiver's engine + receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) + + // allow nodes to heartbeat and discover each other + testutils.OptionalSleep(send) + + for i := 0; i < count; i++ { + // Send the message to receiver + event := &message.TestMessage{ + Text: fmt.Sprintf("hello%d", i), + } + // sends a message from sender to receiver using send wrapper function + require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[rcvID].NodeID)) + + select { + case <-receiver.received: + // evaluates reception of message at the other side + // does not evaluate the content + receiver.RLock() + require.NotNil(suite.Suite.T(), receiver.originID) + require.NotNil(suite.Suite.T(), receiver.event) + assert.Equal(suite.Suite.T(), suite.ids[sndID].NodeID, receiver.originID) + receiver.RUnlock() + + assertMessageReceived(suite.T(), receiver, event, channels.TestNetworkChannel) + + case <-time.After(2 * time.Second): + assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") + } + + // evaluates echo back + if echo { + // evaluates reception of echo response + select { + case <-sender.received: + // evaluates reception of message at the other side + // does not evaluate the content + sender.RLock() + require.NotNil(suite.Suite.T(), sender.originID) + require.NotNil(suite.Suite.T(), sender.event) + assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) + + receiver.RLock() + echoEvent := &message.TestMessage{ + Text: fmt.Sprintf("%s: %s", receiver.echomsg, event.Text), + } + assertMessageReceived(suite.T(), sender, echoEvent, channels.TestNetworkChannel) + receiver.RUnlock() + sender.RUnlock() + + case <-time.After(10 * time.Second): + assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") + } + } + + } + +} + +// multiMessageAsync sends a multiple messages from one network instance to the other one +// it evaluates the correctness of implementation against correct delivery of the messages. +// sender and receiver are async, i.e., sender sends all its message at blast +// count defines number of messages +func (suite *EchoEngineTestSuite) multiMessageAsync(echo bool, count int, send testutils.ConduitSendWrapperFunc) { + sndID := 0 + rcvID := 1 + + // registers engines in the network + // sender's engine + sender := NewEchoEngine(suite.Suite.T(), suite.networks[sndID], 10, channels.TestNetworkChannel, echo, send) + + // receiver's engine + receiver := NewEchoEngine(suite.Suite.T(), suite.networks[rcvID], 10, channels.TestNetworkChannel, echo, send) + + // allow nodes to heartbeat and discover each other + testutils.OptionalSleep(send) + + // keeps track of async received messages at receiver side + received := make(map[string]struct{}) + + // keeps track of async received echo messages at sender side + // echorcv := make(map[string]struct{}) + + for i := 0; i < count; i++ { + // Send the message to node 2 using the conduit of node 1 + event := &message.TestMessage{ + Text: fmt.Sprintf("hello%d", i), + } + require.NoError(suite.Suite.T(), send(event, sender.con, suite.ids[1].NodeID)) + } + + for i := 0; i < count; i++ { + select { + case <-receiver.received: + // evaluates reception of message at the other side + // does not evaluate the content + receiver.RLock() + require.NotNil(suite.Suite.T(), receiver.originID) + require.NotNil(suite.Suite.T(), receiver.event) + assert.Equal(suite.Suite.T(), suite.ids[0].NodeID, receiver.originID) + receiver.RUnlock() + + // wrap blocking channel reads with a timeout + unittest.AssertReturnsBefore(suite.T(), func() { + // evaluates proper reception of event + // casts the received event at the receiver side + rcvEvent, ok := (<-receiver.event).(*message.TestMessage) + // evaluates correctness of casting + require.True(suite.T(), ok) + + // evaluates content of received message + // the content should not yet received and be unique + _, rcv := received[rcvEvent.Text] + assert.False(suite.T(), rcv) + // marking event as received + received[rcvEvent.Text] = struct{}{} + + // evaluates channel that message was received on + assert.Equal(suite.T(), channels.TestNetworkChannel, <-receiver.channel) + }, 100*time.Millisecond) + + case <-time.After(2 * time.Second): + assert.Fail(suite.Suite.T(), "sender failed to send a message to receiver") + } + } + + for i := 0; i < count; i++ { + // evaluates echo back + if echo { + // evaluates reception of echo response + select { + case <-sender.received: + // evaluates reception of message at the other side + // does not evaluate the content + sender.RLock() + require.NotNil(suite.Suite.T(), sender.originID) + require.NotNil(suite.Suite.T(), sender.event) + assert.Equal(suite.Suite.T(), suite.ids[rcvID].NodeID, sender.originID) + sender.RUnlock() + + // wrap blocking channel reads with a timeout + unittest.AssertReturnsBefore(suite.T(), func() { + // evaluates proper reception of event + // casts the received event at the receiver side + rcvEvent, ok := (<-sender.event).(*message.TestMessage) + // evaluates correctness of casting + require.True(suite.T(), ok) + // evaluates content of received echo message + // the content should not yet received and be unique + _, rcv := received[rcvEvent.Text] + assert.False(suite.T(), rcv) + // echo messages should start with prefix msg of receiver that echos back + assert.True(suite.T(), strings.HasPrefix(rcvEvent.Text, receiver.echomsg)) + // marking echo event as received + received[rcvEvent.Text] = struct{}{} + + // evaluates channel that message was received on + assert.Equal(suite.T(), channels.TestNetworkChannel, <-sender.channel) + }, 100*time.Millisecond) + + case <-time.After(10 * time.Second): + assert.Fail(suite.Suite.T(), "receiver failed to send an echo message back to sender") + } + } + } +} + +// assertMessageReceived asserts that the given message was received on the given channel +// for the given engine +func assertMessageReceived(t *testing.T, e *EchoEngine, m *message.TestMessage, c channels.Channel) { + // wrap blocking channel reads with a timeout + unittest.AssertReturnsBefore(t, func() { + // evaluates proper reception of event + // casts the received event at the receiver side + rcvEvent, ok := (<-e.event).(*message.TestMessage) + // evaluates correctness of casting + require.True(t, ok) + // evaluates content of received message + assert.Equal(t, m, rcvEvent) + + // evaluates channel that message was received on + assert.Equal(t, c, <-e.channel) + }, 100*time.Millisecond) +} diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 535f5be2021..90e27a0054b 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -1,470 +1,473 @@ package test -// -//import ( -// "context" -// "fmt" -// "math/rand" -// "os" -// "reflect" -// "runtime" -// "sync" -// "testing" -// "time" -// -// "github.com/ipfs/go-log" -// "github.com/rs/zerolog" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/mock" -// "github.com/stretchr/testify/require" -// "github.com/stretchr/testify/suite" -// -// "github.com/onflow/flow-go/model/flow" -// "github.com/onflow/flow-go/model/flow/filter" -// "github.com/onflow/flow-go/model/libp2p/message" -// "github.com/onflow/flow-go/module/irrecoverable" -// "github.com/onflow/flow-go/network" -// "github.com/onflow/flow-go/network/internal/testutils" -// "github.com/onflow/flow-go/network/p2p" -// mockprotocol "github.com/onflow/flow-go/state/protocol/mock" -// "github.com/onflow/flow-go/utils/unittest" -//) -// -//// MutableIdentityTableSuite tests that the networking layer responds correctly -//// to changes to the identity table. When nodes are added, we should update our -//// topology and accept connections from these new nodes. When nodes are removed -//// or ejected we should update our topology and restrict connections from these -//// nodes. -//type MutableIdentityTableSuite struct { -// suite.Suite -// testutils.ConduitWrapper -// testNodes testNodeList -// removedTestNodes testNodeList // test nodes which might have been removed from the mesh -// state *mockprotocol.State -// snapshot *mockprotocol.Snapshot -// logger zerolog.Logger -// cancels []context.CancelFunc -//} -// -//// testNode encapsulates the node state which includes its identity, middleware, network, -//// mesh engine and the id refresher -//type testNode struct { -// id *flow.Identity -// libp2pNode p2p.LibP2PNode -// mw network.Middleware -// net network.Network -// engine *testutils.MeshEngine -//} -// -//// testNodeList encapsulates a list of test node and -//// has functions to retrieve the different elements of the test nodes in a concurrency safe manner -//type testNodeList struct { -// sync.RWMutex -// nodes []testNode -//} -// -//func newTestNodeList() testNodeList { -// return testNodeList{} -//} -// -//func (t *testNodeList) append(node testNode) { -// t.Lock() -// defer t.Unlock() -// t.nodes = append(t.nodes, node) -//} -// -//func (t *testNodeList) remove() testNode { -// t.Lock() -// defer t.Unlock() -// // choose a random node to remove -// i := rand.Intn(len(t.nodes)) -// removedNode := t.nodes[i] -// t.nodes = append(t.nodes[:i], t.nodes[i+1:]...) -// return removedNode -//} -// -//func (t *testNodeList) ids() flow.IdentityList { -// t.RLock() -// defer t.RUnlock() -// ids := make(flow.IdentityList, len(t.nodes)) -// for i, node := range t.nodes { -// ids[i] = node.id -// } -// return ids -//} -// -//func (t *testNodeList) lastAdded() (testNode, error) { -// t.RLock() -// defer t.RUnlock() -// if len(t.nodes) > 0 { -// return t.nodes[len(t.nodes)-1], nil -// } -// return testNode{}, fmt.Errorf("node list empty") -//} -// -//func (t *testNodeList) engines() []*testutils.MeshEngine { -// t.RLock() -// defer t.RUnlock() -// engs := make([]*testutils.MeshEngine, len(t.nodes)) -// for i, node := range t.nodes { -// engs[i] = node.engine -// } -// return engs -//} -// -//func (t *testNodeList) networks() []network.Network { -// t.RLock() -// defer t.RUnlock() -// nets := make([]network.Network, len(t.nodes)) -// for i, node := range t.nodes { -// nets[i] = node.net -// } -// return nets -//} -// -//func (t *testNodeList) libp2pNodes() []p2p.LibP2PNode { -// t.RLock() -// defer t.RUnlock() -// nodes := make([]p2p.LibP2PNode, len(t.nodes)) -// for i, node := range t.nodes { -// nodes[i] = node.libp2pNode -// } -// return nodes -//} -// -//func TestMutableIdentityTable(t *testing.T) { -// unittest.SkipUnless(t, unittest.TEST_TODO, "broken test") -// suite.Run(t, new(MutableIdentityTableSuite)) -//} -// -//// signalIdentityChanged update IDs for all the current set of nodes (simulating an epoch) -//func (suite *MutableIdentityTableSuite) signalIdentityChanged() { -// for _, n := range suite.testNodes.nodes { -// n.mw.UpdateNodeAddresses() -// } -//} -// -//func (suite *MutableIdentityTableSuite) SetupTest() { -// suite.testNodes = newTestNodeList() -// suite.removedTestNodes = newTestNodeList() -// -// nodeCount := 10 -// suite.logger = zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) -// log.SetAllLoggers(log.LevelError) -// -// suite.setupStateMock() -// suite.addNodes(nodeCount) -// -// // simulate a start of an epoch by signaling a change in the identity table -// suite.signalIdentityChanged() -// -// // wait for two lip2p heatbeats for the nodes to discover each other and form the mesh -// time.Sleep(2 * time.Second) -//} -// -//// TearDownTest closes all the networks within a specified timeout -//func (suite *MutableIdentityTableSuite) TearDownTest() { -// for _, cancel := range suite.cancels { -// cancel() -// } -// networks := append(suite.testNodes.networks(), suite.removedTestNodes.networks()...) -// testutils.StopComponents(suite.T(), networks, 3*time.Second) -//} -// -//// setupStateMock setup state related mocks (all networks share the same state mock) -//func (suite *MutableIdentityTableSuite) setupStateMock() { -// final := unittest.BlockHeaderFixture() -// suite.state = new(mockprotocol.State) -// suite.snapshot = new(mockprotocol.Snapshot) -// suite.snapshot.On("Head").Return(&final, nil) -// suite.snapshot.On("Phase").Return(flow.EpochPhaseCommitted, nil) -// // return all the current list of ids for the state.Final.Identities call made by the network -// suite.snapshot.On("Identities", mock.Anything).Return( -// func(flow.IdentityFilter) flow.IdentityList { -// return suite.testNodes.ids() -// }, -// func(flow.IdentityFilter) error { return nil }) -// suite.state.On("Final").Return(suite.snapshot, nil) -//} -// -//// addNodes creates count many new nodes and appends them to the suite state variables -//func (suite *MutableIdentityTableSuite) addNodes(count int) { -// ctx, cancel := context.WithCancel(context.Background()) -// signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) -// -// // create the ids, middlewares and networks -// sporkId := unittest.IdentifierFixture() -// ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) -// mws, _ := testutils.MiddlewareFixtures( -// suite.T(), -// ids, -// nodes, -// testutils.MiddlewareConfigFixture(suite.T(), sporkId)) -// nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, mws) -// suite.cancels = append(suite.cancels, cancel) -// -// testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, nets, 100*time.Millisecond) -// -// // create the engines for the new nodes -// engines := testutils.GenerateEngines(suite.T(), nets) -// -// // create the test engines -// for i := 0; i < count; i++ { -// node := testNode{ -// id: ids[i], -// libp2pNode: nodes[i], -// mw: mws[i], -// net: nets[i], -// engine: engines[i], -// } -// suite.testNodes.append(node) -// } -//} -// -//// removeNode removes a randomly chosen test node from suite.testNodes and adds it to suite.removedTestNodes -//func (suite *MutableIdentityTableSuite) removeNode() testNode { -// removedNode := suite.testNodes.remove() -// suite.removedTestNodes.append(removedNode) -// return removedNode -//} -// -//// TestNewNodeAdded tests that when a new node is added to the identity list e.g. on an epoch, -//// then it can connect to the network. -//func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { -// -// // add a new node the current list of nodes -// suite.addNodes(1) -// -// newNode, err := suite.testNodes.lastAdded() -// require.NoError(suite.T(), err) -// newID := newNode.id -// -// suite.logger.Debug(). -// Str("new_node", newID.NodeID.String()). -// Msg("added one node") -// -// // update IDs for all the networks (simulating an epoch) -// suite.signalIdentityChanged() -// -// ids := suite.testNodes.ids() -// engs := suite.testNodes.engines() -// -// // check if the new node has sufficient connections with the existing nodes -// // if it does, then it has been inducted successfully in the network -// suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) -// -// // check that all the engines on this new epoch can talk to each other using any of the three networking primitives -// suite.assertNetworkPrimitives(ids, engs, nil, nil) -//} -// -//// TestNodeRemoved tests that when an existing node is removed from the identity -//// list (ie. as a result of an ejection or transition into an epoch where that node -//// has un-staked) then it cannot connect to the network. -//func (suite *MutableIdentityTableSuite) TestNodeRemoved() { -// // removed a node -// removedNode := suite.removeNode() -// removedID := removedNode.id -// removedEngine := removedNode.engine -// -// // update IDs for all the remaining nodes -// // the removed node continues with the old identity list as we don't want to rely on it updating its ids list -// suite.signalIdentityChanged() -// -// remainingIDs := suite.testNodes.ids() -// remainingEngs := suite.testNodes.engines() -// -// // assert that the removed node has no connections with any of the other nodes -// suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) -// -// // check that all remaining engines can still talk to each other while the ones removed can't -// // using any of the three networking primitives -// removedIDs := []*flow.Identity{removedID} -// removedEngines := []*testutils.MeshEngine{removedEngine} -// -// // assert that all three network primitives still work -// suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) -//} -// -//// TestNodesAddedAndRemoved tests that: -//// a. a newly added node can exchange messages with the existing nodes -//// b. a node that has has been removed cannot exchange messages with the existing nodes -//func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { -// -// // remove a node -// removedNode := suite.removeNode() -// removedID := removedNode.id -// removedEngine := removedNode.engine -// -// // add a node -// suite.addNodes(1) -// newNode, err := suite.testNodes.lastAdded() -// require.NoError(suite.T(), err) -// -// // update all current nodes -// suite.signalIdentityChanged() -// -// remainingIDs := suite.testNodes.ids() -// remainingEngs := suite.testNodes.engines() -// -// // check if the new node has sufficient connections with the existing nodes -// suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) -// -// // assert that the removed node has no connections with any of the other nodes -// suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) -// -// // check that all remaining engines can still talk to each other while the ones removed can't -// // using any of the three networking primitives -// removedIDs := []*flow.Identity{removedID} -// removedEngines := []*testutils.MeshEngine{removedEngine} -// -// // assert that all three network primitives still work -// suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) -//} -// -//// assertConnected checks that the middleware of a node is directly connected -//// to at least half of the other nodes. -//func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { -// t := suite.T() -// threshold := len(allNodes) / 2 -// require.Eventuallyf(t, func() bool { -// connections := 0 -// for _, node := range allNodes { -// if node == thisNode { -// // we don't want to check if a node is connected to itself -// continue -// } -// connected, err := thisNode.IsConnected(node.ID()) -// require.NoError(t, err) -// if connected { -// connections++ -// } -// } -// suite.logger.Debug(). -// Int("threshold", threshold). -// Int("connections", connections). -// Msg("current connection count") -// return connections >= threshold -// }, 5*time.Second, 100*time.Millisecond, "node is not connected to enough nodes") -//} -// -//// assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the -//// ids list -//func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { -// t := suite.T() -// require.Eventuallyf(t, func() bool { -// for _, node := range allNodes { -// connected, err := thisNode.IsConnected(node.ID()) -// require.NoError(t, err) -// if connected { -// return false -// } -// } -// return true -// }, 5*time.Second, 100*time.Millisecond, "node is still connected") -//} -// -//// assertNetworkPrimitives asserts that allowed engines can exchange messages between themselves but not with the -//// disallowed engines using each of the three network primitives -//func (suite *MutableIdentityTableSuite) assertNetworkPrimitives( -// allowedIDs flow.IdentityList, -// allowedEngs []*testutils.MeshEngine, -// disallowedIDs flow.IdentityList, -// disallowedEngs []*testutils.MeshEngine) { -// suite.Run("Publish", func() { -// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Publish, false) -// }) -// suite.Run("Multicast", func() { -// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Multicast, false) -// }) -// suite.Run("Unicast", func() { -// // unicast send from or to a node that has been evicted should fail with an error -// suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Unicast, true) -// }) -//} -// -//// exchangeMessages verifies that allowed engines can successfully exchange messages between them while disallowed -//// engines can't using the ConduitSendWrapperFunc network primitive -//func (suite *MutableIdentityTableSuite) exchangeMessages( -// allowedIDs flow.IdentityList, -// allowedEngs []*testutils.MeshEngine, -// disallowedIDs flow.IdentityList, -// disallowedEngs []*testutils.MeshEngine, -// send testutils.ConduitSendWrapperFunc, -// expectSendErrorForDisallowedIDs bool) { -// -// // send a message from each of the allowed engine to the other allowed engines -// for i, allowedEng := range allowedEngs { -// -// fromID := allowedIDs[i].NodeID -// targetIDs := allowedIDs.Filter(filter.Not(filter.HasNodeID(allowedIDs[i].NodeID))) -// -// err := suite.sendMessage(fromID, allowedEng, targetIDs, send) -// require.NoError(suite.T(), err) -// } -// -// // send a message from each of the allowed engine to all of the disallowed engines -// if len(disallowedEngs) > 0 { -// for i, fromEng := range allowedEngs { -// -// fromID := allowedIDs[i].NodeID -// targetIDs := disallowedIDs -// -// err := suite.sendMessage(fromID, fromEng, targetIDs, send) -// if expectSendErrorForDisallowedIDs { -// require.Error(suite.T(), err) -// } -// } -// } -// -// // send a message from each of the disallowed engine to each of the allowed engines -// for i, fromEng := range disallowedEngs { -// -// fromID := disallowedIDs[i].NodeID -// targetIDs := allowedIDs -// -// err := suite.sendMessage(fromID, fromEng, targetIDs, send) -// if expectSendErrorForDisallowedIDs { -// require.Error(suite.T(), err) -// } -// } -// -// count := len(allowedEngs) -// expectedMsgCnt := count - 1 -// wg := sync.WaitGroup{} -// // fires a goroutine for each of the allowed engine to listen for incoming messages -// for i := range allowedEngs { -// wg.Add(expectedMsgCnt) -// go func(e *testutils.MeshEngine) { -// for x := 0; x < expectedMsgCnt; x++ { -// <-e.Received -// wg.Done() -// } -// }(allowedEngs[i]) -// } -// -// // assert that all allowed engines received expectedMsgCnt number of messages -// unittest.AssertReturnsBefore(suite.T(), wg.Wait, 5*time.Second) -// // assert that all allowed engines received no other messages -// for i := range allowedEngs { -// assert.Empty(suite.T(), allowedEngs[i].Received) -// } -// -// // assert that the disallowed engines didn't receive any message -// for i, eng := range disallowedEngs { -// unittest.RequireNeverClosedWithin(suite.T(), eng.Received, time.Millisecond, -// fmt.Sprintf("%s engine should not have recevied message", disallowedIDs[i])) -// } -//} -// -//func (suite *MutableIdentityTableSuite) sendMessage(fromID flow.Identifier, -// fromEngine *testutils.MeshEngine, -// toIDs flow.IdentityList, -// send testutils.ConduitSendWrapperFunc) error { -// -// primitive := runtime.FuncForPC(reflect.ValueOf(send).Pointer()).Name() -// event := &message.TestMessage{ -// Text: fmt.Sprintf("hello from node %s using %s", fromID.String(), primitive), -// } -// -// return send(event, fromEngine.Con, toIDs.NodeIDs()...) -//} +import ( + "context" + "fmt" + "math/rand" + "os" + "reflect" + "runtime" + "sync" + "testing" + "time" + + "github.com/ipfs/go-log" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/p2p" + mockprotocol "github.com/onflow/flow-go/state/protocol/mock" + "github.com/onflow/flow-go/utils/unittest" +) + +// MutableIdentityTableSuite tests that the networking layer responds correctly +// to changes to the identity table. When nodes are added, we should update our +// topology and accept connections from these new nodes. When nodes are removed +// or ejected we should update our topology and restrict connections from these +// nodes. +type MutableIdentityTableSuite struct { + suite.Suite + testutils.ConduitWrapper + testNodes testNodeList + removedTestNodes testNodeList // test nodes which might have been removed from the mesh + state *mockprotocol.State + snapshot *mockprotocol.Snapshot + logger zerolog.Logger + cancels []context.CancelFunc +} + +// testNode encapsulates the node state which includes its identity, middleware, network, +// mesh engine and the id refresher +type testNode struct { + id *flow.Identity + libp2pNode p2p.LibP2PNode + mw network.Middleware + net network.Network + engine *testutils.MeshEngine +} + +// testNodeList encapsulates a list of test node and +// has functions to retrieve the different elements of the test nodes in a concurrency safe manner +type testNodeList struct { + sync.RWMutex + nodes []testNode +} + +func newTestNodeList() testNodeList { + return testNodeList{} +} + +func (t *testNodeList) append(node testNode) { + t.Lock() + defer t.Unlock() + t.nodes = append(t.nodes, node) +} + +func (t *testNodeList) remove() testNode { + t.Lock() + defer t.Unlock() + // choose a random node to remove + i := rand.Intn(len(t.nodes)) + removedNode := t.nodes[i] + t.nodes = append(t.nodes[:i], t.nodes[i+1:]...) + return removedNode +} + +func (t *testNodeList) ids() flow.IdentityList { + t.RLock() + defer t.RUnlock() + ids := make(flow.IdentityList, len(t.nodes)) + for i, node := range t.nodes { + ids[i] = node.id + } + return ids +} + +func (t *testNodeList) lastAdded() (testNode, error) { + t.RLock() + defer t.RUnlock() + if len(t.nodes) > 0 { + return t.nodes[len(t.nodes)-1], nil + } + return testNode{}, fmt.Errorf("node list empty") +} + +func (t *testNodeList) engines() []*testutils.MeshEngine { + t.RLock() + defer t.RUnlock() + engs := make([]*testutils.MeshEngine, len(t.nodes)) + for i, node := range t.nodes { + engs[i] = node.engine + } + return engs +} + +func (t *testNodeList) networks() []network.Network { + t.RLock() + defer t.RUnlock() + nets := make([]network.Network, len(t.nodes)) + for i, node := range t.nodes { + nets[i] = node.net + } + return nets +} + +func (t *testNodeList) libp2pNodes() []p2p.LibP2PNode { + t.RLock() + defer t.RUnlock() + nodes := make([]p2p.LibP2PNode, len(t.nodes)) + for i, node := range t.nodes { + nodes[i] = node.libp2pNode + } + return nodes +} + +func TestMutableIdentityTable(t *testing.T) { + unittest.SkipUnless(t, unittest.TEST_TODO, "broken test") + suite.Run(t, new(MutableIdentityTableSuite)) +} + +// signalIdentityChanged update IDs for all the current set of nodes (simulating an epoch) +func (suite *MutableIdentityTableSuite) signalIdentityChanged() { + for _, n := range suite.testNodes.nodes { + n.mw.UpdateNodeAddresses() + } +} + +func (suite *MutableIdentityTableSuite) SetupTest() { + suite.testNodes = newTestNodeList() + suite.removedTestNodes = newTestNodeList() + + nodeCount := 10 + suite.logger = zerolog.New(os.Stderr).Level(zerolog.ErrorLevel) + log.SetAllLoggers(log.LevelError) + + suite.setupStateMock() + suite.addNodes(nodeCount) + + // simulate a start of an epoch by signaling a change in the identity table + suite.signalIdentityChanged() + + // wait for two lip2p heatbeats for the nodes to discover each other and form the mesh + time.Sleep(2 * time.Second) +} + +// TearDownTest closes all the networks within a specified timeout +func (suite *MutableIdentityTableSuite) TearDownTest() { + for _, cancel := range suite.cancels { + cancel() + } + networks := append(suite.testNodes.networks(), suite.removedTestNodes.networks()...) + testutils.StopComponents(suite.T(), networks, 3*time.Second) +} + +// setupStateMock setup state related mocks (all networks share the same state mock) +func (suite *MutableIdentityTableSuite) setupStateMock() { + final := unittest.BlockHeaderFixture() + suite.state = new(mockprotocol.State) + suite.snapshot = new(mockprotocol.Snapshot) + suite.snapshot.On("Head").Return(&final, nil) + suite.snapshot.On("Phase").Return(flow.EpochPhaseCommitted, nil) + // return all the current list of ids for the state.Final.Identities call made by the network + suite.snapshot.On("Identities", mock.Anything).Return( + func(flow.IdentityFilter) flow.IdentityList { + return suite.testNodes.ids() + }, + func(flow.IdentityFilter) error { return nil }) + suite.state.On("Final").Return(suite.snapshot, nil) +} + +// addNodes creates count many new nodes and appends them to the suite state variables +func (suite *MutableIdentityTableSuite) addNodes(count int) { + ctx, cancel := context.WithCancel(context.Background()) + signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + + // create the ids, middlewares and networks + sporkId := unittest.IdentifierFixture() + ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) + suite.cancels = append(suite.cancels, cancel) + + // starts the nodes and networks + testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + for _, net := range nets { + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) + } + + // create the engines for the new nodes + engines := make([]*testutils.MeshEngine, count) + for i, n := range nets { + eng := testutils.NewMeshEngine(suite.T(), n, 100, channels.TestNetworkChannel) + engines[i] = eng + } + + // create the test engines + for i := 0; i < count; i++ { + node := testNode{ + id: ids[i], + libp2pNode: nodes[i], + net: nets[i], + engine: engines[i], + } + suite.testNodes.append(node) + } +} + +// removeNode removes a randomly chosen test node from suite.testNodes and adds it to suite.removedTestNodes +func (suite *MutableIdentityTableSuite) removeNode() testNode { + removedNode := suite.testNodes.remove() + suite.removedTestNodes.append(removedNode) + return removedNode +} + +// TestNewNodeAdded tests that when a new node is added to the identity list e.g. on an epoch, +// then it can connect to the network. +func (suite *MutableIdentityTableSuite) TestNewNodeAdded() { + + // add a new node the current list of nodes + suite.addNodes(1) + + newNode, err := suite.testNodes.lastAdded() + require.NoError(suite.T(), err) + newID := newNode.id + + suite.logger.Debug(). + Str("new_node", newID.NodeID.String()). + Msg("added one node") + + // update IDs for all the networks (simulating an epoch) + suite.signalIdentityChanged() + + ids := suite.testNodes.ids() + engs := suite.testNodes.engines() + + // check if the new node has sufficient connections with the existing nodes + // if it does, then it has been inducted successfully in the network + suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) + + // check that all the engines on this new epoch can talk to each other using any of the three networking primitives + suite.assertNetworkPrimitives(ids, engs, nil, nil) +} + +// TestNodeRemoved tests that when an existing node is removed from the identity +// list (ie. as a result of an ejection or transition into an epoch where that node +// has un-staked) then it cannot connect to the network. +func (suite *MutableIdentityTableSuite) TestNodeRemoved() { + // removed a node + removedNode := suite.removeNode() + removedID := removedNode.id + removedEngine := removedNode.engine + + // update IDs for all the remaining nodes + // the removed node continues with the old identity list as we don't want to rely on it updating its ids list + suite.signalIdentityChanged() + + remainingIDs := suite.testNodes.ids() + remainingEngs := suite.testNodes.engines() + + // assert that the removed node has no connections with any of the other nodes + suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) + + // check that all remaining engines can still talk to each other while the ones removed can't + // using any of the three networking primitives + removedIDs := []*flow.Identity{removedID} + removedEngines := []*testutils.MeshEngine{removedEngine} + + // assert that all three network primitives still work + suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) +} + +// TestNodesAddedAndRemoved tests that: +// a. a newly added node can exchange messages with the existing nodes +// b. a node that has has been removed cannot exchange messages with the existing nodes +func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { + + // remove a node + removedNode := suite.removeNode() + removedID := removedNode.id + removedEngine := removedNode.engine + + // add a node + suite.addNodes(1) + newNode, err := suite.testNodes.lastAdded() + require.NoError(suite.T(), err) + + // update all current nodes + suite.signalIdentityChanged() + + remainingIDs := suite.testNodes.ids() + remainingEngs := suite.testNodes.engines() + + // check if the new node has sufficient connections with the existing nodes + suite.assertConnected(newNode.libp2pNode, suite.testNodes.libp2pNodes()) + + // assert that the removed node has no connections with any of the other nodes + suite.assertDisconnected(removedNode.libp2pNode, suite.testNodes.libp2pNodes()) + + // check that all remaining engines can still talk to each other while the ones removed can't + // using any of the three networking primitives + removedIDs := []*flow.Identity{removedID} + removedEngines := []*testutils.MeshEngine{removedEngine} + + // assert that all three network primitives still work + suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) +} + +// assertConnected checks that the middleware of a node is directly connected +// to at least half of the other nodes. +func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { + t := suite.T() + threshold := len(allNodes) / 2 + require.Eventuallyf(t, func() bool { + connections := 0 + for _, node := range allNodes { + if node == thisNode { + // we don't want to check if a node is connected to itself + continue + } + connected, err := thisNode.IsConnected(node.ID()) + require.NoError(t, err) + if connected { + connections++ + } + } + suite.logger.Debug(). + Int("threshold", threshold). + Int("connections", connections). + Msg("current connection count") + return connections >= threshold + }, 5*time.Second, 100*time.Millisecond, "node is not connected to enough nodes") +} + +// assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the +// ids list +func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { + t := suite.T() + require.Eventuallyf(t, func() bool { + for _, node := range allNodes { + connected, err := thisNode.IsConnected(node.ID()) + require.NoError(t, err) + if connected { + return false + } + } + return true + }, 5*time.Second, 100*time.Millisecond, "node is still connected") +} + +// assertNetworkPrimitives asserts that allowed engines can exchange messages between themselves but not with the +// disallowed engines using each of the three network primitives +func (suite *MutableIdentityTableSuite) assertNetworkPrimitives( + allowedIDs flow.IdentityList, + allowedEngs []*testutils.MeshEngine, + disallowedIDs flow.IdentityList, + disallowedEngs []*testutils.MeshEngine) { + suite.Run("Publish", func() { + suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Publish, false) + }) + suite.Run("Multicast", func() { + suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Multicast, false) + }) + suite.Run("Unicast", func() { + // unicast send from or to a node that has been evicted should fail with an error + suite.exchangeMessages(allowedIDs, allowedEngs, disallowedIDs, disallowedEngs, suite.Unicast, true) + }) +} + +// exchangeMessages verifies that allowed engines can successfully exchange messages between them while disallowed +// engines can't using the ConduitSendWrapperFunc network primitive +func (suite *MutableIdentityTableSuite) exchangeMessages( + allowedIDs flow.IdentityList, + allowedEngs []*testutils.MeshEngine, + disallowedIDs flow.IdentityList, + disallowedEngs []*testutils.MeshEngine, + send testutils.ConduitSendWrapperFunc, + expectSendErrorForDisallowedIDs bool) { + + // send a message from each of the allowed engine to the other allowed engines + for i, allowedEng := range allowedEngs { + + fromID := allowedIDs[i].NodeID + targetIDs := allowedIDs.Filter(filter.Not(filter.HasNodeID(allowedIDs[i].NodeID))) + + err := suite.sendMessage(fromID, allowedEng, targetIDs, send) + require.NoError(suite.T(), err) + } + + // send a message from each of the allowed engine to all of the disallowed engines + if len(disallowedEngs) > 0 { + for i, fromEng := range allowedEngs { + + fromID := allowedIDs[i].NodeID + targetIDs := disallowedIDs + + err := suite.sendMessage(fromID, fromEng, targetIDs, send) + if expectSendErrorForDisallowedIDs { + require.Error(suite.T(), err) + } + } + } + + // send a message from each of the disallowed engine to each of the allowed engines + for i, fromEng := range disallowedEngs { + + fromID := disallowedIDs[i].NodeID + targetIDs := allowedIDs + + err := suite.sendMessage(fromID, fromEng, targetIDs, send) + if expectSendErrorForDisallowedIDs { + require.Error(suite.T(), err) + } + } + + count := len(allowedEngs) + expectedMsgCnt := count - 1 + wg := sync.WaitGroup{} + // fires a goroutine for each of the allowed engine to listen for incoming messages + for i := range allowedEngs { + wg.Add(expectedMsgCnt) + go func(e *testutils.MeshEngine) { + for x := 0; x < expectedMsgCnt; x++ { + <-e.Received + wg.Done() + } + }(allowedEngs[i]) + } + + // assert that all allowed engines received expectedMsgCnt number of messages + unittest.AssertReturnsBefore(suite.T(), wg.Wait, 5*time.Second) + // assert that all allowed engines received no other messages + for i := range allowedEngs { + assert.Empty(suite.T(), allowedEngs[i].Received) + } + + // assert that the disallowed engines didn't receive any message + for i, eng := range disallowedEngs { + unittest.RequireNeverClosedWithin(suite.T(), eng.Received, time.Millisecond, + fmt.Sprintf("%s engine should not have recevied message", disallowedIDs[i])) + } +} + +func (suite *MutableIdentityTableSuite) sendMessage(fromID flow.Identifier, + fromEngine *testutils.MeshEngine, + toIDs flow.IdentityList, + send testutils.ConduitSendWrapperFunc) error { + + primitive := runtime.FuncForPC(reflect.ValueOf(send).Pointer()).Name() + event := &message.TestMessage{ + Text: fmt.Sprintf("hello from node %s using %s", fromID.String(), primitive), + } + + return send(event, fromEngine.Con, toIDs.NodeIDs()...) +} From 7a71ed648bcbb305f503b47398b1b65409e419c1 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:04:04 -0400 Subject: [PATCH 765/815] fixes all unicast authorization tests --- network/test/unicast_authorization_test.go | 1112 ++++++++++---------- 1 file changed, 556 insertions(+), 556 deletions(-) diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 16d91d9d7b7..a3e14dabd27 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -1,558 +1,558 @@ package test -// -//import ( -// "context" -// "io" -// "reflect" -// "testing" -// "time" -// -// "github.com/rs/zerolog" -// mockery "github.com/stretchr/testify/mock" -// "github.com/stretchr/testify/require" -// "github.com/stretchr/testify/suite" -// -// "github.com/onflow/flow-go/model/flow" -// libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" -// "github.com/onflow/flow-go/model/messages" -// "github.com/onflow/flow-go/module/irrecoverable" -// "github.com/onflow/flow-go/network" -// "github.com/onflow/flow-go/network/channels" -// "github.com/onflow/flow-go/network/codec" -// "github.com/onflow/flow-go/network/internal/testutils" -// "github.com/onflow/flow-go/network/message" -// "github.com/onflow/flow-go/network/mocknetwork" -// "github.com/onflow/flow-go/network/p2p" -// "github.com/onflow/flow-go/network/p2p/middleware" -// "github.com/onflow/flow-go/network/p2p/p2pnet" -// "github.com/onflow/flow-go/network/validator" -// "github.com/onflow/flow-go/utils/unittest" -//) -// -//// UnicastAuthorizationTestSuite tests that messages sent via unicast that are unauthenticated or unauthorized are correctly rejected. Each test on the test suite -//// uses 2 middlewares, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Middleware and the cancel func -//// are set during each test run inside the test and remove after each test run in the TearDownTest callback. -//type UnicastAuthorizationTestSuite struct { -// suite.Suite -// channelCloseDuration time.Duration -// logger zerolog.Logger -// -// codec *overridableMessageEncoder -// -// libP2PNodes []p2p.LibP2PNode -// // senderNetwork is the networking layer instance that will be used to send the message. -// senderNetwork network.Network -// // senderID the identity on the mw sending the message -// senderID *flow.Identity -// // receiverNetwork is the networking layer instance that will be used to receive the message. -// receiverNetwork network.Network -// // receiverID the identity on the mw sending the message -// receiverID *flow.Identity -// // providers id providers generated at beginning of a test run -// providers []*unittest.UpdatableIDProvider -// // cancel is the cancel func from the context that was used to start the middlewares in a test run -// cancel context.CancelFunc -// sporkId flow.Identifier -// // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing -// //violation's consumer before making mock assertions and cleaning up resources -// waitCh chan struct{} -//} -// -//// TestUnicastAuthorizationTestSuite runs all the test methods in this test suit -//func TestUnicastAuthorizationTestSuite(t *testing.T) { -// t.Parallel() -// suite.Run(t, new(UnicastAuthorizationTestSuite)) -//} -// -//func (u *UnicastAuthorizationTestSuite) SetupTest() { -// u.logger = unittest.Logger() -// u.channelCloseDuration = 100 * time.Millisecond -// // this ch will allow us to wait until the expected method call happens before shutting down middleware -// u.waitCh = make(chan struct{}) -//} -// -//func (u *UnicastAuthorizationTestSuite) TearDownTest() { -// u.stopMiddlewares() -//} -// -//// setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. -//func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { -// u.sporkId = unittest.IdentifierFixture() -// ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) -// cfg := testutils.MiddlewareConfigFixture(u.T(), u.sporkId) -// cfg.SlashingViolationConsumerFactory = func() network.ViolationsConsumer { -// return slashingViolationsConsumer -// } -// u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) -// mws, _ := testutils.MiddlewareFixtures(u.T(), ids, libP2PNodes, cfg) -// nets, providers := testutils.NetworksFixture(u.T(), u.sporkId, ids, mws, p2pnet.WithCodec(u.codec)) -// require.Len(u.T(), ids, 2) -// require.Len(u.T(), providers, 2) -// require.Len(u.T(), mws, 2) -// require.Len(u.T(), nets, 2) -// -// u.senderNetwork = nets[0] -// u.receiverNetwork = nets[1] -// u.senderID = ids[0] -// u.receiverID = ids[1] -// u.providers = providers -// u.libP2PNodes = libP2PNodes -//} -// -//// startMiddlewares will start both sender and receiver middlewares with an irrecoverable signaler context and set the context cancel func. -//func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Overlay) { -// ctx, cancel := context.WithCancel(context.Background()) -// sigCtx, _ := irrecoverable.WithSignaler(ctx) -// -// testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) -// testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) -// unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) -// -// u.cancel = cancel -//} -// -//// stopMiddlewares will stop all middlewares. -//func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { -// u.cancel() -// -// testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) -// unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) -//} -// -//// TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// var nilID *flow.Identity -// expectedViolation := &network.Violation{ -// Identity: nilID, // because the peer will be unverified this identity will be nil -// PeerID: expectedSenderPeerID.String(), -// MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type -// Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID -// Protocol: message.ProtocolTypeUnicast, -// Err: validator.ErrIdentityUnverified, -// } -// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation).Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// // overriding the identity provide of the receiver node to return an empty identity list so that the -// // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message -// // from the sender node -// u.providers[1].SetIdentities(nil) -// -// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_EjectedPeer tests that messages sent via unicast by an ejected peer is correctly rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// //NOTE: setup ejected identity -// u.senderID.Ejected = true -// -// // overriding the identity provide of the receiver node to return the ejected identity so that the -// // sender node looks ejected to its networking layer and hence it sends a SenderEjectedError upon receiving a message -// // from the sender node -// u.providers[1].SetIdentities(flow.IdentityList{u.senderID}) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// expectedViolation := &network.Violation{ -// Identity: u.senderID, // we expect this method to be called with the ejected identity -// OriginID: u.senderID.NodeID, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type -// Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID -// Protocol: message.ProtocolTypeUnicast, -// Err: validator.ErrSenderEjected, -// } -// slashingViolationsConsumer.On("OnSenderEjectedError", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_UnauthorizedPeer tests that messages sent via unicast by an unauthorized peer is correctly rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// expectedViolation := &network.Violation{ -// Identity: u.senderID, -// OriginID: u.senderID.NodeID, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "*message.TestMessage", -// Channel: channels.ConsensusCommittee, -// Protocol: message.ProtocolTypeUnicast, -// Err: message.ErrUnauthorizedMessageOnChannel, -// } -// -// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast; a test message must only be unicasted on the TestNetworkChannel, not on the ConsensusCommittee channel -// // so we expect an unauthorized sender error -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_UnknownMsgCode tests that messages sent via unicast with an unknown message code is correctly rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// invalidMessageCode := codec.MessageCode(byte('X')) -// // register a custom encoder that encodes the message with an invalid message code when encoding a string. -// u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { -// e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ -// Text: v.(string), -// }) -// require.NoError(u.T(), err) -// // manipulate message code byte -// invalidMessageCode := codec.MessageCode(byte('X')) -// e[0] = invalidMessageCode.Uint8() -// return e, nil -// }) -// -// var nilID *flow.Identity -// expectedViolation := &network.Violation{ -// Identity: nilID, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "", -// Channel: channels.TestNetworkChannel, -// Protocol: message.ProtocolTypeUnicast, -// Err: codec.NewUnknownMsgCodeErr(invalidMessageCode), -// } -// -// slashingViolationsConsumer.On("OnUnknownMsgTypeError", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast("hello!", u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_WrongMsgCode tests that messages sent via unicast with a message code that does not match the underlying message type are correctly rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// modifiedMessageCode := codec.CodeDKGMessage -// // register a custom encoder that overrides the message code when encoding a TestMessage. -// u.codec.RegisterEncoder(reflect.TypeOf(&libp2pmessage.TestMessage{}), func(v interface{}) ([]byte, error) { -// e, err := unittest.NetworkCodec().Encode(v) -// require.NoError(u.T(), err) -// e[0] = modifiedMessageCode.Uint8() -// return e, nil -// }) -// -// expectedViolation := &network.Violation{ -// Identity: u.senderID, -// OriginID: u.senderID.NodeID, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "*messages.DKGMessage", -// Channel: channels.TestNetworkChannel, -// Protocol: message.ProtocolTypeUnicast, -// Err: message.ErrUnauthorizedMessageOnChannel, -// } -// -// slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_PublicChannel tests that messages sent via unicast on a public channel are not rejected for any reason. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// u.startMiddlewares(nil) -// -// msg := &libp2pmessage.TestMessage{ -// Text: string("hello"), -// } -// -// // mock a message processor that will receive the message. -// receiverEngine := &mocknetwork.MessageProcessor{} -// receiverEngine.On("Process", channels.PublicPushBlocks, u.senderID.NodeID, msg).Run( -// func(args mockery.Arguments) { -// close(u.waitCh) -// }).Return(nil).Once() -// _, err := u.receiverNetwork.Register(channels.PublicPushBlocks, receiverEngine) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.PublicPushBlocks, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_UnauthorizedUnicastOnChannel tests that messages sent via unicast that are not authorized for unicast are rejected. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// // set sender id role to RoleConsensus to avoid unauthorized sender validation error -// u.senderID.Role = flow.RoleConsensus -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// expectedViolation := &network.Violation{ -// Identity: u.senderID, -// OriginID: u.senderID.NodeID, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "*messages.BlockProposal", -// Channel: channels.ConsensusCommittee, -// Protocol: message.ProtocolTypeUnicast, -// Err: message.ErrUnauthorizedUnicastOnChannel, -// } -// -// slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel -// payload := unittest.ProposalFixture() -// // send message via unicast -// err = senderCon.Unicast(payload, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_ReceiverHasNoSubscription tests that messages sent via unicast are rejected on the receiver end if the receiver does not have a subscription -//// to the channel of the message. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// -// expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) -// require.NoError(u.T(), err) -// -// expectedViolation := &network.Violation{ -// Identity: nil, -// PeerID: expectedSenderPeerID.String(), -// MsgType: "*message.TestMessage", -// Channel: channels.TestNetworkChannel, -// Protocol: message.ProtocolTypeUnicast, -// Err: middleware.ErrUnicastMsgWithoutSub, -// } -// -// slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). -// Return(nil).Once().Run(func(args mockery.Arguments) { -// close(u.waitCh) -// }) -// -// u.startMiddlewares(nil) -// -// senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(&libp2pmessage.TestMessage{ -// Text: string("hello"), -// }, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// TestUnicastAuthorization_ReceiverHasSubscription tests that messages sent via unicast are processed on the receiver end if the receiver does have a subscription -//// to the channel of the message. -//func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { -// // setup mock slashing violations consumer and middlewares -// slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) -// u.setupNetworks(slashingViolationsConsumer) -// u.startMiddlewares(nil) -// -// msg := &messages.EntityRequest{ -// EntityIDs: unittest.IdentifierListFixture(10), -// } -// -// // both sender and receiver must have an authorized role to send and receive messages on the ConsensusCommittee channel. -// u.senderID.Role = flow.RoleConsensus -// u.receiverID.Role = flow.RoleExecution -// -// receiverEngine := &mocknetwork.MessageProcessor{} -// receiverEngine.On("Process", channels.RequestReceiptsByBlockID, u.senderID.NodeID, msg).Run( -// func(args mockery.Arguments) { -// close(u.waitCh) -// }).Return(nil).Once() -// _, err := u.receiverNetwork.Register(channels.RequestReceiptsByBlockID, receiverEngine) -// require.NoError(u.T(), err) -// -// senderCon, err := u.senderNetwork.Register(channels.RequestReceiptsByBlockID, &mocknetwork.MessageProcessor{}) -// require.NoError(u.T(), err) -// -// // send message via unicast -// err = senderCon.Unicast(msg, u.receiverID.NodeID) -// require.NoError(u.T(), err) -// -// // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens -// unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") -//} -// -//// overridableMessageEncoder is a codec that allows to override the encoder for a specific type only for sake of testing. -//// We specifically use this to override the encoder for the TestMessage type to encode it with an invalid message code. -//type overridableMessageEncoder struct { -// codec network.Codec -// specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) -//} -// -//var _ network.Codec = (*overridableMessageEncoder)(nil) -// -//func newOverridableMessageEncoder(codec network.Codec) *overridableMessageEncoder { -// return &overridableMessageEncoder{ -// codec: codec, -// specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), -// } -//} -// -//// RegisterEncoder registers an encoder for a specific type, overriding the default encoder for that type. -//func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { -// u.specificEncoder[t] = encoder -//} -// -//// NewEncoder creates a new encoder. -//func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { -// return u.codec.NewEncoder(w) -//} -// -//// NewDecoder creates a new decoder. -//func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { -// return u.codec.NewDecoder(r) -//} -// -//// Encode encodes a value into a byte slice. If a specific encoder is registered for the type of the value, it will be used. -//// Otherwise, the default encoder will be used. -//func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { -// if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { -// return encoder(v) -// } -// return u.codec.Encode(v) -//} -// -//// Decode decodes a byte slice into a value. It uses the default decoder. -//func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { -// return u.codec.Decode(data) -//} +import ( + "context" + "io" + "reflect" + "testing" + "time" + + "github.com/rs/zerolog" + mockery "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/model/flow" + libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" + "github.com/onflow/flow-go/model/messages" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/network" + "github.com/onflow/flow-go/network/channels" + "github.com/onflow/flow-go/network/codec" + "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/message" + "github.com/onflow/flow-go/network/mocknetwork" + "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pnet" + "github.com/onflow/flow-go/network/validator" + "github.com/onflow/flow-go/utils/unittest" +) + +// UnicastAuthorizationTestSuite tests that messages sent via unicast that are unauthenticated or unauthorized are correctly rejected. Each test on the test suite +// uses 2 middlewares, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Middleware and the cancel func +// are set during each test run inside the test and remove after each test run in the TearDownTest callback. +type UnicastAuthorizationTestSuite struct { + suite.Suite + channelCloseDuration time.Duration + logger zerolog.Logger + + codec *overridableMessageEncoder + + libP2PNodes []p2p.LibP2PNode + // senderNetwork is the networking layer instance that will be used to send the message. + senderNetwork network.Network + // senderID the identity on the mw sending the message + senderID *flow.Identity + // receiverNetwork is the networking layer instance that will be used to receive the message. + receiverNetwork network.Network + // receiverID the identity on the mw sending the message + receiverID *flow.Identity + // providers id providers generated at beginning of a test run + providers []*unittest.UpdatableIDProvider + // cancel is the cancel func from the context that was used to start the middlewares in a test run + cancel context.CancelFunc + sporkId flow.Identifier + // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing + //violation's consumer before making mock assertions and cleaning up resources + waitCh chan struct{} +} + +// TestUnicastAuthorizationTestSuite runs all the test methods in this test suit +func TestUnicastAuthorizationTestSuite(t *testing.T) { + t.Parallel() + suite.Run(t, new(UnicastAuthorizationTestSuite)) +} + +func (u *UnicastAuthorizationTestSuite) SetupTest() { + u.logger = unittest.Logger() + u.channelCloseDuration = 100 * time.Millisecond + // this ch will allow us to wait until the expected method call happens before shutting down middleware + u.waitCh = make(chan struct{}) +} + +func (u *UnicastAuthorizationTestSuite) TearDownTest() { + u.stopMiddlewares() +} + +// setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. +func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { + u.sporkId = unittest.IdentifierFixture() + ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) + u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) + nets, providers := testutils.NetworksFixture( + u.T(), + u.sporkId, + ids, + libP2PNodes, + p2pnet.WithCodec(u.codec), + p2pnet.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { + return slashingViolationsConsumer + })) + require.Len(u.T(), ids, 2) + require.Len(u.T(), providers, 2) + require.Len(u.T(), nets, 2) + + u.senderNetwork = nets[0] + u.receiverNetwork = nets[1] + u.senderID = ids[0] + u.receiverID = ids[1] + u.providers = providers + u.libP2PNodes = libP2PNodes +} + +// startMiddlewares will start both sender and receiver middlewares with an irrecoverable signaler context and set the context cancel func. +func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Overlay) { + ctx, cancel := context.WithCancel(context.Background()) + sigCtx, _ := irrecoverable.WithSignaler(ctx) + + testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) + testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) + unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) + + u.cancel = cancel +} + +// stopMiddlewares will stop all middlewares. +func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { + u.cancel() + + testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) + unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) +} + +// TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + var nilID *flow.Identity + expectedViolation := &network.Violation{ + Identity: nilID, // because the peer will be unverified this identity will be nil + PeerID: expectedSenderPeerID.String(), + MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type + Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID + Protocol: message.ProtocolTypeUnicast, + Err: validator.ErrIdentityUnverified, + } + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation).Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + // overriding the identity provide of the receiver node to return an empty identity list so that the + // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message + // from the sender node + u.providers[1].SetIdentities(nil) + + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_EjectedPeer tests that messages sent via unicast by an ejected peer is correctly rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + //NOTE: setup ejected identity + u.senderID.Ejected = true + + // overriding the identity provide of the receiver node to return the ejected identity so that the + // sender node looks ejected to its networking layer and hence it sends a SenderEjectedError upon receiving a message + // from the sender node + u.providers[1].SetIdentities(flow.IdentityList{u.senderID}) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + expectedViolation := &network.Violation{ + Identity: u.senderID, // we expect this method to be called with the ejected identity + OriginID: u.senderID.NodeID, + PeerID: expectedSenderPeerID.String(), + MsgType: "", // message will not be decoded before OnSenderEjectedError is logged, we won't log message type + Channel: channels.TestNetworkChannel, // message will not be decoded before OnSenderEjectedError is logged, we won't log peer ID + Protocol: message.ProtocolTypeUnicast, + Err: validator.ErrSenderEjected, + } + slashingViolationsConsumer.On("OnSenderEjectedError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_UnauthorizedPeer tests that messages sent via unicast by an unauthorized peer is correctly rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + expectedViolation := &network.Violation{ + Identity: u.senderID, + OriginID: u.senderID.NodeID, + PeerID: expectedSenderPeerID.String(), + MsgType: "*message.TestMessage", + Channel: channels.ConsensusCommittee, + Protocol: message.ProtocolTypeUnicast, + Err: message.ErrUnauthorizedMessageOnChannel, + } + + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast; a test message must only be unicasted on the TestNetworkChannel, not on the ConsensusCommittee channel + // so we expect an unauthorized sender error + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_UnknownMsgCode tests that messages sent via unicast with an unknown message code is correctly rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + invalidMessageCode := codec.MessageCode(byte('X')) + // register a custom encoder that encodes the message with an invalid message code when encoding a string. + u.codec.RegisterEncoder(reflect.TypeOf(""), func(v interface{}) ([]byte, error) { + e, err := unittest.NetworkCodec().Encode(&libp2pmessage.TestMessage{ + Text: v.(string), + }) + require.NoError(u.T(), err) + // manipulate message code byte + invalidMessageCode := codec.MessageCode(byte('X')) + e[0] = invalidMessageCode.Uint8() + return e, nil + }) + + var nilID *flow.Identity + expectedViolation := &network.Violation{ + Identity: nilID, + PeerID: expectedSenderPeerID.String(), + MsgType: "", + Channel: channels.TestNetworkChannel, + Protocol: message.ProtocolTypeUnicast, + Err: codec.NewUnknownMsgCodeErr(invalidMessageCode), + } + + slashingViolationsConsumer.On("OnUnknownMsgTypeError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast("hello!", u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_WrongMsgCode tests that messages sent via unicast with a message code that does not match the underlying message type are correctly rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + modifiedMessageCode := codec.CodeDKGMessage + // register a custom encoder that overrides the message code when encoding a TestMessage. + u.codec.RegisterEncoder(reflect.TypeOf(&libp2pmessage.TestMessage{}), func(v interface{}) ([]byte, error) { + e, err := unittest.NetworkCodec().Encode(v) + require.NoError(u.T(), err) + e[0] = modifiedMessageCode.Uint8() + return e, nil + }) + + expectedViolation := &network.Violation{ + Identity: u.senderID, + OriginID: u.senderID.NodeID, + PeerID: expectedSenderPeerID.String(), + MsgType: "*messages.DKGMessage", + Channel: channels.TestNetworkChannel, + Protocol: message.ProtocolTypeUnicast, + Err: message.ErrUnauthorizedMessageOnChannel, + } + + slashingViolationsConsumer.On("OnUnAuthorizedSenderError", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_PublicChannel tests that messages sent via unicast on a public channel are not rejected for any reason. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + u.startMiddlewares(nil) + + msg := &libp2pmessage.TestMessage{ + Text: string("hello"), + } + + // mock a message processor that will receive the message. + receiverEngine := &mocknetwork.MessageProcessor{} + receiverEngine.On("Process", channels.PublicPushBlocks, u.senderID.NodeID, msg).Run( + func(args mockery.Arguments) { + close(u.waitCh) + }).Return(nil).Once() + _, err := u.receiverNetwork.Register(channels.PublicPushBlocks, receiverEngine) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.PublicPushBlocks, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_UnauthorizedUnicastOnChannel tests that messages sent via unicast that are not authorized for unicast are rejected. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + // set sender id role to RoleConsensus to avoid unauthorized sender validation error + u.senderID.Role = flow.RoleConsensus + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + expectedViolation := &network.Violation{ + Identity: u.senderID, + OriginID: u.senderID.NodeID, + PeerID: expectedSenderPeerID.String(), + MsgType: "*messages.BlockProposal", + Channel: channels.ConsensusCommittee, + Protocol: message.ProtocolTypeUnicast, + Err: message.ErrUnauthorizedUnicastOnChannel, + } + + slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // messages.BlockProposal is not authorized to be sent via unicast over the ConsensusCommittee channel + payload := unittest.ProposalFixture() + // send message via unicast + err = senderCon.Unicast(payload, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_ReceiverHasNoSubscription tests that messages sent via unicast are rejected on the receiver end if the receiver does not have a subscription +// to the channel of the message. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + + expectedSenderPeerID, err := unittest.PeerIDFromFlowID(u.senderID) + require.NoError(u.T(), err) + + expectedViolation := &network.Violation{ + Identity: nil, + PeerID: expectedSenderPeerID.String(), + MsgType: "*message.TestMessage", + Channel: channels.TestNetworkChannel, + Protocol: message.ProtocolTypeUnicast, + Err: p2pnet.ErrUnicastMsgWithoutSub, + } + + slashingViolationsConsumer.On("OnUnauthorizedUnicastOnChannel", expectedViolation). + Return(nil).Once().Run(func(args mockery.Arguments) { + close(u.waitCh) + }) + + u.startMiddlewares(nil) + + senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(&libp2pmessage.TestMessage{ + Text: string("hello"), + }, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// TestUnicastAuthorization_ReceiverHasSubscription tests that messages sent via unicast are processed on the receiver end if the receiver does have a subscription +// to the channel of the message. +func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { + // setup mock slashing violations consumer and middlewares + slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) + u.setupNetworks(slashingViolationsConsumer) + u.startMiddlewares(nil) + + msg := &messages.EntityRequest{ + EntityIDs: unittest.IdentifierListFixture(10), + } + + // both sender and receiver must have an authorized role to send and receive messages on the ConsensusCommittee channel. + u.senderID.Role = flow.RoleConsensus + u.receiverID.Role = flow.RoleExecution + + receiverEngine := &mocknetwork.MessageProcessor{} + receiverEngine.On("Process", channels.RequestReceiptsByBlockID, u.senderID.NodeID, msg).Run( + func(args mockery.Arguments) { + close(u.waitCh) + }).Return(nil).Once() + _, err := u.receiverNetwork.Register(channels.RequestReceiptsByBlockID, receiverEngine) + require.NoError(u.T(), err) + + senderCon, err := u.senderNetwork.Register(channels.RequestReceiptsByBlockID, &mocknetwork.MessageProcessor{}) + require.NoError(u.T(), err) + + // send message via unicast + err = senderCon.Unicast(msg, u.receiverID.NodeID) + require.NoError(u.T(), err) + + // wait for slashing violations consumer mock to invoke run func and close ch if expected method call happens + unittest.RequireCloseBefore(u.T(), u.waitCh, u.channelCloseDuration, "could close ch on time") +} + +// overridableMessageEncoder is a codec that allows to override the encoder for a specific type only for sake of testing. +// We specifically use this to override the encoder for the TestMessage type to encode it with an invalid message code. +type overridableMessageEncoder struct { + codec network.Codec + specificEncoder map[reflect.Type]func(interface{}) ([]byte, error) +} + +var _ network.Codec = (*overridableMessageEncoder)(nil) + +func newOverridableMessageEncoder(codec network.Codec) *overridableMessageEncoder { + return &overridableMessageEncoder{ + codec: codec, + specificEncoder: make(map[reflect.Type]func(interface{}) ([]byte, error)), + } +} + +// RegisterEncoder registers an encoder for a specific type, overriding the default encoder for that type. +func (u *overridableMessageEncoder) RegisterEncoder(t reflect.Type, encoder func(interface{}) ([]byte, error)) { + u.specificEncoder[t] = encoder +} + +// NewEncoder creates a new encoder. +func (u *overridableMessageEncoder) NewEncoder(w io.Writer) network.Encoder { + return u.codec.NewEncoder(w) +} + +// NewDecoder creates a new decoder. +func (u *overridableMessageEncoder) NewDecoder(r io.Reader) network.Decoder { + return u.codec.NewDecoder(r) +} + +// Encode encodes a value into a byte slice. If a specific encoder is registered for the type of the value, it will be used. +// Otherwise, the default encoder will be used. +func (u *overridableMessageEncoder) Encode(v interface{}) ([]byte, error) { + if encoder, ok := u.specificEncoder[reflect.TypeOf(v)]; ok { + return encoder(v) + } + return u.codec.Encode(v) +} + +// Decode decodes a byte slice into a value. It uses the default decoder. +func (u *overridableMessageEncoder) Decode(data []byte) (interface{}, error) { + return u.codec.Decode(data) +} From 5584d0d5571ebe0d0cc63d5578eb353c9408d97f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:08:00 -0400 Subject: [PATCH 766/815] deletes middleware package entirely --- network/p2p/middleware/middleware.go | 720 --------------------- network/p2p/middleware/middleware_test.go | 35 - network/p2p/middleware/readSubscription.go | 82 --- network/test/meshengine_test.go | 3 +- network/test/middleware_test.go | 26 +- 5 files changed, 25 insertions(+), 841 deletions(-) delete mode 100644 network/p2p/middleware/middleware.go delete mode 100644 network/p2p/middleware/middleware_test.go delete mode 100644 network/p2p/middleware/readSubscription.go diff --git a/network/p2p/middleware/middleware.go b/network/p2p/middleware/middleware.go deleted file mode 100644 index 932446cd4d6..00000000000 --- a/network/p2p/middleware/middleware.go +++ /dev/null @@ -1,720 +0,0 @@ -// (c) 2019 Dapper Labs - ALL RIGHTS RESERVED - -package middleware - -import ( - "context" - "errors" - "fmt" - "io" - "sync" - "time" - - ggio "github.com/gogo/protobuf/io" - "github.com/ipfs/go-datastore" - libp2pnetwork "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/peerstore" - "github.com/libp2p/go-libp2p/core/protocol" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/codec" - "github.com/onflow/flow-go/network/internal/p2putils" - "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/blob" - "github.com/onflow/flow-go/network/p2p/ping" - "github.com/onflow/flow-go/network/p2p/unicast/protocols" - "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" - "github.com/onflow/flow-go/network/p2p/utils" - "github.com/onflow/flow-go/network/validator" - flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" - _ "github.com/onflow/flow-go/utils/binstat" - "github.com/onflow/flow-go/utils/logging" -) - -const ( - _ = iota - _ = 1 << (10 * iota) - mb - gb -) - -const ( - // DefaultMaxUnicastMsgSize defines maximum message size in unicast mode for most messages - DefaultMaxUnicastMsgSize = 10 * mb // 10 mb - - // LargeMsgMaxUnicastMsgSize defines maximum message size in unicast mode for large messages - LargeMsgMaxUnicastMsgSize = gb // 1 gb - - // DefaultUnicastTimeout is the default maximum time to wait for a default unicast request to complete - // assuming at least a 1mb/sec connection - DefaultUnicastTimeout = 5 * time.Second - - // LargeMsgUnicastTimeout is the maximum time to wait for a unicast request to complete for large message size - LargeMsgUnicastTimeout = 1000 * time.Second -) - -var ( - _ network.Middleware = (*Middleware)(nil) - - // ErrUnicastMsgWithoutSub error is provided to the slashing violations consumer in the case where - // the middleware receives a message via unicast but does not have a corresponding subscription for - // the channel in that message. - ErrUnicastMsgWithoutSub = errors.New("middleware does not have subscription for the channel ID indicated in the unicast message received") -) - -// Middleware handles the input & output on the direct connections we have to -// our neighbours on the peer-to-peer network. -type Middleware struct { - sync.Mutex - component.Component - ctx context.Context - log zerolog.Logger - ov network.Overlay - - // TODO: using a waitgroup here doesn't actually guarantee that we'll wait for all - // goroutines to exit, because new goroutines could be started after we've already - // returned from wg.Wait(). We need to solve this the right way using ComponentManager - // and worker routines. - wg sync.WaitGroup - libP2PNode p2p.LibP2PNode - preferredUnicasts []protocols.ProtocolName - bitswapMetrics module.BitswapMetrics - sporkId flow.Identifier - validators []network.MessageValidator - peerManagerFilters []p2p.PeerFilter - unicastMessageTimeout time.Duration - idTranslator p2p.IDTranslator - previousProtocolStatePeers []peer.AddrInfo - codec network.Codec - slashingViolationsConsumer network.ViolationsConsumer - unicastRateLimiters *ratelimit.RateLimiters - authorizedSenderValidator *validator.AuthorizedSenderValidator -} - -type OptionFn func(*Middleware) - -func WithMessageValidators(validators ...network.MessageValidator) OptionFn { - return func(mw *Middleware) { - mw.validators = validators - } -} - -func WithPreferredUnicastProtocols(unicasts []protocols.ProtocolName) OptionFn { - return func(mw *Middleware) { - mw.preferredUnicasts = unicasts - } -} - -// WithPeerManagerFilters sets a list of p2p.PeerFilter funcs that are used to -// filter out peers provided by the peer manager PeersProvider. -func WithPeerManagerFilters(peerManagerFilters []p2p.PeerFilter) OptionFn { - return func(mw *Middleware) { - mw.peerManagerFilters = peerManagerFilters - } -} - -// WithUnicastRateLimiters sets the unicast rate limiters. -func WithUnicastRateLimiters(rateLimiters *ratelimit.RateLimiters) OptionFn { - return func(mw *Middleware) { - mw.unicastRateLimiters = rateLimiters - } -} - -// Config is the configuration for the middleware. -type Config struct { - Logger zerolog.Logger - Libp2pNode p2p.LibP2PNode - FlowId flow.Identifier // This node's Flow ID - BitSwapMetrics module.BitswapMetrics - SporkId flow.Identifier - UnicastMessageTimeout time.Duration - IdTranslator p2p.IDTranslator - Codec network.Codec - SlashingViolationConsumerFactory func() network.ViolationsConsumer -} - -// Validate validates the configuration, and sets default values for any missing fields. -func (cfg *Config) Validate() { - if cfg.UnicastMessageTimeout <= 0 { - cfg.UnicastMessageTimeout = DefaultUnicastTimeout - } -} - -// NewMiddleware creates a new middleware instance -// libP2PNodeFactory is the factory used to create a LibP2PNode -// flowID is this node's Flow ID -// metrics is the interface to report network related metrics -// unicastMessageTimeout is the timeout used for unicast messages -// connectionGating if set to True, restricts this node to only talk to other nodes which are part of the identity list -// validators are the set of the different message validators that each inbound messages is passed through -// During normal operations any error returned by Middleware.start is considered to be catastrophic -// and will be thrown by the irrecoverable.SignalerContext causing the node to crash. -func NewMiddleware(cfg *Config, opts ...OptionFn) *Middleware { - cfg.Validate() - - // create the node entity and inject dependencies & config - mw := &Middleware{ - log: cfg.Logger, - libP2PNode: cfg.Libp2pNode, - bitswapMetrics: cfg.BitSwapMetrics, - sporkId: cfg.SporkId, - validators: DefaultValidators(cfg.Logger, cfg.FlowId), - unicastMessageTimeout: cfg.UnicastMessageTimeout, - idTranslator: cfg.IdTranslator, - codec: cfg.Codec, - unicastRateLimiters: ratelimit.NoopRateLimiters(), - } - - for _, opt := range opts { - opt(mw) - } - - builder := component.NewComponentManagerBuilder() - for _, limiter := range mw.unicastRateLimiters.Limiters() { - rateLimiter := limiter - builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - ready() - rateLimiter.Start(ctx) - <-rateLimiter.Ready() - ready() - <-rateLimiter.Done() - }) - } - builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - mw.ctx = ctx - if mw.ov == nil { - ctx.Throw(fmt.Errorf("overlay has not been set")) - } - - // creation of slashing violations consumer should be postponed till here where the middleware - // is start and the overlay is set. - mw.slashingViolationsConsumer = cfg.SlashingViolationConsumerFactory() - - mw.authorizedSenderValidator = validator.NewAuthorizedSenderValidator( - mw.log, - mw.slashingViolationsConsumer, - mw.ov.Identity) - - err := mw.libP2PNode.WithDefaultUnicastProtocol(mw.handleIncomingStream, mw.preferredUnicasts) - if err != nil { - ctx.Throw(fmt.Errorf("could not register preferred unicast protocols on libp2p node: %w", err)) - } - - mw.UpdateNodeAddresses() - mw.libP2PNode.WithPeersProvider(mw.authorizedPeers) - - ready() - - <-ctx.Done() - mw.log.Info().Str("component", "middleware").Msg("stopping subroutines") - - // wait for the readConnection and readSubscription routines to stop - mw.wg.Wait() - mw.log.Info().Str("component", "middleware").Msg("stopped subroutines") - }) - - mw.Component = builder.Build() - return mw -} - -func DefaultValidators(log zerolog.Logger, flowID flow.Identifier) []network.MessageValidator { - return []network.MessageValidator{ - validator.ValidateNotSender(flowID), // validator to filter out messages sent by this node itself - validator.ValidateTarget(log, flowID), // validator to filter out messages not intended for this node - } -} - -// isProtocolParticipant returns a PeerFilter that returns true if a peer is a staked node. -func (m *Middleware) isProtocolParticipant() p2p.PeerFilter { - return func(p peer.ID) error { - if _, ok := m.ov.Identity(p); !ok { - return fmt.Errorf("failed to get identity of unknown peer with peer id %s", p.String()) - } - return nil - } -} - -func (m *Middleware) NewBlobService(channel channels.Channel, ds datastore.Batching, opts ...network.BlobServiceOption) network.BlobService { - return blob.NewBlobService(m.libP2PNode.Host(), m.libP2PNode.Routing(), channel.String(), ds, m.bitswapMetrics, m.log, opts...) -} - -func (m *Middleware) NewPingService(pingProtocol protocol.ID, provider network.PingInfoProvider) network.PingService { - return ping.NewPingService(m.libP2PNode.Host(), pingProtocol, m.log, provider) -} - -func (m *Middleware) peerIDs(flowIDs flow.IdentifierList) peer.IDSlice { - result := make([]peer.ID, 0, len(flowIDs)) - - for _, fid := range flowIDs { - pid, err := m.idTranslator.GetPeerID(fid) - if err != nil { - // We probably don't need to fail the entire function here, since the other - // translations may still succeed - m.log.Err(err).Str("flowID", fid.String()).Msg("failed to translate to peer ID") - continue - } - - result = append(result, pid) - } - - return result -} - -func (m *Middleware) UpdateNodeAddresses() { - m.log.Info().Msg("Updating protocol state node addresses") - - ids := m.ov.Identities() - newInfos, invalid := utils.PeerInfosFromIDs(ids) - - for id, err := range invalid { - m.log.Err(err).Str("node_id", id.String()).Msg("failed to extract peer info from identity") - } - - m.Lock() - defer m.Unlock() - - // set old addresses to expire - for _, oldInfo := range m.previousProtocolStatePeers { - m.libP2PNode.Host().Peerstore().SetAddrs(oldInfo.ID, oldInfo.Addrs, peerstore.TempAddrTTL) - } - - for _, info := range newInfos { - m.libP2PNode.Host().Peerstore().SetAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL) - } - - m.previousProtocolStatePeers = newInfos -} - -func (m *Middleware) SetOverlay(ov network.Overlay) { - m.ov = ov -} - -// authorizedPeers is a peer manager callback used by the underlying libp2p node that updates who can connect to this node (as -// well as who this node can connect to). -// and who is not allowed to connect to this node. This function is called by the peer manager and connection gater components -// of libp2p. -// -// Args: -// none -// Returns: -// - peer.IDSlice: a list of peer IDs that are allowed to connect to this node (and that this node can connect to). Any peer -// not in this list is assumed to be disconnected from this node (if connected) and not allowed to connect to this node. -// This is the guarantee that the underlying libp2p node implementation makes. -func (m *Middleware) authorizedPeers() peer.IDSlice { - peerIDs := make([]peer.ID, 0) - for _, id := range m.peerIDs(m.ov.Topology().NodeIDs()) { - peerAllowed := true - for _, filter := range m.peerManagerFilters { - if err := filter(id); err != nil { - m.log.Debug(). - Err(err). - Str("peer_id", id.String()). - Msg("filtering topology peer") - - peerAllowed = false - break - } - } - - if peerAllowed { - peerIDs = append(peerIDs, id) - } - } - - return peerIDs -} - -func (m *Middleware) OnDisallowListNotification(notification *network.DisallowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.libP2PNode.OnDisallowListNotification(pid, notification.Cause) - } -} - -func (m *Middleware) OnAllowListNotification(notification *network.AllowListingUpdate) { - for _, pid := range m.peerIDs(notification.FlowIds) { - m.libP2PNode.OnAllowListNotification(pid, notification.Cause) - } -} - -// OpenProtectedStream acts as a short-circuit method that delegates the opening of a protected stream to the underlying -// libP2PNode. This method is intended to be temporary and is going to be removed in the long term. Users should plan -// to interact with the libP2P node directly in the future. -// -// Parameters: -// ctx: The context used to control the stream's lifecycle. -// peerID: The ID of the peer to open the stream to. -// protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive. -// writingLogic: A callback function that contains the logic for writing to the stream. -// -// Returns: -// error: An error, if any occurred during the process. All returned errors are benign. -// -// Note: This method is subject to removal in future versions and direct use of the libp2p node is encouraged. -// TODO: Remove this method in the future. -func (m *Middleware) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnetwork.Stream) error) error { - return m.libP2PNode.OpenProtectedStream(ctx, peerID, protectionTag, writingLogic) -} - -// handleIncomingStream handles an incoming stream from a remote peer -// it is a callback that gets called for each incoming stream by libp2p with a new stream object -func (m *Middleware) handleIncomingStream(s libp2pnetwork.Stream) { - // qualify the logger with local and remote address - log := p2putils.StreamLogger(m.log, s) - - log.Info().Msg("incoming stream received") - - success := false - - remotePeer := s.Conn().RemotePeer() - - defer func() { - if success { - err := s.Close() - if err != nil { - log.Err(err).Msg("failed to close stream") - } - } else { - err := s.Reset() - if err != nil { - log.Err(err).Msg("failed to reset stream") - } - } - }() - - // check if peer is currently rate limited before continuing to process stream. - if m.unicastRateLimiters.MessageRateLimiter.IsRateLimited(remotePeer) || m.unicastRateLimiters.BandWidthRateLimiter.IsRateLimited(remotePeer) { - log.Debug(). - Bool(logging.KeySuspicious, true). - Msg("dropping unicast stream from rate limited peer") - return - } - - // TODO: We need to allow per-topic timeouts and message size limits. - // This allows us to configure higher limits for topics on which we expect - // to receive large messages (e.g. Chunk Data Packs), and use the normal - // limits for other topics. In order to enable this, we will need to register - // a separate stream handler for each topic. - ctx, cancel := context.WithTimeout(m.ctx, LargeMsgUnicastTimeout) - defer cancel() - - deadline, _ := ctx.Deadline() - - err := s.SetReadDeadline(deadline) - if err != nil { - log.Err(err).Msg("failed to set read deadline for stream") - return - } - - // create the reader - r := ggio.NewDelimitedReader(s, LargeMsgMaxUnicastMsgSize) - for { - if ctx.Err() != nil { - return - } - - // Note: message fields must not be trusted until explicitly validated - var msg message.Message - // read the next message (blocking call) - err = r.ReadMsg(&msg) - if err != nil { - if err == io.EOF { - break - } - - m.log.Err(err).Msg("failed to read message") - return - } - - channel := channels.Channel(msg.ChannelID) - topic := channels.TopicFromChannel(channel, m.sporkId) - - // ignore messages if node does not have subscription to topic - if !m.libP2PNode.HasSubscription(topic) { - violation := &network.Violation{ - Identity: nil, PeerID: remotePeer.String(), Channel: channel, Protocol: message.ProtocolTypeUnicast, - } - - msgCode, err := codec.MessageCodeFromPayload(msg.Payload) - if err != nil { - violation.Err = err - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - } - - // msg type is not guaranteed to be correct since it is set by the client - _, what, err := codec.InterfaceFromMessageCode(msgCode) - if err != nil { - violation.Err = err - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - } - - violation.MsgType = what - violation.Err = ErrUnicastMsgWithoutSub - m.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(violation) - return - } - - // check if unicast messages have reached rate limit before processing next message - if !m.unicastRateLimiters.MessageAllowed(remotePeer) { - return - } - - // check if we can get a role for logging and metrics label if this is not a public channel - role := "" - if !channels.IsPublicChannel(channels.Channel(msg.ChannelID)) { - if identity, ok := m.ov.Identity(remotePeer); ok { - role = identity.Role.String() - } - } - - // check unicast bandwidth rate limiter for peer - if !m.unicastRateLimiters.BandwidthAllowed( - remotePeer, - role, - msg.Size(), - message.MessageType(msg.Payload), - channels.Topic(msg.ChannelID)) { - return - } - - m.wg.Add(1) - go func() { - defer m.wg.Done() - m.processUnicastStreamMessage(remotePeer, &msg) - }() - } - - success = true -} - -// Subscribe subscribes the middleware to a channel. -// No errors are expected during normal operation. -func (m *Middleware) Subscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, m.sporkId) - - var peerFilter p2p.PeerFilter - var validators []validator.PubSubMessageValidator - if channels.IsPublicChannel(channel) { - // NOTE: for public channels the callback used to check if a node is staked will - // return true for every node. - peerFilter = p2p.AllowAllPeerFilter() - } else { - // for channels used by the staked nodes, add the topic validator to filter out messages from non-staked nodes - validators = append(validators, m.authorizedSenderValidator.PubSubMessageValidator(channel)) - - // NOTE: For non-public channels the libP2P node topic validator will reject - // messages from unstaked nodes. - peerFilter = m.isProtocolParticipant() - } - - topicValidator := flowpubsub.TopicValidator(m.log, peerFilter, validators...) - s, err := m.libP2PNode.Subscribe(topic, topicValidator) - if err != nil { - return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) - } - - // create a new readSubscription with the context of the middleware - rs := newReadSubscription(s, m.processPubSubMessages, m.log) - m.wg.Add(1) - - // kick off the receive loop to continuously receive messages - go func() { - defer m.wg.Done() - rs.receiveLoop(m.ctx) - }() - - // update peers to add some nodes interested in the same topic as direct peers - m.libP2PNode.RequestPeerUpdate() - - return nil -} - -// processPubSubMessages processes messages received from the pubsub subscription. -func (m *Middleware) processPubSubMessages(msg *message.Message, peerID peer.ID) { - m.processAuthenticatedMessage(msg, peerID, message.ProtocolTypePubSub) -} - -// Unsubscribe unsubscribes the middleware from a channel. -// The following benign errors are expected during normal operations from libP2P: -// - the libP2P node fails to unsubscribe to the topic created from the provided channel. -// -// All errors returned from this function can be considered benign. -func (m *Middleware) Unsubscribe(channel channels.Channel) error { - topic := channels.TopicFromChannel(channel, m.sporkId) - return m.libP2PNode.Unsubscribe(topic) -} - -// processUnicastStreamMessage will decode, perform authorized sender validation and process a message -// sent via unicast stream. This func should be invoked in a separate goroutine to avoid creating a message decoding bottleneck. -func (m *Middleware) processUnicastStreamMessage(remotePeer peer.ID, msg *message.Message) { - channel := channels.Channel(msg.ChannelID) - - // TODO: once we've implemented per topic message size limits per the TODO above, - // we can remove this check - maxSize, err := UnicastMaxMsgSizeByCode(msg.Payload) - if err != nil { - m.slashingViolationsConsumer.OnUnknownMsgTypeError(&network.Violation{ - Identity: nil, PeerID: remotePeer.String(), MsgType: "", Channel: channel, Protocol: message.ProtocolTypeUnicast, Err: err, - }) - return - } - if msg.Size() > maxSize { - // message size exceeded - m.log.Error(). - Str("peer_id", remotePeer.String()). - Str("channel", msg.ChannelID). - Int("max_size", maxSize). - Int("size", msg.Size()). - Bool(logging.KeySuspicious, true). - Msg("received message exceeded permissible message maxSize") - return - } - - // if message channel is not public perform authorized sender validation - if !channels.IsPublicChannel(channel) { - messageType, err := m.authorizedSenderValidator.Validate(remotePeer, msg.Payload, channel, message.ProtocolTypeUnicast) - if err != nil { - m.log. - Error(). - Err(err). - Str("peer_id", remotePeer.String()). - Str("type", messageType). - Str("channel", msg.ChannelID). - Msg("unicast authorized sender validation failed") - return - } - } - m.processAuthenticatedMessage(msg, remotePeer, message.ProtocolTypeUnicast) -} - -// processAuthenticatedMessage processes a message and a source (indicated by its peer ID) and eventually passes it to the overlay -// In particular, it populates the `OriginID` field of the message with a Flow ID translated from this source. -func (m *Middleware) processAuthenticatedMessage(msg *message.Message, peerID peer.ID, protocol message.ProtocolType) { - originId, err := m.idTranslator.GetFlowID(peerID) - if err != nil { - // this error should never happen. by the time the message gets here, the peer should be - // authenticated which means it must be known - m.log.Error(). - Err(err). - Str("peer_id", peerID.String()). - Bool(logging.KeySuspicious, true). - Msg("dropped message from unknown peer") - return - } - - channel := channels.Channel(msg.ChannelID) - decodedMsgPayload, err := m.codec.Decode(msg.Payload) - switch { - case codec.IsErrUnknownMsgCode(err): - // slash peer if message contains unknown message code byte - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnUnknownMsgTypeError(violation) - return - case codec.IsErrMsgUnmarshal(err) || codec.IsErrInvalidEncoding(err): - // slash if peer sent a message that could not be marshalled into the message type denoted by the message code byte - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnInvalidMsgError(violation) - return - case err != nil: - // this condition should never happen and indicates there's a bug - // don't crash as a result of external inputs since that creates a DoS vector - // collect slashing data because this could potentially lead to slashing - err = fmt.Errorf("unexpected error during message validation: %w", err) - violation := &network.Violation{ - PeerID: peerID.String(), OriginID: originId, Channel: channel, Protocol: protocol, Err: err, - } - m.slashingViolationsConsumer.OnUnexpectedError(violation) - return - } - - scope, err := message.NewIncomingScope(originId, protocol, msg, decodedMsgPayload) - if err != nil { - m.log.Error(). - Err(err). - Str("peer_id", peerID.String()). - Str("origin_id", originId.String()). - Msg("could not create incoming message scope") - return - } - - m.processMessage(scope) -} - -// processMessage processes a message and eventually passes it to the overlay -func (m *Middleware) processMessage(scope network.IncomingMessageScope) { - logger := m.log.With(). - Str("channel", scope.Channel().String()). - Str("type", scope.Protocol().String()). - Int("msg_size", scope.Size()). - Hex("origin_id", logging.ID(scope.OriginId())). - Logger() - - // run through all the message validators - for _, v := range m.validators { - // if any one fails, stop message propagation - if !v.Validate(scope) { - logger.Debug().Msg("new message filtered by message validators") - return - } - } - - logger.Debug().Msg("processing new message") - - // if validation passed, send the message to the overlay - err := m.ov.Receive(scope) - if err != nil { - m.log.Error().Err(err).Msg("could not deliver payload") - } -} - -// Publish publishes a message on the channel. It models a distributed broadcast where the message is meant for all or -// a many nodes subscribing to the channel. It does not guarantee the delivery though, and operates on a best -// effort. -// The following benign errors are expected during normal operations: -// - the msg cannot be marshalled. -// - the msg size exceeds DefaultMaxPubSubMsgSize. -// - the libP2P node fails to publish the message. -// -// All errors returned from this function can be considered benign. -// TODO: DO NOT USE. Publish is ready to be removed from middleware. Use libp2pNode.Publish directly. -func (m *Middleware) Publish(msg network.OutgoingMessageScope) error { - return m.libP2PNode.Publish(m.ctx, msg) -} - -// unicastMaxMsgSize returns the max permissible size for a unicast message -func unicastMaxMsgSize(messageType string) int { - switch messageType { - case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": - return LargeMsgMaxUnicastMsgSize - default: - return DefaultMaxUnicastMsgSize - } -} - -// UnicastMaxMsgSizeByCode returns the max permissible size for a unicast message code -func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { - msgCode, err := codec.MessageCodeFromPayload(payload) - if err != nil { - return 0, err - } - _, messageType, err := codec.InterfaceFromMessageCode(msgCode) - if err != nil { - return 0, err - } - - maxSize := unicastMaxMsgSize(messageType) - return maxSize, nil -} diff --git a/network/p2p/middleware/middleware_test.go b/network/p2p/middleware/middleware_test.go deleted file mode 100644 index b1708701418..00000000000 --- a/network/p2p/middleware/middleware_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package middleware_test - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/messages" - "github.com/onflow/flow-go/network/channels" - "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/p2p/middleware" - "github.com/onflow/flow-go/utils/unittest" -) - -// TestChunkDataPackMaxMessageSize tests that the max message size for a chunk data pack response is set to the large message size. -func TestChunkDataPackMaxMessageSize(t *testing.T) { - // creates an outgoing chunk data pack response message (imitating an EN is sending a chunk data pack response to VN). - msg, err := message.NewOutgoingScope( - flow.IdentifierList{unittest.IdentifierFixture()}, - channels.TopicFromChannel(channels.ProvideChunks, unittest.IdentifierFixture()), - &messages.ChunkDataResponse{ - ChunkDataPack: *unittest.ChunkDataPackFixture(unittest.IdentifierFixture()), - Nonce: rand.Uint64(), - }, - unittest.NetworkCodec().Encode, - message.ProtocolTypeUnicast) - require.NoError(t, err) - - // get the max message size for the message - size, err := middleware.UnicastMaxMsgSizeByCode(msg.Proto().Payload) - require.NoError(t, err) - require.Equal(t, middleware.LargeMsgMaxUnicastMsgSize, size) -} diff --git a/network/p2p/middleware/readSubscription.go b/network/p2p/middleware/readSubscription.go deleted file mode 100644 index 442fb152453..00000000000 --- a/network/p2p/middleware/readSubscription.go +++ /dev/null @@ -1,82 +0,0 @@ -package middleware - -import ( - "context" - "errors" - "fmt" - "strings" - - "github.com/libp2p/go-libp2p/core/peer" - - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/network/message" - "github.com/onflow/flow-go/network/p2p" - validator "github.com/onflow/flow-go/network/validator/pubsub" - "github.com/onflow/flow-go/utils/logging" -) - -// ReadSubscriptionCallBackFunction the callback called when a new message is received on the read subscription -type ReadSubscriptionCallBackFunction func(msg *message.Message, peerID peer.ID) - -// readSubscription reads the messages coming in on the subscription and calls the given callback until -// the context of the subscription is cancelled. -type readSubscription struct { - log zerolog.Logger - sub p2p.Subscription - callback ReadSubscriptionCallBackFunction -} - -// newReadSubscription reads the messages coming in on the subscription -func newReadSubscription(sub p2p.Subscription, callback ReadSubscriptionCallBackFunction, log zerolog.Logger) *readSubscription { - r := readSubscription{ - log: log.With().Str("channel", sub.Topic()).Logger(), - sub: sub, - callback: callback, - } - - return &r -} - -// receiveLoop must be run in a goroutine. It continuously receives -// messages for the topic and calls the callback synchronously -func (r *readSubscription) receiveLoop(ctx context.Context) { - defer r.log.Debug().Msg("exiting receive routine") - - for { - // read the next message from libp2p's subscription (blocking call) - rawMsg, err := r.sub.Next(ctx) - - if err != nil { - // middleware may have cancelled the context - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - return - } - - // subscription may have just been cancelled if node is being stopped or the topic has been unsubscribed, - // don't log error in that case - // (https://github.com/ipsn/go-ipfs/blob/master/gxlibs/github.com/libp2p/go-libp2p-pubsub/pubsub.go#L435) - if strings.Contains(err.Error(), "subscription cancelled") { - return - } - - // log any other error - r.log.Err(err).Msg("failed to read subscription message") - - return - } - - validatorData, ok := rawMsg.ValidatorData.(validator.TopicValidatorData) - if !ok { - r.log.Error(). - Str("raw_msg", rawMsg.String()). - Bool(logging.KeySuspicious, true). - Str("received_validator_data_type", fmt.Sprintf("%T", rawMsg.ValidatorData)). - Msg("[BUG] validator data missing!") - return - } - - // call the callback - r.callback(validatorData.Message, validatorData.From) - } -} diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 4d24ae8b062..b359eed123a 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -29,7 +29,6 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/p2pnode" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -175,7 +174,7 @@ func (suite *MeshEngineTestSuite) TestTargetedValidators_Publish() { // TestMaxMessageSize_Unicast evaluates the messageSizeScenario scenario using // the Unicast method of conduits. func (suite *MeshEngineTestSuite) TestMaxMessageSize_Unicast() { - suite.messageSizeScenario(suite.Unicast, middleware.DefaultMaxUnicastMsgSize) + suite.messageSizeScenario(suite.Unicast, p2pnet.DefaultMaxUnicastMsgSize) } // TestMaxMessageSize_Multicast evaluates the messageSizeScenario scenario using diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index b219fce8dc8..92730a0c25a 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -3,6 +3,7 @@ package test import ( "context" "fmt" + "math/rand" "regexp" "strings" "sync" @@ -31,6 +32,7 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/p2pfixtures" "github.com/onflow/flow-go/network/internal/testutils" + "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/middleware" @@ -749,7 +751,7 @@ func (m *MiddlewareTestSuite) TestMaxMessageSize_Unicast() { // so the generated payload is 1000 bytes below the maximum unicast message size. // We hence add up 1000 bytes to the input of network payload fixture to make // sure that payload is beyond the permissible size. - payload := testutils.NetworkPayloadFixture(m.T(), uint(middleware.DefaultMaxUnicastMsgSize)+1000) + payload := testutils.NetworkPayloadFixture(m.T(), uint(p2pnet.DefaultMaxUnicastMsgSize)+1000) event := &libp2pmessage.TestMessage{ Text: string(payload), } @@ -768,7 +770,7 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { targetId := m.ids[targetIndex].NodeID // creates a network payload with a size greater than the default max size using a known large message type - targetSize := uint64(middleware.DefaultMaxUnicastMsgSize) + 1000 + targetSize := uint64(p2pnet.DefaultMaxUnicastMsgSize) + 1000 event := unittest.ChunkDataResponseMsgFixture(unittest.IdentifierFixture(), unittest.WithApproximateSize(targetSize)) // expect one message to be received by the target @@ -890,3 +892,23 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { // assert that the new message is not received by the target node unittest.RequireNeverReturnBefore(m.T(), msgRcvdFun, 2*time.Second, "message received unexpectedly") } + +// TestChunkDataPackMaxMessageSize tests that the max message size for a chunk data pack response is set to the large message size. +func TestChunkDataPackMaxMessageSize(t *testing.T) { + // creates an outgoing chunk data pack response message (imitating an EN is sending a chunk data pack response to VN). + msg, err := message.NewOutgoingScope( + flow.IdentifierList{unittest.IdentifierFixture()}, + channels.TopicFromChannel(channels.ProvideChunks, unittest.IdentifierFixture()), + &messages.ChunkDataResponse{ + ChunkDataPack: *unittest.ChunkDataPackFixture(unittest.IdentifierFixture()), + Nonce: rand.Uint64(), + }, + unittest.NetworkCodec().Encode, + message.ProtocolTypeUnicast) + require.NoError(t, err) + + // get the max message size for the message + size, err := middleware.UnicastMaxMsgSizeByCode(msg.Proto().Payload) + require.NoError(t, err) + require.Equal(t, p2pnet.LargeMsgMaxUnicastMsgSize, size) +} From 4b1d2cc59d08c7556e61db8cf8477e5be6b74c79 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:09:03 -0400 Subject: [PATCH 767/815] lint fix --- network/test/middleware_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/test/middleware_test.go b/network/test/middleware_test.go index 92730a0c25a..ab71413228f 100644 --- a/network/test/middleware_test.go +++ b/network/test/middleware_test.go @@ -35,7 +35,6 @@ import ( "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/mocknetwork" "github.com/onflow/flow-go/network/p2p" - "github.com/onflow/flow-go/network/p2p/middleware" "github.com/onflow/flow-go/network/p2p/p2pnet" "github.com/onflow/flow-go/network/p2p/p2pnode" p2ptest "github.com/onflow/flow-go/network/p2p/test" @@ -908,7 +907,7 @@ func TestChunkDataPackMaxMessageSize(t *testing.T) { require.NoError(t, err) // get the max message size for the message - size, err := middleware.UnicastMaxMsgSizeByCode(msg.Proto().Payload) + size, err := p2pnet.UnicastMaxMsgSizeByCode(msg.Proto().Payload) require.NoError(t, err) require.Equal(t, p2pnet.LargeMsgMaxUnicastMsgSize, size) } From bbde45e1f2664474bfb884111128712c0d59f7c9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:43:41 -0400 Subject: [PATCH 768/815] removes middleware keyword --- network/alsp/manager/manager.go | 4 +- network/alsp/manager/manager_test.go | 20 +- network/internal/testutils/testUtil.go | 45 +- network/middleware.go | 52 -- network/mocknetwork/middleware.go | 112 ---- network/network.go | 40 +- network/p2p/cache/node_blocklist_wrapper.go | 2 +- network/p2p/p2pnet/network.go | 23 +- network/p2p/p2pnet/readSubscription.go | 2 +- .../p2p/subscription/subscriptionManager.go | 6 +- network/test/blob_service_test.go | 2 +- network/test/echoengine_test.go | 4 +- network/test/epochtransition_test.go | 20 +- .../{middleware_test.go => network_test.go} | 540 +++++++++--------- network/test/unicast_authorization_test.go | 49 +- network/validator.go | 2 +- 16 files changed, 370 insertions(+), 553 deletions(-) delete mode 100644 network/middleware.go delete mode 100644 network/mocknetwork/middleware.go rename network/test/{middleware_test.go => network_test.go} (58%) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index fdd0e079519..082cd98be77 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -311,7 +311,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { } // TODO: this can be done in batch but at this stage let's send individual notifications. - // (it requires enabling the batch mode end-to-end including the cache in middleware). + // (it requires enabling the batch mode end-to-end including the cache in Network). // as long as record.Penalty is NOT below model.DisallowListingThreshold, // the node is considered allow-listed and can conduct inbound and outbound connections. // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. @@ -352,7 +352,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { Msg("heartbeat interval, spam record penalty adjusted by decay function") // TODO: this can be done in batch but at this stage let's send individual notifications. - // (it requires enabling the batch mode end-to-end including the cache in middleware). + // (it requires enabling the batch mode end-to-end including the cache in network). if record.Penalty == float64(0) && record.DisallowListed { record.DisallowListed = false diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index a3baa989656..8d5400d6f6e 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -57,7 +57,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { sporkId := unittest.IdentifierFixture() misbehaviorReportManger.On("Ready").Return(readyDoneChan).Once() misbehaviorReportManger.On("Done").Return(readyDoneChan).Once() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0]) net, err := p2pnet.NewNetwork(networkCfg, p2pnet.WithAlspManager(misbehaviorReportManger)) @@ -115,7 +115,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { } sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) net, err := p2pnet.NewNetwork(networkCfg) @@ -210,7 +210,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) } sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture( + ids, nodes := testutils.LibP2PNodeForNetworkFixture( t, sporkId, 3, @@ -307,7 +307,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio alspmgr.WithDecayFunc(fastDecayFunc), } - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 3, + ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 3, p2ptest.WithPeerManagerEnabled(p2ptest.PeerManagerConfigFixture(p2ptest.WithZeroJitterAndZeroBackoff(t)), nil)) idProvider := unittest.NewUpdatableIDProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) @@ -459,16 +459,14 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t sporkId := unittest.IdentifierFixture() // create 1 victim node, 1 honest node and a node for each slashing violation - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). + ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). idProvider := id.NewFixedIdentityProvider(ids) - // we want to override the middleware config to use the slashing violations consumer. However, we need the network - // instance to do that, but for the network instance we need the middleware config. So, we create the adapter first, which - // is a placeholder for the network instance, and then we create the network instance with the middleware config. + // we want to override the network config to use the slashing violations consumer. However, we need the network + // instance to do that, but for the network instance we need the network config. So, we create the adapter first, which + // is a placeholder for the network instance, and then we create the network instance with the network config. // Network adapter is a sub-interface of the network instance, so we can use it as a placeholder for the network instance. - // This is a bit of a chicken and egg problem; you see! that is why we must get rid of the middleware soon! var adapter network.Adapter - // also a placeholder for the slashing violations consumer. var violationsConsumer network.ViolationsConsumer networkCfg := testutils.NetworkConfigFixture( @@ -568,7 +566,7 @@ func TestMisbehaviorReportMetrics(t *testing.T) { cfg.AlspMetrics = alspMetrics sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(t, sporkId, 1) + ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 1) idProvider := id.NewFixedIdentityProvider(ids) networkCfg := testutils.NetworkConfigFixture(t, *ids[0], idProvider, sporkId, nodes[0], p2pnet.WithAlspConfig(cfg)) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 83604d9272a..b04e217e1aa 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -109,9 +109,9 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti }, nil } -// LibP2PNodeForMiddlewareFixture is a test helper that generate flow identities with a valid port and libp2p nodes. -// Note that the LibP2PNode created by this fixture is meant to used with a middleware component. -// If you want to create a standalone LibP2PNode without network and middleware components, please use p2ptest.NodeFixture. +// LibP2PNodeForNetworkFixture is a test helper that generate flow identities with a valid port and libp2p nodes. +// Note that the LibP2PNode created by this fixture is meant to used with a network component. +// If you want to create a standalone LibP2PNode without network component, please use p2ptest.NodeFixture. // Args: // // t: testing.T- the test object @@ -125,7 +125,7 @@ func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnecti // // []p2p.LibP2PNode - list of libp2p nodes created. // TODO: several test cases only need a single node, consider encapsulating this function in a single node fixture. -func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode) { +func LibP2PNodeForNetworkFixture(t *testing.T, sporkId flow.Identifier, n int, opts ...p2ptest.NodeFixtureParameterOption) (flow.IdentityList, []p2p.LibP2PNode) { libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) @@ -144,41 +144,7 @@ func LibP2PNodeForMiddlewareFixture(t *testing.T, sporkId flow.Identifier, n int return identities, libP2PNodes } -//// MiddlewareConfigFixture is a test helper that generates a middleware config for testing. -//// Args: -//// - t: the test instance. -//// Returns: -//// - a middleware config. -//func MiddlewareConfigFixture(t *testing.T, sporkId flow.Identifier) *middleware.Config { -// return &middleware.Config{ -// Logger: unittest.Logger(), -// SporkId: sporkId, -// UnicastMessageTimeout: middleware.DefaultUnicastTimeout, -// Codec: unittest.NetworkCodec(), -// } -//} - -//// MiddlewareFixtures is a test helper that generates middlewares with the given identities and libp2p nodes. -//// It also generates a list of UpdatableIDProvider that can be used to update the identities of the middlewares. -//// The number of identities and libp2p nodes must be the same. -//// Args: -//// - identities: a list of flow identities that correspond to the libp2p nodes. -//// - libP2PNodes: a list of libp2p nodes that correspond to the identities. -//// - cfg: the middleware config. -//// - opts: a list of middleware option functions. -//// Returns: -//// - a list of middlewares - one for each identity. -//// - a list of UpdatableIDProvider - one for each identity. -//func MiddlewareFixtures( -// t *testing.T, -// identities flow.IdentityList, -// libP2PNodes []p2p.LibP2PNode, -// cfg *middleware.Config, -// opts ...middleware.OptionFn) ([]network.Middleware, []*unittest.UpdatableIDProvider) { -// return nil, nil -//} - -// NetworksFixture generates the network for the given middlewares +// NetworksFixture generates the network for the given libp2p nodes. func NetworksFixture(t *testing.T, sporkId flow.Identifier, ids flow.IdentityList, @@ -288,7 +254,6 @@ func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, node // // This function fails the test if the networks do not start within the given timeout. func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.Network, duration time.Duration) { - // start up networks (this will implicitly start middlewares) for _, net := range nets { net.Start(ctx) unittest.RequireComponentsReadyBefore(t, duration, net) diff --git a/network/middleware.go b/network/middleware.go deleted file mode 100644 index e4bcdf19b5c..00000000000 --- a/network/middleware.go +++ /dev/null @@ -1,52 +0,0 @@ -// (c) 2019 Dapper Labs - ALL RIGHTS RESERVED - -package network - -import ( - "github.com/libp2p/go-libp2p/core/peer" - - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/network/channels" -) - -// Middleware represents the middleware layer, which manages the connections to -// our direct neighbours on the network. It handles the creation & teardown of -// connections, as well as reading & writing to/from the connections. -type Middleware interface { - component.Component - DisallowListNotificationConsumer - - // Subscribe subscribes the middleware to a channel. - // No errors are expected during normal operation. - Subscribe(channel channels.Channel) error - - // Unsubscribe unsubscribes the middleware from a channel. - // All errors returned from this function can be considered benign. - Unsubscribe(channel channels.Channel) error - - // UpdateNodeAddresses fetches and updates the addresses of all the authorized participants - // in the Flow protocol. - UpdateNodeAddresses() -} - -// Overlay represents the interface that middleware uses to interact with the -// overlay network layer. -type Overlay interface { - // Topology returns an identity list of nodes which this node should be directly connected to as peers - Topology() flow.IdentityList - - // Identities returns a list of all Flow identities on the network - Identities() flow.IdentityList - - // Identity returns the Identity associated with the given peer ID, if it exists - Identity(peer.ID) (*flow.Identity, bool) - - Receive(IncomingMessageScope) error -} - -// Connection represents an interface to read from & write to a connection. -type Connection interface { - Send(msg interface{}) error - Receive() (interface{}, error) -} diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go deleted file mode 100644 index 3816ee996f5..00000000000 --- a/network/mocknetwork/middleware.go +++ /dev/null @@ -1,112 +0,0 @@ -// Code generated by mockery v2.21.4. DO NOT EDIT. - -package mocknetwork - -import ( - irrecoverable "github.com/onflow/flow-go/module/irrecoverable" - channels "github.com/onflow/flow-go/network/channels" - - mock "github.com/stretchr/testify/mock" - - network "github.com/onflow/flow-go/network" -) - -// Middleware is an autogenerated mock type for the Middleware type -type Middleware struct { - mock.Mock -} - -// Done provides a mock function with given fields: -func (_m *Middleware) Done() <-chan struct{} { - ret := _m.Called() - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// OnAllowListNotification provides a mock function with given fields: _a0 -func (_m *Middleware) OnAllowListNotification(_a0 *network.AllowListingUpdate) { - _m.Called(_a0) -} - -// OnDisallowListNotification provides a mock function with given fields: _a0 -func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpdate) { - _m.Called(_a0) -} - -// Ready provides a mock function with given fields: -func (_m *Middleware) Ready() <-chan struct{} { - ret := _m.Called() - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// Start provides a mock function with given fields: _a0 -func (_m *Middleware) Start(_a0 irrecoverable.SignalerContext) { - _m.Called(_a0) -} - -// Subscribe provides a mock function with given fields: channel -func (_m *Middleware) Subscribe(channel channels.Channel) error { - ret := _m.Called(channel) - - var r0 error - if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { - r0 = rf(channel) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Unsubscribe provides a mock function with given fields: channel -func (_m *Middleware) Unsubscribe(channel channels.Channel) error { - ret := _m.Called(channel) - - var r0 error - if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { - r0 = rf(channel) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateNodeAddresses provides a mock function with given fields: -func (_m *Middleware) UpdateNodeAddresses() { - _m.Called() -} - -type mockConstructorTestingTNewMiddleware interface { - mock.TestingT - Cleanup(func()) -} - -// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware { - mock := &Middleware{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/network/network.go b/network/network.go index 38896633e4d..cf781856fe4 100644 --- a/network/network.go +++ b/network/network.go @@ -33,10 +33,10 @@ const ( PublicNetwork ) -// Network represents the network layer of the node. It allows processes that -// work across the peer-to-peer network to register themselves as an engine with -// a unique engine ID. The returned conduit allows the process to communicate to -// the same engine on other nodes across the network in a network-agnostic way. +// Network is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// offers to the Flow protocol layer, i.e., engines. It is responsible for creating conduits through which engines +// can send and receive messages to and from other engines on the network, as well as registering other services +// such as BlobService and PingService. type Network interface { component.Component // Register will subscribe to the channel with the given engine and @@ -54,9 +54,9 @@ type Network interface { RegisterPingService(pingProtocolID protocol.ID, pingInfoProvider PingInfoProvider) (PingService, error) } -// Adapter is a wrapper around the Network implementation. It only exposes message dissemination functionalities. -// Adapter is meant to be utilized by the Conduit interface to send messages to the Network layer to be -// delivered to the remote targets. +// Adapter is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// offers to a single conduit which enables the conduit to send different types of messages i.e., unicast, multicast, +// and publish, to other conduits on the network. type Adapter interface { MisbehaviorReportConsumer // UnicastOnChannel sends the message in a reliable way to the given recipient. @@ -74,6 +74,32 @@ type Adapter interface { UnRegisterChannel(channel channels.Channel) error } +// Middleware is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// offers to lower level networking components such as libp2p. It is responsible for subscribing to and unsubscribing +// from channels, as well as updating the addresses of all the authorized participants in the Flow protocol. +type Middleware interface { + component.Component + DisallowListNotificationConsumer + + // Subscribe subscribes the middleware to a channel. + // No errors are expected during normal operation. + Subscribe(channel channels.Channel) error + + // Unsubscribe unsubscribes the middleware from a channel. + // All errors returned from this function can be considered benign. + Unsubscribe(channel channels.Channel) error + + // UpdateNodeAddresses fetches and updates the addresses of all the authorized participants + // in the Flow protocol. + UpdateNodeAddresses() +} + +// Connection represents an interface to read from & write to a connection. +type Connection interface { + Send(msg interface{}) error + Receive() (interface{}, error) +} + // MisbehaviorReportConsumer set of funcs used to handle MisbehaviorReport disseminated from misbehavior reporters. type MisbehaviorReportConsumer interface { // ReportMisbehaviorOnChannel reports the misbehavior of a node on sending a message to the current node that appears diff --git a/network/p2p/cache/node_blocklist_wrapper.go b/network/p2p/cache/node_blocklist_wrapper.go index f655215178a..10279953578 100644 --- a/network/p2p/cache/node_blocklist_wrapper.go +++ b/network/p2p/cache/node_blocklist_wrapper.go @@ -45,7 +45,7 @@ type NodeDisallowListingWrapper struct { // updateConsumerOracle is called whenever the disallow-list is updated. // Note that we do not use the `updateConsumer` directly due to the circular dependency between the - // middleware (i.e., updateConsumer), and the wrapper (i.e., NodeDisallowListingWrapper). + // networking layer Middleware interface (i.e., updateConsumer), and the wrapper (i.e., NodeDisallowListingWrapper). // Middleware needs identity provider to be initialized, and identity provider needs this wrapper to be initialized. // Hence, if we pass the updateConsumer by the interface value, it will be nil at the time of initialization. // Instead, we use the oracle function to get the updateConsumer whenever we need it. diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index d05c9fcc2e0..8942654f896 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -120,7 +120,6 @@ type Network struct { } var _ network.Network = &Network{} -var _ network.Overlay = &Network{} var _ network.Middleware = &Network{} type registerEngineRequest struct { @@ -259,10 +258,12 @@ func WithMessageValidators(validators ...network.MessageValidator) NetworkOption } } -// NewNetwork creates a new naive overlay network, using the given middleware to -// communicate to direct peers, using the given codec for serialization, and -// using the given state & cache interfaces to track volatile information. -// csize determines the size of the cache dedicated to keep track of received messages +// NewNetwork creates a new network with the given configuration. +// Args: +// param: network configuration +// opts: network options +// Returns: +// Network: a new network func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { param.Validate() @@ -342,7 +343,7 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { } builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { - // creation of slashing violations consumer should be postponed till here where the middleware + // creation of slashing violations consumer should be postponed till here where the network // is start and the overlay is set. n.slashingViolationsConsumer = param.SlashingViolationConsumerFactory() @@ -362,11 +363,11 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { ready() <-ctx.Done() - n.logger.Info().Str("component", "middleware").Msg("stopping subroutines, blocking on read connection loops to end") + n.logger.Info().Str("component", "network").Msg("stopping subroutines, blocking on read connection loops to end") // wait for the readConnection and readSubscription routines to stop n.wg.Wait() - n.logger.Info().Str("component", "middleware").Msg("stopped subroutines") + n.logger.Info().Str("component", "network").Msg("stopped subroutines") }) builder.AddWorker(n.createInboundMessageQueue) @@ -1065,7 +1066,7 @@ func (n *Network) handleIncomingStream(s libp2pnet.Stream) { success = true } -// Subscribe subscribes the middleware to a channel. +// Subscribe subscribes the network to a channel. // No errors are expected during normal operation. func (n *Network) Subscribe(channel channels.Channel) error { topic := channels.TopicFromChannel(channel, n.sporkId) @@ -1091,7 +1092,7 @@ func (n *Network) Subscribe(channel channels.Channel) error { return fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) } - // create a new readSubscription with the context of the middleware + // create a new readSubscription with the context of the network rs := NewReadSubscription(s, n.processPubSubMessages, n.logger) n.wg.Add(1) @@ -1112,7 +1113,7 @@ func (n *Network) processPubSubMessages(msg *message.Message, peerID peer.ID) { n.processAuthenticatedMessage(msg, peerID, message.ProtocolTypePubSub) } -// Unsubscribe unsubscribes the middleware from a channel. +// Unsubscribe unsubscribes the network from a channel. // The following benign errors are expected during normal operations from libP2P: // - the libP2P node fails to unsubscribe to the topic created from the provided channel. // diff --git a/network/p2p/p2pnet/readSubscription.go b/network/p2p/p2pnet/readSubscription.go index c0565dab924..0e458a7eab3 100644 --- a/network/p2p/p2pnet/readSubscription.go +++ b/network/p2p/p2pnet/readSubscription.go @@ -48,7 +48,7 @@ func (r *ReadSubscription) ReceiveLoop(ctx context.Context) { rawMsg, err := r.sub.Next(ctx) if err != nil { - // middleware may have cancelled the context + // network may have cancelled the context if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return } diff --git a/network/p2p/subscription/subscriptionManager.go b/network/p2p/subscription/subscriptionManager.go index d73350129d7..9221273faa4 100644 --- a/network/p2p/subscription/subscriptionManager.go +++ b/network/p2p/subscription/subscriptionManager.go @@ -13,12 +13,14 @@ import ( type ChannelSubscriptionManager struct { mu sync.RWMutex engines map[channels.Channel]network.MessageProcessor - middleware network.Middleware + middleware network.Middleware // the Middleware interface of the network layer } // NewChannelSubscriptionManager creates a new subscription manager. // Args: -// - subscribeFunction: a function that subscribes to a channel. +// - middleware: the Middleware interface of the network layer. +// Returns: +// - a new subscription manager. func NewChannelSubscriptionManager(middleware network.Middleware) *ChannelSubscriptionManager { return &ChannelSubscriptionManager{ engines: make(map[channels.Channel]network.MessageProcessor), diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 0c054d57075..afde15f78ea 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -84,7 +84,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), + ids, nodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), sporkId, suite.numNodes, p2ptest.WithDHTOptions(dht.AsServer()), diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 3730ab25e99..75ab81edc65 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -24,7 +24,7 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> middleware -> libp2p +// EchoEngineTestSuite tests the correctness of the entire pipeline of network -> libp2p // protocol stack. It creates two instances of a stubengine, connects them through network, and sends a // single message from one engine to the other one through different scenarios. type EchoEngineTestSuite struct { @@ -56,7 +56,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() - suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + suite.ids, nodes = testutils.LibP2PNodeForNetworkFixture(suite.T(), sporkId, count) suite.libp2pNodes = nodes suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, nodes) // starts the nodes and networks diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 90e27a0054b..5a77e835432 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -26,6 +26,7 @@ import ( "github.com/onflow/flow-go/network/channels" "github.com/onflow/flow-go/network/internal/testutils" "github.com/onflow/flow-go/network/p2p" + "github.com/onflow/flow-go/network/p2p/p2pnet" mockprotocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/utils/unittest" ) @@ -46,13 +47,12 @@ type MutableIdentityTableSuite struct { cancels []context.CancelFunc } -// testNode encapsulates the node state which includes its identity, middleware, network, +// testNode encapsulates the node state which includes its identity, libp2p node, network, // mesh engine and the id refresher type testNode struct { id *flow.Identity libp2pNode p2p.LibP2PNode - mw network.Middleware - net network.Network + network *p2pnet.Network engine *testutils.MeshEngine } @@ -117,7 +117,7 @@ func (t *testNodeList) networks() []network.Network { defer t.RUnlock() nets := make([]network.Network, len(t.nodes)) for i, node := range t.nodes { - nets[i] = node.net + nets[i] = node.network } return nets } @@ -140,7 +140,7 @@ func TestMutableIdentityTable(t *testing.T) { // signalIdentityChanged update IDs for all the current set of nodes (simulating an epoch) func (suite *MutableIdentityTableSuite) signalIdentityChanged() { for _, n := range suite.testNodes.nodes { - n.mw.UpdateNodeAddresses() + n.network.UpdateNodeAddresses() } } @@ -194,7 +194,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // create the ids, middlewares and networks sporkId := unittest.IdentifierFixture() - ids, nodes := testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) + ids, nodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), sporkId, count) nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) suite.cancels = append(suite.cancels, cancel) @@ -217,7 +217,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { node := testNode{ id: ids[i], libp2pNode: nodes[i], - net: nets[i], + network: nets[i], engine: engines[i], } suite.testNodes.append(node) @@ -324,7 +324,7 @@ func (suite *MutableIdentityTableSuite) TestNodesAddedAndRemoved() { suite.assertNetworkPrimitives(remainingIDs, remainingEngs, removedIDs, removedEngines) } -// assertConnected checks that the middleware of a node is directly connected +// assertConnected checks that a libp2p node is directly connected // to at least half of the other nodes. func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { t := suite.T() @@ -350,8 +350,8 @@ func (suite *MutableIdentityTableSuite) assertConnected(thisNode p2p.LibP2PNode, }, 5*time.Second, 100*time.Millisecond, "node is not connected to enough nodes") } -// assertDisconnected checks that the middleware of a node is not connected to any of the other nodes specified in the -// ids list +// assertDisconnected checks that a libp2p node is not connected to any of the other nodes specified in the +// ids list. func (suite *MutableIdentityTableSuite) assertDisconnected(thisNode p2p.LibP2PNode, allNodes []p2p.LibP2PNode) { t := suite.T() require.Eventuallyf(t, func() bool { diff --git a/network/test/middleware_test.go b/network/test/network_test.go similarity index 58% rename from network/test/middleware_test.go rename to network/test/network_test.go index ab71413228f..466d3194275 100644 --- a/network/test/middleware_test.go +++ b/network/test/network_test.go @@ -83,11 +83,11 @@ func (co *tagsObserver) OnComplete() { close(co.tags) } -// TODO: eventually this should be renamed to NetworkTesTSuite and should be moved to the p2p.network package. -type MiddlewareTestSuite struct { +// TODO: eventually this should be moved to the p2pnet package. +type NetworkTestSuite struct { suite.Suite sync.RWMutex - size int // used to determine number of middlewares under test + size int // used to determine number of networks under test libP2PNodes []p2p.LibP2PNode networks []*p2pnet.Network obs chan string // used to keep track of Protect events tagged by pubsub messages @@ -100,50 +100,50 @@ type MiddlewareTestSuite struct { mwCtx irrecoverable.SignalerContext } -// TestMiddlewareTestSuit runs all the test methods in this test suit -func TestMiddlewareTestSuite(t *testing.T) { +// TestNetworkTestSuit runs all the test methods in this test suit +func TestNetworkTestSuite(t *testing.T) { // should not run in parallel, some tests are stateful. - suite.Run(t, new(MiddlewareTestSuite)) + suite.Run(t, new(NetworkTestSuite)) } // SetupTest initiates the test setups prior to each test -func (m *MiddlewareTestSuite) SetupTest() { - m.logger = unittest.Logger() +func (suite *NetworkTestSuite) SetupTest() { + suite.logger = unittest.Logger() - m.size = 2 // operates on two middlewares - m.metrics = metrics.NewNoopCollector() + suite.size = 2 // operates on two networks + suite.metrics = metrics.NewNoopCollector() - // create and start the middlewares and inject a connection observer + // create and start the networks and inject a connection observer peerChannel := make(chan string) ob := tagsObserver{ tags: peerChannel, - log: m.logger, + log: suite.logger, } - m.sporkId = unittest.IdentifierFixture() + suite.sporkId = unittest.IdentifierFixture() libP2PNodes := make([]p2p.LibP2PNode, 0) identities := make(flow.IdentityList, 0) tagObservables := make([]observable.Observable, 0) idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) defaultFlowConfig, err := config.DefaultConfig() - require.NoError(m.T(), err) + require.NoError(suite.T(), err) opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} - for i := 0; i < m.size; i++ { + for i := 0; i < suite.size; i++ { connManager, err := testutils.NewTagWatchingConnManager( unittest.Logger(), metrics.NewNoopCollector(), &defaultFlowConfig.NetworkConfig.ConnectionManagerConfig) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) opts = append(opts, p2ptest.WithConnectionManager(connManager), p2ptest.WithRole(flow.RoleExecution)) - node, nodeId := p2ptest.NodeFixture(m.T(), - m.sporkId, - m.T().Name(), + node, nodeId := p2ptest.NodeFixture(suite.T(), + suite.sporkId, + suite.T().Name(), idProvider, opts...) libP2PNodes = append(libP2PNodes, node) @@ -152,119 +152,119 @@ func (m *MiddlewareTestSuite) SetupTest() { } idProvider.SetIdentities(identities) - m.ids = identities - m.libP2PNodes = libP2PNodes + suite.ids = identities + suite.libP2PNodes = libP2PNodes - m.networks, m.providers = testutils.NetworksFixture(m.T(), m.sporkId, m.ids, m.libP2PNodes) + suite.networks, suite.providers = testutils.NetworksFixture(suite.T(), suite.sporkId, suite.ids, suite.libP2PNodes) for _, observableConnMgr := range tagObservables { observableConnMgr.Subscribe(&ob) } - m.obs = peerChannel + suite.obs = peerChannel - require.Len(m.Suite.T(), tagObservables, m.size) - require.Len(m.Suite.T(), m.ids, m.size) + require.Len(suite.Suite.T(), tagObservables, suite.size) + require.Len(suite.Suite.T(), suite.ids, suite.size) ctx, cancel := context.WithCancel(context.Background()) - m.mwCancel = cancel + suite.mwCancel = cancel - m.mwCtx = irrecoverable.NewMockSignalerContext(m.T(), ctx) + suite.mwCtx = irrecoverable.NewMockSignalerContext(suite.T(), ctx) - testutils.StartNodes(m.mwCtx, m.T(), m.libP2PNodes, 1*time.Second) + testutils.StartNodes(suite.mwCtx, suite.T(), suite.libP2PNodes, 1*time.Second) - for i, net := range m.networks { - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, libP2PNodes[i]) - net.Start(m.mwCtx) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, net) + for i, net := range suite.networks { + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, libP2PNodes[i]) + net.Start(suite.mwCtx) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } } -func (m *MiddlewareTestSuite) TearDownTest() { - m.mwCancel() +func (suite *NetworkTestSuite) TearDownTest() { + suite.mwCancel() - testutils.StopComponents(m.T(), m.networks, 1*time.Second) - testutils.StopComponents(m.T(), m.libP2PNodes, 1*time.Second) - m.libP2PNodes = nil - m.ids = nil - m.size = 0 + testutils.StopComponents(suite.T(), suite.networks, 1*time.Second) + testutils.StopComponents(suite.T(), suite.libP2PNodes, 1*time.Second) + suite.libP2PNodes = nil + suite.ids = nil + suite.size = 0 } // TestUpdateNodeAddresses tests that the UpdateNodeAddresses method correctly updates // the addresses of the staked network participants. -func (m *MiddlewareTestSuite) TestUpdateNodeAddresses() { - ctx, cancel := context.WithCancel(m.mwCtx) - irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) +func (suite *NetworkTestSuite) TestUpdateNodeAddresses() { + ctx, cancel := context.WithCancel(suite.mwCtx) + irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) // create a new staked identity - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), m.sporkId, 1) + ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), suite.sporkId, 1) idProvider := unittest.NewUpdatableIDProvider(ids) networkCfg := testutils.NetworkConfigFixture( - m.T(), + suite.T(), *ids[0], idProvider, - m.sporkId, + suite.sporkId, libP2PNodes[0]) newNet, err := p2pnet.NewNetwork(networkCfg) - require.NoError(m.T(), err) - require.Len(m.T(), ids, 1) + require.NoError(suite.T(), err) + require.Len(suite.T(), ids, 1) newId := ids[0] // start up nodes and peer managers - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) - defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) newNet.Start(irrecoverableCtx) - defer testutils.StopComponents(m.T(), []network.Network{newNet}, 1*time.Second) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + defer testutils.StopComponents(suite.T(), []network.Network{newNet}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) - idList := flow.IdentityList(append(m.ids, newId)) + idList := flow.IdentityList(append(suite.ids, newId)) // needed to enable ID translation - m.providers[0].SetIdentities(idList) + suite.providers[0].SetIdentities(idList) // unicast should fail to send because no address is known yet for the new identity - con, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) err = con.Unicast(&libp2pmessage.TestMessage{ Text: "TestUpdateNodeAddresses", }, newId.NodeID) - require.True(m.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) + require.True(suite.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) // update the addresses - m.networks[0].UpdateNodeAddresses() + suite.networks[0].UpdateNodeAddresses() // now the message should send successfully err = con.Unicast(&libp2pmessage.TestMessage{ Text: "TestUpdateNodeAddresses", }, newId.NodeID) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) cancel() - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) } -func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { +func (suite *NetworkTestSuite) TestUnicastRateLimit_Messages() { // limiter limit will be set to 5 events/sec the 6th event per interval will be rate limited limit := rate.Limit(5) // burst per interval burst := 5 - for _, net := range m.networks { - require.NoError(m.T(), net.Subscribe(channels.TestNetworkChannel)) + for _, net := range suite.networks { + require.NoError(suite.T(), net.Subscribe(channels.TestNetworkChannel)) } messageRateLimiter := ratelimiter.NewRateLimiter(limit, burst, 3*time.Second) - // we only expect messages from the first middleware on the test suite - expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) - require.NoError(m.T(), err) + // we only expect messages from the first networks on the test suite + expectedPID, err := unittest.PeerIDFromFlowID(suite.ids[0]) + require.NoError(suite.T(), err) // the onRateLimit call back we will use to keep track of how many times a rate limit happens. rateLimits := atomic.NewUint64(0) onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { - require.Equal(m.T(), reason, ratelimit.ReasonMessageCount.String()) - require.Equal(m.T(), expectedPID, peerID) + require.Equal(suite.T(), reason, ratelimit.ReasonMessageCount.String()) + require.Equal(suite.T(), expectedPID, peerID) // update hook calls rateLimits.Inc() } @@ -277,9 +277,9 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { opts := []ratelimit.RateLimitersOption{ratelimit.WithMessageRateLimiter(messageRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} rateLimiters := ratelimit.NewRateLimiters(opts...) - idProvider := unittest.NewUpdatableIDProvider(m.ids) - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), - m.sporkId, + idProvider := unittest.NewUpdatableIDProvider(suite.ids) + ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), + suite.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { @@ -288,39 +288,39 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { } return nil }))) - idProvider.SetIdentities(append(m.ids, ids...)) + idProvider.SetIdentities(append(suite.ids, ids...)) netCfg := testutils.NetworkConfigFixture( - m.T(), + suite.T(), *ids[0], idProvider, - m.sporkId, + suite.sporkId, libP2PNodes[0]) newNet, err := p2pnet.NewNetwork( netCfg, p2pnet.WithUnicastRateLimiters(rateLimiters), p2pnet.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(messageRateLimiter))) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) - require.Len(m.T(), ids, 1) + require.Len(suite.T(), ids, 1) newId := ids[0] - idList := flow.IdentityList(append(m.ids, newId)) + idList := flow.IdentityList(append(suite.ids, newId)) - m.providers[0].SetIdentities(idList) + suite.providers[0].SetIdentities(idList) - ctx, cancel := context.WithCancel(m.mwCtx) - irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) - defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, m.T(), []network.Network{newNet}, 1*time.Second) + ctx, cancel := context.WithCancel(suite.mwCtx) + irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}, 1*time.Second) calls := atomic.NewUint64(0) ch := make(chan struct{}) // registers an engine on the new network newEngine := &mocknetwork.MessageProcessor{} _, err = newNet.Register(channels.TestNetworkChannel, newEngine) - require.NoError(m.T(), err) - newEngine.On("Process", channels.TestNetworkChannel, m.ids[0].NodeID, mockery.Anything).Run(func(args mockery.Arguments) { + require.NoError(suite.T(), err) + newEngine.On("Process", channels.TestNetworkChannel, suite.ids[0].NodeID, mockery.Anything).Run(func(args mockery.Arguments) { calls.Inc() if calls.Load() >= 5 { close(ch) @@ -328,20 +328,20 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { }).Return(nil) // needed to enable ID translation - m.providers[0].SetIdentities(idList) + suite.providers[0].SetIdentities(idList) // update the addresses - m.networks[0].UpdateNodeAddresses() + suite.networks[0].UpdateNodeAddresses() // add our sender node as a direct peer to our receiving node, this allows us to ensure // that connections to peers that are rate limited are completely prune. IsConnected will // return true only if the node is a direct peer of the other, after rate limiting this direct // peer should be removed by the peer manager. - p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) - p2ptest.TryConnectionAndEnsureConnected(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}) + p2ptest.LetNodesDiscoverEachOther(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}, flow.IdentityList{ids[0], suite.ids[0]}) + p2ptest.TryConnectionAndEnsureConnected(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}) - con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con0, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) // with the rate limit configured to 5 msg/sec we send 10 messages at once and expect the rate limiter // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream @@ -350,45 +350,45 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Messages() { err = con0.Unicast(&libp2pmessage.TestMessage{ Text: fmt.Sprintf("hello-%d", i), }, newId.NodeID) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) } - // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop rate limit test ch on time") + // wait for all rate limits before shutting down network + unittest.RequireCloseBefore(suite.T(), ch, 100*time.Millisecond, "could not stop rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) // ensure connection to rate limited peer is pruned - p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + p2ptest.EnsureNotConnectedBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) // eventually the rate limited node should be able to reconnect and send messages - require.Eventually(m.T(), func() bool { + require.Eventually(suite.T(), func() bool { err = con0.Unicast(&libp2pmessage.TestMessage{ Text: "hello", }, newId.NodeID) return err == nil }, 3*time.Second, 100*time.Millisecond) - // shutdown our middleware so that each message can be processed + // shutdown our network so that each message can be processed cancel() - unittest.RequireCloseBefore(m.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") - unittest.RequireCloseBefore(m.T(), newNet.Done(), 100*time.Millisecond, "could not stop middleware on time") + unittest.RequireCloseBefore(suite.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") + unittest.RequireCloseBefore(suite.T(), newNet.Done(), 100*time.Millisecond, "could not stop network on time") // expect our rate limited peer callback to be invoked once - require.True(m.T(), rateLimits.Load() > 0) + require.True(suite.T(), rateLimits.Load() > 0) } -func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { +func (suite *NetworkTestSuite) TestUnicastRateLimit_Bandwidth() { //limiter limit will be set up to 1000 bytes/sec limit := rate.Limit(1000) //burst per interval burst := 1000 - // we only expect messages from the first middleware on the test suite - expectedPID, err := unittest.PeerIDFromFlowID(m.ids[0]) - require.NoError(m.T(), err) + // we only expect messages from the first network on the test suite + expectedPID, err := unittest.PeerIDFromFlowID(suite.ids[0]) + require.NoError(suite.T(), err) // setup bandwidth rate limiter bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) @@ -398,11 +398,11 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { ch := make(chan struct{}) rateLimits := atomic.NewUint64(0) onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { - require.Equal(m.T(), reason, ratelimit.ReasonBandwidth.String()) + require.Equal(suite.T(), reason, ratelimit.ReasonBandwidth.String()) - // we only expect messages from the first middleware on the test suite - require.NoError(m.T(), err) - require.Equal(m.T(), expectedPID, peerID) + // we only expect messages from the first network on the test suite + require.NoError(suite.T(), err) + require.Equal(suite.T(), expectedPID, peerID) // update hook calls rateLimits.Inc() close(ch) @@ -414,10 +414,10 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} rateLimiters := ratelimit.NewRateLimiters(opts...) - idProvider := unittest.NewUpdatableIDProvider(m.ids) + idProvider := unittest.NewUpdatableIDProvider(suite.ids) // create a new staked identity - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(m.T(), - m.sporkId, + ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), + suite.sporkId, 1, p2ptest.WithUnicastRateLimitDistributor(distributor), p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { @@ -428,51 +428,51 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { return nil }))) - idProvider.SetIdentities(append(m.ids, ids...)) - m.providers[0].SetIdentities(append(m.ids, ids...)) + idProvider.SetIdentities(append(suite.ids, ids...)) + suite.providers[0].SetIdentities(append(suite.ids, ids...)) netCfg := testutils.NetworkConfigFixture( - m.T(), + suite.T(), *ids[0], idProvider, - m.sporkId, + suite.sporkId, libP2PNodes[0]) newNet, err := p2pnet.NewNetwork( netCfg, p2pnet.WithUnicastRateLimiters(rateLimiters), p2pnet.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter))) - require.NoError(m.T(), err) - require.Len(m.T(), ids, 1) + require.NoError(suite.T(), err) + require.Len(suite.T(), ids, 1) newId := ids[0] - ctx, cancel := context.WithCancel(m.mwCtx) - irrecoverableCtx := irrecoverable.NewMockSignalerContext(m.T(), ctx) + ctx, cancel := context.WithCancel(suite.mwCtx) + irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - testutils.StartNodes(irrecoverableCtx, m.T(), libP2PNodes, 1*time.Second) - defer testutils.StopComponents(m.T(), libP2PNodes, 1*time.Second) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, m.T(), []network.Network{newNet}, 1*time.Second) - unittest.RequireComponentsReadyBefore(m.T(), 1*time.Second, newNet) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}, 1*time.Second) + unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) // registers an engine on the new network so that it can receive messages on the TestNetworkChannel newEngine := &mocknetwork.MessageProcessor{} _, err = newNet.Register(channels.TestNetworkChannel, newEngine) - require.NoError(m.T(), err) - newEngine.On("Process", channels.TestNetworkChannel, m.ids[0].NodeID, mockery.Anything).Return(nil) + require.NoError(suite.T(), err) + newEngine.On("Process", channels.TestNetworkChannel, suite.ids[0].NodeID, mockery.Anything).Return(nil) - idList := flow.IdentityList(append(m.ids, newId)) + idList := flow.IdentityList(append(suite.ids, newId)) // needed to enable ID translation - m.providers[0].SetIdentities(idList) + suite.providers[0].SetIdentities(idList) // update the addresses - m.networks[0].UpdateNodeAddresses() + suite.networks[0].UpdateNodeAddresses() // add our sender node as a direct peer to our receiving node, this allows us to ensure // that connections to peers that are rate limited are completely prune. IsConnected will // return true only if the node is a direct peer of the other, after rate limiting this direct // peer should be removed by the peer manager. - p2ptest.LetNodesDiscoverEachOther(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], m.libP2PNodes[0]}, flow.IdentityList{ids[0], m.ids[0]}) + p2ptest.LetNodesDiscoverEachOther(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}, flow.IdentityList{ids[0], suite.ids[0]}) // create message with about 400bytes (300 random bytes + 100bytes message info) b := make([]byte, 300) @@ -481,42 +481,42 @@ func (m *MiddlewareTestSuite) TestUnicastRateLimit_Bandwidth() { } // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited // as it is more than our allowed bandwidth of 1000 bytes. - con0, err := m.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con0, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) for i := 0; i < 3; i++ { err = con0.Unicast(&libp2pmessage.TestMessage{ Text: string(b), }, newId.NodeID) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) } - // wait for all rate limits before shutting down middleware - unittest.RequireCloseBefore(m.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") + // wait for all rate limits before shutting down network + unittest.RequireCloseBefore(suite.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") // sleep for 1 seconds to allow connection pruner to prune connections time.Sleep(1 * time.Second) // ensure connection to rate limited peer is pruned - p2ptest.EnsureNotConnectedBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) - p2pfixtures.EnsureNoStreamCreationBetweenGroups(m.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{m.libP2PNodes[0]}) + p2ptest.EnsureNotConnectedBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) + p2pfixtures.EnsureNoStreamCreationBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) // eventually the rate limited node should be able to reconnect and send messages - require.Eventually(m.T(), func() bool { + require.Eventually(suite.T(), func() bool { err = con0.Unicast(&libp2pmessage.TestMessage{ Text: "", }, newId.NodeID) return err == nil }, 3*time.Second, 100*time.Millisecond) - // shutdown our middleware so that each message can be processed + // shutdown our network so that each message can be processed cancel() - unittest.RequireComponentsDoneBefore(m.T(), 100*time.Millisecond, newNet) + unittest.RequireComponentsDoneBefore(suite.T(), 100*time.Millisecond, newNet) // expect our rate limited peer callback to be invoked once - require.Equal(m.T(), uint64(1), rateLimits.Load()) + require.Equal(suite.T(), uint64(1), rateLimits.Load()) } -func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvider) *mocknetwork.Overlay { +func (suite *NetworkTestSuite) createOverlay(provider *unittest.UpdatableIDProvider) *mocknetwork.Overlay { overlay := &mocknetwork.Overlay{} overlay.On("Identities").Maybe().Return(func() flow.IdentityList { return provider.Identities(filter.Any) @@ -532,78 +532,77 @@ func (m *MiddlewareTestSuite) createOverlay(provider *unittest.UpdatableIDProvid return overlay } -// TestPing sends a message from the first middleware of the test suit to the last one and checks that the -// last middleware receives the message and that the message is correctly decoded. -func (m *MiddlewareTestSuite) TestPing() { +// TestPing sends a message from the first network of the test suit to the last one and checks that the +// last network receives the message and that the message is correctly decoded. +func (suite *NetworkTestSuite) TestPing() { receiveWG := sync.WaitGroup{} receiveWG.Add(1) // extracts sender id based on the mock option var err error - // mocks Overlay.Receive for middleware.Overlay.Receive(*nodeID, payload) senderNodeIndex := 0 - targetNodeIndex := m.size - 1 + targetNodeIndex := suite.size - 1 expectedPayload := "TestPingContentReception" // mocks a target engine on the last node of the test suit that will receive the message on the test channel. targetEngine := &mocknetwork.MessageProcessor{} // target engine on the last node of the test suit that will receive the message - _, err = m.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) - require.NoError(m.T(), err) + _, err = suite.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) + require.NoError(suite.T(), err) // target engine must receive the message once with the expected payload targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { receiveWG.Done() msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[senderNodeIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok) - require.Equal(m.T(), expectedPayload, msgPayload.Text) // payload + require.True(suite.T(), ok) + require.Equal(suite.T(), expectedPayload, msgPayload.Text) // payload }).Return(nil).Once() // sends a direct message from first node to the last node - con0, err := m.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con0, err := suite.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) err = con0.Unicast(&libp2pmessage.TestMessage{ Text: expectedPayload, - }, m.ids[targetNodeIndex].NodeID) - require.NoError(m.Suite.T(), err) + }, suite.ids[targetNodeIndex].NodeID) + require.NoError(suite.Suite.T(), err) - unittest.RequireReturnsBefore(m.T(), receiveWG.Wait, 1*time.Second, "did not receive message") + unittest.RequireReturnsBefore(suite.T(), receiveWG.Wait, 1*time.Second, "did not receive message") } -// TestMultiPing_TwoPings sends two messages concurrently from the first middleware of the test suit to the last one, -// and checks that the last middleware receives the messages and that the messages are correctly decoded. -func (m *MiddlewareTestSuite) TestMultiPing_TwoPings() { - m.MultiPing(2) +// TestMultiPing_TwoPings sends two messages concurrently from the first network of the test suit to the last one, +// and checks that the last network receives the messages and that the messages are correctly decoded. +func (suite *NetworkTestSuite) TestMultiPing_TwoPings() { + suite.MultiPing(2) } -// TestMultiPing_FourPings sends four messages concurrently from the first middleware of the test suit to the last one, -// and checks that the last middleware receives the messages and that the messages are correctly decoded. -func (m *MiddlewareTestSuite) TestMultiPing_FourPings() { - m.MultiPing(4) +// TestMultiPing_FourPings sends four messages concurrently from the first network of the test suit to the last one, +// and checks that the last network receives the messages and that the messages are correctly decoded. +func (suite *NetworkTestSuite) TestMultiPing_FourPings() { + suite.MultiPing(4) } -// TestMultiPing_EightPings sends eight messages concurrently from the first middleware of the test suit to the last one, -// and checks that the last middleware receives the messages and that the messages are correctly decoded. -func (m *MiddlewareTestSuite) TestMultiPing_EightPings() { - m.MultiPing(8) +// TestMultiPing_EightPings sends eight messages concurrently from the first network of the test suit to the last one, +// and checks that the last network receives the messages and that the messages are correctly decoded. +func (suite *NetworkTestSuite) TestMultiPing_EightPings() { + suite.MultiPing(8) } -// MultiPing sends count-many distinct messages concurrently from the first middleware of the test suit to the last one. +// MultiPing sends count-many distinct messages concurrently from the first network of the test suit to the last one. // It evaluates the correctness of reception of the content of the messages. Each message must be received by the -// last middleware of the test suit exactly once. -func (m *MiddlewareTestSuite) MultiPing(count int) { +// last network of the test suit exactly once. +func (suite *NetworkTestSuite) MultiPing(count int) { receiveWG := sync.WaitGroup{} sendWG := sync.WaitGroup{} senderNodeIndex := 0 - targetNodeIndex := m.size - 1 + targetNodeIndex := suite.size - 1 receivedPayloads := unittest.NewProtectedMap[string, struct{}]() // keep track of unique payloads received. @@ -611,29 +610,29 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { regex := regexp.MustCompile(`^hello from: \d`) // creates a conduit on sender to send messages to the target on the test channel. - con0, err := m.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con0, err := suite.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) // mocks a target engine on the last node of the test suit that will receive the message on the test channel. targetEngine := &mocknetwork.MessageProcessor{} - _, err = m.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) + _, err = suite.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { receiveWG.Done() msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[senderNodeIndex].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[senderNodeIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok) + require.True(suite.T(), ok) // payload - require.True(m.T(), regex.MatchString(msgPayload.Text)) - require.False(m.T(), receivedPayloads.Has(msgPayload.Text)) // payload must be unique + require.True(suite.T(), regex.MatchString(msgPayload.Text)) + require.False(suite.T(), receivedPayloads.Has(msgPayload.Text)) // payload must be unique receivedPayloads.Add(msgPayload.Text, struct{}{}) }).Return(nil) @@ -642,46 +641,45 @@ func (m *MiddlewareTestSuite) MultiPing(count int) { sendWG.Add(1) expectedPayloadText := fmt.Sprintf("hello from: %d", i) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) err = con0.Unicast(&libp2pmessage.TestMessage{ Text: expectedPayloadText, - }, m.ids[targetNodeIndex].NodeID) - require.NoError(m.Suite.T(), err) + }, suite.ids[targetNodeIndex].NodeID) + require.NoError(suite.Suite.T(), err) go func() { // sends a direct message from first node to the last node err := con0.Unicast(&libp2pmessage.TestMessage{ Text: expectedPayloadText, - }, m.ids[targetNodeIndex].NodeID) - require.NoError(m.Suite.T(), err) + }, suite.ids[targetNodeIndex].NodeID) + require.NoError(suite.Suite.T(), err) sendWG.Done() }() } - unittest.RequireReturnsBefore(m.T(), sendWG.Wait, 1*time.Second, "could not send unicasts on time") - unittest.RequireReturnsBefore(m.T(), receiveWG.Wait, 1*time.Second, "could not receive unicasts on time") + unittest.RequireReturnsBefore(suite.T(), sendWG.Wait, 1*time.Second, "could not send unicasts on time") + unittest.RequireReturnsBefore(suite.T(), receiveWG.Wait, 1*time.Second, "could not receive unicasts on time") } -// TestEcho sends an echo message from first middleware to the last middleware -// the last middleware echos back the message. The test evaluates the correctness +// TestEcho sends an echo message from first network to the last network +// the last network echos back the message. The test evaluates the correctness // of the message reception as well as its content -func (m *MiddlewareTestSuite) TestEcho() { +func (suite *NetworkTestSuite) TestEcho() { wg := sync.WaitGroup{} // extracts sender id based on the mock option var err error wg.Add(2) - // mocks Overlay.Receive for middleware.Overlay.Receive(*nodeID, payload) first := 0 - last := m.size - 1 + last := suite.size - 1 // mocks a target engine on the first and last nodes of the test suit that will receive the message on the test channel. targetEngine1 := &mocknetwork.MessageProcessor{} - con1, err := m.networks[first].Register(channels.TestNetworkChannel, targetEngine1) - require.NoError(m.T(), err) + con1, err := suite.networks[first].Register(channels.TestNetworkChannel, targetEngine1) + require.NoError(suite.T(), err) targetEngine2 := &mocknetwork.MessageProcessor{} - con2, err := m.networks[last].Register(channels.TestNetworkChannel, targetEngine2) - require.NoError(m.T(), err) + con2, err := suite.networks[last].Register(channels.TestNetworkChannel, targetEngine2) + require.NoError(suite.T(), err) // message sent from first node to the last node. expectedSendMsg := "TestEcho" @@ -695,21 +693,21 @@ func (m *MiddlewareTestSuite) TestEcho() { wg.Done() msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[first].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[first].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok) - require.Equal(m.T(), expectedSendMsg, msgPayload.Text) // payload + require.True(suite.T(), ok) + require.Equal(suite.T(), expectedSendMsg, msgPayload.Text) // payload // echos back the same message back to the sender - require.NoError(m.T(), con2.Unicast(&libp2pmessage.TestMessage{ + require.NoError(suite.T(), con2.Unicast(&libp2pmessage.TestMessage{ Text: expectedReplyMsg, - }, m.ids[first].NodeID)) + }, suite.ids[first].NodeID)) }).Return(nil).Once() // mocks the target engine on the last node of the test suit that will receive the message on the test channel, and @@ -719,54 +717,54 @@ func (m *MiddlewareTestSuite) TestEcho() { wg.Done() msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[last].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[last].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok) - require.Equal(m.T(), expectedReplyMsg, msgPayload.Text) // payload + require.True(suite.T(), ok) + require.Equal(suite.T(), expectedReplyMsg, msgPayload.Text) // payload }).Return(nil) // sends a direct message from first node to the last node - require.NoError(m.T(), con1.Unicast(&libp2pmessage.TestMessage{ + require.NoError(suite.T(), con1.Unicast(&libp2pmessage.TestMessage{ Text: expectedSendMsg, - }, m.ids[last].NodeID)) + }, suite.ids[last].NodeID)) - unittest.RequireReturnsBefore(m.T(), wg.Wait, 5*time.Second, "could not receive unicast on time") + unittest.RequireReturnsBefore(suite.T(), wg.Wait, 5*time.Second, "could not receive unicast on time") } -// TestMaxMessageSize_Unicast evaluates that invoking Unicast method of the middleware on a message +// TestMaxMessageSize_Unicast evaluates that invoking Unicast method of the network on a message // size beyond the permissible unicast message size returns an error. -func (m *MiddlewareTestSuite) TestMaxMessageSize_Unicast() { +func (suite *NetworkTestSuite) TestMaxMessageSize_Unicast() { first := 0 - last := m.size - 1 + last := suite.size - 1 // creates a network payload beyond the maximum message size // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, // so the generated payload is 1000 bytes below the maximum unicast message size. // We hence add up 1000 bytes to the input of network payload fixture to make // sure that payload is beyond the permissible size. - payload := testutils.NetworkPayloadFixture(m.T(), uint(p2pnet.DefaultMaxUnicastMsgSize)+1000) + payload := testutils.NetworkPayloadFixture(suite.T(), uint(p2pnet.DefaultMaxUnicastMsgSize)+1000) event := &libp2pmessage.TestMessage{ Text: string(payload), } // sends a direct message from first node to the last node - con0, err := m.networks[first].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) - require.Error(m.T(), con0.Unicast(event, m.ids[last].NodeID)) + con0, err := suite.networks[first].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) + require.Error(suite.T(), con0.Unicast(event, suite.ids[last].NodeID)) } // TestLargeMessageSize_SendDirect asserts that a ChunkDataResponse is treated as a large message and can be unicasted // successfully even though it's size is greater than the default message size. -func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { +func (suite *NetworkTestSuite) TestLargeMessageSize_SendDirect() { sourceIndex := 0 - targetIndex := m.size - 1 - targetId := m.ids[targetIndex].NodeID + targetIndex := suite.size - 1 + targetId := suite.ids[targetIndex].NodeID // creates a network payload with a size greater than the default max size using a known large message type targetSize := uint64(p2pnet.DefaultMaxUnicastMsgSize) + 1000 @@ -776,64 +774,64 @@ func (m *MiddlewareTestSuite) TestLargeMessageSize_SendDirect() { ch := make(chan struct{}) // mocks a target engine on the last node of the test suit that will receive the message on the test channel. targetEngine := &mocknetwork.MessageProcessor{} - _, err := m.networks[targetIndex].Register(channels.ProvideChunks, targetEngine) - require.NoError(m.T(), err) + _, err := suite.networks[targetIndex].Register(channels.ProvideChunks, targetEngine) + require.NoError(suite.T(), err) targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { defer close(ch) msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.ProvideChunks, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.ProvideChunks, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[sourceIndex].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[sourceIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*messages.ChunkDataResponse) - require.True(m.T(), ok) - require.Equal(m.T(), event, msgPayload) // payload + require.True(suite.T(), ok) + require.Equal(suite.T(), event, msgPayload) // payload }).Return(nil).Once() // sends a direct message from source node to the target node - con0, err := m.networks[sourceIndex].Register(channels.ProvideChunks, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) - require.NoError(m.T(), con0.Unicast(event, targetId)) + con0, err := suite.networks[sourceIndex].Register(channels.ProvideChunks, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) + require.NoError(suite.T(), con0.Unicast(event, targetId)) // check message reception on target - unittest.RequireCloseBefore(m.T(), ch, 5*time.Second, "source node failed to send large message to target") + unittest.RequireCloseBefore(suite.T(), ch, 5*time.Second, "source node failed to send large message to target") } -// TestMaxMessageSize_Publish evaluates that invoking Publish method of the middleware on a message +// TestMaxMessageSize_Publish evaluates that invoking Publish method of the network on a message // size beyond the permissible publish message size returns an error. -func (m *MiddlewareTestSuite) TestMaxMessageSize_Publish() { +func (suite *NetworkTestSuite) TestMaxMessageSize_Publish() { firstIndex := 0 - lastIndex := m.size - 1 - lastNodeId := m.ids[lastIndex].NodeID + lastIndex := suite.size - 1 + lastNodeId := suite.ids[lastIndex].NodeID // creates a network payload beyond the maximum message size // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, // so the generated payload is 1000 bytes below the maximum publish message size. // We hence add up 1000 bytes to the input of network payload fixture to make // sure that payload is beyond the permissible size. - payload := testutils.NetworkPayloadFixture(m.T(), uint(p2pnode.DefaultMaxPubSubMsgSize)+1000) + payload := testutils.NetworkPayloadFixture(suite.T(), uint(p2pnode.DefaultMaxPubSubMsgSize)+1000) event := &libp2pmessage.TestMessage{ Text: string(payload), } - con0, err := m.networks[firstIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con0, err := suite.networks[firstIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) err = con0.Publish(event, lastNodeId) - require.Error(m.Suite.T(), err) - require.ErrorContains(m.T(), err, "exceeds configured max message size") + require.Error(suite.Suite.T(), err) + require.ErrorContains(suite.T(), err, "exceeds configured max message size") } // TestUnsubscribe tests that an engine can unsubscribe from a topic it was earlier subscribed to and stop receiving // messages. -func (m *MiddlewareTestSuite) TestUnsubscribe() { +func (suite *NetworkTestSuite) TestUnsubscribe() { senderIndex := 0 - targetIndex := m.size - 1 - targetId := m.ids[targetIndex].NodeID + targetIndex := suite.size - 1 + targetId := suite.ids[targetIndex].NodeID msgRcvd := make(chan struct{}, 2) msgRcvdFun := func() { @@ -841,55 +839,55 @@ func (m *MiddlewareTestSuite) TestUnsubscribe() { } targetEngine := &mocknetwork.MessageProcessor{} - con2, err := m.networks[targetIndex].Register(channels.TestNetworkChannel, targetEngine) - require.NoError(m.T(), err) + con2, err := suite.networks[targetIndex].Register(channels.TestNetworkChannel, targetEngine) + require.NoError(suite.T(), err) targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). Run(func(args mockery.Arguments) { msgChannel, ok := args[0].(channels.Channel) - require.True(m.T(), ok) - require.Equal(m.T(), channels.TestNetworkChannel, msgChannel) // channel + require.True(suite.T(), ok) + require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel msgOriginID, ok := args[1].(flow.Identifier) - require.True(m.T(), ok) - require.Equal(m.T(), m.ids[senderIndex].NodeID, msgOriginID) // sender id + require.True(suite.T(), ok) + require.Equal(suite.T(), suite.ids[senderIndex].NodeID, msgOriginID) // sender id msgPayload, ok := args[2].(*libp2pmessage.TestMessage) - require.True(m.T(), ok) - require.True(m.T(), msgPayload.Text == "hello1") // payload, we only expect hello 1 that was sent before unsubscribe. + require.True(suite.T(), ok) + require.True(suite.T(), msgPayload.Text == "hello1") // payload, we only expect hello 1 that was sent before unsubscribe. msgRcvd <- struct{}{} }).Return(nil) // first test that when both nodes are subscribed to the channel, the target node receives the message - con1, err := m.networks[senderIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) - require.NoError(m.T(), err) + con1, err := suite.networks[senderIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) + require.NoError(suite.T(), err) - // set up waiting for m.size pubsub tags indicating a mesh has formed - for i := 0; i < m.size; i++ { + // set up waiting for suite.size pubsub tags indicating a mesh has formed + for i := 0; i < suite.size; i++ { select { - case <-m.obs: + case <-suite.obs: case <-time.After(2 * time.Second): - assert.FailNow(m.T(), "could not receive pubsub tag indicating mesh formed") + assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") } } err = con1.Publish(&libp2pmessage.TestMessage{ Text: string("hello1"), }, targetId) - require.NoError(m.T(), err) + require.NoError(suite.T(), err) - unittest.RequireReturnsBefore(m.T(), msgRcvdFun, 3*time.Second, "message not received") + unittest.RequireReturnsBefore(suite.T(), msgRcvdFun, 3*time.Second, "message not received") // now unsubscribe the target node from the channel - require.NoError(m.T(), con2.Close()) + require.NoError(suite.T(), con2.Close()) // now publish a new message from the first node err = con1.Publish(&libp2pmessage.TestMessage{ Text: string("hello2"), }, targetId) - assert.NoError(m.T(), err) + assert.NoError(suite.T(), err) // assert that the new message is not received by the target node - unittest.RequireNeverReturnBefore(m.T(), msgRcvdFun, 2*time.Second, "message received unexpectedly") + unittest.RequireNeverReturnBefore(suite.T(), msgRcvdFun, 2*time.Second, "message received unexpectedly") } // TestChunkDataPackMaxMessageSize tests that the max message size for a chunk data pack response is set to the large message size. diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index a3e14dabd27..2a7e96dcde7 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -29,7 +29,7 @@ import ( ) // UnicastAuthorizationTestSuite tests that messages sent via unicast that are unauthenticated or unauthorized are correctly rejected. Each test on the test suite -// uses 2 middlewares, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Middleware and the cancel func +// uses 2 networks, a sender and receiver. A mock slashing violation's consumer is used to assert the messages were rejected. Networks and the cancel func // are set during each test run inside the test and remove after each test run in the TearDownTest callback. type UnicastAuthorizationTestSuite struct { suite.Suite @@ -49,10 +49,10 @@ type UnicastAuthorizationTestSuite struct { receiverID *flow.Identity // providers id providers generated at beginning of a test run providers []*unittest.UpdatableIDProvider - // cancel is the cancel func from the context that was used to start the middlewares in a test run + // cancel is the cancel func from the context that was used to start the networks in a test run cancel context.CancelFunc sporkId flow.Identifier - // waitCh is the channel used to wait for the middleware to perform authorization and invoke the slashing + // waitCh is the channel used to wait for the networks to perform authorization and invoke the slashing //violation's consumer before making mock assertions and cleaning up resources waitCh chan struct{} } @@ -66,18 +66,18 @@ func TestUnicastAuthorizationTestSuite(t *testing.T) { func (u *UnicastAuthorizationTestSuite) SetupTest() { u.logger = unittest.Logger() u.channelCloseDuration = 100 * time.Millisecond - // this ch will allow us to wait until the expected method call happens before shutting down middleware + // this ch will allow us to wait until the expected method call happens before shutting down networks. u.waitCh = make(chan struct{}) } func (u *UnicastAuthorizationTestSuite) TearDownTest() { - u.stopMiddlewares() + u.stopNetworksAndLibp2pNodes() } // setupNetworks will setup the sender and receiver networks with the given slashing violations consumer. func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer network.ViolationsConsumer) { u.sporkId = unittest.IdentifierFixture() - ids, libP2PNodes := testutils.LibP2PNodeForMiddlewareFixture(u.T(), u.sporkId, 2) + ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(u.T(), u.sporkId, 2) u.codec = newOverridableMessageEncoder(unittest.NetworkCodec()) nets, providers := testutils.NetworksFixture( u.T(), @@ -100,8 +100,8 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer u.libP2PNodes = libP2PNodes } -// startMiddlewares will start both sender and receiver middlewares with an irrecoverable signaler context and set the context cancel func. -func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Overlay) { +// startNetworksAndLibp2pNodes will start both sender and receiver networks with an irrecoverable signaler context and set the context cancel func. +func (u *UnicastAuthorizationTestSuite) startNetworksAndLibp2pNodes() { ctx, cancel := context.WithCancel(context.Background()) sigCtx, _ := irrecoverable.WithSignaler(ctx) @@ -112,9 +112,9 @@ func (u *UnicastAuthorizationTestSuite) startMiddlewares(overlay *mocknetwork.Ov u.cancel = cancel } -// stopMiddlewares will stop all middlewares. -func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { - u.cancel() +// stopNetworksAndLibp2pNodes will stop all networks and libp2p nodes and wait for them to stop. +func (u *UnicastAuthorizationTestSuite) stopNetworksAndLibp2pNodes() { + u.cancel() // cancel context to stop libp2p nodes. testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) @@ -122,7 +122,6 @@ func (u *UnicastAuthorizationTestSuite) stopMiddlewares() { // TestUnicastAuthorization_UnstakedPeer tests that messages sent via unicast by an unstaked peer is correctly rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -142,7 +141,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() // overriding the identity provide of the receiver node to return an empty identity list so that the // sender node looks unstaked to its networking layer and hence it sends an UnAuthorizedSenderError upon receiving a message @@ -167,7 +166,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnstakedPeer() // TestUnicastAuthorization_EjectedPeer tests that messages sent via unicast by an ejected peer is correctly rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) //NOTE: setup ejected identity @@ -195,7 +193,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -215,7 +213,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_EjectedPeer() { // TestUnicastAuthorization_UnauthorizedPeer tests that messages sent via unicast by an unauthorized peer is correctly rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPeer() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -237,7 +234,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -258,7 +255,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedPee // TestUnicastAuthorization_UnknownMsgCode tests that messages sent via unicast with an unknown message code is correctly rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -293,7 +289,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -311,7 +307,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnknownMsgCode( // TestUnicastAuthorization_WrongMsgCode tests that messages sent via unicast with a message code that does not match the underlying message type are correctly rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -342,7 +337,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() _, err = u.receiverNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -362,10 +357,9 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_WrongMsgCode() // TestUnicastAuthorization_PublicChannel tests that messages sent via unicast on a public channel are not rejected for any reason. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() msg := &libp2pmessage.TestMessage{ Text: string("hello"), @@ -395,7 +389,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_PublicChannel() // TestUnicastAuthorization_UnauthorizedUnicastOnChannel tests that messages sent via unicast that are not authorized for unicast are rejected. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUnicastOnChannel() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -420,7 +413,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() _, err = u.receiverNetwork.Register(channels.ConsensusCommittee, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -441,7 +434,6 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_UnauthorizedUni // TestUnicastAuthorization_ReceiverHasNoSubscription tests that messages sent via unicast are rejected on the receiver end if the receiver does not have a subscription // to the channel of the message. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSubscription() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) @@ -462,7 +454,7 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu close(u.waitCh) }) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() senderCon, err := u.senderNetwork.Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) require.NoError(u.T(), err) @@ -480,10 +472,9 @@ func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasNoSu // TestUnicastAuthorization_ReceiverHasSubscription tests that messages sent via unicast are processed on the receiver end if the receiver does have a subscription // to the channel of the message. func (u *UnicastAuthorizationTestSuite) TestUnicastAuthorization_ReceiverHasSubscription() { - // setup mock slashing violations consumer and middlewares slashingViolationsConsumer := mocknetwork.NewViolationsConsumer(u.T()) u.setupNetworks(slashingViolationsConsumer) - u.startMiddlewares(nil) + u.startNetworksAndLibp2pNodes() msg := &messages.EntityRequest{ EntityIDs: unittest.IdentifierListFixture(10), diff --git a/network/validator.go b/network/validator.go index 0d40b9290c5..07d5c90daa2 100644 --- a/network/validator.go +++ b/network/validator.go @@ -1,6 +1,6 @@ package network -// MessageValidator validates the incoming message. Message validation happens in the middleware right before it is +// MessageValidator validates the incoming message. Message validation happens in the network right before it is // delivered to the network. type MessageValidator interface { // Validate validates the message and returns true if the message is to be retained and false if it needs to be dropped From 74d7e8474254de1b80909d78c4d21c385b6add7d Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 11:53:00 -0400 Subject: [PATCH 769/815] fixes merge errors --- network/test/blob_service_test.go | 13 +++---------- network/test/echoengine_test.go | 13 ++----------- network/test/epochtransition_test.go | 4 ++-- network/test/meshengine_test.go | 13 +++---------- network/test/network_test.go | 12 ++++++------ network/test/unicast_authorization_test.go | 4 ++-- 6 files changed, 18 insertions(+), 41 deletions(-) diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index 8cb4b36df73..bc8def05537 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -92,19 +92,12 @@ func (suite *BlobServiceTestSuite) SetupTest() { ConnectionPruning: true, ConnectorFactory: connection.DefaultLibp2pBackoffConnectorFactory(), }, nil)) - mws, _ := testutils.MiddlewareFixtures( - suite.T(), - ids, - nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) - suite.networks = testutils.NetworksFixture(suite.T(), sporkId, ids, mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.networks) + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) // starts the nodes and networks - testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index c7fc17dcf0d..e3a0de08558 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -56,23 +56,14 @@ func (suite *EchoEngineTestSuite) SetupTest() { // both nodes should be of the same role to get connected on epidemic dissemination var nodes []p2p.LibP2PNode sporkId := unittest.IdentifierFixture() - suite.ids, nodes = testutils.LibP2PNodeForMiddlewareFixture(suite.T(), sporkId, count) - suite.mws, _ = testutils.MiddlewareFixtures( - suite.T(), - suite.ids, - nodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, suite.nets) suite.ids, nodes = testutils.LibP2PNodeForNetworkFixture(suite.T(), sporkId, count) suite.libp2pNodes = nodes suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, nodes) // starts the nodes and networks - testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 5a77e835432..8f4fa2d2eba 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -199,9 +199,9 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { suite.cancels = append(suite.cancels, cancel) // starts the nodes and networks - testutils.StartNodes(signalerCtx, suite.T(), nodes, 1*time.Second) + testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range nets { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 3ce33ba08c2..cf1d10dd731 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -114,19 +114,12 @@ func (suite *MeshEngineTestSuite) SetupTest() { suite.libp2pNodes = libP2PNodes suite.ids = identities - suite.mws, _ = testutils.MiddlewareFixtures( - suite.T(), - suite.ids, - libP2PNodes, - testutils.MiddlewareConfigFixture(suite.T(), sporkId), - mocknetwork.NewViolationsConsumer(suite.T())) - suite.nets = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.mws) - testutils.StartNodesAndNetworks(signalerCtx, suite.T(), libP2PNodes, suite.nets) + suite.networks, _ = testutils.NetworksFixture(suite.T(), sporkId, suite.ids, suite.libp2pNodes) // starts the nodes and networks - testutils.StartNodes(signalerCtx, suite.T(), suite.libp2pNodes, 1*time.Second) + testutils.StartNodes(signalerCtx, suite.T(), suite.libp2pNodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}, 1*time.Second) + testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/network_test.go b/network/test/network_test.go index 466d3194275..882123fdcd5 100644 --- a/network/test/network_test.go +++ b/network/test/network_test.go @@ -169,7 +169,7 @@ func (suite *NetworkTestSuite) SetupTest() { suite.mwCtx = irrecoverable.NewMockSignalerContext(suite.T(), ctx) - testutils.StartNodes(suite.mwCtx, suite.T(), suite.libP2PNodes, 1*time.Second) + testutils.StartNodes(suite.mwCtx, suite.T(), suite.libP2PNodes) for i, net := range suite.networks { unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, libP2PNodes[i]) @@ -209,7 +209,7 @@ func (suite *NetworkTestSuite) TestUpdateNodeAddresses() { newId := ids[0] // start up nodes and peer managers - testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) newNet.Start(irrecoverableCtx) @@ -310,9 +310,9 @@ func (suite *NetworkTestSuite) TestUnicastRateLimit_Messages() { ctx, cancel := context.WithCancel(suite.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}, 1*time.Second) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}) calls := atomic.NewUint64(0) ch := make(chan struct{}) @@ -448,10 +448,10 @@ func (suite *NetworkTestSuite) TestUnicastRateLimit_Bandwidth() { ctx, cancel := context.WithCancel(suite.mwCtx) irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes, 1*time.Second) + testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}, 1*time.Second) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) // registers an engine on the new network so that it can receive messages on the TestNetworkChannel diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 6bc0ace338a..cef751ab531 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -104,8 +104,8 @@ func (u *UnicastAuthorizationTestSuite) startNetworksAndLibp2pNodes() { ctx, cancel := context.WithCancel(context.Background()) sigCtx, _ := irrecoverable.WithSignaler(ctx) - testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes, 1*time.Second) - testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) + testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes) + testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}) unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) u.cancel = cancel From 49b6587e748ab33740fc73e6b28702cf2b702f53 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 13:07:51 -0400 Subject: [PATCH 770/815] fixes the exposable lock with network --- network/p2p/p2pnet/network.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index 8942654f896..2525149d2f5 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -83,8 +83,6 @@ var NotEjectedFilter = filter.Not(filter.Ejected) // Network represents the overlay network of our peer-to-peer network, including // the protocols for handshakes, authentication, gossiping and heartbeats. type Network struct { - sync.RWMutex - // TODO: using a waitgroup here doesn't actually guarantee that we'll wait for all // goroutines to exit, because new goroutines could be started after we've already // returned from wg.Wait(). We need to solve this the right way using ComponentManager @@ -110,6 +108,7 @@ type Network struct { unicastMessageTimeout time.Duration libP2PNode p2p.LibP2PNode bitswapMetrics module.BitswapMetrics + peerUpdateLock sync.Mutex // protects the peer update process previousProtocolStatePeers []peer.AddrInfo slashingViolationsConsumer network.ViolationsConsumer peerManagerFilters []p2p.PeerFilter @@ -870,8 +869,8 @@ func (n *Network) UpdateNodeAddresses() { Msg("failed to extract peer info from identity") } - n.Lock() - defer n.Unlock() + n.peerUpdateLock.Lock() + defer n.peerUpdateLock.Unlock() // set old addresses to expire for _, oldInfo := range n.previousProtocolStatePeers { From 1f2840ea1219175021379c8320cd2b7ced42f14f Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 13:36:36 -0400 Subject: [PATCH 771/815] refactors all slashing violation consumers --- cmd/access/node_builder/access_node_builder.go | 10 +--------- cmd/observer/node_builder/observer_builder.go | 10 +--------- cmd/scaffold.go | 10 +--------- follower/follower_builder.go | 10 +--------- network/alsp/manager/manager_test.go | 8 +------- network/internal/testutils/testUtil.go | 2 +- network/p2p/p2pnet/network.go | 7 ++++--- network/test/unicast_authorization_test.go | 2 +- 8 files changed, 11 insertions(+), 48 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index a289c021399..9d4e02ed901 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1269,15 +1269,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { NetworkType: network.PublicNetwork, HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), }, - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } + SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(msgValidators...)) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 5bf54c7cef9..259d9aab54c 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -814,15 +814,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, }, - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } + SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index ac7aebe66e3..d4aafb17e7d 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -469,15 +469,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( HeroCacheMetricsFactory: fnb.HeroCacheMetricsFactory(), NetworkType: network.PrivateNetwork, }, - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := fnb.Network.(network.Adapter) - if !ok { - fnb.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } + SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network, adapter) }, }, networkOptions...) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 7dd88e3c554..147d51b445d 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -703,15 +703,7 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, }, - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { - // this is temporary; we are in the process of removing the middleware; hence all this logic must be - // eventually moved to the network component. - // Network has two interfaces; Network which is exposed to the engines; Adapter which is exposed to the middleware. - // Here we are casting the Network to Adapter to get access to the misbehavior report consumer. - adapter, ok := builder.Network.(network.Adapter) - if !ok { - builder.Logger.Fatal().Msg("network must be an adapter to use slashing violations consumer") - } + SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 3819e2298c4..c0ea806974d 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -462,11 +462,6 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t ids, nodes := testutils.LibP2PNodeForNetworkFixture(t, sporkId, 7) // creates 7 nodes (1 victim, 1 honest, 5 spammer nodes one for each slashing violation). idProvider := id.NewFixedIdentityProvider(ids) - // we want to override the network config to use the slashing violations consumer. However, we need the network - // instance to do that, but for the network instance we need the network config. So, we create the adapter first, which - // is a placeholder for the network instance, and then we create the network instance with the network config. - // Network adapter is a sub-interface of the network instance, so we can use it as a placeholder for the network instance. - var adapter network.Adapter // also a placeholder for the slashing violations consumer. var violationsConsumer network.ViolationsConsumer networkCfg := testutils.NetworkConfigFixture( @@ -476,13 +471,12 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t sporkId, nodes[0], p2pnet.WithAlspConfig(managerCfgFixture(t)), - p2pnet.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { + p2pnet.WithSlashingViolationConsumerFactory(func(adapter network.Adapter) network.ViolationsConsumer { violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) return violationsConsumer })) victimNetwork, err := p2pnet.NewNetwork(networkCfg) require.NoError(t, err) - adapter = victimNetwork ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 717920b137b..cf1a3363964 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -217,7 +217,7 @@ func NetworkConfigFixture( AlspMetrics: metrics.NewNoopCollector(), HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), }, - SlashingViolationConsumerFactory: func() network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(_ network.Adapter) network.ViolationsConsumer { return mocknetwork.NewViolationsConsumer(t) }, } diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index 2525149d2f5..b042d61a0ef 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -120,6 +120,7 @@ type Network struct { var _ network.Network = &Network{} var _ network.Middleware = &Network{} +var _ network.Adapter = &Network{} type registerEngineRequest struct { channel channels.Channel @@ -163,7 +164,7 @@ type NetworkConfig struct { UnicastMessageTimeout time.Duration Libp2pNode p2p.LibP2PNode BitSwapMetrics module.BitswapMetrics - SlashingViolationConsumerFactory func() network.ViolationsConsumer + SlashingViolationConsumerFactory func(network.Adapter) network.ViolationsConsumer } // Validate validates the configuration, and sets default values for any missing fields. @@ -196,7 +197,7 @@ func WithCodec(codec network.Codec) NetworkConfigOption { } } -func WithSlashingViolationConsumerFactory(factory func() network.ViolationsConsumer) NetworkConfigOption { +func WithSlashingViolationConsumerFactory(factory func(adapter network.Adapter) network.ViolationsConsumer) NetworkConfigOption { return func(params *NetworkConfig) { params.SlashingViolationConsumerFactory = factory } @@ -344,7 +345,7 @@ func NewNetwork(param *NetworkConfig, opts ...NetworkOption) (*Network, error) { builder.AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { // creation of slashing violations consumer should be postponed till here where the network // is start and the overlay is set. - n.slashingViolationsConsumer = param.SlashingViolationConsumerFactory() + n.slashingViolationsConsumer = param.SlashingViolationConsumerFactory(n) n.authorizedSenderValidator = validator.NewAuthorizedSenderValidator( n.logger, diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index cef751ab531..849557eecb9 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -84,7 +84,7 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer ids, libP2PNodes, p2pnet.WithCodec(u.codec), - p2pnet.WithSlashingViolationConsumerFactory(func() network.ViolationsConsumer { + p2pnet.WithSlashingViolationConsumerFactory(func(_ network.Adapter) network.ViolationsConsumer { return slashingViolationsConsumer })) require.Len(u.T(), ids, 2) From 85ab2662c285a876759e24740c2f62b5833bd3bb Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:57:37 -0700 Subject: [PATCH 772/815] [Benchnet] Small improvements to the loader to support exec data testing --- integration/benchmark/cmd/manual/main.go | 8 +++++--- integration/benchmark/contLoadGenerator.go | 5 +++++ integration/benchmark/scripts.go | 7 +++++++ .../benchmark/scripts/compHeavyTransaction.cdc | 2 +- .../benchmark/scripts/eventHeavyTransaction.cdc | 2 +- .../benchmark/scripts/execDataHeavyTransaction.cdc | 9 +++++++++ .../benchmark/scripts/ledgerHeavyTransaction.cdc | 2 +- integration/benchmark/scripts/myFavContract.cdc | 12 ++++++------ 8 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 integration/benchmark/scripts/execDataHeavyTransaction.cdc diff --git a/integration/benchmark/cmd/manual/main.go b/integration/benchmark/cmd/manual/main.go index 9250b2a1521..bbdd014d242 100644 --- a/integration/benchmark/cmd/manual/main.go +++ b/integration/benchmark/cmd/manual/main.go @@ -35,7 +35,7 @@ const ( func main() { sleep := flag.Duration("sleep", 0, "duration to sleep before benchmarking starts") - loadTypeFlag := flag.String("load-type", "token-transfer", "type of loads (\"token-transfer\", \"add-keys\", \"computation-heavy\", \"event-heavy\", \"ledger-heavy\", \"const-exec\")") + loadTypeFlag := flag.String("load-type", "token-transfer", "type of loads (\"token-transfer\", \"add-keys\", \"computation-heavy\", \"event-heavy\", \"ledger-heavy\", \"const-exec\", \"exec-data-heavy\")") tpsFlag := flag.String("tps", "1", "transactions per second (TPS) to send, accepts a comma separated list of values if used in conjunction with `tps-durations`") tpsDurationsFlag := flag.String("tps-durations", "0", "duration that each load test will run, accepts a comma separted list that will be applied to multiple values of the `tps` flag (defaults to infinite if not provided, meaning only the first tps case will be tested; additional values will be ignored)") chainIDStr := flag.String("chain", string(flowsdk.Emulator), "chain ID") @@ -70,8 +70,10 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - sp := benchmark.NewStatsPusher(ctx, log, *pushgateway, "loader", prometheus.DefaultGatherer) - defer sp.Stop() + if *pushgateway != "disabled" { + sp := benchmark.NewStatsPusher(ctx, log, *pushgateway, "loader", prometheus.DefaultGatherer) + defer sp.Stop() + } addressGen := flowsdk.NewAddressGenerator(chainID) serviceAccountAddress := addressGen.NextAddress() diff --git a/integration/benchmark/contLoadGenerator.go b/integration/benchmark/contLoadGenerator.go index 941e061acca..5bec66f46c7 100644 --- a/integration/benchmark/contLoadGenerator.go +++ b/integration/benchmark/contLoadGenerator.go @@ -30,6 +30,7 @@ const ( EventHeavyLoadType LoadType = "event-heavy" LedgerHeavyLoadType LoadType = "ledger-heavy" ConstExecCostLoadType LoadType = "const-exec" // for an empty transactions with various tx arguments + ExecDataHeavyLoadType LoadType = "exec-data-heavy" ) const lostTransactionThreshold = 90 * time.Second @@ -913,6 +914,8 @@ func (lg *ContLoadGenerator) sendFavContractTx(workerID int) { txScript = EventHeavyScript(*lg.favContractAddress) case LedgerHeavyLoadType: txScript = LedgerHeavyScript(*lg.favContractAddress) + case ExecDataHeavyLoadType: + txScript = ExecDataHeavyScript(*lg.favContractAddress) default: log.Error().Msg("unknown load type") return @@ -944,7 +947,9 @@ func (lg *ContLoadGenerator) sendFavContractTx(workerID int) { return } defer key.IncrementSequenceNumber() + <-ch + lg.workerStatsTracker.IncTxExecuted() } func (lg *ContLoadGenerator) sendTx(workerID int, tx *flowsdk.Transaction) (<-chan flowsdk.TransactionResult, error) { diff --git a/integration/benchmark/scripts.go b/integration/benchmark/scripts.go index 2ebce479b49..7a3b85438b0 100644 --- a/integration/benchmark/scripts.go +++ b/integration/benchmark/scripts.go @@ -89,6 +89,13 @@ func LedgerHeavyScript(favContractAddress flowsdk.Address) []byte { return []byte(fmt.Sprintf(ledgerHeavyScriptTemplate, favContractAddress)) } +//go:embed scripts/execDataHeavyTransaction.cdc +var execDataHeavyScriptTemplate string + +func ExecDataHeavyScript(favContractAddress flowsdk.Address) []byte { + return []byte(fmt.Sprintf(execDataHeavyScriptTemplate, favContractAddress)) +} + //go:embed scripts/constExecCostTransaction.cdc var constExecTransactionTemplate string diff --git a/integration/benchmark/scripts/compHeavyTransaction.cdc b/integration/benchmark/scripts/compHeavyTransaction.cdc index 00215f27b8f..2fc698c3cad 100644 --- a/integration/benchmark/scripts/compHeavyTransaction.cdc +++ b/integration/benchmark/scripts/compHeavyTransaction.cdc @@ -3,6 +3,6 @@ import MyFavContract from 0x%s transaction { prepare(acct: AuthAccount) {} execute { - MyFavContract.ComputationHeavy() + MyFavContract.ComputationHeavy(15000) } } diff --git a/integration/benchmark/scripts/eventHeavyTransaction.cdc b/integration/benchmark/scripts/eventHeavyTransaction.cdc index b9df85b2192..a78edc4522c 100644 --- a/integration/benchmark/scripts/eventHeavyTransaction.cdc +++ b/integration/benchmark/scripts/eventHeavyTransaction.cdc @@ -3,6 +3,6 @@ import MyFavContract from 0x%s transaction { prepare(acct: AuthAccount) {} execute { - MyFavContract.EventHeavy() + MyFavContract.EventHeavy(220) } } diff --git a/integration/benchmark/scripts/execDataHeavyTransaction.cdc b/integration/benchmark/scripts/execDataHeavyTransaction.cdc new file mode 100644 index 00000000000..443006c5a17 --- /dev/null +++ b/integration/benchmark/scripts/execDataHeavyTransaction.cdc @@ -0,0 +1,9 @@ +import MyFavContract from 0x%s + +transaction { + prepare(acct: AuthAccount) {} + execute { + MyFavContract.LedgerInteractionHeavy(100) + MyFavContract.EventHeavy(100) + } +} diff --git a/integration/benchmark/scripts/ledgerHeavyTransaction.cdc b/integration/benchmark/scripts/ledgerHeavyTransaction.cdc index 0b07e590c8a..7fe6698736d 100644 --- a/integration/benchmark/scripts/ledgerHeavyTransaction.cdc +++ b/integration/benchmark/scripts/ledgerHeavyTransaction.cdc @@ -3,6 +3,6 @@ import MyFavContract from 0x%s transaction { prepare(acct: AuthAccount) {} execute { - MyFavContract.LedgerInteractionHeavy() + MyFavContract.LedgerInteractionHeavy(700) } } diff --git a/integration/benchmark/scripts/myFavContract.cdc b/integration/benchmark/scripts/myFavContract.cdc index 48182a11431..c4645f1e14b 100644 --- a/integration/benchmark/scripts/myFavContract.cdc +++ b/integration/benchmark/scripts/myFavContract.cdc @@ -45,13 +45,13 @@ access(all) contract MyFavContract { // heavy operations // computation heavy function - access(all) fun ComputationHeavy() { + access(all) fun ComputationHeavy(_ n: Int) { var s: Int256 = 1024102410241024 var i = 0 var a = Int256(7) var b = Int256(5) var c = Int256(2) - while i < 15000 { + while i < n { s = s * a s = s / b s = s / c @@ -63,18 +63,18 @@ access(all) contract MyFavContract { access(all) event LargeEvent(value: Int256, str: String, list: [UInt256], dic: {String: String}) // event heavy function - access(all) fun EventHeavy() { + access(all) fun EventHeavy(_ n: Int) { var s: Int256 = 1024102410241024 var i = 0 - while i < 220 { + while i < n { emit LargeEvent(value: s, str: s.toString(), list:[], dic:{s.toString():s.toString()}) i = i + 1 } log(i) } - access(all) fun LedgerInteractionHeavy() { - MyFavContract.AddManyRandomItems(700) + access(all) fun LedgerInteractionHeavy(_ n: Int) { + MyFavContract.AddManyRandomItems(n) } } From 34dcfc06e62c1996c5e3cd3751c1c8ad9a59c999 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 14:01:36 -0400 Subject: [PATCH 773/815] fixes TestCreateStream_WithDefaultUnicast --- network/p2p/p2pnode/libp2pStream_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 6d9384cff01..8d693a501cf 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -176,7 +176,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol require.NoError(t, err) nodes[0].Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) go func() { - err = nodes[0].OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { + err := nodes[0].OpenProtectedStream(ctx, pInfo.ID, t.Name(), func(stream network.Stream) error { require.NotNil(t, stream) streams = append(streams, stream) // if we return this function, the stream will be closed, but we need to keep it open for the test @@ -185,7 +185,10 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol allStreamsClosedWg.Done() return nil }) - require.NoError(t, err) + if err != nil { + // we omit errors due to closing the stream. This is because we close the stream in the test. + require.Contains(t, err.Error(), "failed to close the stream") + } }() } From 6e1115793618a32edefa702b79036e902ba3b2cb Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 31 Aug 2023 20:20:06 +0200 Subject: [PATCH 774/815] expose query executor and remove default config --- cmd/execution_builder.go | 23 +------------ engine/execution/computation/manager.go | 43 ++++++++++++------------- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 51348f3a7ef..3d170efd61e 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -47,7 +47,6 @@ import ( "github.com/onflow/flow-go/engine/execution/checker" "github.com/onflow/flow-go/engine/execution/computation" "github.com/onflow/flow-go/engine/execution/computation/committer" - "github.com/onflow/flow-go/engine/execution/computation/query" "github.com/onflow/flow-go/engine/execution/ingestion" "github.com/onflow/flow-go/engine/execution/ingestion/stop" "github.com/onflow/flow-go/engine/execution/ingestion/uploader" @@ -57,7 +56,6 @@ import ( "github.com/onflow/flow-go/engine/execution/state" "github.com/onflow/flow-go/engine/execution/state/bootstrap" "github.com/onflow/flow-go/fvm" - "github.com/onflow/flow-go/fvm/storage/derived" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/ledger/common/pathfinder" @@ -903,30 +901,11 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( // create scripts engine for handling script execution func (exeNode *ExecutionNode) LoadScriptsEngine(node *NodeConfig) (module.ReadyDoneAware, error) { - // for RPC to load it - vm := fvm.NewVirtualMachine() - - options := computation.DefaultFVMOptions(node.RootChainID, false, false) - vmCtx := fvm.NewContext(options...) - - derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize) - if err != nil { - return nil, err - } - - queryExecutor := query.NewQueryExecutor( - exeNode.exeConf.computationConfig.QueryConfig, - node.Logger, - metrics.NewExecutionCollector(node.Tracer), - vm, - vmCtx, - derivedChainData, - ) exeNode.scriptsEng = scripts.New( node.Logger, node.State, - queryExecutor, + exeNode.computationManager.QueryExecutor(), exeNode.executionState, ) diff --git a/engine/execution/computation/manager.go b/engine/execution/computation/manager.go index a238fc5ec7d..ce2c24d76d6 100644 --- a/engine/execution/computation/manager.go +++ b/engine/execution/computation/manager.go @@ -106,7 +106,25 @@ func New( } chainID := vmCtx.Chain.ChainID() - options := DefaultFVMOptions(chainID, params.CadenceTracing, params.ExtensiveTracing) + options := []fvm.Option{ + fvm.WithReusableCadenceRuntimePool( + reusableRuntime.NewReusableCadenceRuntimePool( + ReusableCadenceRuntimePoolSize, + runtime.Config{ + TracingEnabled: params.CadenceTracing, + AccountLinkingEnabled: true, + // Attachments are enabled everywhere except for Mainnet + AttachmentsEnabled: chainID != flow.Mainnet, + // Capability Controllers are enabled everywhere except for Mainnet + CapabilityControllersEnabled: chainID != flow.Mainnet, + }, + )), + } + + if params.ExtensiveTracing { + options = append(options, fvm.WithExtensiveTracing()) + } + vmCtx = fvm.NewContextFromParent(vmCtx, options...) blockComputer, err := computer.NewBlockComputer( @@ -222,25 +240,6 @@ func (e *Manager) GetAccount( snapshot) } -func DefaultFVMOptions(chainID flow.ChainID, cadenceTracing bool, extensiveTracing bool) []fvm.Option { - options := []fvm.Option{ - fvm.WithReusableCadenceRuntimePool( - reusableRuntime.NewReusableCadenceRuntimePool( - ReusableCadenceRuntimePoolSize, - runtime.Config{ - TracingEnabled: cadenceTracing, - AccountLinkingEnabled: true, - // Attachments are enabled everywhere except for Mainnet - AttachmentsEnabled: chainID != flow.Mainnet, - // Capability Controllers are enabled everywhere except for Mainnet - CapabilityControllersEnabled: chainID != flow.Mainnet, - }, - )), - } - - if extensiveTracing { - options = append(options, fvm.WithExtensiveTracing()) - } - - return options +func (e *Manager) QueryExecutor() query.Executor { + return e.queryExecutor } From 32c50115794884b4a678cb8f0c1d6d4f6781c40f Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 31 Aug 2023 20:44:06 +0200 Subject: [PATCH 775/815] explain script execution interface --- engine/execution/scripts/engine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index 46505ca802e..31b7ab125cf 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -15,6 +15,8 @@ import ( "github.com/onflow/flow-go/state/protocol" ) +// ScriptExecutionState is a subset of the `state.ExecutionState` interface purposed to only access the state +// used for script execution and not mutate the execution state of the blockchain. type ScriptExecutionState interface { // NewStorageSnapshot creates a new ready-only view at the given state commitment. NewStorageSnapshot(flow.StateCommitment) snapshot.StorageSnapshot From 5fc0cc34bbf4a9611191a85021d105827cff68bb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 15:06:21 -0400 Subject: [PATCH 776/815] fixes panic in observer --- cmd/observer/node_builder/observer_builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 259d9aab54c..a35439be372 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -796,6 +796,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { Codec: builder.CodecFactory(), Me: builder.Me, Topology: nil, // topology is nil since it is managed by libp2p; //TODO: can we set empty topology? + Libp2pNode: publicLibp2pNode, Metrics: builder.Metrics.Network, BitSwapMetrics: builder.Metrics.Bitswap, IdentityProvider: builder.IdentityProvider, From b265179f09ec512a32bd56d2fad2424f611b9aef Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 15:35:56 -0400 Subject: [PATCH 777/815] changes middleware interface to be ready-done-aware instead of a component --- network/network.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/network.go b/network/network.go index cf781856fe4..939a62f651a 100644 --- a/network/network.go +++ b/network/network.go @@ -5,6 +5,7 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/network/channels" ) @@ -78,7 +79,7 @@ type Adapter interface { // offers to lower level networking components such as libp2p. It is responsible for subscribing to and unsubscribing // from channels, as well as updating the addresses of all the authorized participants in the Flow protocol. type Middleware interface { - component.Component + module.ReadyDoneAware DisallowListNotificationConsumer // Subscribe subscribes the middleware to a channel. From 41d4a466a5aa1e19560536ac051dff1df25a193a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 15:39:25 -0400 Subject: [PATCH 778/815] temp; experimentally removing the skipped tests --- network/test/echoengine_test.go | 2 +- network/test/meshengine_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index e3a0de08558..100e2f08fdb 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -40,7 +40,7 @@ type EchoEngineTestSuite struct { // TestEchoEngineTestSuite runs all the test methods in this test suit func TestEchoEngineTestSuite(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") + // unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(EchoEngineTestSuite)) } diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index cf1d10dd731..8b07651cb88 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -49,7 +49,7 @@ type MeshEngineTestSuite struct { // TestMeshNetTestSuite runs all tests in this test suit func TestMeshNetTestSuite(t *testing.T) { - unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") + // unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(MeshEngineTestSuite)) } From 4ee5bd4dd66663122bb4a9de0204af6d869fcb06 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Thu, 31 Aug 2023 15:39:25 -0400 Subject: [PATCH 779/815] Revert "temp; experimentally removing the skipped tests" This reverts commit 41d4a466a5aa1e19560536ac051dff1df25a193a. --- network/test/echoengine_test.go | 2 +- network/test/meshengine_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index 100e2f08fdb..e3a0de08558 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -40,7 +40,7 @@ type EchoEngineTestSuite struct { // TestEchoEngineTestSuite runs all the test methods in this test suit func TestEchoEngineTestSuite(t *testing.T) { - // unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") + unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(EchoEngineTestSuite)) } diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index 8b07651cb88..cf1d10dd731 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -49,7 +49,7 @@ type MeshEngineTestSuite struct { // TestMeshNetTestSuite runs all tests in this test suit func TestMeshNetTestSuite(t *testing.T) { - // unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") + unittest.SkipUnless(t, unittest.TEST_FLAKY, "this should be revisited once network/test is running in a separate CI job, runs fine locally") suite.Run(t, new(MeshEngineTestSuite)) } From 0c906265c7204358886690c2e6dbe95663318902 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 1 Sep 2023 10:25:15 +0300 Subject: [PATCH 780/815] Add a NO-OP implementation of ReadRandom on facadeEnvironment This is mainly to unblock certain downstream dependencies, until the concrete implementation. --- fvm/environment/facade_env.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fvm/environment/facade_env.go b/fvm/environment/facade_env.go index 76ac5205725..9c27f6c53e3 100644 --- a/fvm/environment/facade_env.go +++ b/fvm/environment/facade_env.go @@ -315,3 +315,9 @@ func (env *facadeEnvironment) SetInterpreterSharedState(state *interpreter.Share func (env *facadeEnvironment) GetInterpreterSharedState() *interpreter.SharedState { return nil } + +func (env *facadeEnvironment) ReadRandom(buffer []byte) error { + // NO-OP for now, to unblock certain downstream dependencies. + // E.g. cadence-tools/test + return nil +} From 816216652ee0965f0dd168795f99ea8da875d42b Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 1 Sep 2023 10:25:15 -0400 Subject: [PATCH 781/815] sub package test in listTargetPackages() --- utils/test_matrix/test_matrix_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/utils/test_matrix/test_matrix_test.go b/utils/test_matrix/test_matrix_test.go index 8e5d2e6a976..3b0b820a6eb 100644 --- a/utils/test_matrix/test_matrix_test.go +++ b/utils/test_matrix/test_matrix_test.go @@ -46,6 +46,21 @@ func TestListTargetPackages(t *testing.T) { require.Contains(t, seenPackages, flowPackagePrefix+"ghi") } +// TestListTargetSubPackages tests that if a subpackage is specified as a target package, then the sub package and +// all children of the sub package are also included in the target packages. +func TestListTargetSubPackages(t *testing.T) { + targetPackages, seenPackages := listTargetPackages([]string{"abc/def"}, getAllFlowPackages()) + require.Equal(t, 1, len(targetPackages)) + require.Equal(t, 2, len(seenPackages)) + + // there should be 2 packages that starts with "abc/def" + require.Equal(t, 2, len(targetPackages["abc/def"])) + require.Contains(t, targetPackages["abc/def"], flowPackagePrefix+"abc/def") + + require.Contains(t, seenPackages, flowPackagePrefix+"abc/def") + require.Contains(t, seenPackages, flowPackagePrefix+"abc/def/ghi") +} + func TestListOtherPackages(t *testing.T) { var seenPackages = make(map[string]string) seenPackages[flowPackagePrefix+"abc"] = flowPackagePrefix + "abc" From 2cf194cbd5d135a0a8d2b60f00f3694896192098 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 1 Sep 2023 10:38:42 -0400 Subject: [PATCH 782/815] sub package test in listOtherPackages() --- utils/test_matrix/test_matrix_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/test_matrix/test_matrix_test.go b/utils/test_matrix/test_matrix_test.go index 3b0b820a6eb..ba9b9caf415 100644 --- a/utils/test_matrix/test_matrix_test.go +++ b/utils/test_matrix/test_matrix_test.go @@ -63,17 +63,17 @@ func TestListTargetSubPackages(t *testing.T) { func TestListOtherPackages(t *testing.T) { var seenPackages = make(map[string]string) - seenPackages[flowPackagePrefix+"abc"] = flowPackagePrefix + "abc" + seenPackages[flowPackagePrefix+"abc/def"] = flowPackagePrefix + "abc/def" + seenPackages[flowPackagePrefix+"abc/def/ghi"] = flowPackagePrefix + "abc/def/ghi" seenPackages[flowPackagePrefix+"ghi"] = flowPackagePrefix + "ghi" seenPackages[flowPackagePrefix+"mno/abc"] = flowPackagePrefix + "mno/abc" seenPackages[flowPackagePrefix+"stu"] = flowPackagePrefix + "stu" otherPackages := listOtherPackages(getAllFlowPackages(), seenPackages) - require.Equal(t, 9, len(otherPackages)) + require.Equal(t, 8, len(otherPackages)) - require.Contains(t, otherPackages, flowPackagePrefix+"abc/def") - require.Contains(t, otherPackages, flowPackagePrefix+"abc/def/ghi") + require.Contains(t, otherPackages, flowPackagePrefix+"abc") require.Contains(t, otherPackages, flowPackagePrefix+"def") require.Contains(t, otherPackages, flowPackagePrefix+"def/abc") require.Contains(t, otherPackages, flowPackagePrefix+"jkl") From ad9872db723a5b6577ff6e726976b31e2d1e216c Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 1 Sep 2023 10:44:33 -0400 Subject: [PATCH 783/815] split up network tests to network/test network/p2p --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d510a8d196..f90002a7479 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix - run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network utils + run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network/test network/p2p utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) From d659d4b3a2108e893fecdc65f5707ce9b92eeaba Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 1 Sep 2023 16:28:29 -0400 Subject: [PATCH 784/815] flaky test CI - split up network tests to network/test network/p2p --- .github/workflows/ci.yml | 4 ++-- .github/workflows/flaky-test-monitor.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f90002a7479..b5e8f75f510 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix - run: go run utils/test_matrix/test_matrix.go access admin cmd consensus engine fvm ledger module network/test network/p2p utils + run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network/test network/p2p utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) @@ -118,7 +118,7 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: true - - name: Setup tests (${{ matrix.targets.name }} + - name: Setup tests (${{ matrix.targets.name }}) run: VERBOSE=1 make -e GO_TEST_PACKAGES="${{ matrix.targets.packages }}" install-tools - name: Run tests (${{ matrix.targets.name }}) uses: nick-fields/retry@v2 diff --git a/.github/workflows/flaky-test-monitor.yml b/.github/workflows/flaky-test-monitor.yml index ec0852485e2..f1c87d03348 100644 --- a/.github/workflows/flaky-test-monitor.yml +++ b/.github/workflows/flaky-test-monitor.yml @@ -43,7 +43,7 @@ jobs: cache: true - name: Set Test Matrix id: set-test-matrix - run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network utils + run: go run utils/test_matrix/test_matrix.go admin cmd consensus engine fvm ledger module network/test network/p2p utils unit-test: name: Unit Tests (${{ matrix.targets.name }}) From 86bc8c9ac27c28c59345dd39f2f02d1a3ffab962 Mon Sep 17 00:00:00 2001 From: Misha Date: Fri, 1 Sep 2023 17:35:36 -0400 Subject: [PATCH 785/815] more sub packages testing --- utils/test_matrix/test_matrix_test.go | 39 ++++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/utils/test_matrix/test_matrix_test.go b/utils/test_matrix/test_matrix_test.go index ba9b9caf415..af442e13a63 100644 --- a/utils/test_matrix/test_matrix_test.go +++ b/utils/test_matrix/test_matrix_test.go @@ -10,6 +10,7 @@ import ( func getAllFlowPackages() []string { return []string{ flowPackagePrefix + "abc", + flowPackagePrefix + "abc/123", flowPackagePrefix + "abc/def", flowPackagePrefix + "abc/def/ghi", flowPackagePrefix + "def", @@ -25,14 +26,15 @@ func getAllFlowPackages() []string { } } +// TestListTargetPackages tests that the target packages are included in the target packages and seen packages. func TestListTargetPackages(t *testing.T) { targetPackages, seenPackages := listTargetPackages([]string{"abc", "ghi"}, getAllFlowPackages()) require.Equal(t, 2, len(targetPackages)) - require.Equal(t, 4, len(seenPackages)) - // there should be 3 packages that start with "abc" - require.Equal(t, 3, len(targetPackages["abc"])) + // there should be 4 packages that start with "abc" + require.Equal(t, 4, len(targetPackages["abc"])) require.Contains(t, targetPackages["abc"], flowPackagePrefix+"abc") + require.Contains(t, targetPackages["abc"], flowPackagePrefix+"abc/123") require.Contains(t, targetPackages["abc"], flowPackagePrefix+"abc/def") require.Contains(t, targetPackages["abc"], flowPackagePrefix+"abc/def/ghi") @@ -40,7 +42,10 @@ func TestListTargetPackages(t *testing.T) { require.Equal(t, 1, len(targetPackages["ghi"])) require.Contains(t, targetPackages["ghi"], flowPackagePrefix+"ghi") + // there should be 5 packages that start with "abc" + require.Equal(t, 5, len(seenPackages)) require.Contains(t, seenPackages, flowPackagePrefix+"abc") + require.Contains(t, seenPackages, flowPackagePrefix+"abc/123") require.Contains(t, seenPackages, flowPackagePrefix+"abc/def") require.Contains(t, seenPackages, flowPackagePrefix+"abc/def/ghi") require.Contains(t, seenPackages, flowPackagePrefix+"ghi") @@ -51,16 +56,17 @@ func TestListTargetPackages(t *testing.T) { func TestListTargetSubPackages(t *testing.T) { targetPackages, seenPackages := listTargetPackages([]string{"abc/def"}, getAllFlowPackages()) require.Equal(t, 1, len(targetPackages)) - require.Equal(t, 2, len(seenPackages)) // there should be 2 packages that starts with "abc/def" require.Equal(t, 2, len(targetPackages["abc/def"])) require.Contains(t, targetPackages["abc/def"], flowPackagePrefix+"abc/def") + require.Equal(t, 2, len(seenPackages)) require.Contains(t, seenPackages, flowPackagePrefix+"abc/def") require.Contains(t, seenPackages, flowPackagePrefix+"abc/def/ghi") } +// TestListOtherPackages tests that the remaining packages that don't match any of the target packages are included func TestListOtherPackages(t *testing.T) { var seenPackages = make(map[string]string) seenPackages[flowPackagePrefix+"abc/def"] = flowPackagePrefix + "abc/def" @@ -71,9 +77,10 @@ func TestListOtherPackages(t *testing.T) { otherPackages := listOtherPackages(getAllFlowPackages(), seenPackages) - require.Equal(t, 8, len(otherPackages)) + require.Equal(t, 9, len(otherPackages)) require.Contains(t, otherPackages, flowPackagePrefix+"abc") + require.Contains(t, otherPackages, flowPackagePrefix+"abc/123") require.Contains(t, otherPackages, flowPackagePrefix+"def") require.Contains(t, otherPackages, flowPackagePrefix+"def/abc") require.Contains(t, otherPackages, flowPackagePrefix+"jkl") @@ -83,21 +90,27 @@ func TestListOtherPackages(t *testing.T) { require.Contains(t, otherPackages, flowPackagePrefix+"yz") } +// TestGenerateTestMatrix tests that the test matrix is generated correctly where the target packages include top level +// packages as well as sub packages. func TestGenerateTestMatrix(t *testing.T) { - targetPackages, seenPackages := listTargetPackages([]string{"abc", "ghi"}, getAllFlowPackages()) - require.Equal(t, 2, len(targetPackages)) - require.Equal(t, 4, len(seenPackages)) + targetPackages, seenPackages := listTargetPackages([]string{"abc/def", "def", "ghi"}, getAllFlowPackages()) + require.Equal(t, 3, len(targetPackages)) + require.Equal(t, 5, len(seenPackages)) otherPackages := listOtherPackages(getAllFlowPackages(), seenPackages) matrix := generateTestMatrix(targetPackages, otherPackages) - // should be 3 groups in test matrix: abc, ghi, others - require.Equal(t, 3, len(matrix)) + // should be 3 groups in test matrix: abc/def, def, ghi, others + require.Equal(t, 4, len(matrix)) require.Contains(t, matrix, testMatrix{ - Name: "abc", - Packages: "github.com/onflow/flow-go/abc github.com/onflow/flow-go/abc/def github.com/onflow/flow-go/abc/def/ghi"}, + Name: "abc/def", + Packages: "github.com/onflow/flow-go/abc/def github.com/onflow/flow-go/abc/def/ghi"}, + ) + require.Contains(t, matrix, testMatrix{ + Name: "def", + Packages: "github.com/onflow/flow-go/def github.com/onflow/flow-go/def/abc"}, ) require.Contains(t, matrix, testMatrix{ Name: "ghi", @@ -105,6 +118,6 @@ func TestGenerateTestMatrix(t *testing.T) { ) require.Contains(t, matrix, testMatrix{ Name: "others", - Packages: "github.com/onflow/flow-go/def github.com/onflow/flow-go/def/abc github.com/onflow/flow-go/jkl github.com/onflow/flow-go/mno/abc github.com/onflow/flow-go/pqr github.com/onflow/flow-go/stu github.com/onflow/flow-go/vwx github.com/onflow/flow-go/vwx/ghi github.com/onflow/flow-go/yz"}, + Packages: "github.com/onflow/flow-go/abc github.com/onflow/flow-go/abc/123 github.com/onflow/flow-go/jkl github.com/onflow/flow-go/mno/abc github.com/onflow/flow-go/pqr github.com/onflow/flow-go/stu github.com/onflow/flow-go/vwx github.com/onflow/flow-go/vwx/ghi github.com/onflow/flow-go/yz"}, ) } From 5c8aec08fa6327e1a84bfce1667db1e341cb0b73 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Sat, 2 Sep 2023 19:45:06 +0100 Subject: [PATCH 786/815] Fix goimports --- network/p2p/libp2pNode.go | 1 + 1 file changed, 1 insertion(+) diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 0f64e61fe62..5bc9b2d158c 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -9,6 +9,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/routing" + "github.com/onflow/flow-go/engine/collection" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/component" From 9cbb6537aa41748ba7faad546b562790564d77ff Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Mon, 4 Sep 2023 09:32:23 -0700 Subject: [PATCH 787/815] log num txs and state changed in block executed log --- engine/execution/ingestion/engine.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go index 53ed58c99c6..724b048d675 100644 --- a/engine/execution/ingestion/engine.go +++ b/engine/execution/ingestion/engine.go @@ -698,6 +698,8 @@ func (e *Engine) executeBlock( Hex("result_id", logging.Entity(receipt.ExecutionResult)). Hex("execution_data_id", receipt.ExecutionResult.ExecutionDataID[:]). Bool("sealed", isExecutedBlockSealed). + Bool("state_changed", finalEndState != *executableBlock.StartState). + Uint64("num_txs", transactionCount(receipt.ExecutionResult)). Bool("broadcasted", broadcasted). Int64("timeSpentInMS", time.Since(startedAt).Milliseconds()). Msg("block executed") @@ -717,6 +719,14 @@ func (e *Engine) executeBlock( } +func transactionCount(result flow.ExecutionResult) uint64 { + count := uint64(0) + for _, chunk := range result.Chunks { + count += chunk.NumberOfTransactions + } + return count +} + // we've executed the block, now we need to check: // 1. whether the state syncing can be turned off // 2. whether its children can be executed From 54c3f5569e4e710803973cc44b12f074d600d958 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 5 Sep 2023 09:50:33 -0700 Subject: [PATCH 788/815] removes capturing loop variable from test utils --- network/internal/testutils/testUtil.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index cf1a3363964..053144466e9 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -156,8 +156,6 @@ func NetworksFixture(t *testing.T, idProviders := make([]*unittest.UpdatableIDProvider, 0) for i := 0; i < count; i++ { - i := i // capture loop variable - idProvider := unittest.NewUpdatableIDProvider(ids) params := NetworkConfigFixture(t, *ids[i], idProvider, sporkId, libp2pNodes[i]) From 6f2810681074a583da71e123920fa2116a5e23e8 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Tue, 5 Sep 2023 09:51:03 -0700 Subject: [PATCH 789/815] Update network/test/network_test.go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/test/network_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/test/network_test.go b/network/test/network_test.go index 882123fdcd5..d133a61e941 100644 --- a/network/test/network_test.go +++ b/network/test/network_test.go @@ -43,7 +43,7 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSUb mesh, see: +// libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSub mesh, see: // https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go // One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSubD such calls, // and that's what we do with tagsObserver. From 3dd45788646ad3d10b71528b191348bbe7165ba9 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 5 Sep 2023 09:51:40 -0700 Subject: [PATCH 790/815] removes dead comment --- network/p2p/p2pnode/libp2pNode.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index f8995465d29..7c2b9bc6f59 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -213,8 +213,7 @@ func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protecti if err != nil { return fmt.Errorf("failed to set write deadline for stream: %w", err) } - - // create a gogo protobuf writer + err = writingLogic(s) if err != nil { // reset the stream to ensure that the next stream creation is not affected by the error. From 6a1e097cacaf4ceadc03f50b5a4618540c5a0f71 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Tue, 5 Sep 2023 09:52:24 -0700 Subject: [PATCH 791/815] Update network/test/network_test.go Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> --- network/test/network_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/test/network_test.go b/network/test/network_test.go index d133a61e941..b6a68f0a498 100644 --- a/network/test/network_test.go +++ b/network/test/network_test.go @@ -45,7 +45,7 @@ import ( // libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSub mesh, see: // https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go -// One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSubD such calls, +// One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSub such calls, // and that's what we do with tagsObserver. // Usage: // The tagsObserver struct observes the OnNext, OnError, and OnComplete events related to peer tags. From 24e2b24c472c43bb0b0ba7e56eecacca50677e6b Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Tue, 5 Sep 2023 10:12:41 -0700 Subject: [PATCH 792/815] ports in the missing parts from master --- network/p2p/p2pnet/network.go | 46 +++++++++++++++---------------- network/p2p/p2pnode/libp2pNode.go | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index b042d61a0ef..995f4a2cc7d 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -794,29 +794,6 @@ func (n *Network) ReportMisbehaviorOnChannel(channel channels.Channel, report ne n.misbehaviorReportManager.HandleMisbehaviorReport(channel, report) } -// unicastMaxMsgSize returns the max permissible size for a unicast message -func unicastMaxMsgSize(messageType string) int { - switch messageType { - case "*messages.ChunkDataResponse": - return LargeMsgMaxUnicastMsgSize - default: - return DefaultMaxUnicastMsgSize - } -} - -// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete -func (n *Network) unicastMaxMsgDuration(messageType string) time.Duration { - switch messageType { - case "messages.ChunkDataResponse": - if LargeMsgUnicastTimeout > n.unicastMessageTimeout { - return LargeMsgUnicastTimeout - } - return n.unicastMessageTimeout - default: - return n.unicastMessageTimeout - } -} - func DefaultValidators(log zerolog.Logger, flowID flow.Identifier) []network.MessageValidator { return []network.MessageValidator{ validator.ValidateNotSender(flowID), // validator to filter out messages sent by this node itself @@ -1264,3 +1241,26 @@ func UnicastMaxMsgSizeByCode(payload []byte) (int, error) { maxSize := unicastMaxMsgSize(messageType) return maxSize, nil } + +// unicastMaxMsgSize returns the max permissible size for a unicast message +func unicastMaxMsgSize(messageType string) int { + switch messageType { + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": + return LargeMsgMaxUnicastMsgSize + default: + return DefaultMaxUnicastMsgSize + } +} + +// unicastMaxMsgDuration returns the max duration to allow for a unicast send to complete +func (n *Network) unicastMaxMsgDuration(messageType string) time.Duration { + switch messageType { + case "*messages.ChunkDataResponse", "messages.ChunkDataResponse": + if LargeMsgUnicastTimeout > n.unicastMessageTimeout { + return LargeMsgUnicastTimeout + } + return n.unicastMessageTimeout + default: + return n.unicastMessageTimeout + } +} diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 7c2b9bc6f59..e61dede16e1 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -213,7 +213,7 @@ func (n *Node) OpenProtectedStream(ctx context.Context, peerID peer.ID, protecti if err != nil { return fmt.Errorf("failed to set write deadline for stream: %w", err) } - + err = writingLogic(s) if err != nil { // reset the stream to ensure that the next stream creation is not affected by the error. From 69b43393374c088876a7fe5afcbe1e4f047f9357 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 5 Sep 2023 18:17:28 +0100 Subject: [PATCH 793/815] Remove unused CallAvailableNode types and add godoc for it's parameters --- engine/access/rpc/backend/node_communicator.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 448b0c7b267..004f6339c06 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -12,18 +12,15 @@ import ( // maxFailedRequestCount represents the maximum number of failed requests before returning errors. const maxFailedRequestCount = 3 -// NodeAction is a callback function type that represents an action to be performed on a node. -// It takes a node as input and returns an error indicating the result of the action. -type NodeAction func(node *flow.Identity) error - -// ErrorTerminator is a callback function that determines whether an error should terminate further execution. -// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. -type ErrorTerminator func(node *flow.Identity, err error) bool - type Communicator interface { CallAvailableNode( + //List of node identifiers to execute callback on nodes flow.IdentityList, + //Callback function that represents an action to be performed on a node. + //It takes a node as input and returns an error indicating the result of the action. call func(node *flow.Identity) error, + // Callback function that determines whether an error should terminate further execution. + // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error } @@ -48,8 +45,13 @@ func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { // If the error occurs in circuit breaker, it continues to the next node. // If the maximum failed request count is reached, it returns the accumulated errors. func (b *NodeCommunicator) CallAvailableNode( +//List of node identifiers to execute callback on nodes flow.IdentityList, +//Callback function that determines whether an error should terminate further execution. +// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. call func(id *flow.Identity) error, +// Callback function that determines whether an error should terminate further execution. +// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error { var errs *multierror.Error From 54fd79a86120c9d75f3ded85e4df3dd4133a3081 Mon Sep 17 00:00:00 2001 From: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:32:29 -0700 Subject: [PATCH 794/815] add ExecDataHeavyLoadType to initialization --- integration/benchmark/contLoadGenerator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/benchmark/contLoadGenerator.go b/integration/benchmark/contLoadGenerator.go index 5bec66f46c7..4d8a93fe3ca 100644 --- a/integration/benchmark/contLoadGenerator.go +++ b/integration/benchmark/contLoadGenerator.go @@ -180,7 +180,7 @@ func New( lg.workFunc = lg.sendAddKeyTx case ConstExecCostLoadType: lg.workFunc = lg.sendConstExecCostTx - case CompHeavyLoadType, EventHeavyLoadType, LedgerHeavyLoadType: + case CompHeavyLoadType, EventHeavyLoadType, LedgerHeavyLoadType, ExecDataHeavyLoadType: lg.workFunc = lg.sendFavContractTx default: return nil, fmt.Errorf("unknown load type: %s", loadParams.LoadType) From d2b082655394ac83aa0f4dd87a24a69a35e40b31 Mon Sep 17 00:00:00 2001 From: Nozim Mehrubonov Date: Tue, 5 Sep 2023 19:23:37 +0100 Subject: [PATCH 795/815] Fix linter errors --- .../access/rpc/backend/node_communicator.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/engine/access/rpc/backend/node_communicator.go b/engine/access/rpc/backend/node_communicator.go index 004f6339c06..ba4b005a70f 100644 --- a/engine/access/rpc/backend/node_communicator.go +++ b/engine/access/rpc/backend/node_communicator.go @@ -14,13 +14,13 @@ const maxFailedRequestCount = 3 type Communicator interface { CallAvailableNode( - //List of node identifiers to execute callback on + //List of node identifiers to execute callback on nodes flow.IdentityList, - //Callback function that represents an action to be performed on a node. - //It takes a node as input and returns an error indicating the result of the action. + //Callback function that represents an action to be performed on a node. + //It takes a node as input and returns an error indicating the result of the action. call func(node *flow.Identity) error, - // Callback function that determines whether an error should terminate further execution. - // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. + // Callback function that determines whether an error should terminate further execution. + // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error } @@ -45,13 +45,13 @@ func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { // If the error occurs in circuit breaker, it continues to the next node. // If the maximum failed request count is reached, it returns the accumulated errors. func (b *NodeCommunicator) CallAvailableNode( -//List of node identifiers to execute callback on + //List of node identifiers to execute callback on nodes flow.IdentityList, -//Callback function that determines whether an error should terminate further execution. -// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. + //Callback function that determines whether an error should terminate further execution. + // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. call func(id *flow.Identity) error, -// Callback function that determines whether an error should terminate further execution. -// It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. + // Callback function that determines whether an error should terminate further execution. + // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. shouldTerminateOnError func(node *flow.Identity, err error) bool, ) error { var errs *multierror.Error From 789c662d001f50a1d7661a10f7514528432bffa9 Mon Sep 17 00:00:00 2001 From: Misha Date: Wed, 6 Sep 2023 06:38:53 -0400 Subject: [PATCH 796/815] updated subpackage test, docs --- utils/test_matrix/test_matrix_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/test_matrix/test_matrix_test.go b/utils/test_matrix/test_matrix_test.go index af442e13a63..d585ba7bba7 100644 --- a/utils/test_matrix/test_matrix_test.go +++ b/utils/test_matrix/test_matrix_test.go @@ -57,10 +57,12 @@ func TestListTargetSubPackages(t *testing.T) { targetPackages, seenPackages := listTargetPackages([]string{"abc/def"}, getAllFlowPackages()) require.Equal(t, 1, len(targetPackages)) - // there should be 2 packages that starts with "abc/def" + // there should be 2 target subpackages that starts with "abc/def" require.Equal(t, 2, len(targetPackages["abc/def"])) require.Contains(t, targetPackages["abc/def"], flowPackagePrefix+"abc/def") + require.Contains(t, targetPackages["abc/def"], flowPackagePrefix+"abc/def/ghi") + // there should be 2 seen subpackages that start with "abc/def" require.Equal(t, 2, len(seenPackages)) require.Contains(t, seenPackages, flowPackagePrefix+"abc/def") require.Contains(t, seenPackages, flowPackagePrefix+"abc/def/ghi") From 2903497db3f7718389ebf49d608f39fafeceb309 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 6 Sep 2023 16:29:28 +0200 Subject: [PATCH 797/815] move script execution interface and compose it --- engine/execution/scripts/engine.go | 19 +++---------------- engine/execution/state/state.go | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index 31b7ab125cf..55ba967f8a9 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -10,30 +10,17 @@ import ( "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/execution" "github.com/onflow/flow-go/engine/execution/computation/query" - "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/flow-go/engine/execution/state" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" ) -// ScriptExecutionState is a subset of the `state.ExecutionState` interface purposed to only access the state -// used for script execution and not mutate the execution state of the blockchain. -type ScriptExecutionState interface { - // NewStorageSnapshot creates a new ready-only view at the given state commitment. - NewStorageSnapshot(flow.StateCommitment) snapshot.StorageSnapshot - - // StateCommitmentByBlockID returns the final state commitment for the provided block ID. - StateCommitmentByBlockID(context.Context, flow.Identifier) (flow.StateCommitment, error) - - // HasState returns true if the state with the given state commitment exists in memory - HasState(flow.StateCommitment) bool -} - type Engine struct { unit *engine.Unit log zerolog.Logger state protocol.State queryExecutor query.Executor - execState ScriptExecutionState + execState state.ScriptExecutionState } var _ execution.ScriptExecutor = (*Engine)(nil) @@ -42,7 +29,7 @@ func New( logger zerolog.Logger, state protocol.State, queryExecutor query.Executor, - execState ScriptExecutionState, + execState state.ScriptExecutionState, ) *Engine { return &Engine{ unit: engine.NewUnit(), diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 5da53e16cd7..863c38fd60f 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -22,6 +22,19 @@ import ( // ReadOnlyExecutionState allows to read the execution state type ReadOnlyExecutionState interface { + ScriptExecutionState + + // ChunkDataPackByChunkID retrieve a chunk data pack given the chunk ID. + ChunkDataPackByChunkID(flow.Identifier) (*flow.ChunkDataPack, error) + + GetExecutionResultID(context.Context, flow.Identifier) (flow.Identifier, error) + + GetHighestExecutedBlockID(context.Context) (uint64, flow.Identifier, error) +} + +// ScriptExecutionState is a subset of the `state.ExecutionState` interface purposed to only access the state +// used for script execution and not mutate the execution state of the blockchain. +type ScriptExecutionState interface { // NewStorageSnapshot creates a new ready-only view at the given state commitment. NewStorageSnapshot(flow.StateCommitment) snapshot.StorageSnapshot @@ -30,13 +43,6 @@ type ReadOnlyExecutionState interface { // HasState returns true if the state with the given state commitment exists in memory HasState(flow.StateCommitment) bool - - // ChunkDataPackByChunkID retrieve a chunk data pack given the chunk ID. - ChunkDataPackByChunkID(flow.Identifier) (*flow.ChunkDataPack, error) - - GetExecutionResultID(context.Context, flow.Identifier) (flow.Identifier, error) - - GetHighestExecutedBlockID(context.Context) (uint64, flow.Identifier, error) } // TODO Many operations here are should be transactional, so we need to refactor this From 4bdfa466aa0afdad689233675a9d4a1ab7bfa4d2 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 6 Sep 2023 09:47:06 -0700 Subject: [PATCH 798/815] Update network/internal/p2pfixtures/fixtures.go Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com> --- network/internal/p2pfixtures/fixtures.go | 1 - 1 file changed, 1 deletion(-) diff --git a/network/internal/p2pfixtures/fixtures.go b/network/internal/p2pfixtures/fixtures.go index 0caef608af9..9ef0a1c0bbc 100644 --- a/network/internal/p2pfixtures/fixtures.go +++ b/network/internal/p2pfixtures/fixtures.go @@ -366,7 +366,6 @@ func EnsureStreamCreation(t *testing.T, ctx context.Context, from []p2p.LibP2PNo return nil }) require.NoError(t, err) - } } } From 5a047eb7884d5048bf4087b6c4704f6900cead37 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 6 Sep 2023 09:51:20 -0700 Subject: [PATCH 799/815] Update network/p2p/p2pnode/libp2pNode.go Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com> --- network/p2p/p2pnode/libp2pNode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index e61dede16e1..f7b691115a0 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -146,7 +146,7 @@ func (n *Node) Stop() error { return nil } -// AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. +// ConnectToPeerAddrInfo adds a peer to this node by adding it to this node's peerstore and connecting to it. // All errors returned from this function can be considered benign. func (n *Node) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { return n.host.Connect(ctx, peerInfo) From 7c1333850e17f3af381e7fab04f3dc91891c05ce Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 21 Jul 2023 15:23:42 +0200 Subject: [PATCH 800/815] Adding an array of state lists --- .../herocache/backdata/heropool/linkedlist.go | 9 + .../herocache/backdata/heropool/pool.go | 234 +++++++----------- .../herocache/backdata/heropool/pool_test.go | 113 ++++----- 3 files changed, 156 insertions(+), 200 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/linkedlist.go b/module/mempool/herocache/backdata/heropool/linkedlist.go index 99823070d69..30414ed9de5 100644 --- a/module/mempool/herocache/backdata/heropool/linkedlist.go +++ b/module/mempool/herocache/backdata/heropool/linkedlist.go @@ -15,3 +15,12 @@ type state struct { tail EIndex size uint32 } + +// NewStates constructs an array of a doubly linked-lists. +func NewStates(numberOfStates int) []state { + result := make([]state, numberOfStates) + for i := 1; i < numberOfStates; i++ { + result[i] = state{head: InvalidIndex, tail: InvalidIndex, size: 0} + } + return result +} diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 87e21abb33e..d8c4140ebc1 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,7 +1,6 @@ package heropool import ( - "fmt" "math" "github.com/rs/zerolog" @@ -18,21 +17,21 @@ const ( NoEjection = EjectionMode("no-ejection") ) +// StateIndex is a type of a state of a placeholder in a pool. +type StateIndex uint + +const numberOfStates = 2 +const ( // iota is reset to 0 + stateFree StateIndex = iota + stateUsed +) + // EIndex is data type representing an entity index in Pool. type EIndex uint32 // InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil address. const InvalidIndex EIndex = math.MaxUint32 -// A type dedicated to describe possible states of placeholders for entities in the pool. -type StateType string - -// A placeholder in a free state can be used to store an entity. -const stateFree StateType = "free-state" - -// A placeholder in a used state stores currently an entity. -const stateUsed StateType = "used-state" - // poolEntity represents the data type that is maintained by type poolEntity struct { PoolEntity @@ -64,8 +63,7 @@ func (p PoolEntity) Entity() flow.Entity { type Pool struct { logger zerolog.Logger - free state // keeps track of free slots. - used state // keeps track of allocated slots to cachedEntities. + states []state // keeps track of a slot's state poolEntities []poolEntity ejectionMode EjectionMode } @@ -74,16 +72,8 @@ type Pool struct { // logger and a provided fixed size. func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { l := &Pool{ - free: state{ - head: InvalidIndex, - tail: InvalidIndex, - size: 0, - }, - used: state{ - head: InvalidIndex, - tail: InvalidIndex, - size: 0, - }, + //constructor for states make them invalid + states: NewStates(numberOfStates), poolEntities: make([]poolEntity, sizeLimit), ejectionMode: ejectionMode, logger: logger, @@ -105,8 +95,16 @@ func (p *Pool) setDefaultNodeLinkValues() { // initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities. func (p *Pool) initFreeEntities() { - for i := 0; i < len(p.poolEntities); i++ { - p.appendEntity(stateFree, EIndex(i)) + p.states[stateFree].head = 0 + p.states[stateFree].tail = 0 + p.poolEntities[p.states[stateFree].head].node.prev = InvalidIndex + p.poolEntities[p.states[stateFree].tail].node.next = InvalidIndex + p.states[stateFree].size = 1 + for i := 1; i < len(p.poolEntities); i++ { + p.connect(p.states[stateFree].tail, EIndex(i)) + p.states[stateFree].tail = EIndex(i) + p.poolEntities[p.states[stateFree].tail].node.next = InvalidIndex + p.states[stateFree].size++ } } @@ -124,7 +122,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) ( p.poolEntities[entityIndex].entity = entity p.poolEntities[entityIndex].id = entityId p.poolEntities[entityIndex].owner = owner - p.appendEntity(stateUsed, entityIndex) + p.switchState(stateFree, stateUsed, entityIndex) } return entityIndex, slotAvailable, ejectedEntity @@ -137,10 +135,10 @@ func (p *Pool) Get(entityIndex EIndex) (flow.Identifier, flow.Entity, uint64) { // All returns all stored entities in this pool. func (p Pool) All() []PoolEntity { - all := make([]PoolEntity, p.used.size) - next := p.used.head + all := make([]PoolEntity, p.states[stateUsed].size) + next := p.states[stateUsed].head - for i := uint32(0); i < p.used.size; i++ { + for i := uint32(0); i < p.states[stateUsed].size; i++ { e := p.poolEntities[next] all[i] = e.PoolEntity next = e.node.next @@ -149,13 +147,14 @@ func (p Pool) All() []PoolEntity { return all } -// Head returns the head of used items. Assuming no ejection happened and pool never goes beyond limit, Head returns +// Head returns the head of states[stateUsed] items. Assuming no ejection happened and pool never goes beyond limit, Head returns // the first inserted element. func (p Pool) Head() (flow.Entity, bool) { - if p.used.size == 0 { + + if p.states[stateUsed].size == 0 { return nil, false } - e := p.poolEntities[p.used.head] + e := p.poolEntities[p.states[stateUsed].head] return e.Entity(), true } @@ -169,12 +168,12 @@ func (p Pool) Head() (flow.Entity, bool) { func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEntity flow.Entity) { lruEject := func() (EIndex, bool, flow.Entity) { // LRU ejection - // the used head is the oldest entity, so we turn the used head to a free head here. + // the states[stateUsed] head is the oldest entity, so we turn the states[stateUsed] head to a states[stateFree] head here. invalidatedEntity := p.invalidateUsedHead() - return p.claimFreeHead(), true, invalidatedEntity + return p.states[stateFree].head, true, invalidatedEntity } - if p.free.size == 0 { + if p.states[stateFree].size == 0 { // the free list is empty, so we are out of space, and we need to eject. switch p.ejectionMode { case NoEjection: @@ -182,7 +181,7 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn return InvalidIndex, false, nil case RandomEjection: // we only eject randomly when the pool is full and random ejection is on. - random, err := rand.Uint32n(p.used.size) + random, err := rand.Uint32n(p.states[stateUsed].size) if err != nil { p.logger.Fatal().Err(err). Msg("hero pool random ejection failed - falling back to LRU ejection") @@ -191,45 +190,44 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn } randomIndex := EIndex(random) invalidatedEntity := p.invalidateEntityAtIndex(randomIndex) - return p.claimFreeHead(), true, invalidatedEntity + return p.states[stateFree].head, true, invalidatedEntity case LRUEjection: // LRU ejection return lruEject() } } - // claiming the head of free list as the slice index for the next entity to be added - return p.claimFreeHead(), true, nil + // returning the head of states[stateFree] list as the slice index for the next entity to be added + return p.states[stateFree].head, true, nil } // Size returns total number of entities that this list maintains. func (p Pool) Size() uint32 { - return p.used.size + return p.states[stateUsed].size } // getHeads returns entities corresponding to the used and free heads. -func (p *Pool) getHeads() (*poolEntity, *poolEntity) { +func (p Pool) getHeads() (*poolEntity, *poolEntity) { var usedHead, freeHead *poolEntity - if p.used.size != 0 { - usedHead = &p.poolEntities[p.used.head] + if p.states[stateUsed].size != 0 { + usedHead = &p.poolEntities[p.states[stateUsed].head] } - if p.free.size != 0 { - freeHead = &p.poolEntities[p.free.head] + if p.states[stateFree].size != 0 { + freeHead = &p.poolEntities[p.states[stateFree].head] } return usedHead, freeHead } -// getTails returns entities corresponding to the used and free tails. -func (p *Pool) getTails() (*poolEntity, *poolEntity) { +// getTails returns entities corresponding to the states[stateUsed] and states[stateFree] tails. +func (p Pool) getTails() (*poolEntity, *poolEntity) { var usedTail, freeTail *poolEntity - if p.used.size != 0 { - usedTail = &p.poolEntities[p.used.tail] + if p.states[stateUsed].size != 0 { + usedTail = &p.poolEntities[p.states[stateUsed].tail] } - - if p.free.size != 0 { - freeTail = &p.poolEntities[p.free.tail] + if p.states[stateFree].size != 0 { + freeTail = &p.poolEntities[p.states[stateFree].tail] } return usedTail, freeTail @@ -241,40 +239,28 @@ func (p *Pool) connect(prev EIndex, next EIndex) { p.poolEntities[next].node.prev = prev } -// invalidateUsedHead moves current used head forward by one node. It +// invalidateUsedHead moves current states[stateUsed] head forward by one node. It // also removes the entity the invalidated head is presenting and appends the -// node represented by the used head to the tail of the free list. +// node represented by the states[stateUsed] head to the tail of the states[stateFree] list. func (p *Pool) invalidateUsedHead() flow.Entity { - headSliceIndex := p.used.head + headSliceIndex := p.states[stateUsed].head return p.invalidateEntityAtIndex(headSliceIndex) } -// claimFreeHead moves the free head forward, and returns the slice index of the -// old free head to host a new entity. -func (p *Pool) claimFreeHead() EIndex { - oldFreeHeadIndex := p.free.head - p.removeEntity(stateFree, oldFreeHeadIndex) - return oldFreeHeadIndex -} - // Remove removes entity corresponding to given getSliceIndex from the list. func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { return p.invalidateEntityAtIndex(sliceIndex) } // invalidateEntityAtIndex invalidates the given getSliceIndex in the linked list by -// removing its corresponding linked-list node from the used linked list, and appending -// it to the tail of the free list. It also removes the entity that the invalidated node is presenting. +// removing its corresponding linked-list node from the states[stateUsed] linked list, and appending +// it to the tail of the states[stateFree] list. It also removes the entity that the invalidated node is presenting. func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { - invalidatedEntity := p.poolEntities[sliceIndex].entity - if invalidatedEntity == nil { - panic(fmt.Sprintf("removing an entity from an empty slot with an index : %d", sliceIndex)) - } - p.removeEntity(stateUsed, sliceIndex) + poolEntity := p.poolEntities[sliceIndex] + invalidatedEntity := poolEntity.entity + p.switchState(stateUsed, stateFree, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil - p.appendEntity(stateFree, EIndex(sliceIndex)) - return invalidatedEntity } @@ -292,77 +278,47 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { return true } -// utility method that removes an entity from one of the states. -// NOTE: a removed entity has to be added to another state. -func (p *Pool) removeEntity(stateType StateType, entityIndex EIndex) { - var s *state = nil - switch stateType { - case stateFree: - s = &p.free - case stateUsed: - s = &p.used - default: - panic(fmt.Sprintf("unknown state type: %s", stateType)) - } - - if s.size == 0 { +// switches state of an entity. +func (p *Pool) switchState(stateFrom StateIndex, stateTo StateIndex, entityIndex EIndex) error { + // Remove from stateFrom list + if p.states[stateFrom].size == 0 { panic("Removing an entity from an empty list") - } - if s.size == 1 { - // here set to InvalidIndex - s.head = InvalidIndex - s.tail = InvalidIndex - s.size-- - p.poolEntities[entityIndex].node.next = InvalidIndex - p.poolEntities[entityIndex].node.prev = InvalidIndex - return - } - node := p.poolEntities[entityIndex].node - - if entityIndex != s.head && entityIndex != s.tail { - // links next and prev elements for non-head and non-tail element - p.connect(node.prev, node.next) - } - - if entityIndex == s.head { - // moves head forward - s.head = node.next - p.poolEntities[s.head].node.prev = InvalidIndex - } + } else if p.states[stateFrom].size == 1 { + p.states[stateFrom].head = InvalidIndex + p.states[stateFrom].tail = InvalidIndex + } else { + node := p.poolEntities[entityIndex].node + + if entityIndex != p.states[stateFrom].head && entityIndex != p.states[stateFrom].tail { + // links next and prev elements for non-head and non-tail element + p.connect(node.prev, node.next) + } - if entityIndex == s.tail { - // moves tail backwards - s.tail = node.prev - p.poolEntities[s.tail].node.next = InvalidIndex - } - s.size-- - p.poolEntities[entityIndex].node.next = InvalidIndex - p.poolEntities[entityIndex].node.prev = InvalidIndex -} + if entityIndex == p.states[stateFrom].head { + // moves head forward + p.states[stateFrom].head = node.next + p.poolEntities[p.states[stateFrom].head].node.prev = InvalidIndex + } -// appends an entity to the tail of the state or creates a first element. -// NOTE: entity should not be in any list before this method is applied -func (p *Pool) appendEntity(stateType StateType, entityIndex EIndex) { - var s *state = nil - switch stateType { - case stateFree: - s = &p.free - case stateUsed: - s = &p.used - default: - panic(fmt.Sprintf("unknown state type: %s", stateType)) + if entityIndex == p.states[stateFrom].tail { + // moves tail backwards + p.states[stateFrom].tail = node.prev + p.poolEntities[p.states[stateFrom].tail].node.next = InvalidIndex + } } - - if s.size == 0 { - s.head = entityIndex - s.tail = entityIndex - p.poolEntities[s.head].node.prev = InvalidIndex - p.poolEntities[s.tail].node.next = InvalidIndex - s.size = 1 - return + p.states[stateFrom].size-- + + // Add to stateTo list + if p.states[stateTo].size == 0 { + p.states[stateTo].head = entityIndex + p.states[stateTo].tail = entityIndex + p.poolEntities[p.states[stateTo].head].node.prev = InvalidIndex + p.poolEntities[p.states[stateTo].tail].node.next = InvalidIndex + } else { + p.connect(p.states[stateTo].tail, entityIndex) + p.states[stateTo].tail = entityIndex + p.poolEntities[p.states[stateTo].tail].node.next = InvalidIndex } - p.connect(s.tail, entityIndex) - s.size++ - s.tail = entityIndex - p.poolEntities[s.tail].node.next = InvalidIndex + p.states[stateTo].size++ + return nil } diff --git a/module/mempool/herocache/backdata/heropool/pool_test.go b/module/mempool/herocache/backdata/heropool/pool_test.go index 5cc19609753..07ca3c366dc 100644 --- a/module/mempool/herocache/backdata/heropool/pool_test.go +++ b/module/mempool/herocache/backdata/heropool/pool_test.go @@ -380,17 +380,17 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // size of list should be decremented after each invalidation. require.Equal(t, uint32(totalEntitiesStored-i-1), pool.Size()) // invalidated head should be appended to free entities - require.Equal(t, pool.free.tail, EIndex(i)) + require.Equal(t, pool.states[stateFree].tail, EIndex(i)) if freeListInitialSize != 0 { // number of entities is below limit, hence free list is not empty. // invalidating used head must not change the free head. - require.Equal(t, EIndex(totalEntitiesStored), pool.free.head) + require.Equal(t, EIndex(totalEntitiesStored), pool.states[stateFree].head) } else { // number of entities is greater than or equal to limit, hence free list is empty. // free head must be updated to the first invalidated head (index 0), // and must be kept there for entire test (as we invalidate head not tail). - require.Equal(t, EIndex(0), pool.free.head) + require.Equal(t, EIndex(0), pool.states[stateFree].head) } // except when the list is empty, head must be updated after invalidation, @@ -399,14 +399,14 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt if i != totalEntitiesStored-1 { // used linked-list tailAccessibleFromHead(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, pool.Size()) headAccessibleFromTail(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, pool.Size()) @@ -414,14 +414,14 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // // after invalidating each item, size of free linked-list is incremented by one. tailAccessibleFromHead(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, uint32(i+1+freeListInitialSize)) headAccessibleFromTail(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, uint32(i+1+freeListInitialSize)) } @@ -435,21 +435,21 @@ func testInvalidatingHead(t *testing.T, pool *Pool, entities []*unittest.MockEnt // used tail should point to the last element in pool, since we are // invalidating head. require.Equal(t, entities[totalEntitiesStored-1].ID(), usedTail.id) - require.Equal(t, EIndex(totalEntitiesStored-1), pool.used.tail) + require.Equal(t, EIndex(totalEntitiesStored-1), pool.states[stateUsed].tail) // used head must point to the next element in the pool, // i.e., invalidating head moves it forward. require.Equal(t, entities[i+1].ID(), usedHead.id) - require.Equal(t, EIndex(i+1), pool.used.head) + require.Equal(t, EIndex(i+1), pool.states[stateUsed].head) } else { // pool is empty // used head and tail must be nil and their corresponding // pointer indices must be undefined. require.Nil(t, usedHead) require.Nil(t, usedTail) - require.True(t, pool.used.size == 0) - require.Equal(t, pool.used.tail, InvalidIndex) - require.Equal(t, pool.used.head, InvalidIndex) + require.True(t, pool.states[stateUsed].size == 0) + require.Equal(t, pool.states[stateUsed].tail, InvalidIndex) + require.Equal(t, pool.states[stateUsed].head, InvalidIndex) } checkEachEntityIsInFreeOrUsedState(t, pool) } @@ -461,25 +461,25 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt offset := len(pool.poolEntities) - size for i := 0; i < size; i++ { // invalidates tail index - tailIndex := pool.used.tail + tailIndex := pool.states[stateUsed].tail require.Equal(t, EIndex(size-1-i), tailIndex) pool.invalidateEntityAtIndex(tailIndex) // old head index must be invalidated require.True(t, pool.isInvalidated(tailIndex)) // unclaimed head should be appended to free entities - require.Equal(t, pool.free.tail, tailIndex) + require.Equal(t, pool.states[stateFree].tail, tailIndex) if offset != 0 { // number of entities is below limit // free must head keeps pointing to first empty index after // adding all entities. - require.Equal(t, EIndex(size), pool.free.head) + require.Equal(t, EIndex(size), pool.states[stateFree].head) } else { // number of entities is greater than or equal to limit // free head must be updated to last element in the pool (size - 1), // and must be kept there for entire test (as we invalidate tail not head). - require.Equal(t, EIndex(size-1), pool.free.head) + require.Equal(t, EIndex(size-1), pool.states[stateFree].head) } // size of pool should be shrunk after each invalidation. @@ -492,27 +492,27 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // used linked-list tailAccessibleFromHead(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, pool.Size()) headAccessibleFromTail(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, pool.Size()) // free linked-list tailAccessibleFromHead(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, uint32(i+1+offset)) headAccessibleFromTail(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, uint32(i+1+offset)) } @@ -524,20 +524,20 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // // used tail should move backward after each invalidation require.Equal(t, entities[size-i-2].ID(), usedTail.id) - require.Equal(t, EIndex(size-i-2), pool.used.tail) + require.Equal(t, EIndex(size-i-2), pool.states[stateUsed].tail) // used head must point to the first element in the pool, require.Equal(t, entities[0].ID(), usedHead.id) - require.Equal(t, EIndex(0), pool.used.head) + require.Equal(t, EIndex(0), pool.states[stateUsed].head) } else { // pool is empty // used head and tail must be nil and their corresponding // pointer indices must be undefined. require.Nil(t, usedHead) require.Nil(t, usedTail) - require.True(t, pool.used.size == 0) - require.Equal(t, pool.used.head, InvalidIndex) - require.Equal(t, pool.used.tail, InvalidIndex) + require.True(t, pool.states[stateUsed].size == 0) + require.Equal(t, pool.states[stateUsed].head, InvalidIndex) + require.Equal(t, pool.states[stateUsed].tail, InvalidIndex) } checkEachEntityIsInFreeOrUsedState(t, pool) } @@ -546,14 +546,14 @@ func testInvalidatingTail(t *testing.T, pool *Pool, entities []*unittest.MockEnt // testInitialization evaluates the state of an initialized pool before adding any element to it. func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { // "used" linked-list must have a zero size, since we have no elements in the list. - require.True(t, pool.used.size == 0) - require.Equal(t, pool.used.head, InvalidIndex) - require.Equal(t, pool.used.tail, InvalidIndex) + require.True(t, pool.states[stateUsed].size == 0) + require.Equal(t, pool.states[stateUsed].head, InvalidIndex) + require.Equal(t, pool.states[stateUsed].tail, InvalidIndex) for i := 0; i < len(pool.poolEntities); i++ { if i == 0 { // head of "free" linked-list should point to InvalidIndex of entities slice. - require.Equal(t, EIndex(i), pool.free.head) + require.Equal(t, EIndex(i), pool.states[stateFree].head) // previous element of head must be undefined (linked-list head feature). require.Equal(t, pool.poolEntities[i].node.prev, InvalidIndex) } @@ -570,7 +570,7 @@ func testInitialization(t *testing.T, pool *Pool, _ []*unittest.MockEntity) { if i == len(pool.poolEntities)-1 { // tail of "free" linked-list should point to the last index in entities slice. - require.Equal(t, EIndex(i), pool.free.tail) + require.Equal(t, EIndex(i), pool.states[stateFree].tail) // next element of tail must be undefined. require.Equal(t, pool.poolEntities[i].node.next, InvalidIndex) } @@ -690,7 +690,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. if i < len(pool.poolEntities)-1 { // as long as we are below limit, after adding i element, free head // should move to i+1 element. - require.Equal(t, EIndex(i+1), pool.free.head) + require.Equal(t, EIndex(i+1), pool.states[stateFree].head) // head must be healthy and point back to undefined. require.Equal(t, freeHead.node.prev, InvalidIndex) } else { @@ -706,7 +706,7 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. // must keep pointing to last index of the array-based linked-list. In other // words, adding element must not change free tail (since only free head is // updated). - require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.free.tail) + require.Equal(t, EIndex(len(pool.poolEntities)-1), pool.states[stateFree].tail) // head tail be healthy and point next to undefined. require.Equal(t, freeTail.node.next, InvalidIndex) } else { @@ -726,13 +726,13 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. usedTraverseStep = uint32(len(pool.poolEntities)) } tailAccessibleFromHead(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, usedTraverseStep) headAccessibleFromTail(t, - pool.used.head, - pool.used.tail, + pool.states[stateUsed].head, + pool.states[stateUsed].tail, pool, usedTraverseStep) @@ -750,13 +750,13 @@ func testAddingEntities(t *testing.T, pool *Pool, entitiesToBeAdded []*unittest. freeTraverseStep = uint32(0) } tailAccessibleFromHead(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, freeTraverseStep) headAccessibleFromTail(t, - pool.free.head, - pool.free.tail, + pool.states[stateFree].head, + pool.states[stateFree].tail, pool, freeTraverseStep) @@ -804,7 +804,7 @@ func withTestScenario(t *testing.T, pool := NewHeroPool(limit, ejectionMode, unittest.Logger()) // head on underlying linked-list value should be uninitialized - require.True(t, pool.used.size == 0) + require.True(t, pool.states[stateUsed].size == 0) require.Equal(t, pool.Size(), uint32(0)) entities := unittest.EntityListFixture(uint(entityCount)) @@ -857,7 +857,7 @@ func headAccessibleFromTail(t *testing.T, headSliceIndex EIndex, tailSliceIndex func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { pool_capacity := len(pool.poolEntities) // check size - require.Equal(t, int(pool.free.size+pool.used.size), pool_capacity, "Pool capacity is not equal to the sum of used and free sizes") + require.Equal(t, int(pool.states[stateFree].size+pool.states[stateUsed].size), pool_capacity, "Pool capacity is not equal to the sum of used and free sizes") // check elelments nodesInFree := discoverEntitiesBelongingToStateList(t, pool, stateFree) nodesInUsed := discoverEntitiesBelongingToStateList(t, pool, stateUsed) @@ -868,18 +868,9 @@ func checkEachEntityIsInFreeOrUsedState(t *testing.T, pool *Pool) { } // discoverEntitiesBelongingToStateList discovers all entities in the pool that belong to the given list. -func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, stateType StateType) []bool { - var s *state = nil - switch stateType { - case stateFree: - s = &pool.free - case stateUsed: - s = &pool.used - default: - panic(fmt.Sprintf("unknown state type: %s", stateType)) - } +func discoverEntitiesBelongingToStateList(t *testing.T, pool *Pool, stateType StateIndex) []bool { result := make([]bool, len(pool.poolEntities)) - for node_index := s.head; node_index != InvalidIndex; { + for node_index := pool.states[stateType].head; node_index != InvalidIndex; { require.False(t, result[node_index], "A node is present two times in the same state list") result[node_index] = true node_index = pool.poolEntities[node_index].node.next From 3b5cc0ffad5a27a47ac43b4e3f1360dd894d1c83 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 4 Aug 2023 16:09:12 +0200 Subject: [PATCH 801/815] Improved comments --- module/mempool/herocache/backdata/heropool/pool.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index d8c4140ebc1..23e49cea9d7 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -1,6 +1,7 @@ package heropool import ( + "fmt" "math" "github.com/rs/zerolog" @@ -72,7 +73,7 @@ type Pool struct { // logger and a provided fixed size. func NewHeroPool(sizeLimit uint32, ejectionMode EjectionMode, logger zerolog.Logger) *Pool { l := &Pool{ - //constructor for states make them invalid + //construcs states initialized to InvalidIndexes states: NewStates(numberOfStates), poolEntities: make([]poolEntity, sizeLimit), ejectionMode: ejectionMode, @@ -150,7 +151,6 @@ func (p Pool) All() []PoolEntity { // Head returns the head of states[stateUsed] items. Assuming no ejection happened and pool never goes beyond limit, Head returns // the first inserted element. func (p Pool) Head() (flow.Entity, bool) { - if p.states[stateUsed].size == 0 { return nil, false } @@ -256,8 +256,10 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { // removing its corresponding linked-list node from the states[stateUsed] linked list, and appending // it to the tail of the states[stateFree] list. It also removes the entity that the invalidated node is presenting. func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { - poolEntity := p.poolEntities[sliceIndex] - invalidatedEntity := poolEntity.entity + invalidatedEntity := p.poolEntities[sliceIndex].entity + if invalidatedEntity == nil { + panic(fmt.Sprintf("removing an entity from an empty slot with an index : %d", sliceIndex)) + } p.switchState(stateUsed, stateFree, sliceIndex) p.poolEntities[sliceIndex].id = flow.ZeroID p.poolEntities[sliceIndex].entity = nil From 21129fe50c0e105b8892f7542ea6843289004173 Mon Sep 17 00:00:00 2001 From: Boris Kozlov Date: Fri, 25 Aug 2023 13:29:47 +0200 Subject: [PATCH 802/815] Applied minor comments --- .../herocache/backdata/heropool/pool.go | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/module/mempool/herocache/backdata/heropool/pool.go b/module/mempool/herocache/backdata/heropool/pool.go index 23e49cea9d7..54191cba592 100644 --- a/module/mempool/herocache/backdata/heropool/pool.go +++ b/module/mempool/herocache/backdata/heropool/pool.go @@ -148,7 +148,7 @@ func (p Pool) All() []PoolEntity { return all } -// Head returns the head of states[stateUsed] items. Assuming no ejection happened and pool never goes beyond limit, Head returns +// Head returns the head of used items. Assuming no ejection happened and pool never goes beyond limit, Head returns // the first inserted element. func (p Pool) Head() (flow.Entity, bool) { if p.states[stateUsed].size == 0 { @@ -159,6 +159,9 @@ func (p Pool) Head() (flow.Entity, bool) { } // sliceIndexForEntity returns a slice index which hosts the next entity to be added to the list. +// This index is invalid if there are no available slots or ejection could not be performed. +// If the valid index is returned then it is guaranteed that it corresponds to a free list head. +// Thus when filled with a new entity a switchState must be applied. // // The first boolean return value (hasAvailableSlot) says whether pool has an available slot. // Pool goes out of available slots if it is full and no ejection is set. @@ -168,7 +171,7 @@ func (p Pool) Head() (flow.Entity, bool) { func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEntity flow.Entity) { lruEject := func() (EIndex, bool, flow.Entity) { // LRU ejection - // the states[stateUsed] head is the oldest entity, so we turn the states[stateUsed] head to a states[stateFree] head here. + // the used head is the oldest entity, so we turn the used head to a free head here. invalidatedEntity := p.invalidateUsedHead() return p.states[stateFree].head, true, invalidatedEntity } @@ -197,7 +200,7 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn } } - // returning the head of states[stateFree] list as the slice index for the next entity to be added + // returning the head of free list as the slice index for the next entity to be added return p.states[stateFree].head, true, nil } @@ -220,7 +223,7 @@ func (p Pool) getHeads() (*poolEntity, *poolEntity) { return usedHead, freeHead } -// getTails returns entities corresponding to the states[stateUsed] and states[stateFree] tails. +// getTails returns entities corresponding to the used and free tails. func (p Pool) getTails() (*poolEntity, *poolEntity) { var usedTail, freeTail *poolEntity if p.states[stateUsed].size != 0 { @@ -239,9 +242,9 @@ func (p *Pool) connect(prev EIndex, next EIndex) { p.poolEntities[next].node.prev = prev } -// invalidateUsedHead moves current states[stateUsed] head forward by one node. It +// invalidateUsedHead moves current used head forward by one node. It // also removes the entity the invalidated head is presenting and appends the -// node represented by the states[stateUsed] head to the tail of the states[stateFree] list. +// node represented by the used head to the tail of the free list. func (p *Pool) invalidateUsedHead() flow.Entity { headSliceIndex := p.states[stateUsed].head return p.invalidateEntityAtIndex(headSliceIndex) @@ -253,8 +256,8 @@ func (p *Pool) Remove(sliceIndex EIndex) flow.Entity { } // invalidateEntityAtIndex invalidates the given getSliceIndex in the linked list by -// removing its corresponding linked-list node from the states[stateUsed] linked list, and appending -// it to the tail of the states[stateFree] list. It also removes the entity that the invalidated node is presenting. +// removing its corresponding linked-list node from the used linked list, and appending +// it to the tail of the free list. It also removes the entity that the invalidated node is presenting. func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity { invalidatedEntity := p.poolEntities[sliceIndex].entity if invalidatedEntity == nil { @@ -281,7 +284,7 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool { } // switches state of an entity. -func (p *Pool) switchState(stateFrom StateIndex, stateTo StateIndex, entityIndex EIndex) error { +func (p *Pool) switchState(stateFrom StateIndex, stateTo StateIndex, entityIndex EIndex) { // Remove from stateFrom list if p.states[stateFrom].size == 0 { panic("Removing an entity from an empty list") @@ -322,5 +325,4 @@ func (p *Pool) switchState(stateFrom StateIndex, stateTo StateIndex, entityIndex p.poolEntities[p.states[stateTo].tail].node.next = InvalidIndex } p.states[stateTo].size++ - return nil } From 04e8919a6e2d7706a90945f8e642374294c0c18a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:24:44 -0700 Subject: [PATCH 803/815] re-generates mocks --- network/mocknetwork/middleware.go | 105 ++++++++++ network/p2p/libp2pNode.go | 46 ++-- network/p2p/mock/core_p2_p.go | 103 +++++++++ network/p2p/mock/peer_management.go | 279 +++++++++++++++++++++++++ network/p2p/mock/pub_sub.go | 94 +++++++++ network/p2p/mock/routable.go | 67 ++++++ network/p2p/mock/unicast_management.go | 62 ++++++ 7 files changed, 730 insertions(+), 26 deletions(-) create mode 100644 network/mocknetwork/middleware.go create mode 100644 network/p2p/mock/core_p2_p.go create mode 100644 network/p2p/mock/peer_management.go create mode 100644 network/p2p/mock/pub_sub.go create mode 100644 network/p2p/mock/routable.go create mode 100644 network/p2p/mock/unicast_management.go diff --git a/network/mocknetwork/middleware.go b/network/mocknetwork/middleware.go new file mode 100644 index 00000000000..f2552d768f1 --- /dev/null +++ b/network/mocknetwork/middleware.go @@ -0,0 +1,105 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + channels "github.com/onflow/flow-go/network/channels" + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" +) + +// Middleware is an autogenerated mock type for the Middleware type +type Middleware struct { + mock.Mock +} + +// Done provides a mock function with given fields: +func (_m *Middleware) Done() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// OnAllowListNotification provides a mock function with given fields: _a0 +func (_m *Middleware) OnAllowListNotification(_a0 *network.AllowListingUpdate) { + _m.Called(_a0) +} + +// OnDisallowListNotification provides a mock function with given fields: _a0 +func (_m *Middleware) OnDisallowListNotification(_a0 *network.DisallowListingUpdate) { + _m.Called(_a0) +} + +// Ready provides a mock function with given fields: +func (_m *Middleware) Ready() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Subscribe provides a mock function with given fields: channel +func (_m *Middleware) Subscribe(channel channels.Channel) error { + ret := _m.Called(channel) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Unsubscribe provides a mock function with given fields: channel +func (_m *Middleware) Unsubscribe(channel channels.Channel) error { + ret := _m.Called(channel) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateNodeAddresses provides a mock function with given fields: +func (_m *Middleware) UpdateNodeAddresses() { + _m.Called() +} + +type mockConstructorTestingTNewMiddleware interface { + mock.TestingT + Cleanup(func()) +} + +// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware { + mock := &Middleware{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 54c94e99848..0e37be1a7f8 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -37,8 +37,6 @@ type CoreP2P interface { // PeerManagement set of node traits related to its lifecycle and metadata retrieval type PeerManagement interface { - // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. - AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error // ConnectToPeerAddrInfo connects to the peer with the given peer address information. // This method is used to connect to a peer that is not in the peer store. ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error @@ -48,31 +46,10 @@ type PeerManagement interface { ListPeers(topic string) []peer.ID // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. GetPeersForProtocol(pid protocol.ID) peer.IDSlice - // OpenProtectedStream opens a new stream to a peer with a protection tag. The protection tag can be used to ensure - // that the connection to the peer is maintained for a particular purpose. The stream is opened to the given peerID - // and writingLogic is executed on the stream. The created stream does not need to be reused and can be inexpensively - // created for each send. Moreover, the stream creation does not incur a round-trip time as the stream negotiation happens - // on an existing connection. - // - // Args: - // - ctx: The context used to control the stream's lifecycle. - // - peerID: The ID of the peer to open the stream to. - // - protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive, and - // won't prune the connection while the tag is active. - // - writingLogic: A callback function that contains the logic for writing to the stream. It allows an external caller to - // write to the stream without having to worry about the stream creation and management. - // - // Returns: - // error: An error, if any occurred during the process. This includes failure in creating the stream, setting the write - // deadline, executing the writing logic, resetting the stream if the writing logic fails, or closing the stream. - // All returned errors during this process can be considered benign. - OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error // GetIPPort returns the IP and Port the libp2p node is listening on. GetIPPort() (string, string, error) // RoutingTable returns the node routing table RoutingTable() *kbucket.RoutingTable - // ListPeers returns list of peer IDs for peers subscribed to the topic. - ListPeers(topic string) []peer.ID // Subscribe subscribes the node to the given topic and returns the subscription Subscribe(topic channels.Topic, topicValidator TopicValidatorFunc) (Subscription, error) // Unsubscribe cancels the subscriber and closes the topic corresponding to the given channel. @@ -106,10 +83,27 @@ type Routable interface { Routing() routing.Routing } -// StreamManagement peer to peer stream management functions +// UnicastManagement abstracts the unicast management capabilities of the node. type UnicastManagement interface { - // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. - CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) + // OpenProtectedStream opens a new stream to a peer with a protection tag. The protection tag can be used to ensure + // that the connection to the peer is maintained for a particular purpose. The stream is opened to the given peerID + // and writingLogic is executed on the stream. The created stream does not need to be reused and can be inexpensively + // created for each send. Moreover, the stream creation does not incur a round-trip time as the stream negotiation happens + // on an existing connection. + // + // Args: + // - ctx: The context used to control the stream's lifecycle. + // - peerID: The ID of the peer to open the stream to. + // - protectionTag: A tag that protects the connection and ensures that the connection manager keeps it alive, and + // won't prune the connection while the tag is active. + // - writingLogic: A callback function that contains the logic for writing to the stream. It allows an external caller to + // write to the stream without having to worry about the stream creation and management. + // + // Returns: + // error: An error, if any occurred during the process. This includes failure in creating the stream, setting the write + // deadline, executing the writing logic, resetting the stream if the writing logic fails, or closing the stream. + // All returned errors during this process can be considered benign. + OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(stream libp2pnet.Stream) error) error // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []protocols.ProtocolName) error } diff --git a/network/p2p/mock/core_p2_p.go b/network/p2p/mock/core_p2_p.go new file mode 100644 index 00000000000..f2d2cda99fa --- /dev/null +++ b/network/p2p/mock/core_p2_p.go @@ -0,0 +1,103 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + host "github.com/libp2p/go-libp2p/core/host" + component "github.com/onflow/flow-go/module/component" + + irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + + mock "github.com/stretchr/testify/mock" +) + +// CoreP2P is an autogenerated mock type for the CoreP2P type +type CoreP2P struct { + mock.Mock +} + +// GetIPPort provides a mock function with given fields: +func (_m *CoreP2P) GetIPPort() (string, string, error) { + ret := _m.Called() + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func() (string, string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Host provides a mock function with given fields: +func (_m *CoreP2P) Host() host.Host { + ret := _m.Called() + + var r0 host.Host + if rf, ok := ret.Get(0).(func() host.Host); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(host.Host) + } + } + + return r0 +} + +// SetComponentManager provides a mock function with given fields: cm +func (_m *CoreP2P) SetComponentManager(cm *component.ComponentManager) { + _m.Called(cm) +} + +// Start provides a mock function with given fields: ctx +func (_m *CoreP2P) Start(ctx irrecoverable.SignalerContext) { + _m.Called(ctx) +} + +// Stop provides a mock function with given fields: +func (_m *CoreP2P) Stop() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewCoreP2P interface { + mock.TestingT + Cleanup(func()) +} + +// NewCoreP2P creates a new instance of CoreP2P. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewCoreP2P(t mockConstructorTestingTNewCoreP2P) *CoreP2P { + mock := &CoreP2P{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/peer_management.go b/network/p2p/mock/peer_management.go new file mode 100644 index 00000000000..0049b060ec8 --- /dev/null +++ b/network/p2p/mock/peer_management.go @@ -0,0 +1,279 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + component "github.com/onflow/flow-go/module/component" + channels "github.com/onflow/flow-go/network/channels" + + context "context" + + corenetwork "github.com/libp2p/go-libp2p/core/network" + + host "github.com/libp2p/go-libp2p/core/host" + + kbucket "github.com/libp2p/go-libp2p-kbucket" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" + + p2p "github.com/onflow/flow-go/network/p2p" + + peer "github.com/libp2p/go-libp2p/core/peer" + + protocol "github.com/libp2p/go-libp2p/core/protocol" + + protocols "github.com/onflow/flow-go/network/p2p/unicast/protocols" +) + +// PeerManagement is an autogenerated mock type for the PeerManagement type +type PeerManagement struct { + mock.Mock +} + +// ConnectToPeerAddrInfo provides a mock function with given fields: ctx, peerInfo +func (_m *PeerManagement) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { + ret := _m.Called(ctx, peerInfo) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, peer.AddrInfo) error); ok { + r0 = rf(ctx, peerInfo) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetIPPort provides a mock function with given fields: +func (_m *PeerManagement) GetIPPort() (string, string, error) { + ret := _m.Called() + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func() (string, string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetPeersForProtocol provides a mock function with given fields: pid +func (_m *PeerManagement) GetPeersForProtocol(pid protocol.ID) peer.IDSlice { + ret := _m.Called(pid) + + var r0 peer.IDSlice + if rf, ok := ret.Get(0).(func(protocol.ID) peer.IDSlice); ok { + r0 = rf(pid) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(peer.IDSlice) + } + } + + return r0 +} + +// Host provides a mock function with given fields: +func (_m *PeerManagement) Host() host.Host { + ret := _m.Called() + + var r0 host.Host + if rf, ok := ret.Get(0).(func() host.Host); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(host.Host) + } + } + + return r0 +} + +// ID provides a mock function with given fields: +func (_m *PeerManagement) ID() peer.ID { + ret := _m.Called() + + var r0 peer.ID + if rf, ok := ret.Get(0).(func() peer.ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(peer.ID) + } + + return r0 +} + +// ListPeers provides a mock function with given fields: topic +func (_m *PeerManagement) ListPeers(topic string) []peer.ID { + ret := _m.Called(topic) + + var r0 []peer.ID + if rf, ok := ret.Get(0).(func(string) []peer.ID); ok { + r0 = rf(topic) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]peer.ID) + } + } + + return r0 +} + +// PeerManagerComponent provides a mock function with given fields: +func (_m *PeerManagement) PeerManagerComponent() component.Component { + ret := _m.Called() + + var r0 component.Component + if rf, ok := ret.Get(0).(func() component.Component); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(component.Component) + } + } + + return r0 +} + +// Publish provides a mock function with given fields: ctx, messageScope +func (_m *PeerManagement) Publish(ctx context.Context, messageScope network.OutgoingMessageScope) error { + ret := _m.Called(ctx, messageScope) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, network.OutgoingMessageScope) error); ok { + r0 = rf(ctx, messageScope) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemovePeer provides a mock function with given fields: peerID +func (_m *PeerManagement) RemovePeer(peerID peer.ID) error { + ret := _m.Called(peerID) + + var r0 error + if rf, ok := ret.Get(0).(func(peer.ID) error); ok { + r0 = rf(peerID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RequestPeerUpdate provides a mock function with given fields: +func (_m *PeerManagement) RequestPeerUpdate() { + _m.Called() +} + +// RoutingTable provides a mock function with given fields: +func (_m *PeerManagement) RoutingTable() *kbucket.RoutingTable { + ret := _m.Called() + + var r0 *kbucket.RoutingTable + if rf, ok := ret.Get(0).(func() *kbucket.RoutingTable); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*kbucket.RoutingTable) + } + } + + return r0 +} + +// Subscribe provides a mock function with given fields: topic, topicValidator +func (_m *PeerManagement) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidatorFunc) (p2p.Subscription, error) { + ret := _m.Called(topic, topicValidator) + + var r0 p2p.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(channels.Topic, p2p.TopicValidatorFunc) (p2p.Subscription, error)); ok { + return rf(topic, topicValidator) + } + if rf, ok := ret.Get(0).(func(channels.Topic, p2p.TopicValidatorFunc) p2p.Subscription); ok { + r0 = rf(topic, topicValidator) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(p2p.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(channels.Topic, p2p.TopicValidatorFunc) error); ok { + r1 = rf(topic, topicValidator) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Unsubscribe provides a mock function with given fields: topic +func (_m *PeerManagement) Unsubscribe(topic channels.Topic) error { + ret := _m.Called(topic) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Topic) error); ok { + r0 = rf(topic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WithDefaultUnicastProtocol provides a mock function with given fields: defaultHandler, preferred +func (_m *PeerManagement) WithDefaultUnicastProtocol(defaultHandler corenetwork.StreamHandler, preferred []protocols.ProtocolName) error { + ret := _m.Called(defaultHandler, preferred) + + var r0 error + if rf, ok := ret.Get(0).(func(corenetwork.StreamHandler, []protocols.ProtocolName) error); ok { + r0 = rf(defaultHandler, preferred) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WithPeersProvider provides a mock function with given fields: peersProvider +func (_m *PeerManagement) WithPeersProvider(peersProvider p2p.PeersProvider) { + _m.Called(peersProvider) +} + +type mockConstructorTestingTNewPeerManagement interface { + mock.TestingT + Cleanup(func()) +} + +// NewPeerManagement creates a new instance of PeerManagement. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPeerManagement(t mockConstructorTestingTNewPeerManagement) *PeerManagement { + mock := &PeerManagement{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/pub_sub.go b/network/p2p/mock/pub_sub.go new file mode 100644 index 00000000000..b035607ef57 --- /dev/null +++ b/network/p2p/mock/pub_sub.go @@ -0,0 +1,94 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + context "context" + + channels "github.com/onflow/flow-go/network/channels" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" + + p2p "github.com/onflow/flow-go/network/p2p" +) + +// PubSub is an autogenerated mock type for the PubSub type +type PubSub struct { + mock.Mock +} + +// Publish provides a mock function with given fields: ctx, messageScope +func (_m *PubSub) Publish(ctx context.Context, messageScope network.OutgoingMessageScope) error { + ret := _m.Called(ctx, messageScope) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, network.OutgoingMessageScope) error); ok { + r0 = rf(ctx, messageScope) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetPubSub provides a mock function with given fields: ps +func (_m *PubSub) SetPubSub(ps p2p.PubSubAdapter) { + _m.Called(ps) +} + +// Subscribe provides a mock function with given fields: topic, topicValidator +func (_m *PubSub) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidatorFunc) (p2p.Subscription, error) { + ret := _m.Called(topic, topicValidator) + + var r0 p2p.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(channels.Topic, p2p.TopicValidatorFunc) (p2p.Subscription, error)); ok { + return rf(topic, topicValidator) + } + if rf, ok := ret.Get(0).(func(channels.Topic, p2p.TopicValidatorFunc) p2p.Subscription); ok { + r0 = rf(topic, topicValidator) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(p2p.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(channels.Topic, p2p.TopicValidatorFunc) error); ok { + r1 = rf(topic, topicValidator) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Unsubscribe provides a mock function with given fields: topic +func (_m *PubSub) Unsubscribe(topic channels.Topic) error { + ret := _m.Called(topic) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Topic) error); ok { + r0 = rf(topic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewPubSub interface { + mock.TestingT + Cleanup(func()) +} + +// NewPubSub creates a new instance of PubSub. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPubSub(t mockConstructorTestingTNewPubSub) *PubSub { + mock := &PubSub{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/routable.go b/network/p2p/mock/routable.go new file mode 100644 index 00000000000..a41738b4625 --- /dev/null +++ b/network/p2p/mock/routable.go @@ -0,0 +1,67 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + kbucket "github.com/libp2p/go-libp2p-kbucket" + mock "github.com/stretchr/testify/mock" + + routing "github.com/libp2p/go-libp2p/core/routing" +) + +// Routable is an autogenerated mock type for the Routable type +type Routable struct { + mock.Mock +} + +// Routing provides a mock function with given fields: +func (_m *Routable) Routing() routing.Routing { + ret := _m.Called() + + var r0 routing.Routing + if rf, ok := ret.Get(0).(func() routing.Routing); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(routing.Routing) + } + } + + return r0 +} + +// RoutingTable provides a mock function with given fields: +func (_m *Routable) RoutingTable() *kbucket.RoutingTable { + ret := _m.Called() + + var r0 *kbucket.RoutingTable + if rf, ok := ret.Get(0).(func() *kbucket.RoutingTable); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*kbucket.RoutingTable) + } + } + + return r0 +} + +// SetRouting provides a mock function with given fields: r +func (_m *Routable) SetRouting(r routing.Routing) { + _m.Called(r) +} + +type mockConstructorTestingTNewRoutable interface { + mock.TestingT + Cleanup(func()) +} + +// NewRoutable creates a new instance of Routable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRoutable(t mockConstructorTestingTNewRoutable) *Routable { + mock := &Routable{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/mock/unicast_management.go b/network/p2p/mock/unicast_management.go new file mode 100644 index 00000000000..e45531c1a25 --- /dev/null +++ b/network/p2p/mock/unicast_management.go @@ -0,0 +1,62 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mockp2p + +import ( + context "context" + + network "github.com/libp2p/go-libp2p/core/network" + mock "github.com/stretchr/testify/mock" + + peer "github.com/libp2p/go-libp2p/core/peer" + + protocols "github.com/onflow/flow-go/network/p2p/unicast/protocols" +) + +// UnicastManagement is an autogenerated mock type for the UnicastManagement type +type UnicastManagement struct { + mock.Mock +} + +// OpenProtectedStream provides a mock function with given fields: ctx, peerID, protectionTag, writingLogic +func (_m *UnicastManagement) OpenProtectedStream(ctx context.Context, peerID peer.ID, protectionTag string, writingLogic func(network.Stream) error) error { + ret := _m.Called(ctx, peerID, protectionTag, writingLogic) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, peer.ID, string, func(network.Stream) error) error); ok { + r0 = rf(ctx, peerID, protectionTag, writingLogic) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WithDefaultUnicastProtocol provides a mock function with given fields: defaultHandler, preferred +func (_m *UnicastManagement) WithDefaultUnicastProtocol(defaultHandler network.StreamHandler, preferred []protocols.ProtocolName) error { + ret := _m.Called(defaultHandler, preferred) + + var r0 error + if rf, ok := ret.Get(0).(func(network.StreamHandler, []protocols.ProtocolName) error); ok { + r0 = rf(defaultHandler, preferred) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewUnicastManagement interface { + mock.TestingT + Cleanup(func()) +} + +// NewUnicastManagement creates a new instance of UnicastManagement. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewUnicastManagement(t mockConstructorTestingTNewUnicastManagement) *UnicastManagement { + mock := &UnicastManagement{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 6eb91a99bcaf5a8739483a6de75b209549ea3630 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:26:00 -0700 Subject: [PATCH 804/815] moves read subscription to the internal package --- network/p2p/p2pnet/{ => internal}/readSubscription.go | 2 +- network/p2p/p2pnet/network.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) rename network/p2p/p2pnet/{ => internal}/readSubscription.go (99%) diff --git a/network/p2p/p2pnet/readSubscription.go b/network/p2p/p2pnet/internal/readSubscription.go similarity index 99% rename from network/p2p/p2pnet/readSubscription.go rename to network/p2p/p2pnet/internal/readSubscription.go index 0e458a7eab3..3e0a63e6dd3 100644 --- a/network/p2p/p2pnet/readSubscription.go +++ b/network/p2p/p2pnet/internal/readSubscription.go @@ -1,4 +1,4 @@ -package p2pnet +package internal import ( "context" diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index 995f4a2cc7d..9cb018892d3 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -31,6 +31,7 @@ import ( "github.com/onflow/flow-go/network/message" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/blob" + "github.com/onflow/flow-go/network/p2p/p2pnet/internal" "github.com/onflow/flow-go/network/p2p/ping" "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/unicast/protocols" @@ -1070,7 +1071,7 @@ func (n *Network) Subscribe(channel channels.Channel) error { } // create a new readSubscription with the context of the network - rs := NewReadSubscription(s, n.processPubSubMessages, n.logger) + rs := internal.NewReadSubscription(s, n.processPubSubMessages, n.logger) n.wg.Add(1) // kick off the receive loop to continuously receive messages From 24cbe582986ad9e024df68cba7a9ffbf43899bbb Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:33:48 -0700 Subject: [PATCH 805/815] renames network to engine registry --- cmd/access/node_builder/access_node_builder.go | 2 +- cmd/node_builder.go | 2 +- cmd/scaffold.go | 2 +- consensus/integration/network_test.go | 2 +- engine/access/ingestion/engine.go | 2 +- .../access/integration_unsecure_grpc_server_test.go | 4 ++-- engine/access/relay/engine.go | 4 ++-- engine/access/rest_api_test.go | 4 ++-- engine/access/rpc/rate_limit_test.go | 4 ++-- engine/access/secure_grpcr_test.go | 4 ++-- engine/collection/epochmgr/factories/compliance.go | 4 ++-- engine/collection/epochmgr/factories/hub.go | 4 ++-- engine/collection/epochmgr/factories/sync.go | 4 ++-- engine/collection/ingest/engine.go | 2 +- engine/collection/message_hub/message_hub.go | 2 +- engine/collection/pusher/engine.go | 2 +- engine/collection/synchronization/engine.go | 2 +- engine/common/follower/compliance_engine.go | 2 +- engine/common/provider/engine.go | 2 +- engine/common/requester/engine.go | 2 +- engine/common/splitter/network/network.go | 6 +++--- engine/common/synchronization/engine.go | 2 +- .../common/synchronization/request_handler_engine.go | 2 +- engine/consensus/dkg/messaging_engine.go | 2 +- engine/consensus/ingestion/engine.go | 2 +- engine/consensus/matching/engine.go | 2 +- engine/consensus/message_hub/message_hub.go | 2 +- engine/consensus/sealing/engine.go | 2 +- engine/execution/ingestion/engine.go | 2 +- engine/execution/provider/engine.go | 2 +- engine/ghost/engine/rpc.go | 4 ++-- engine/verification/requester/requester.go | 2 +- engine/verification/verifier/engine.go | 2 +- insecure/corruptnet/network.go | 8 ++++---- network/alsp/manager/manager_test.go | 12 ++++++------ network/converter/network.go | 8 ++++---- network/internal/testutils/meshengine.go | 2 +- network/internal/testutils/testUtil.go | 4 ++-- network/network.go | 8 ++++---- network/p2p/p2pnet/network.go | 2 +- network/proxy/network.go | 6 +++--- network/proxy/network_test.go | 2 +- network/relay/network.go | 10 +++++----- network/relay/relayer.go | 2 +- network/stub/network.go | 2 +- network/test/blob_service_test.go | 2 +- network/test/echoengine.go | 2 +- network/test/echoengine_test.go | 2 +- network/test/epochtransition_test.go | 6 +++--- network/test/meshengine_test.go | 2 +- network/test/network_test.go | 6 +++--- network/test/unicast_authorization_test.go | 8 ++++---- utils/unittest/network/network.go | 2 +- 53 files changed, 92 insertions(+), 92 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 03279d328ff..144c6481967 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -138,7 +138,7 @@ type AccessNodeConfig struct { type PublicNetworkConfig struct { // NetworkKey crypto.PublicKey // TODO: do we need a different key for the public network? BindAddress string - Network network.Network + Network network.EngineRegistry Metrics module.NetworkMetrics } diff --git a/cmd/node_builder.go b/cmd/node_builder.go index d68737df9de..65adfdd3af3 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -194,7 +194,7 @@ type NodeConfig struct { ProtocolEvents *events.Distributor State protocol.State Resolver madns.BasicResolver - Network network.Network + Network network.EngineRegistry Middleware network.Middleware ConduitFactory network.ConduitFactory PingService network.PingService diff --git a/cmd/scaffold.go b/cmd/scaffold.go index d4aafb17e7d..1e384ecde88 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -415,7 +415,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( node *NodeConfig, cf network.ConduitFactory, unicastRateLimiters *ratelimit.RateLimiters, - peerManagerFilters []p2p.PeerFilter) (network.Network, error) { + peerManagerFilters []p2p.PeerFilter) (network.EngineRegistry, error) { var networkOptions []p2pnet.NetworkOption if len(fnb.MsgValidators) > 0 { diff --git a/consensus/integration/network_test.go b/consensus/integration/network_test.go index 1a3eb31d58b..aae9b80841b 100644 --- a/consensus/integration/network_test.go +++ b/consensus/integration/network_test.go @@ -67,7 +67,7 @@ type Network struct { mocknetwork.Network } -var _ network.Network = (*Network)(nil) +var _ network.EngineRegistry = (*Network)(nil) // Register registers an Engine of the attached node to the channel via a Conduit, and returns the // Conduit instance. diff --git a/engine/access/ingestion/engine.go b/engine/access/ingestion/engine.go index 74f3721823f..54369ccebdb 100644 --- a/engine/access/ingestion/engine.go +++ b/engine/access/ingestion/engine.go @@ -87,7 +87,7 @@ type Engine struct { // New creates a new access ingestion engine func New( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, state protocol.State, me module.Local, request module.Requester, diff --git a/engine/access/integration_unsecure_grpc_server_test.go b/engine/access/integration_unsecure_grpc_server_test.go index cf9ec1fa744..3033c67ac90 100644 --- a/engine/access/integration_unsecure_grpc_server_test.go +++ b/engine/access/integration_unsecure_grpc_server_test.go @@ -47,7 +47,7 @@ type SameGRPCPortTestSuite struct { snapshot *protocol.Snapshot epochQuery *protocol.EpochQuery log zerolog.Logger - net *network.Network + net *network.EngineRegistry request *module.Requester collClient *accessmock.AccessAPIClient execClient *accessmock.ExecutionAPIClient @@ -84,7 +84,7 @@ type SameGRPCPortTestSuite struct { func (suite *SameGRPCPortTestSuite) SetupTest() { suite.log = zerolog.New(os.Stdout) - suite.net = new(network.Network) + suite.net = new(network.EngineRegistry) suite.state = new(protocol.State) suite.snapshot = new(protocol.Snapshot) diff --git a/engine/access/relay/engine.go b/engine/access/relay/engine.go index d277b102c0f..19c7472a4e2 100644 --- a/engine/access/relay/engine.go +++ b/engine/access/relay/engine.go @@ -21,8 +21,8 @@ type Engine struct { func New( log zerolog.Logger, channelList channels.ChannelList, - net network.Network, - unstakedNet network.Network, + net network.EngineRegistry, + unstakedNet network.EngineRegistry, ) (*Engine, error) { e := &Engine{ unit: engine.NewUnit(), diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index dfb40b596ea..c0fcced090d 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -47,7 +47,7 @@ type RestAPITestSuite struct { sealedSnaphost *protocol.Snapshot finalizedSnapshot *protocol.Snapshot log zerolog.Logger - net *network.Network + net *network.EngineRegistry request *module.Requester collClient *accessmock.AccessAPIClient execClient *accessmock.ExecutionAPIClient @@ -76,7 +76,7 @@ type RestAPITestSuite struct { func (suite *RestAPITestSuite) SetupTest() { suite.log = zerolog.New(os.Stdout) - suite.net = new(network.Network) + suite.net = new(network.EngineRegistry) suite.state = new(protocol.State) suite.sealedSnaphost = new(protocol.Snapshot) suite.finalizedSnapshot = new(protocol.Snapshot) diff --git a/engine/access/rpc/rate_limit_test.go b/engine/access/rpc/rate_limit_test.go index ef0bcbc1258..b28b55e680c 100644 --- a/engine/access/rpc/rate_limit_test.go +++ b/engine/access/rpc/rate_limit_test.go @@ -40,7 +40,7 @@ type RateLimitTestSuite struct { snapshot *protocol.Snapshot epochQuery *protocol.EpochQuery log zerolog.Logger - net *network.Network + net *network.EngineRegistry request *module.Requester collClient *accessmock.AccessAPIClient execClient *accessmock.ExecutionAPIClient @@ -72,7 +72,7 @@ type RateLimitTestSuite struct { func (suite *RateLimitTestSuite) SetupTest() { suite.log = zerolog.New(os.Stdout) - suite.net = new(network.Network) + suite.net = new(network.EngineRegistry) suite.state = new(protocol.State) suite.snapshot = new(protocol.Snapshot) diff --git a/engine/access/secure_grpcr_test.go b/engine/access/secure_grpcr_test.go index d5f64352cf6..3cad438ba18 100644 --- a/engine/access/secure_grpcr_test.go +++ b/engine/access/secure_grpcr_test.go @@ -39,7 +39,7 @@ type SecureGRPCTestSuite struct { snapshot *protocol.Snapshot epochQuery *protocol.EpochQuery log zerolog.Logger - net *network.Network + net *network.EngineRegistry request *module.Requester collClient *accessmock.AccessAPIClient execClient *accessmock.ExecutionAPIClient @@ -66,7 +66,7 @@ type SecureGRPCTestSuite struct { func (suite *SecureGRPCTestSuite) SetupTest() { suite.log = zerolog.New(os.Stdout) - suite.net = new(network.Network) + suite.net = new(network.EngineRegistry) suite.state = new(protocol.State) suite.snapshot = new(protocol.Snapshot) diff --git a/engine/collection/epochmgr/factories/compliance.go b/engine/collection/epochmgr/factories/compliance.go index 8988c8d3615..c82a65b4a93 100644 --- a/engine/collection/epochmgr/factories/compliance.go +++ b/engine/collection/epochmgr/factories/compliance.go @@ -20,7 +20,7 @@ import ( type ComplianceEngineFactory struct { log zerolog.Logger me module.Local - net network.Network + net network.EngineRegistry colMetrics module.CollectionMetrics engMetrics module.EngineMetrics mempoolMetrics module.MempoolMetrics @@ -32,7 +32,7 @@ type ComplianceEngineFactory struct { // NewComplianceEngineFactory returns a new collection compliance engine factory. func NewComplianceEngineFactory( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, colMetrics module.CollectionMetrics, engMetrics module.EngineMetrics, diff --git a/engine/collection/epochmgr/factories/hub.go b/engine/collection/epochmgr/factories/hub.go index 434a699aef7..eae1f79bf82 100644 --- a/engine/collection/epochmgr/factories/hub.go +++ b/engine/collection/epochmgr/factories/hub.go @@ -16,13 +16,13 @@ import ( type MessageHubFactory struct { log zerolog.Logger me module.Local - net network.Network + net network.EngineRegistry protoState protocol.State engineMetrics module.EngineMetrics } func NewMessageHubFactory(log zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, engineMetrics module.EngineMetrics, protoState protocol.State) *MessageHubFactory { diff --git a/engine/collection/epochmgr/factories/sync.go b/engine/collection/epochmgr/factories/sync.go index 98bf10c4142..020895ee22d 100644 --- a/engine/collection/epochmgr/factories/sync.go +++ b/engine/collection/epochmgr/factories/sync.go @@ -15,7 +15,7 @@ import ( type SyncEngineFactory struct { log zerolog.Logger - net network.Network + net network.EngineRegistry me module.Local metrics module.EngineMetrics } @@ -23,7 +23,7 @@ type SyncEngineFactory struct { func NewSyncEngineFactory( log zerolog.Logger, metrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, ) (*SyncEngineFactory, error) { diff --git a/engine/collection/ingest/engine.go b/engine/collection/ingest/engine.go index 31aadf451e2..d635332ba27 100644 --- a/engine/collection/ingest/engine.go +++ b/engine/collection/ingest/engine.go @@ -46,7 +46,7 @@ type Engine struct { // New creates a new collection ingest engine. func New( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, state protocol.State, engMetrics module.EngineMetrics, mempoolMetrics module.MempoolMetrics, diff --git a/engine/collection/message_hub/message_hub.go b/engine/collection/message_hub/message_hub.go index 6c73ec2ab22..6ce3492a7bb 100644 --- a/engine/collection/message_hub/message_hub.go +++ b/engine/collection/message_hub/message_hub.go @@ -102,7 +102,7 @@ var _ hotstuff.CommunicatorConsumer = (*MessageHub)(nil) // No errors are expected during normal operations. func NewMessageHub(log zerolog.Logger, engineMetrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, compliance collection.Compliance, hotstuff module.HotStuff, diff --git a/engine/collection/pusher/engine.go b/engine/collection/pusher/engine.go index 226b866bf5e..5e09ea7418e 100644 --- a/engine/collection/pusher/engine.go +++ b/engine/collection/pusher/engine.go @@ -35,7 +35,7 @@ type Engine struct { transactions storage.Transactions } -func New(log zerolog.Logger, net network.Network, state protocol.State, engMetrics module.EngineMetrics, colMetrics module.CollectionMetrics, me module.Local, collections storage.Collections, transactions storage.Transactions) (*Engine, error) { +func New(log zerolog.Logger, net network.EngineRegistry, state protocol.State, engMetrics module.EngineMetrics, colMetrics module.CollectionMetrics, me module.Local, collections storage.Collections, transactions storage.Transactions) (*Engine, error) { e := &Engine{ unit: engine.NewUnit(), log: log.With().Str("engine", "pusher").Logger(), diff --git a/engine/collection/synchronization/engine.go b/engine/collection/synchronization/engine.go index 8c6fbafa806..622145860af 100644 --- a/engine/collection/synchronization/engine.go +++ b/engine/collection/synchronization/engine.go @@ -62,7 +62,7 @@ type Engine struct { func New( log zerolog.Logger, metrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, participants flow.IdentityList, state cluster.State, diff --git a/engine/common/follower/compliance_engine.go b/engine/common/follower/compliance_engine.go index d7d8c2cb95c..8374955d27b 100644 --- a/engine/common/follower/compliance_engine.go +++ b/engine/common/follower/compliance_engine.go @@ -92,7 +92,7 @@ var _ consensus.Compliance = (*ComplianceEngine)(nil) // interface `complianceCore` (this package) for detailed documentation of the algorithm. func NewComplianceLayer( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, engMetrics module.EngineMetrics, headers storage.Headers, diff --git a/engine/common/provider/engine.go b/engine/common/provider/engine.go index 69195a36145..3b492bd8788 100644 --- a/engine/common/provider/engine.go +++ b/engine/common/provider/engine.go @@ -66,7 +66,7 @@ var _ network.MessageProcessor = (*Engine)(nil) func New( log zerolog.Logger, metrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, state protocol.State, requestQueue engine.MessageStore, diff --git a/engine/common/requester/engine.go b/engine/common/requester/engine.go index bc54d3dd45b..48d8e2bad24 100644 --- a/engine/common/requester/engine.go +++ b/engine/common/requester/engine.go @@ -56,7 +56,7 @@ type Engine struct { // New creates a new requester engine, operating on the provided network channel, and requesting entities from a node // within the set obtained by applying the provided selector filter. The options allow customization of the parameters // related to the batch and retry logic. -func New(log zerolog.Logger, metrics module.EngineMetrics, net network.Network, me module.Local, state protocol.State, +func New(log zerolog.Logger, metrics module.EngineMetrics, net network.EngineRegistry, me module.Local, state protocol.State, channel channels.Channel, selector flow.IdentityFilter, create CreateFunc, options ...OptionFunc) (*Engine, error) { // initialize the default config diff --git a/engine/common/splitter/network/network.go b/engine/common/splitter/network/network.go index 13f1dfb8cc3..c2ab9db1097 100644 --- a/engine/common/splitter/network/network.go +++ b/engine/common/splitter/network/network.go @@ -24,7 +24,7 @@ import ( // splitter engine. As a result, multiple engines can register with the splitter network on // the same channel and will each receive all events on that channel. type Network struct { - net network.Network + net network.EngineRegistry mu sync.RWMutex log zerolog.Logger splitters map[channels.Channel]*splitterEngine.Engine // stores splitters for each channel @@ -32,11 +32,11 @@ type Network struct { *component.ComponentManager } -var _ network.Network = (*Network)(nil) +var _ network.EngineRegistry = (*Network)(nil) // NewNetwork returns a new splitter network. func NewNetwork( - net network.Network, + net network.EngineRegistry, log zerolog.Logger, ) *Network { n := &Network{ diff --git a/engine/common/synchronization/engine.go b/engine/common/synchronization/engine.go index 5ee427546c2..54221b07dca 100644 --- a/engine/common/synchronization/engine.go +++ b/engine/common/synchronization/engine.go @@ -71,7 +71,7 @@ var _ component.Component = (*Engine)(nil) func New( log zerolog.Logger, metrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, state protocol.State, blocks storage.Blocks, diff --git a/engine/common/synchronization/request_handler_engine.go b/engine/common/synchronization/request_handler_engine.go index ebbced56455..20dfb37a808 100644 --- a/engine/common/synchronization/request_handler_engine.go +++ b/engine/common/synchronization/request_handler_engine.go @@ -68,7 +68,7 @@ var _ hotstuff.FinalizationConsumer = (*RequestHandlerEngine)(nil) func NewRequestHandlerEngine( logger zerolog.Logger, metrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, state protocol.State, blocks storage.Blocks, diff --git a/engine/consensus/dkg/messaging_engine.go b/engine/consensus/dkg/messaging_engine.go index 18862110083..d9e0f3ca8d9 100644 --- a/engine/consensus/dkg/messaging_engine.go +++ b/engine/consensus/dkg/messaging_engine.go @@ -70,7 +70,7 @@ var _ component.Component = (*MessagingEngine)(nil) // NewMessagingEngine returns a new MessagingEngine. func NewMessagingEngine( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, tunnel *dkg.BrokerTunnel, collector module.MempoolMetrics, diff --git a/engine/consensus/ingestion/engine.go b/engine/consensus/ingestion/engine.go index cb98d34632f..6082f4053d0 100644 --- a/engine/consensus/ingestion/engine.go +++ b/engine/consensus/ingestion/engine.go @@ -43,7 +43,7 @@ type Engine struct { func New( log zerolog.Logger, engineMetrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, core *Core, ) (*Engine, error) { diff --git a/engine/consensus/matching/engine.go b/engine/consensus/matching/engine.go index 2fc6e679d9a..6d97370e688 100644 --- a/engine/consensus/matching/engine.go +++ b/engine/consensus/matching/engine.go @@ -44,7 +44,7 @@ type Engine struct { func NewEngine( log zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, engineMetrics module.EngineMetrics, mempool module.MempoolMetrics, diff --git a/engine/consensus/message_hub/message_hub.go b/engine/consensus/message_hub/message_hub.go index 6c674c219ff..2deb22a7332 100644 --- a/engine/consensus/message_hub/message_hub.go +++ b/engine/consensus/message_hub/message_hub.go @@ -100,7 +100,7 @@ var _ hotstuff.CommunicatorConsumer = (*MessageHub)(nil) // No errors are expected during normal operations. func NewMessageHub(log zerolog.Logger, engineMetrics module.EngineMetrics, - net network.Network, + net network.EngineRegistry, me module.Local, compliance consensus.Compliance, hotstuff module.HotStuff, diff --git a/engine/consensus/sealing/engine.go b/engine/consensus/sealing/engine.go index 60d38a57fe7..9786c6aa0b6 100644 --- a/engine/consensus/sealing/engine.go +++ b/engine/consensus/sealing/engine.go @@ -92,7 +92,7 @@ func NewEngine(log zerolog.Logger, engineMetrics module.EngineMetrics, mempool module.MempoolMetrics, sealingTracker consensus.SealingTracker, - net network.Network, + net network.EngineRegistry, me module.Local, headers storage.Headers, payloads storage.Payloads, diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go index 53ed58c99c6..88e0793e9e5 100644 --- a/engine/execution/ingestion/engine.go +++ b/engine/execution/ingestion/engine.go @@ -74,7 +74,7 @@ var onlyOnflowRegex = regexp.MustCompile(`.*\.onflow\.org:3569$`) func New( unit *engine.Unit, logger zerolog.Logger, - net network.Network, + net network.EngineRegistry, me module.Local, request module.Requester, state protocol.State, diff --git a/engine/execution/provider/engine.go b/engine/execution/provider/engine.go index 3343dab1648..a2eb212d56a 100644 --- a/engine/execution/provider/engine.go +++ b/engine/execution/provider/engine.go @@ -73,7 +73,7 @@ type Engine struct { func New( logger zerolog.Logger, tracer module.Tracer, - net network.Network, + net network.EngineRegistry, state protocol.State, execState state.ReadOnlyExecutionState, metrics module.ExecutionMetrics, diff --git a/engine/ghost/engine/rpc.go b/engine/ghost/engine/rpc.go index b859bb12a8e..4d0d83a503c 100644 --- a/engine/ghost/engine/rpc.go +++ b/engine/ghost/engine/rpc.go @@ -40,7 +40,7 @@ type RPC struct { } // New returns a new RPC engine. -func New(net network.Network, log zerolog.Logger, me module.Local, state protocol.State, config Config) (*RPC, error) { +func New(net network.EngineRegistry, log zerolog.Logger, me module.Local, state protocol.State, config Config) (*RPC, error) { log = log.With().Str("engine", "rpc").Logger() @@ -76,7 +76,7 @@ func New(net network.Network, log zerolog.Logger, me module.Local, state protoco } // registerConduits registers for ALL channels and returns a map of engine id to conduit -func registerConduits(net network.Network, state protocol.State, eng network.Engine) (map[channels.Channel]network.Conduit, error) { +func registerConduits(net network.EngineRegistry, state protocol.State, eng network.Engine) (map[channels.Channel]network.Conduit, error) { // create a list of all channels that don't change over time channelList := channels.ChannelList{ diff --git a/engine/verification/requester/requester.go b/engine/verification/requester/requester.go index 2285da61025..fddb922ec2e 100644 --- a/engine/verification/requester/requester.go +++ b/engine/verification/requester/requester.go @@ -67,7 +67,7 @@ type Engine struct { func New(log zerolog.Logger, state protocol.State, - net network.Network, + net network.EngineRegistry, tracer module.Tracer, metrics module.VerificationMetrics, pendingRequests mempool.ChunkRequests, diff --git a/engine/verification/verifier/engine.go b/engine/verification/verifier/engine.go index f870e888340..3c79a8a6057 100644 --- a/engine/verification/verifier/engine.go +++ b/engine/verification/verifier/engine.go @@ -50,7 +50,7 @@ func New( log zerolog.Logger, metrics module.VerificationMetrics, tracer module.Tracer, - net network.Network, + net network.EngineRegistry, state protocol.State, me module.Local, chVerif module.ChunkVerifier, diff --git a/insecure/corruptnet/network.go b/insecure/corruptnet/network.go index 14486a1c286..c2f4c48d5b5 100644 --- a/insecure/corruptnet/network.go +++ b/insecure/corruptnet/network.go @@ -47,8 +47,8 @@ type Network struct { codec flownet.Codec mu sync.Mutex me module.Local - flowNetwork flownet.Network // original flow network of the node. - server *grpc.Server // touch point of orchestrator network to this factory. + flowNetwork flownet.EngineRegistry // original flow network of the node. + server *grpc.Server // touch point of orchestrator network to this factory. gRPCListenAddress net.Addr conduitFactory insecure.CorruptConduitFactory attackerInboundStream insecure.CorruptNetwork_ConnectAttackerServer // inbound stream to attack orchestrator @@ -63,7 +63,7 @@ type Network struct { approvalHasher hash.Hasher } -var _ flownet.Network = (*Network)(nil) +var _ flownet.EngineRegistry = (*Network)(nil) var _ insecure.EgressController = (*Network)(nil) var _ insecure.IngressController = (*Network)(nil) var _ insecure.CorruptNetworkServer = (*Network)(nil) @@ -74,7 +74,7 @@ func NewCorruptNetwork( address string, me module.Local, codec flownet.Codec, - flowNetwork flownet.Network, + flowNetwork flownet.EngineRegistry, conduitFactory insecure.CorruptConduitFactory) (*Network, error) { if chainId != flow.BftTestnet { panic("illegal chain id for using corrupt network") diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index c0ea806974d..83527577712 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -65,7 +65,7 @@ func TestNetworkPassesReportedMisbehavior(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -123,7 +123,7 @@ func TestHandleReportedMisbehavior_Cache_Integration(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -230,7 +230,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_Integration(t *testing.T) ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -324,7 +324,7 @@ func TestHandleReportedMisbehavior_And_DisallowListing_RepeatOffender_Integratio ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -480,7 +480,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{victimNetwork}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{victimNetwork}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() @@ -570,7 +570,7 @@ func TestMisbehaviorReportMetrics(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) - testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.Network{net}) + testutils.StartNodesAndNetworks(signalerCtx, t, nodes, []network.EngineRegistry{net}) defer testutils.StopComponents[p2p.LibP2PNode](t, nodes, 100*time.Millisecond) defer cancel() diff --git a/network/converter/network.go b/network/converter/network.go index a30bb683d61..a6947f390e3 100644 --- a/network/converter/network.go +++ b/network/converter/network.go @@ -6,14 +6,14 @@ import ( ) type Network struct { - network.Network + network.EngineRegistry from channels.Channel to channels.Channel } -var _ network.Network = (*Network)(nil) +var _ network.EngineRegistry = (*Network)(nil) -func NewNetwork(net network.Network, from channels.Channel, to channels.Channel) *Network { +func NewNetwork(net network.EngineRegistry, from channels.Channel, to channels.Channel) *Network { return &Network{net, from, to} } @@ -25,5 +25,5 @@ func (n *Network) convert(channel channels.Channel) channels.Channel { } func (n *Network) Register(channel channels.Channel, engine network.MessageProcessor) (network.Conduit, error) { - return n.Network.Register(n.convert(channel), engine) + return n.EngineRegistry.Register(n.convert(channel), engine) } diff --git a/network/internal/testutils/meshengine.go b/network/internal/testutils/meshengine.go index f792899410d..a8dafef6df7 100644 --- a/network/internal/testutils/meshengine.go +++ b/network/internal/testutils/meshengine.go @@ -26,7 +26,7 @@ type MeshEngine struct { mockcomponent.Component } -func NewMeshEngine(t *testing.T, net network.Network, cap int, channel channels.Channel) *MeshEngine { +func NewMeshEngine(t *testing.T, net network.EngineRegistry, cap int, channel channels.Channel) *MeshEngine { te := &MeshEngine{ t: t, Event: make(chan interface{}, cap), diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 053144466e9..b07f68dc7b7 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -236,7 +236,7 @@ func NetworkConfigFixture( // - timeout: the timeout to use for waiting for the nodes and networks to start. // // This function fails the test if the nodes or networks do not start within the given timeout. -func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, nets []network.Network) { +func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, nets []network.EngineRegistry) { StartNetworks(ctx, t, nets) // start up nodes and Peer managers @@ -251,7 +251,7 @@ func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, node // - duration: the timeout to use for waiting for the networks to start. // // This function fails the test if the networks do not start within the given timeout. -func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.Network) { +func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.EngineRegistry) { // start up networks (this will implicitly start middlewares) for _, net := range nets { net.Start(ctx) diff --git a/network/network.go b/network/network.go index 939a62f651a..02da472e754 100644 --- a/network/network.go +++ b/network/network.go @@ -34,11 +34,11 @@ const ( PublicNetwork ) -// Network is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// EngineRegistry is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer // offers to the Flow protocol layer, i.e., engines. It is responsible for creating conduits through which engines // can send and receive messages to and from other engines on the network, as well as registering other services // such as BlobService and PingService. -type Network interface { +type EngineRegistry interface { component.Component // Register will subscribe to the channel with the given engine and // the engine will be notified with incoming messages on the channel. @@ -55,7 +55,7 @@ type Network interface { RegisterPingService(pingProtocolID protocol.ID, pingInfoProvider PingInfoProvider) (PingService, error) } -// Adapter is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// Adapter is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer // offers to a single conduit which enables the conduit to send different types of messages i.e., unicast, multicast, // and publish, to other conduits on the network. type Adapter interface { @@ -75,7 +75,7 @@ type Adapter interface { UnRegisterChannel(channel channels.Channel) error } -// Middleware is one of the networking layer interfaces in Flow (i.e., Network, Adapter, and Middleware). It represents the interface that networking layer +// Middleware is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer // offers to lower level networking components such as libp2p. It is responsible for subscribing to and unsubscribing // from channels, as well as updating the addresses of all the authorized participants in the Flow protocol. type Middleware interface { diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index 9cb018892d3..debbfd6592b 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -119,7 +119,7 @@ type Network struct { preferredUnicasts []protocols.ProtocolName } -var _ network.Network = &Network{} +var _ network.EngineRegistry = &Network{} var _ network.Middleware = &Network{} var _ network.Adapter = &Network{} diff --git a/network/proxy/network.go b/network/proxy/network.go index 57ce6d2f965..6fb270bd30e 100644 --- a/network/proxy/network.go +++ b/network/proxy/network.go @@ -7,13 +7,13 @@ import ( ) type ProxyNetwork struct { - network.Network + network.EngineRegistry targetNodeID flow.Identifier } // NewProxyNetwork creates a new proxy network. All messages sent on this network are // sent only to the node identified by the given target ID. -func NewProxyNetwork(net network.Network, targetNodeID flow.Identifier) *ProxyNetwork { +func NewProxyNetwork(net network.EngineRegistry, targetNodeID flow.Identifier) *ProxyNetwork { return &ProxyNetwork{ net, targetNodeID, @@ -22,7 +22,7 @@ func NewProxyNetwork(net network.Network, targetNodeID flow.Identifier) *ProxyNe // Register registers an engine with the proxy network. func (n *ProxyNetwork) Register(channel channels.Channel, engine network.Engine) (network.Conduit, error) { - con, err := n.Network.Register(channel, engine) + con, err := n.EngineRegistry.Register(channel, engine) if err != nil { return nil, err diff --git a/network/proxy/network_test.go b/network/proxy/network_test.go index d3452fb88c8..2395b9f8efa 100644 --- a/network/proxy/network_test.go +++ b/network/proxy/network_test.go @@ -24,7 +24,7 @@ func getEvent() interface{} { type Suite struct { suite.Suite - net network.Network + net network.EngineRegistry targetNodeID flow.Identifier proxyNet *proxy.ProxyNetwork con *mocknetwork.Conduit diff --git a/network/relay/network.go b/network/relay/network.go index 347dbed3069..4e65acbc318 100644 --- a/network/relay/network.go +++ b/network/relay/network.go @@ -14,17 +14,17 @@ import ( ) type RelayNetwork struct { - originNet network.Network - destinationNet network.Network + originNet network.EngineRegistry + destinationNet network.EngineRegistry logger zerolog.Logger channels map[channels.Channel]channels.Channel } -var _ network.Network = (*RelayNetwork)(nil) +var _ network.EngineRegistry = (*RelayNetwork)(nil) func NewRelayNetwork( - originNetwork network.Network, - destinationNetwork network.Network, + originNetwork network.EngineRegistry, + destinationNetwork network.EngineRegistry, logger zerolog.Logger, channels map[channels.Channel]channels.Channel, ) *RelayNetwork { diff --git a/network/relay/relayer.go b/network/relay/relayer.go index 01ddb48f9ef..87120d27d73 100644 --- a/network/relay/relayer.go +++ b/network/relay/relayer.go @@ -25,7 +25,7 @@ func (n *noopProcessor) Process(channel channels.Channel, originID flow.Identifi var _ network.MessageProcessor = (*Relayer)(nil) -func NewRelayer(destinationNetwork network.Network, channel channels.Channel, processor network.MessageProcessor) (*Relayer, error) { +func NewRelayer(destinationNetwork network.EngineRegistry, channel channels.Channel, processor network.MessageProcessor) (*Relayer, error) { conduit, err := destinationNetwork.Register(channel, &noopProcessor{}) if err != nil { diff --git a/network/stub/network.go b/network/stub/network.go index 5990e245944..6c45d547ae9 100644 --- a/network/stub/network.go +++ b/network/stub/network.go @@ -41,7 +41,7 @@ func WithConduitFactory(factory network.ConduitFactory) func(*Network) { } } -var _ network.Network = (*Network)(nil) +var _ network.EngineRegistry = (*Network)(nil) var _ network.Adapter = (*Network)(nil) // NewNetwork create a mocked Network. diff --git a/network/test/blob_service_test.go b/network/test/blob_service_test.go index bc8def05537..25c40c508df 100644 --- a/network/test/blob_service_test.go +++ b/network/test/blob_service_test.go @@ -97,7 +97,7 @@ func (suite *BlobServiceTestSuite) SetupTest() { // starts the nodes and networks testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) + testutils.StartNetworks(signalerCtx, suite.T(), []network.EngineRegistry{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/echoengine.go b/network/test/echoengine.go index d9fdf1cbeac..b5ffd72ad54 100644 --- a/network/test/echoengine.go +++ b/network/test/echoengine.go @@ -34,7 +34,7 @@ type EchoEngine struct { mockcomponent.Component } -func NewEchoEngine(t *testing.T, net network.Network, cap int, channel channels.Channel, echo bool, send testutils.ConduitSendWrapperFunc) *EchoEngine { +func NewEchoEngine(t *testing.T, net network.EngineRegistry, cap int, channel channels.Channel, echo bool, send testutils.ConduitSendWrapperFunc) *EchoEngine { te := &EchoEngine{ t: t, echomsg: "this is an echo", diff --git a/network/test/echoengine_test.go b/network/test/echoengine_test.go index e3a0de08558..18384ce1fdd 100644 --- a/network/test/echoengine_test.go +++ b/network/test/echoengine_test.go @@ -63,7 +63,7 @@ func (suite *EchoEngineTestSuite) SetupTest() { // starts the nodes and networks testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) + testutils.StartNetworks(signalerCtx, suite.T(), []network.EngineRegistry{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } } diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 8f4fa2d2eba..3d087019031 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -112,10 +112,10 @@ func (t *testNodeList) engines() []*testutils.MeshEngine { return engs } -func (t *testNodeList) networks() []network.Network { +func (t *testNodeList) networks() []network.EngineRegistry { t.RLock() defer t.RUnlock() - nets := make([]network.Network, len(t.nodes)) + nets := make([]network.EngineRegistry, len(t.nodes)) for i, node := range t.nodes { nets[i] = node.network } @@ -201,7 +201,7 @@ func (suite *MutableIdentityTableSuite) addNodes(count int) { // starts the nodes and networks testutils.StartNodes(signalerCtx, suite.T(), nodes) for _, net := range nets { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) + testutils.StartNetworks(signalerCtx, suite.T(), []network.EngineRegistry{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/meshengine_test.go b/network/test/meshengine_test.go index cf1d10dd731..48800ab609e 100644 --- a/network/test/meshengine_test.go +++ b/network/test/meshengine_test.go @@ -119,7 +119,7 @@ func (suite *MeshEngineTestSuite) SetupTest() { // starts the nodes and networks testutils.StartNodes(signalerCtx, suite.T(), suite.libp2pNodes) for _, net := range suite.networks { - testutils.StartNetworks(signalerCtx, suite.T(), []network.Network{net}) + testutils.StartNetworks(signalerCtx, suite.T(), []network.EngineRegistry{net}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) } diff --git a/network/test/network_test.go b/network/test/network_test.go index b6a68f0a498..07065be5654 100644 --- a/network/test/network_test.go +++ b/network/test/network_test.go @@ -213,7 +213,7 @@ func (suite *NetworkTestSuite) TestUpdateNodeAddresses() { defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) newNet.Start(irrecoverableCtx) - defer testutils.StopComponents(suite.T(), []network.Network{newNet}, 1*time.Second) + defer testutils.StopComponents(suite.T(), []network.EngineRegistry{newNet}, 1*time.Second) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) idList := flow.IdentityList(append(suite.ids, newId)) @@ -312,7 +312,7 @@ func (suite *NetworkTestSuite) TestUnicastRateLimit_Messages() { irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.EngineRegistry{newNet}) calls := atomic.NewUint64(0) ch := make(chan struct{}) @@ -451,7 +451,7 @@ func (suite *NetworkTestSuite) TestUnicastRateLimit_Bandwidth() { testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) - testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.Network{newNet}) + testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.EngineRegistry{newNet}) unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) // registers an engine on the new network so that it can receive messages on the TestNetworkChannel diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index 849557eecb9..e77de9af971 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -40,11 +40,11 @@ type UnicastAuthorizationTestSuite struct { libP2PNodes []p2p.LibP2PNode // senderNetwork is the networking layer instance that will be used to send the message. - senderNetwork network.Network + senderNetwork network.EngineRegistry // senderID the identity on the mw sending the message senderID *flow.Identity // receiverNetwork is the networking layer instance that will be used to receive the message. - receiverNetwork network.Network + receiverNetwork network.EngineRegistry // receiverID the identity on the mw sending the message receiverID *flow.Identity // providers id providers generated at beginning of a test run @@ -105,7 +105,7 @@ func (u *UnicastAuthorizationTestSuite) startNetworksAndLibp2pNodes() { sigCtx, _ := irrecoverable.WithSignaler(ctx) testutils.StartNodes(sigCtx, u.T(), u.libP2PNodes) - testutils.StartNetworks(sigCtx, u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}) + testutils.StartNetworks(sigCtx, u.T(), []network.EngineRegistry{u.senderNetwork, u.receiverNetwork}) unittest.RequireComponentsReadyBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) u.cancel = cancel @@ -115,7 +115,7 @@ func (u *UnicastAuthorizationTestSuite) startNetworksAndLibp2pNodes() { func (u *UnicastAuthorizationTestSuite) stopNetworksAndLibp2pNodes() { u.cancel() // cancel context to stop libp2p nodes. - testutils.StopComponents(u.T(), []network.Network{u.senderNetwork, u.receiverNetwork}, 1*time.Second) + testutils.StopComponents(u.T(), []network.EngineRegistry{u.senderNetwork, u.receiverNetwork}, 1*time.Second) unittest.RequireComponentsDoneBefore(u.T(), 1*time.Second, u.senderNetwork, u.receiverNetwork) } diff --git a/utils/unittest/network/network.go b/utils/unittest/network/network.go index 369e014f52a..ba16fa3cc65 100644 --- a/utils/unittest/network/network.go +++ b/utils/unittest/network/network.go @@ -24,7 +24,7 @@ type Network struct { publishFunc PublishFunc } -var _ network.Network = (*Network)(nil) +var _ network.EngineRegistry = (*Network)(nil) // NewNetwork returns a new mock network. func NewNetwork() *Network { From 3bcada5ed391e0c9c830e0e75afe80683ded0001 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:41:17 -0700 Subject: [PATCH 806/815] renames --- Makefile | 2 +- engine/execution/ingestion/engine_test.go | 4 +- network/mocknetwork/engine_registry.go | 158 ++++++++++++++++++++++ network/mocknetwork/mock_network.go | 60 ++++---- 4 files changed, 191 insertions(+), 33 deletions(-) create mode 100644 network/mocknetwork/engine_registry.go diff --git a/Makefile b/Makefile index bacabd003aa..6b54defe9eb 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ generate-mocks: install-mock-generators mockery --name '(Connector|PingInfoProvider)' --dir=network/p2p --case=underscore --output="./network/mocknetwork" --outpkg="mocknetwork" mockgen -destination=storage/mocks/storage.go -package=mocks github.com/onflow/flow-go/storage Blocks,Headers,Payloads,Collections,Commits,Events,ServiceEvents,TransactionResults mockgen -destination=module/mocks/network.go -package=mocks github.com/onflow/flow-go/module Local,Requester - mockgen -destination=network/mocknetwork/mock_network.go -package=mocknetwork github.com/onflow/flow-go/network Network + mockgen -destination=network/mocknetwork/mock_network.go -package=mocknetwork github.com/onflow/flow-go/network EngineRegistry mockery --name='.*' --dir=integration/benchmark/mocksiface --case=underscore --output="integration/benchmark/mock" --outpkg="mock" mockery --name=ExecutionDataStore --dir=module/executiondatasync/execution_data --case=underscore --output="./module/executiondatasync/execution_data/mock" --outpkg="mock" mockery --name=Downloader --dir=module/executiondatasync/execution_data --case=underscore --output="./module/executiondatasync/execution_data/mock" --outpkg="mock" diff --git a/engine/execution/ingestion/engine_test.go b/engine/execution/ingestion/engine_test.go index 541063b50a6..16f2f2713f6 100644 --- a/engine/execution/ingestion/engine_test.go +++ b/engine/execution/ingestion/engine_test.go @@ -133,7 +133,7 @@ func runWithEngine(t *testing.T, f func(testingContext)) { ctrl := gomock.NewController(t) - net := mocknetwork.NewMockNetwork(ctrl) + net := mocknetwork.NewMockEngineRegistry(ctrl) request := module.NewMockRequester(ctrl) // initialize the mocks and engine @@ -1490,7 +1490,7 @@ func newIngestionEngine(t *testing.T, ps *mocks.ProtocolState, es *mockExecution tracer, err := trace.NewTracer(log, "test", "test", trace.SensitivityCaptureAll) require.NoError(t, err) ctrl := gomock.NewController(t) - net := mocknetwork.NewMockNetwork(ctrl) + net := mocknetwork.NewMockEngineRegistry(ctrl) request := module.NewMockRequester(ctrl) var engine *Engine diff --git a/network/mocknetwork/engine_registry.go b/network/mocknetwork/engine_registry.go new file mode 100644 index 00000000000..982f1ada77d --- /dev/null +++ b/network/mocknetwork/engine_registry.go @@ -0,0 +1,158 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + datastore "github.com/ipfs/go-datastore" + channels "github.com/onflow/flow-go/network/channels" + + irrecoverable "github.com/onflow/flow-go/module/irrecoverable" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" + + protocol "github.com/libp2p/go-libp2p/core/protocol" +) + +// EngineRegistry is an autogenerated mock type for the EngineRegistry type +type EngineRegistry struct { + mock.Mock +} + +// Done provides a mock function with given fields: +func (_m *EngineRegistry) Done() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Ready provides a mock function with given fields: +func (_m *EngineRegistry) Ready() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Register provides a mock function with given fields: channel, messageProcessor +func (_m *EngineRegistry) Register(channel channels.Channel, messageProcessor network.MessageProcessor) (network.Conduit, error) { + ret := _m.Called(channel, messageProcessor) + + var r0 network.Conduit + var r1 error + if rf, ok := ret.Get(0).(func(channels.Channel, network.MessageProcessor) (network.Conduit, error)); ok { + return rf(channel, messageProcessor) + } + if rf, ok := ret.Get(0).(func(channels.Channel, network.MessageProcessor) network.Conduit); ok { + r0 = rf(channel, messageProcessor) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(network.Conduit) + } + } + + if rf, ok := ret.Get(1).(func(channels.Channel, network.MessageProcessor) error); ok { + r1 = rf(channel, messageProcessor) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RegisterBlobService provides a mock function with given fields: channel, store, opts +func (_m *EngineRegistry) RegisterBlobService(channel channels.Channel, store datastore.Batching, opts ...network.BlobServiceOption) (network.BlobService, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, channel, store) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 network.BlobService + var r1 error + if rf, ok := ret.Get(0).(func(channels.Channel, datastore.Batching, ...network.BlobServiceOption) (network.BlobService, error)); ok { + return rf(channel, store, opts...) + } + if rf, ok := ret.Get(0).(func(channels.Channel, datastore.Batching, ...network.BlobServiceOption) network.BlobService); ok { + r0 = rf(channel, store, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(network.BlobService) + } + } + + if rf, ok := ret.Get(1).(func(channels.Channel, datastore.Batching, ...network.BlobServiceOption) error); ok { + r1 = rf(channel, store, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RegisterPingService provides a mock function with given fields: pingProtocolID, pingInfoProvider +func (_m *EngineRegistry) RegisterPingService(pingProtocolID protocol.ID, pingInfoProvider network.PingInfoProvider) (network.PingService, error) { + ret := _m.Called(pingProtocolID, pingInfoProvider) + + var r0 network.PingService + var r1 error + if rf, ok := ret.Get(0).(func(protocol.ID, network.PingInfoProvider) (network.PingService, error)); ok { + return rf(pingProtocolID, pingInfoProvider) + } + if rf, ok := ret.Get(0).(func(protocol.ID, network.PingInfoProvider) network.PingService); ok { + r0 = rf(pingProtocolID, pingInfoProvider) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(network.PingService) + } + } + + if rf, ok := ret.Get(1).(func(protocol.ID, network.PingInfoProvider) error); ok { + r1 = rf(pingProtocolID, pingInfoProvider) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Start provides a mock function with given fields: _a0 +func (_m *EngineRegistry) Start(_a0 irrecoverable.SignalerContext) { + _m.Called(_a0) +} + +type mockConstructorTestingTNewEngineRegistry interface { + mock.TestingT + Cleanup(func()) +} + +// NewEngineRegistry creates a new instance of EngineRegistry. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEngineRegistry(t mockConstructorTestingTNewEngineRegistry) *EngineRegistry { + mock := &EngineRegistry{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/mock_network.go b/network/mocknetwork/mock_network.go index 413122da44b..c41c14f5903 100644 --- a/network/mocknetwork/mock_network.go +++ b/network/mocknetwork/mock_network.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/onflow/flow-go/network (interfaces: Network) +// Source: github.com/onflow/flow-go/network (interfaces: EngineRegistry) // Package mocknetwork is a generated GoMock package. package mocknetwork @@ -15,31 +15,31 @@ import ( channels "github.com/onflow/flow-go/network/channels" ) -// MockNetwork is a mock of Network interface. -type MockNetwork struct { +// MockEngineRegistry is a mock of EngineRegistry interface. +type MockEngineRegistry struct { ctrl *gomock.Controller - recorder *MockNetworkMockRecorder + recorder *MockEngineRegistryMockRecorder } -// MockNetworkMockRecorder is the mock recorder for MockNetwork. -type MockNetworkMockRecorder struct { - mock *MockNetwork +// MockEngineRegistryMockRecorder is the mock recorder for MockEngineRegistry. +type MockEngineRegistryMockRecorder struct { + mock *MockEngineRegistry } -// NewMockNetwork creates a new mock instance. -func NewMockNetwork(ctrl *gomock.Controller) *MockNetwork { - mock := &MockNetwork{ctrl: ctrl} - mock.recorder = &MockNetworkMockRecorder{mock} +// NewMockEngineRegistry creates a new mock instance. +func NewMockEngineRegistry(ctrl *gomock.Controller) *MockEngineRegistry { + mock := &MockEngineRegistry{ctrl: ctrl} + mock.recorder = &MockEngineRegistryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockNetwork) EXPECT() *MockNetworkMockRecorder { +func (m *MockEngineRegistry) EXPECT() *MockEngineRegistryMockRecorder { return m.recorder } // Done mocks base method. -func (m *MockNetwork) Done() <-chan struct{} { +func (m *MockEngineRegistry) Done() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Done") ret0, _ := ret[0].(<-chan struct{}) @@ -47,13 +47,13 @@ func (m *MockNetwork) Done() <-chan struct{} { } // Done indicates an expected call of Done. -func (mr *MockNetworkMockRecorder) Done() *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) Done() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Done", reflect.TypeOf((*MockNetwork)(nil).Done)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Done", reflect.TypeOf((*MockEngineRegistry)(nil).Done)) } // Ready mocks base method. -func (m *MockNetwork) Ready() <-chan struct{} { +func (m *MockEngineRegistry) Ready() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Ready") ret0, _ := ret[0].(<-chan struct{}) @@ -61,13 +61,13 @@ func (m *MockNetwork) Ready() <-chan struct{} { } // Ready indicates an expected call of Ready. -func (mr *MockNetworkMockRecorder) Ready() *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) Ready() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ready", reflect.TypeOf((*MockNetwork)(nil).Ready)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ready", reflect.TypeOf((*MockEngineRegistry)(nil).Ready)) } // Register mocks base method. -func (m *MockNetwork) Register(arg0 channels.Channel, arg1 network.MessageProcessor) (network.Conduit, error) { +func (m *MockEngineRegistry) Register(arg0 channels.Channel, arg1 network.MessageProcessor) (network.Conduit, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Register", arg0, arg1) ret0, _ := ret[0].(network.Conduit) @@ -76,13 +76,13 @@ func (m *MockNetwork) Register(arg0 channels.Channel, arg1 network.MessageProces } // Register indicates an expected call of Register. -func (mr *MockNetworkMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockNetwork)(nil).Register), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockEngineRegistry)(nil).Register), arg0, arg1) } // RegisterBlobService mocks base method. -func (m *MockNetwork) RegisterBlobService(arg0 channels.Channel, arg1 datastore.Batching, arg2 ...network.BlobServiceOption) (network.BlobService, error) { +func (m *MockEngineRegistry) RegisterBlobService(arg0 channels.Channel, arg1 datastore.Batching, arg2 ...network.BlobServiceOption) (network.BlobService, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { @@ -95,14 +95,14 @@ func (m *MockNetwork) RegisterBlobService(arg0 channels.Channel, arg1 datastore. } // RegisterBlobService indicates an expected call of RegisterBlobService. -func (mr *MockNetworkMockRecorder) RegisterBlobService(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) RegisterBlobService(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterBlobService", reflect.TypeOf((*MockNetwork)(nil).RegisterBlobService), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterBlobService", reflect.TypeOf((*MockEngineRegistry)(nil).RegisterBlobService), varargs...) } // RegisterPingService mocks base method. -func (m *MockNetwork) RegisterPingService(arg0 protocol.ID, arg1 network.PingInfoProvider) (network.PingService, error) { +func (m *MockEngineRegistry) RegisterPingService(arg0 protocol.ID, arg1 network.PingInfoProvider) (network.PingService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RegisterPingService", arg0, arg1) ret0, _ := ret[0].(network.PingService) @@ -111,19 +111,19 @@ func (m *MockNetwork) RegisterPingService(arg0 protocol.ID, arg1 network.PingInf } // RegisterPingService indicates an expected call of RegisterPingService. -func (mr *MockNetworkMockRecorder) RegisterPingService(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) RegisterPingService(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterPingService", reflect.TypeOf((*MockNetwork)(nil).RegisterPingService), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterPingService", reflect.TypeOf((*MockEngineRegistry)(nil).RegisterPingService), arg0, arg1) } // Start mocks base method. -func (m *MockNetwork) Start(arg0 irrecoverable.SignalerContext) { +func (m *MockEngineRegistry) Start(arg0 irrecoverable.SignalerContext) { m.ctrl.T.Helper() m.ctrl.Call(m, "Start", arg0) } // Start indicates an expected call of Start. -func (mr *MockNetworkMockRecorder) Start(arg0 interface{}) *gomock.Call { +func (mr *MockEngineRegistryMockRecorder) Start(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockNetwork)(nil).Start), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockEngineRegistry)(nil).Start), arg0) } From c9e10ee7f5ca147ab05cf1971beb142ddcf2da3e Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:47:27 -0700 Subject: [PATCH 807/815] renames adapter to conduit adapter --- .../node_builder/access_node_builder.go | 2 +- cmd/observer/node_builder/observer_builder.go | 2 +- cmd/scaffold.go | 2 +- follower/follower_builder.go | 2 +- insecure/corruptnet/conduit_factory.go | 10 +- insecure/mock/corrupt_conduit_factory.go | 4 +- network/alsp/manager/manager_test.go | 2 +- network/conduit.go | 8 +- network/internal/testutils/testUtil.go | 2 +- network/mocknetwork/conduit_adapter.go | 107 ++++++++++++++++++ network/mocknetwork/conduit_factory.go | 4 +- network/network.go | 8 +- network/p2p/conduit/conduit.go | 16 +-- network/p2p/p2pnet/network.go | 6 +- network/stub/network.go | 2 +- network/test/unicast_authorization_test.go | 2 +- 16 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 network/mocknetwork/conduit_adapter.go diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 144c6481967..810e005e8b0 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -1283,7 +1283,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { NetworkType: network.PublicNetwork, HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), }, - SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(msgValidators...)) diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 73aa80721d1..07d9b302bda 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -824,7 +824,7 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, }, - SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 1e384ecde88..50a4f5eccd0 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -469,7 +469,7 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( HeroCacheMetricsFactory: fnb.HeroCacheMetricsFactory(), NetworkType: network.PrivateNetwork, }, - SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(fnb.Logger, fnb.Metrics.Network, adapter) }, }, networkOptions...) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 147d51b445d..96ef743cdf2 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -703,7 +703,7 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { HeroCacheMetricsFactory: builder.HeroCacheMetricsFactory(), NetworkType: network.PublicNetwork, }, - SlashingViolationConsumerFactory: func(adapter network.Adapter) network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(adapter network.ConduitAdapter) network.ViolationsConsumer { return slashing.NewSlashingViolationsConsumer(builder.Logger, builder.Metrics.Network, adapter) }, }, p2pnet.WithMessageValidators(publicNetworkMsgValidators(node.Logger, node.IdentityProvider, node.NodeID)...)) diff --git a/insecure/corruptnet/conduit_factory.go b/insecure/corruptnet/conduit_factory.go index c62ab0b2340..4a0ba877cb2 100644 --- a/insecure/corruptnet/conduit_factory.go +++ b/insecure/corruptnet/conduit_factory.go @@ -18,7 +18,7 @@ const networkingProtocolTCP = "tcp" // ConduitFactory implements a corrupt conduit factory, that creates corrupt conduits. type ConduitFactory struct { logger zerolog.Logger - adapter network.Adapter + adapter network.ConduitAdapter egressController insecure.EgressController } @@ -36,10 +36,10 @@ func NewCorruptConduitFactory(logger zerolog.Logger, chainId flow.ChainID) *Cond return factory } -// RegisterAdapter sets the Adapter component of the factory. -// The Adapter is a wrapper around the Network layer that only exposes the set of methods +// RegisterAdapter sets the ConduitAdapter component of the factory. +// The ConduitAdapter is a wrapper around the Network layer that only exposes the set of methods // that are needed by a conduit. -func (c *ConduitFactory) RegisterAdapter(adapter network.Adapter) error { +func (c *ConduitFactory) RegisterAdapter(adapter network.ConduitAdapter) error { if c.adapter != nil { return fmt.Errorf("could not register a new network adapter, one already exists") } @@ -61,7 +61,7 @@ func (c *ConduitFactory) RegisterEgressController(controller insecure.EgressCont } // NewConduit creates a conduit on the specified channel. -// Prior to creating any conduit, the factory requires an Adapter to be registered with it. +// Prior to creating any conduit, the factory requires an ConduitAdapter to be registered with it. func (c *ConduitFactory) NewConduit(ctx context.Context, channel channels.Channel) (network.Conduit, error) { if c.adapter == nil { return nil, fmt.Errorf("could not create a new conduit, missing a registered network adapter") diff --git a/insecure/mock/corrupt_conduit_factory.go b/insecure/mock/corrupt_conduit_factory.go index 5e51f6e832c..4c7659385ad 100644 --- a/insecure/mock/corrupt_conduit_factory.go +++ b/insecure/mock/corrupt_conduit_factory.go @@ -48,11 +48,11 @@ func (_m *CorruptConduitFactory) NewConduit(_a0 context.Context, _a1 channels.Ch } // RegisterAdapter provides a mock function with given fields: _a0 -func (_m *CorruptConduitFactory) RegisterAdapter(_a0 network.Adapter) error { +func (_m *CorruptConduitFactory) RegisterAdapter(_a0 network.ConduitAdapter) error { ret := _m.Called(_a0) var r0 error - if rf, ok := ret.Get(0).(func(network.Adapter) error); ok { + if rf, ok := ret.Get(0).(func(network.ConduitAdapter) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) diff --git a/network/alsp/manager/manager_test.go b/network/alsp/manager/manager_test.go index 83527577712..d2178f359f6 100644 --- a/network/alsp/manager/manager_test.go +++ b/network/alsp/manager/manager_test.go @@ -471,7 +471,7 @@ func TestHandleReportedMisbehavior_And_SlashingViolationsConsumer_Integration(t sporkId, nodes[0], p2pnet.WithAlspConfig(managerCfgFixture(t)), - p2pnet.WithSlashingViolationConsumerFactory(func(adapter network.Adapter) network.ViolationsConsumer { + p2pnet.WithSlashingViolationConsumerFactory(func(adapter network.ConduitAdapter) network.ViolationsConsumer { violationsConsumer = slashing.NewSlashingViolationsConsumer(unittest.Logger(), metrics.NewNoopCollector(), adapter) return violationsConsumer })) diff --git a/network/conduit.go b/network/conduit.go index fa6e891e09a..4eb459c758d 100644 --- a/network/conduit.go +++ b/network/conduit.go @@ -13,13 +13,13 @@ import ( // ConduitFactory is an interface type that is utilized by the Network to create conduits for the channels. type ConduitFactory interface { - // RegisterAdapter sets the Adapter component of the factory. - // The Adapter is a wrapper around the Network layer that only exposes the set of methods + // RegisterAdapter sets the ConduitAdapter component of the factory. + // The ConduitAdapter is a wrapper around the Network layer that only exposes the set of methods // that are needed by a conduit. - RegisterAdapter(Adapter) error + RegisterAdapter(ConduitAdapter) error // NewConduit creates a conduit on the specified channel. - // Prior to creating any conduit, the factory requires an Adapter to be registered with it. + // Prior to creating any conduit, the factory requires an ConduitAdapter to be registered with it. NewConduit(context.Context, channels.Channel) (Conduit, error) } diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index b07f68dc7b7..7ac88f8fd1c 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -215,7 +215,7 @@ func NetworkConfigFixture( AlspMetrics: metrics.NewNoopCollector(), HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), }, - SlashingViolationConsumerFactory: func(_ network.Adapter) network.ViolationsConsumer { + SlashingViolationConsumerFactory: func(_ network.ConduitAdapter) network.ViolationsConsumer { return mocknetwork.NewViolationsConsumer(t) }, } diff --git a/network/mocknetwork/conduit_adapter.go b/network/mocknetwork/conduit_adapter.go new file mode 100644 index 00000000000..360aa12ccfc --- /dev/null +++ b/network/mocknetwork/conduit_adapter.go @@ -0,0 +1,107 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + flow "github.com/onflow/flow-go/model/flow" + channels "github.com/onflow/flow-go/network/channels" + + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" +) + +// ConduitAdapter is an autogenerated mock type for the ConduitAdapter type +type ConduitAdapter struct { + mock.Mock +} + +// MulticastOnChannel provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *ConduitAdapter) MulticastOnChannel(_a0 channels.Channel, _a1 interface{}, _a2 uint, _a3 ...flow.Identifier) error { + _va := make([]interface{}, len(_a3)) + for _i := range _a3 { + _va[_i] = _a3[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1, _a2) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel, interface{}, uint, ...flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2, _a3...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PublishOnChannel provides a mock function with given fields: _a0, _a1, _a2 +func (_m *ConduitAdapter) PublishOnChannel(_a0 channels.Channel, _a1 interface{}, _a2 ...flow.Identifier) error { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel, interface{}, ...flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ReportMisbehaviorOnChannel provides a mock function with given fields: channel, report +func (_m *ConduitAdapter) ReportMisbehaviorOnChannel(channel channels.Channel, report network.MisbehaviorReport) { + _m.Called(channel, report) +} + +// UnRegisterChannel provides a mock function with given fields: channel +func (_m *ConduitAdapter) UnRegisterChannel(channel channels.Channel) error { + ret := _m.Called(channel) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UnicastOnChannel provides a mock function with given fields: _a0, _a1, _a2 +func (_m *ConduitAdapter) UnicastOnChannel(_a0 channels.Channel, _a1 interface{}, _a2 flow.Identifier) error { + ret := _m.Called(_a0, _a1, _a2) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel, interface{}, flow.Identifier) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewConduitAdapter interface { + mock.TestingT + Cleanup(func()) +} + +// NewConduitAdapter creates a new instance of ConduitAdapter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewConduitAdapter(t mockConstructorTestingTNewConduitAdapter) *ConduitAdapter { + mock := &ConduitAdapter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/mocknetwork/conduit_factory.go b/network/mocknetwork/conduit_factory.go index abd1b8bdd6e..4eafe3d8448 100644 --- a/network/mocknetwork/conduit_factory.go +++ b/network/mocknetwork/conduit_factory.go @@ -44,11 +44,11 @@ func (_m *ConduitFactory) NewConduit(_a0 context.Context, _a1 channels.Channel) } // RegisterAdapter provides a mock function with given fields: _a0 -func (_m *ConduitFactory) RegisterAdapter(_a0 network.Adapter) error { +func (_m *ConduitFactory) RegisterAdapter(_a0 network.ConduitAdapter) error { ret := _m.Called(_a0) var r0 error - if rf, ok := ret.Get(0).(func(network.Adapter) error); ok { + if rf, ok := ret.Get(0).(func(network.ConduitAdapter) error); ok { r0 = rf(_a0) } else { r0 = ret.Error(0) diff --git a/network/network.go b/network/network.go index 02da472e754..5e4a1413c78 100644 --- a/network/network.go +++ b/network/network.go @@ -34,7 +34,7 @@ const ( PublicNetwork ) -// EngineRegistry is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer +// EngineRegistry is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer // offers to the Flow protocol layer, i.e., engines. It is responsible for creating conduits through which engines // can send and receive messages to and from other engines on the network, as well as registering other services // such as BlobService and PingService. @@ -55,10 +55,10 @@ type EngineRegistry interface { RegisterPingService(pingProtocolID protocol.ID, pingInfoProvider PingInfoProvider) (PingService, error) } -// Adapter is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer +// ConduitAdapter is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer // offers to a single conduit which enables the conduit to send different types of messages i.e., unicast, multicast, // and publish, to other conduits on the network. -type Adapter interface { +type ConduitAdapter interface { MisbehaviorReportConsumer // UnicastOnChannel sends the message in a reliable way to the given recipient. UnicastOnChannel(channels.Channel, interface{}, flow.Identifier) error @@ -75,7 +75,7 @@ type Adapter interface { UnRegisterChannel(channel channels.Channel) error } -// Middleware is one of the networking layer interfaces in Flow (i.e., EngineRegistry, Adapter, and Middleware). It represents the interface that networking layer +// Middleware is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer // offers to lower level networking components such as libp2p. It is responsible for subscribing to and unsubscribing // from channels, as well as updating the addresses of all the authorized participants in the Flow protocol. type Middleware interface { diff --git a/network/p2p/conduit/conduit.go b/network/p2p/conduit/conduit.go index eef36cecbaa..76758e6d7be 100644 --- a/network/p2p/conduit/conduit.go +++ b/network/p2p/conduit/conduit.go @@ -9,11 +9,11 @@ import ( "github.com/onflow/flow-go/network/channels" ) -// DefaultConduitFactory is a wrapper around the network Adapter. +// DefaultConduitFactory is a wrapper around the network ConduitAdapter. // It directly passes the incoming messages to the corresponding methods of the -// network Adapter. +// network ConduitAdapter. type DefaultConduitFactory struct { - adapter network.Adapter + adapter network.ConduitAdapter } var _ network.ConduitFactory = (*DefaultConduitFactory)(nil) @@ -30,10 +30,10 @@ func NewDefaultConduitFactory() *DefaultConduitFactory { return &DefaultConduitFactory{} } -// RegisterAdapter sets the Adapter component of the factory. -// The Adapter is a wrapper around the Network layer that only exposes the set of methods +// RegisterAdapter sets the ConduitAdapter component of the factory. +// The ConduitAdapter is a wrapper around the Network layer that only exposes the set of methods // that are needed by a conduit. -func (d *DefaultConduitFactory) RegisterAdapter(adapter network.Adapter) error { +func (d *DefaultConduitFactory) RegisterAdapter(adapter network.ConduitAdapter) error { if d.adapter != nil { return fmt.Errorf("could not register a new network adapter, one already exists") } @@ -44,7 +44,7 @@ func (d *DefaultConduitFactory) RegisterAdapter(adapter network.Adapter) error { } // NewConduit creates a conduit on the specified channel. -// Prior to creating any conduit, the factory requires an Adapter to be registered with it. +// Prior to creating any conduit, the factory requires an ConduitAdapter to be registered with it. func (d *DefaultConduitFactory) NewConduit(ctx context.Context, channel channels.Channel) (network.Conduit, error) { if d.adapter == nil { return nil, fmt.Errorf("could not create a new conduit, missing a registered network adapter") @@ -67,7 +67,7 @@ type Conduit struct { ctx context.Context cancel context.CancelFunc channel channels.Channel - adapter network.Adapter + adapter network.ConduitAdapter } var _ network.Conduit = (*Conduit)(nil) diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index debbfd6592b..af1fc55657c 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -121,7 +121,7 @@ type Network struct { var _ network.EngineRegistry = &Network{} var _ network.Middleware = &Network{} -var _ network.Adapter = &Network{} +var _ network.ConduitAdapter = &Network{} type registerEngineRequest struct { channel channels.Channel @@ -165,7 +165,7 @@ type NetworkConfig struct { UnicastMessageTimeout time.Duration Libp2pNode p2p.LibP2PNode BitSwapMetrics module.BitswapMetrics - SlashingViolationConsumerFactory func(network.Adapter) network.ViolationsConsumer + SlashingViolationConsumerFactory func(network.ConduitAdapter) network.ViolationsConsumer } // Validate validates the configuration, and sets default values for any missing fields. @@ -198,7 +198,7 @@ func WithCodec(codec network.Codec) NetworkConfigOption { } } -func WithSlashingViolationConsumerFactory(factory func(adapter network.Adapter) network.ViolationsConsumer) NetworkConfigOption { +func WithSlashingViolationConsumerFactory(factory func(adapter network.ConduitAdapter) network.ViolationsConsumer) NetworkConfigOption { return func(params *NetworkConfig) { params.SlashingViolationConsumerFactory = factory } diff --git a/network/stub/network.go b/network/stub/network.go index 6c45d547ae9..391aadd3d46 100644 --- a/network/stub/network.go +++ b/network/stub/network.go @@ -42,7 +42,7 @@ func WithConduitFactory(factory network.ConduitFactory) func(*Network) { } var _ network.EngineRegistry = (*Network)(nil) -var _ network.Adapter = (*Network)(nil) +var _ network.ConduitAdapter = (*Network)(nil) // NewNetwork create a mocked Network. // The committee has the identity of the node already, so only `committee` is needed diff --git a/network/test/unicast_authorization_test.go b/network/test/unicast_authorization_test.go index e77de9af971..e39c2ae630c 100644 --- a/network/test/unicast_authorization_test.go +++ b/network/test/unicast_authorization_test.go @@ -84,7 +84,7 @@ func (u *UnicastAuthorizationTestSuite) setupNetworks(slashingViolationsConsumer ids, libP2PNodes, p2pnet.WithCodec(u.codec), - p2pnet.WithSlashingViolationConsumerFactory(func(_ network.Adapter) network.ViolationsConsumer { + p2pnet.WithSlashingViolationConsumerFactory(func(_ network.ConduitAdapter) network.ViolationsConsumer { return slashingViolationsConsumer })) require.Len(u.T(), ids, 2) From 6c01ff5c904a67607709686a972284ec0e3e558c Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 10:58:08 -0700 Subject: [PATCH 808/815] cleans up middleware and dead codes --- .../node_builder/access_node_builder.go | 18 ++++++++--------- cmd/collection/main.go | 16 +++++++-------- cmd/consensus/main.go | 14 ++++++------- cmd/execution_builder.go | 14 ++++++------- cmd/ghost/main.go | 2 +- cmd/node_builder.go | 6 +++--- cmd/observer/node_builder/observer_builder.go | 14 ++++++------- cmd/scaffold.go | 18 ++++++++--------- cmd/verification_builder.go | 8 ++++---- follower/follower_builder.go | 15 +++++++------- insecure/cmd/corrupted_builder.go | 2 +- network/internal/testutils/testUtil.go | 1 - network/network.go | 12 +++++------ network/p2p/cache/node_blocklist_wrapper.go | 4 ++-- network/p2p/p2pnet/network.go | 2 +- .../p2p/subscription/subscriptionManager.go | 6 +++--- network/test/epochtransition_test.go | 2 -- utils/unittest/unittest.go | 20 ------------------- 18 files changed, 75 insertions(+), 99 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 810e005e8b0..aab1e7a7154 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -387,7 +387,7 @@ func (builder *FlowAccessNodeBuilder) buildFollowerEngine() *FlowAccessNodeBuild builder.FollowerEng, err = followereng.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -411,7 +411,7 @@ func (builder *FlowAccessNodeBuilder) buildSyncEngine() *FlowAccessNodeBuilder { sync, err := synceng.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, @@ -514,7 +514,7 @@ func (builder *FlowAccessNodeBuilder) BuildExecutionDataRequester() *FlowAccessN } var err error - bs, err = node.Network.RegisterBlobService(channels.ExecutionDataService, ds, opts...) + bs, err = node.EngineRegistry.RegisterBlobService(channels.ExecutionDataService, ds, opts...) if err != nil { return nil, fmt.Errorf("could not register blob service: %w", err) } @@ -821,7 +821,7 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.Middleware + return builder.UnderlayNetwork }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -885,14 +885,14 @@ func (builder *FlowAccessNodeBuilder) Initialize() error { func (builder *FlowAccessNodeBuilder) enqueueRelayNetwork() { builder.Component("relay network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { relayNet := relaynet.NewRelayNetwork( - node.Network, + node.EngineRegistry, builder.AccessNodeConfig.PublicNetworkConfig.Network, node.Logger, map[channels.Channel]channels.Channel{ channels.ReceiveBlocks: channels.PublicReceiveBlocks, }, ) - node.Network = relayNet + node.EngineRegistry = relayNet return relayNet, nil }) } @@ -1125,7 +1125,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.RequestEng, err = requester.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, channels.RequestCollections, @@ -1138,7 +1138,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.IngestEng, err = ingestion.New( node.Logger, - node.Network, + node.EngineRegistry, node.State, node.Me, builder.RequestEng, @@ -1291,7 +1291,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.Middleware = net + builder.UnderlayNetwork = net builder.AccessNodeConfig.PublicNetworkConfig.Network = net node.Logger.Info().Msgf("network will run on address: %s", builder.PublicNetworkConfig.BindAddress) diff --git a/cmd/collection/main.go b/cmd/collection/main.go index e53762ffb35..f285911bfdd 100644 --- a/cmd/collection/main.go +++ b/cmd/collection/main.go @@ -338,7 +338,7 @@ func main() { followerEng, err = followereng.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -359,7 +359,7 @@ func main() { sync, err := consync.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, @@ -378,7 +378,7 @@ func main() { Component("ingestion engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { ing, err = ingest.New( node.Logger, - node.Network, + node.EngineRegistry, node.State, node.Metrics.Engine, node.Metrics.Mempool, @@ -416,7 +416,7 @@ func main() { return provider.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, collectionRequestQueue, @@ -432,7 +432,7 @@ func main() { Component("pusher engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { push, err = pusher.New( node.Logger, - node.Network, + node.EngineRegistry, node.State, node.Metrics.Engine, colMetrics, @@ -479,7 +479,7 @@ func main() { complianceEngineFactory, err := factories.NewComplianceEngineFactory( node.Logger, - node.Network, + node.EngineRegistry, node.Me, colMetrics, node.Metrics.Engine, @@ -500,7 +500,7 @@ func main() { syncFactory, err := factories.NewSyncEngineFactory( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, ) if err != nil { @@ -554,7 +554,7 @@ func main() { messageHubFactory := factories.NewMessageHubFactory( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.State, diff --git a/cmd/consensus/main.go b/cmd/consensus/main.go index 2ef9380f661..2090311b1ae 100644 --- a/cmd/consensus/main.go +++ b/cmd/consensus/main.go @@ -457,7 +457,7 @@ func main() { node.Metrics.Engine, node.Metrics.Mempool, sealingTracker, - node.Network, + node.EngineRegistry, node.Me, node.Storage.Headers, node.Storage.Payloads, @@ -480,7 +480,7 @@ func main() { receiptRequester, err = requester.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, channels.RequestReceiptsByBlockID, @@ -511,7 +511,7 @@ func main() { e, err := matching.NewEngine( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Metrics.Mempool, @@ -544,7 +544,7 @@ func main() { ing, err := ingestion.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, core, ) @@ -823,7 +823,7 @@ func main() { messageHub, err := message_hub.NewMessageHub( createLogger(node.Logger, node.RootChainID), node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, comp, hot, @@ -842,7 +842,7 @@ func main() { sync, err := synceng.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, @@ -872,7 +872,7 @@ func main() { // exchange private DKG messages messagingEngine, err := dkgeng.NewMessagingEngine( node.Logger, - node.Network, + node.EngineRegistry, node.Me, dkgBrokerTunnel, node.Metrics.Mempool, diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 80ca887e727..db90b68aa18 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -336,7 +336,7 @@ func (exeNode *ExecutionNode) LoadBlobService( opts = append(opts, blob.WithRateLimit(float64(exeNode.exeConf.blobstoreRateLimit), exeNode.exeConf.blobstoreBurstLimit)) } - bs, err := node.Network.RegisterBlobService(channels.ExecutionDataService, exeNode.executionDataDatastore, opts...) + bs, err := node.EngineRegistry.RegisterBlobService(channels.ExecutionDataService, exeNode.executionDataDatastore, opts...) if err != nil { return nil, fmt.Errorf("failed to register blob service: %w", err) } @@ -515,7 +515,7 @@ func (exeNode *ExecutionNode) LoadProviderEngine( exeNode.providerEngine, err = exeprovider.New( node.Logger, node.Tracer, - node.Network, + node.EngineRegistry, node.State, exeNode.executionState, exeNode.collector, @@ -849,7 +849,7 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( error, ) { var err error - exeNode.collectionRequester, err = requester.New(node.Logger, node.Metrics.Engine, node.Network, node.Me, node.State, + exeNode.collectionRequester, err = requester.New(node.Logger, node.Metrics.Engine, node.EngineRegistry, node.Me, node.State, channels.RequestCollections, filter.Any, func() flow.Entity { return &flow.Collection{} }, @@ -867,7 +867,7 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( exeNode.ingestionEng, err = ingestion.New( exeNode.ingestionUnit, node.Logger, - node.Network, + node.EngineRegistry, node.Me, exeNode.collectionRequester, node.State, @@ -1001,7 +1001,7 @@ func (exeNode *ExecutionNode) LoadFollowerEngine( exeNode.followerEng, err = followereng.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -1048,7 +1048,7 @@ func (exeNode *ExecutionNode) LoadReceiptProviderEngine( eng, err := provider.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, receiptRequestQueue, @@ -1074,7 +1074,7 @@ func (exeNode *ExecutionNode) LoadSynchronizationEngine( exeNode.syncEngine, err = synchronization.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, diff --git a/cmd/ghost/main.go b/cmd/ghost/main.go index d49f11d9aca..eade979e506 100644 --- a/cmd/ghost/main.go +++ b/cmd/ghost/main.go @@ -37,7 +37,7 @@ func main() { return nil }). Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { - rpcEng, err := engine.New(node.Network, node.Logger, node.Me, node.State, rpcConf) + rpcEng, err := engine.New(node.EngineRegistry, node.Logger, node.Me, node.State, rpcConf) return rpcEng, err }) diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 65adfdd3af3..2bfd5bbe373 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -194,8 +194,8 @@ type NodeConfig struct { ProtocolEvents *events.Distributor State protocol.State Resolver madns.BasicResolver - Network network.EngineRegistry - Middleware network.Middleware + EngineRegistry network.EngineRegistry + UnderlayNetwork network.Underlay ConduitFactory network.ConduitFactory PingService network.PingService MsgValidators []network.MessageValidator @@ -206,7 +206,7 @@ type NodeConfig struct { // list of dependencies for network peer manager startup PeerManagerDependencies *DependencyList // ReadyDoneAware implementation of the network middleware for DependableComponents - middlewareDependable *module.ProxiedReadyDoneAware + networkUnderlayDependable *module.ProxiedReadyDoneAware // ID providers IdentityProvider module.IdentityProvider diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 07d9b302bda..bf2b5f886f5 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -370,7 +370,7 @@ func (builder *ObserverServiceBuilder) buildFollowerEngine() *ObserverServiceBui builder.FollowerEng, err = follower.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -395,7 +395,7 @@ func (builder *ObserverServiceBuilder) buildSyncEngine() *ObserverServiceBuilder sync, err := synceng.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, @@ -568,7 +568,7 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { // The following wrapper allows to black-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.Middleware + return builder.UnderlayNetwork }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -832,15 +832,15 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.Middleware = net - builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) + builder.UnderlayNetwork = net + builder.EngineRegistry = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) - idEvents := gadgets.NewIdentityDeltas(builder.Middleware.UpdateNodeAddresses) + idEvents := gadgets.NewIdentityDeltas(builder.UnderlayNetwork.UpdateNodeAddresses) builder.ProtocolEvents.AddConsumer(idEvents) - return builder.Network, nil + return builder.EngineRegistry, nil }) } diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 50a4f5eccd0..9e1202308c3 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -231,7 +231,7 @@ func (fnb *FlowNodeBuilder) EnqueuePingService() { } } - pingService, err := node.Network.RegisterPingService(pingLibP2PProtocolID, pingInfoProvider) + pingService, err := node.EngineRegistry.RegisterPingService(pingLibP2PProtocolID, pingInfoProvider) node.PingService = pingService @@ -389,9 +389,9 @@ func (fnb *FlowNodeBuilder) EnqueueNetworkInit() { peerManagerFilters) }) - fnb.Module("middleware dependency", func(node *NodeConfig) error { - fnb.middlewareDependable = module.NewProxiedReadyDoneAware() - fnb.PeerManagerDependencies.Add(fnb.middlewareDependable) + fnb.Module("network underlay dependency", func(node *NodeConfig) error { + fnb.networkUnderlayDependable = module.NewProxiedReadyDoneAware() + fnb.PeerManagerDependencies.Add(fnb.networkUnderlayDependable) return nil }) @@ -477,12 +477,12 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( return nil, fmt.Errorf("could not initialize network: %w", err) } - fnb.Network = net // setting network as the fnb.Network for the engine-level components - fnb.Middleware = net // setting network as the fnb.Middleware for the lower-level components + fnb.EngineRegistry = net // setting network as the fnb.Network for the engine-level components + fnb.UnderlayNetwork = net // setting network as the fnb.Underlay for the lower-level components // register network ReadyDoneAware interface so other components can depend on it for startup - if fnb.middlewareDependable != nil { - fnb.middlewareDependable.Init(fnb.Middleware) + if fnb.networkUnderlayDependable != nil { + fnb.networkUnderlayDependable.Init(fnb.UnderlayNetwork) } idEvents := gadgets.NewIdentityDeltas(net.UpdateNodeAddresses) @@ -1008,7 +1008,7 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return fnb.Middleware + return fnb.UnderlayNetwork }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) diff --git a/cmd/verification_builder.go b/cmd/verification_builder.go index e7e42d224f7..ea5ddf65a8e 100644 --- a/cmd/verification_builder.go +++ b/cmd/verification_builder.go @@ -201,7 +201,7 @@ func (v *VerificationNodeBuilder) LoadComponentsAndModules() { node.Logger, collector, node.Tracer, - node.Network, + node.EngineRegistry, node.State, node.Me, chunkVerifier, @@ -214,7 +214,7 @@ func (v *VerificationNodeBuilder) LoadComponentsAndModules() { requesterEngine, err = requester.New( node.Logger, node.State, - node.Network, + node.EngineRegistry, node.Tracer, collector, chunkRequests, @@ -379,7 +379,7 @@ func (v *VerificationNodeBuilder) LoadComponentsAndModules() { followerEng, err = followereng.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -398,7 +398,7 @@ func (v *VerificationNodeBuilder) LoadComponentsAndModules() { sync, err := commonsync.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 96ef743cdf2..192f1238f1b 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -265,7 +265,7 @@ func (builder *FollowerServiceBuilder) buildFollowerEngine() *FollowerServiceBui builder.FollowerEng, err = follower.NewComplianceLayer( node.Logger, - node.Network, + node.EngineRegistry, node.Me, node.Metrics.Engine, node.Storage.Headers, @@ -290,7 +290,7 @@ func (builder *FollowerServiceBuilder) buildSyncEngine() *FollowerServiceBuilder sync, err := synceng.New( node.Logger, node.Metrics.Engine, - node.Network, + node.EngineRegistry, node.Me, node.State, node.Storage.Blocks, @@ -450,7 +450,7 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of the disallow-listed nodes to true builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.Middleware + return builder.UnderlayNetwork }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -540,7 +540,6 @@ func (builder *FollowerServiceBuilder) validateParams() error { } // initPublicLibp2pNode creates a libp2p node for the follower service in public (unstaked) network. -// The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance // The LibP2P host is created with the following options: // - DHT as client and seeded with the given bootstrap peers // - The specified bind address as the listen address @@ -711,15 +710,15 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.Middleware = net - builder.Network = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) + builder.UnderlayNetwork = net + builder.EngineRegistry = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) - idEvents := gadgets.NewIdentityDeltas(builder.Middleware.UpdateNodeAddresses) + idEvents := gadgets.NewIdentityDeltas(builder.UnderlayNetwork.UpdateNodeAddresses) builder.ProtocolEvents.AddConsumer(idEvents) - return builder.Network, nil + return builder.EngineRegistry, nil }) } diff --git a/insecure/cmd/corrupted_builder.go b/insecure/cmd/corrupted_builder.go index 34de857fda5..6ceaa1b1a2a 100644 --- a/insecure/cmd/corrupted_builder.go +++ b/insecure/cmd/corrupted_builder.go @@ -162,7 +162,7 @@ func (cnb *CorruptedNodeBuilder) enqueueNetworkingLayer() { cnb.Logger.Info().Hex("node_id", logging.ID(cnb.NodeID)).Str("address", address).Msg("corruptible network initiated") // override the original flow network with the corruptible network. - cnb.Network = corruptibleNetwork + cnb.EngineRegistry = corruptibleNetwork return corruptibleNetwork, nil }) diff --git a/network/internal/testutils/testUtil.go b/network/internal/testutils/testUtil.go index 7ac88f8fd1c..c817fba98f8 100644 --- a/network/internal/testutils/testUtil.go +++ b/network/internal/testutils/testUtil.go @@ -252,7 +252,6 @@ func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, node // // This function fails the test if the networks do not start within the given timeout. func StartNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nets []network.EngineRegistry) { - // start up networks (this will implicitly start middlewares) for _, net := range nets { net.Start(ctx) unittest.RequireComponentsReadyBefore(t, 5*time.Second, net) diff --git a/network/network.go b/network/network.go index 5e4a1413c78..e34160770ee 100644 --- a/network/network.go +++ b/network/network.go @@ -34,7 +34,7 @@ const ( PublicNetwork ) -// EngineRegistry is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer +// EngineRegistry is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Underlay). It represents the interface that networking layer // offers to the Flow protocol layer, i.e., engines. It is responsible for creating conduits through which engines // can send and receive messages to and from other engines on the network, as well as registering other services // such as BlobService and PingService. @@ -55,7 +55,7 @@ type EngineRegistry interface { RegisterPingService(pingProtocolID protocol.ID, pingInfoProvider PingInfoProvider) (PingService, error) } -// ConduitAdapter is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer +// ConduitAdapter is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Underlay). It represents the interface that networking layer // offers to a single conduit which enables the conduit to send different types of messages i.e., unicast, multicast, // and publish, to other conduits on the network. type ConduitAdapter interface { @@ -75,18 +75,18 @@ type ConduitAdapter interface { UnRegisterChannel(channel channels.Channel) error } -// Middleware is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Middleware). It represents the interface that networking layer +// Underlay is one of the networking layer interfaces in Flow (i.e., EngineRegistry, ConduitAdapter, and Underlay). It represents the interface that networking layer // offers to lower level networking components such as libp2p. It is responsible for subscribing to and unsubscribing // from channels, as well as updating the addresses of all the authorized participants in the Flow protocol. -type Middleware interface { +type Underlay interface { module.ReadyDoneAware DisallowListNotificationConsumer - // Subscribe subscribes the middleware to a channel. + // Subscribe subscribes the network Underlay to a channel. // No errors are expected during normal operation. Subscribe(channel channels.Channel) error - // Unsubscribe unsubscribes the middleware from a channel. + // Unsubscribe unsubscribes the network Underlay from a channel. // All errors returned from this function can be considered benign. Unsubscribe(channel channels.Channel) error diff --git a/network/p2p/cache/node_blocklist_wrapper.go b/network/p2p/cache/node_blocklist_wrapper.go index 10279953578..fab3d27b56c 100644 --- a/network/p2p/cache/node_blocklist_wrapper.go +++ b/network/p2p/cache/node_blocklist_wrapper.go @@ -45,8 +45,8 @@ type NodeDisallowListingWrapper struct { // updateConsumerOracle is called whenever the disallow-list is updated. // Note that we do not use the `updateConsumer` directly due to the circular dependency between the - // networking layer Middleware interface (i.e., updateConsumer), and the wrapper (i.e., NodeDisallowListingWrapper). - // Middleware needs identity provider to be initialized, and identity provider needs this wrapper to be initialized. + // networking layer Underlay interface (i.e., updateConsumer), and the wrapper (i.e., NodeDisallowListingWrapper). + // Underlay needs identity provider to be initialized, and identity provider needs this wrapper to be initialized. // Hence, if we pass the updateConsumer by the interface value, it will be nil at the time of initialization. // Instead, we use the oracle function to get the updateConsumer whenever we need it. updateConsumerOracle func() network.DisallowListNotificationConsumer diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index af1fc55657c..f57611f73d3 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -120,7 +120,7 @@ type Network struct { } var _ network.EngineRegistry = &Network{} -var _ network.Middleware = &Network{} +var _ network.Underlay = &Network{} var _ network.ConduitAdapter = &Network{} type registerEngineRequest struct { diff --git a/network/p2p/subscription/subscriptionManager.go b/network/p2p/subscription/subscriptionManager.go index 9221273faa4..e1c7efb6694 100644 --- a/network/p2p/subscription/subscriptionManager.go +++ b/network/p2p/subscription/subscriptionManager.go @@ -13,15 +13,15 @@ import ( type ChannelSubscriptionManager struct { mu sync.RWMutex engines map[channels.Channel]network.MessageProcessor - middleware network.Middleware // the Middleware interface of the network layer + middleware network.Underlay // the Underlay interface of the network layer } // NewChannelSubscriptionManager creates a new subscription manager. // Args: -// - middleware: the Middleware interface of the network layer. +// - middleware: the Underlay interface of the network layer. // Returns: // - a new subscription manager. -func NewChannelSubscriptionManager(middleware network.Middleware) *ChannelSubscriptionManager { +func NewChannelSubscriptionManager(middleware network.Underlay) *ChannelSubscriptionManager { return &ChannelSubscriptionManager{ engines: make(map[channels.Channel]network.MessageProcessor), middleware: middleware, diff --git a/network/test/epochtransition_test.go b/network/test/epochtransition_test.go index 3d087019031..b3132d5b398 100644 --- a/network/test/epochtransition_test.go +++ b/network/test/epochtransition_test.go @@ -191,8 +191,6 @@ func (suite *MutableIdentityTableSuite) setupStateMock() { func (suite *MutableIdentityTableSuite) addNodes(count int) { ctx, cancel := context.WithCancel(context.Background()) signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) - - // create the ids, middlewares and networks sporkId := unittest.IdentifierFixture() ids, nodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), sporkId, count) nets, _ := testutils.NetworksFixture(suite.T(), sporkId, ids, nodes) diff --git a/utils/unittest/unittest.go b/utils/unittest/unittest.go index 0d9949ffc2d..a07cea71b46 100644 --- a/utils/unittest/unittest.go +++ b/utils/unittest/unittest.go @@ -13,7 +13,6 @@ import ( "time" "github.com/dgraph-io/badger/v2" - "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,9 +20,7 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/util" "github.com/onflow/flow-go/network" - "github.com/onflow/flow-go/network/channels" cborcodec "github.com/onflow/flow-go/network/codec/cbor" - "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/topology" ) @@ -437,20 +434,3 @@ func GenerateRandomStringWithLen(commentLen uint) string { } return string(bytes) } - -// NetworkSlashingViolationsConsumer returns a slashing violations consumer for network middleware -func NetworkSlashingViolationsConsumer(logger zerolog.Logger, metrics module.NetworkSecurityMetrics, consumer network.MisbehaviorReportConsumer) network.ViolationsConsumer { - return slashing.NewSlashingViolationsConsumer(logger, metrics, consumer) -} - -type MisbehaviorReportConsumerFixture struct { - network.MisbehaviorReportManager -} - -func (c *MisbehaviorReportConsumerFixture) ReportMisbehaviorOnChannel(channel channels.Channel, report network.MisbehaviorReport) { - c.HandleMisbehaviorReport(channel, report) -} - -func NewMisbehaviorReportConsumerFixture(manager network.MisbehaviorReportManager) *MisbehaviorReportConsumerFixture { - return &MisbehaviorReportConsumerFixture{manager} -} From dbc13f5768242194c65c374da4bd4c86a9222848 Mon Sep 17 00:00:00 2001 From: "Yahya Hassanzadeh, Ph.D" Date: Wed, 6 Sep 2023 10:58:46 -0700 Subject: [PATCH 809/815] Update network/alsp/manager/manager.go Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com> --- network/alsp/manager/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/alsp/manager/manager.go b/network/alsp/manager/manager.go index 082cd98be77..f29fbc694b4 100644 --- a/network/alsp/manager/manager.go +++ b/network/alsp/manager/manager.go @@ -311,7 +311,7 @@ func (m *MisbehaviorReportManager) onHeartbeat() error { } // TODO: this can be done in batch but at this stage let's send individual notifications. - // (it requires enabling the batch mode end-to-end including the cache in Network). + // (it requires enabling the batch mode end-to-end including the cache in network). // as long as record.Penalty is NOT below model.DisallowListingThreshold, // the node is considered allow-listed and can conduct inbound and outbound connections. // Once it falls below model.DisallowListingThreshold, it needs to be disallow listed. From 717f854cfed9b26334aa95cbe9a2ad0d21d5728a Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 11:04:18 -0700 Subject: [PATCH 810/815] updates documentation of network --- network/p2p/p2pnet/network.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/network/p2p/p2pnet/network.go b/network/p2p/p2pnet/network.go index f57611f73d3..c971f6b56f4 100644 --- a/network/p2p/p2pnet/network.go +++ b/network/p2p/p2pnet/network.go @@ -81,8 +81,11 @@ var ( // be included in network communication. We omit any nodes that have been ejected. var NotEjectedFilter = filter.Not(filter.Ejected) -// Network represents the overlay network of our peer-to-peer network, including -// the protocols for handshakes, authentication, gossiping and heartbeats. +// Network serves as the comprehensive networking layer that integrates three interfaces within Flow; Underlay, EngineRegistry, and ConduitAdapter. +// It is responsible for creating conduits through which engines can send and receive messages to and from other engines on the network, as well as registering other services +// such as BlobService and PingService. It also provides a set of APIs that can be used to send messages to other nodes on the network. +// Network is also responsible for managing the topology of the network, i.e., the set of nodes that are connected to each other. +// It is also responsible for managing the set of nodes that are connected to each other. type Network struct { // TODO: using a waitgroup here doesn't actually guarantee that we'll wait for all // goroutines to exit, because new goroutines could be started after we've already From eb615ba9e1e6011141922c2df864cd23e71fe789 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 12:26:51 -0700 Subject: [PATCH 811/815] renames UnderlayNetwork to NetworkUnderlay --- cmd/access/node_builder/access_node_builder.go | 4 ++-- cmd/node_builder.go | 2 +- cmd/observer/node_builder/observer_builder.go | 6 +++--- cmd/scaffold.go | 6 +++--- follower/follower_builder.go | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index aab1e7a7154..d5b0e688cd4 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -821,7 +821,7 @@ func (builder *FlowAccessNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.UnderlayNetwork + return builder.NetworkUnderlay }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -1291,7 +1291,7 @@ func (builder *FlowAccessNodeBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.UnderlayNetwork = net + builder.NetworkUnderlay = net builder.AccessNodeConfig.PublicNetworkConfig.Network = net node.Logger.Info().Msgf("network will run on address: %s", builder.PublicNetworkConfig.BindAddress) diff --git a/cmd/node_builder.go b/cmd/node_builder.go index 2bfd5bbe373..96bbf0069c8 100644 --- a/cmd/node_builder.go +++ b/cmd/node_builder.go @@ -195,7 +195,7 @@ type NodeConfig struct { State protocol.State Resolver madns.BasicResolver EngineRegistry network.EngineRegistry - UnderlayNetwork network.Underlay + NetworkUnderlay network.Underlay ConduitFactory network.ConduitFactory PingService network.PingService MsgValidators []network.MessageValidator diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index bf2b5f886f5..507de880479 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -568,7 +568,7 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { // The following wrapper allows to black-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.UnderlayNetwork + return builder.NetworkUnderlay }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -832,12 +832,12 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.UnderlayNetwork = net + builder.NetworkUnderlay = net builder.EngineRegistry = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) - idEvents := gadgets.NewIdentityDeltas(builder.UnderlayNetwork.UpdateNodeAddresses) + idEvents := gadgets.NewIdentityDeltas(builder.NetworkUnderlay.UpdateNodeAddresses) builder.ProtocolEvents.AddConsumer(idEvents) return builder.EngineRegistry, nil diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 9e1202308c3..ebfba2614c1 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -478,11 +478,11 @@ func (fnb *FlowNodeBuilder) InitFlowNetworkWithConduitFactory( } fnb.EngineRegistry = net // setting network as the fnb.Network for the engine-level components - fnb.UnderlayNetwork = net // setting network as the fnb.Underlay for the lower-level components + fnb.NetworkUnderlay = net // setting network as the fnb.Underlay for the lower-level components // register network ReadyDoneAware interface so other components can depend on it for startup if fnb.networkUnderlayDependable != nil { - fnb.networkUnderlayDependable.Init(fnb.UnderlayNetwork) + fnb.networkUnderlayDependable.Init(fnb.NetworkUnderlay) } idEvents := gadgets.NewIdentityDeltas(net.UpdateNodeAddresses) @@ -1008,7 +1008,7 @@ func (fnb *FlowNodeBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of disallow-listed nodes to true disallowListWrapper, err := cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return fnb.UnderlayNetwork + return fnb.NetworkUnderlay }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 192f1238f1b..bf7c3a40eb8 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -450,7 +450,7 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { // The following wrapper allows to disallow-list byzantine nodes via an admin command: // the wrapper overrides the 'Ejected' flag of the disallow-listed nodes to true builder.IdentityProvider, err = cache.NewNodeDisallowListWrapper(idCache, node.DB, func() network.DisallowListNotificationConsumer { - return builder.UnderlayNetwork + return builder.NetworkUnderlay }) if err != nil { return fmt.Errorf("could not initialize NodeBlockListWrapper: %w", err) @@ -710,12 +710,12 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { return nil, fmt.Errorf("could not initialize network: %w", err) } - builder.UnderlayNetwork = net + builder.NetworkUnderlay = net builder.EngineRegistry = converter.NewNetwork(net, channels.SyncCommittee, channels.PublicSyncCommittee) builder.Logger.Info().Msgf("network will run on address: %s", builder.BindAddr) - idEvents := gadgets.NewIdentityDeltas(builder.UnderlayNetwork.UpdateNodeAddresses) + idEvents := gadgets.NewIdentityDeltas(builder.NetworkUnderlay.UpdateNodeAddresses) builder.ProtocolEvents.AddConsumer(idEvents) return builder.EngineRegistry, nil From a6626945afae993415f2acaba2eb41b4e04cbed7 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 12:31:05 -0700 Subject: [PATCH 812/815] renames connect to peer method --- module/upstream/upstream_connector.go | 2 +- network/mocknetwork/underlay.go | 105 ++++++++++++++++++ network/p2p/libp2pNode.go | 4 +- network/p2p/mock/lib_p2_p_node.go | 4 +- network/p2p/mock/peer_management.go | 4 +- network/p2p/p2pnode/libp2pNode.go | 2 +- network/p2p/p2pnode/libp2pNode_test.go | 4 +- .../subscription/subscription_filter_test.go | 4 +- network/p2p/test/fixtures.go | 2 +- network/p2p/test/sporking_test.go | 2 +- network/p2p/test/topic_validator_test.go | 22 ++-- 11 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 network/mocknetwork/underlay.go diff --git a/module/upstream/upstream_connector.go b/module/upstream/upstream_connector.go index 1f052f667a6..d115aedee59 100644 --- a/module/upstream/upstream_connector.go +++ b/module/upstream/upstream_connector.go @@ -102,7 +102,7 @@ func (connector *upstreamConnector) connect(ctx context.Context, bootstrapPeer f } // try and connect to the bootstrap server - return connector.unstakedNode.ConnectToPeerAddrInfo(ctx, peerAddrInfo) + return connector.unstakedNode.ConnectToPeer(ctx, peerAddrInfo) } func (connector *upstreamConnector) Done() <-chan struct{} { diff --git a/network/mocknetwork/underlay.go b/network/mocknetwork/underlay.go new file mode 100644 index 00000000000..214bff8dbad --- /dev/null +++ b/network/mocknetwork/underlay.go @@ -0,0 +1,105 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mocknetwork + +import ( + channels "github.com/onflow/flow-go/network/channels" + mock "github.com/stretchr/testify/mock" + + network "github.com/onflow/flow-go/network" +) + +// Underlay is an autogenerated mock type for the Underlay type +type Underlay struct { + mock.Mock +} + +// Done provides a mock function with given fields: +func (_m *Underlay) Done() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// OnAllowListNotification provides a mock function with given fields: _a0 +func (_m *Underlay) OnAllowListNotification(_a0 *network.AllowListingUpdate) { + _m.Called(_a0) +} + +// OnDisallowListNotification provides a mock function with given fields: _a0 +func (_m *Underlay) OnDisallowListNotification(_a0 *network.DisallowListingUpdate) { + _m.Called(_a0) +} + +// Ready provides a mock function with given fields: +func (_m *Underlay) Ready() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Subscribe provides a mock function with given fields: channel +func (_m *Underlay) Subscribe(channel channels.Channel) error { + ret := _m.Called(channel) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Unsubscribe provides a mock function with given fields: channel +func (_m *Underlay) Unsubscribe(channel channels.Channel) error { + ret := _m.Called(channel) + + var r0 error + if rf, ok := ret.Get(0).(func(channels.Channel) error); ok { + r0 = rf(channel) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateNodeAddresses provides a mock function with given fields: +func (_m *Underlay) UpdateNodeAddresses() { + _m.Called() +} + +type mockConstructorTestingTNewUnderlay interface { + mock.TestingT + Cleanup(func()) +} + +// NewUnderlay creates a new instance of Underlay. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewUnderlay(t mockConstructorTestingTNewUnderlay) *Underlay { + mock := &Underlay{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/network/p2p/libp2pNode.go b/network/p2p/libp2pNode.go index 0e37be1a7f8..f7bd5f39efb 100644 --- a/network/p2p/libp2pNode.go +++ b/network/p2p/libp2pNode.go @@ -37,9 +37,9 @@ type CoreP2P interface { // PeerManagement set of node traits related to its lifecycle and metadata retrieval type PeerManagement interface { - // ConnectToPeerAddrInfo connects to the peer with the given peer address information. + // ConnectToPeer connects to the peer with the given peer address information. // This method is used to connect to a peer that is not in the peer store. - ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error + ConnectToPeer(ctx context.Context, peerInfo peer.AddrInfo) error // RemovePeer closes the connection with the peer. RemovePeer(peerID peer.ID) error // ListPeers returns list of peer IDs for peers subscribed to the topic. diff --git a/network/p2p/mock/lib_p2_p_node.go b/network/p2p/mock/lib_p2_p_node.go index 5d0abf8318b..d8b590cb541 100644 --- a/network/p2p/mock/lib_p2_p_node.go +++ b/network/p2p/mock/lib_p2_p_node.go @@ -43,8 +43,8 @@ func (_m *LibP2PNode) ActiveClustersChanged(_a0 flow.ChainIDList) { _m.Called(_a0) } -// ConnectToPeerAddrInfo provides a mock function with given fields: ctx, peerInfo -func (_m *LibP2PNode) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { +// ConnectToPeer provides a mock function with given fields: ctx, peerInfo +func (_m *LibP2PNode) ConnectToPeer(ctx context.Context, peerInfo peer.AddrInfo) error { ret := _m.Called(ctx, peerInfo) var r0 error diff --git a/network/p2p/mock/peer_management.go b/network/p2p/mock/peer_management.go index 0049b060ec8..278ab628743 100644 --- a/network/p2p/mock/peer_management.go +++ b/network/p2p/mock/peer_management.go @@ -32,8 +32,8 @@ type PeerManagement struct { mock.Mock } -// ConnectToPeerAddrInfo provides a mock function with given fields: ctx, peerInfo -func (_m *PeerManagement) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { +// ConnectToPeer provides a mock function with given fields: ctx, peerInfo +func (_m *PeerManagement) ConnectToPeer(ctx context.Context, peerInfo peer.AddrInfo) error { ret := _m.Called(ctx, peerInfo) var r0 error diff --git a/network/p2p/p2pnode/libp2pNode.go b/network/p2p/p2pnode/libp2pNode.go index 17de3e0d2f3..5f1580c9d02 100644 --- a/network/p2p/p2pnode/libp2pNode.go +++ b/network/p2p/p2pnode/libp2pNode.go @@ -146,7 +146,7 @@ func (n *Node) Stop() error { // ConnectToPeerAddrInfo adds a peer to this node by adding it to this node's peerstore and connecting to it. // All errors returned from this function can be considered benign. -func (n *Node) ConnectToPeerAddrInfo(ctx context.Context, peerInfo peer.AddrInfo) error { +func (n *Node) ConnectToPeer(ctx context.Context, peerInfo peer.AddrInfo) error { return n.host.Connect(ctx, peerInfo) } diff --git a/network/p2p/p2pnode/libp2pNode_test.go b/network/p2p/p2pnode/libp2pNode_test.go index 6fde6e0252a..3a42b2bbe24 100644 --- a/network/p2p/p2pnode/libp2pNode_test.go +++ b/network/p2p/p2pnode/libp2pNode_test.go @@ -123,7 +123,7 @@ func TestAddPeers(t *testing.T) { for _, identity := range identities[1:] { peerInfo, err := utils.PeerAddressInfo(*identity) require.NoError(t, err) - require.NoError(t, nodes[0].ConnectToPeerAddrInfo(ctx, peerInfo)) + require.NoError(t, nodes[0].ConnectToPeer(ctx, peerInfo)) } // Checks if both of the other nodes have been added as peers to the first node @@ -146,7 +146,7 @@ func TestRemovePeers(t *testing.T) { // add nodes two and three to the first node as its peers for _, pInfo := range peerInfos[1:] { - require.NoError(t, nodes[0].ConnectToPeerAddrInfo(ctx, pInfo)) + require.NoError(t, nodes[0].ConnectToPeer(ctx, pInfo)) } // check if all other nodes have been added as peers to the first node diff --git a/network/p2p/subscription/subscription_filter_test.go b/network/p2p/subscription/subscription_filter_test.go index 006484da8e6..da6429e3d7c 100644 --- a/network/p2p/subscription/subscription_filter_test.go +++ b/network/p2p/subscription/subscription_filter_test.go @@ -42,8 +42,8 @@ func TestFilterSubscribe(t *testing.T) { unstakedKey := unittest.NetworkingPrivKeyFixture() unstakedNode := p2pfixtures.CreateNode(t, unstakedKey, sporkId, zerolog.Nop(), ids) - require.NoError(t, node1.ConnectToPeerAddrInfo(context.TODO(), *host.InfoFromHost(node2.Host()))) - require.NoError(t, node1.ConnectToPeerAddrInfo(context.TODO(), *host.InfoFromHost(unstakedNode.Host()))) + require.NoError(t, node1.ConnectToPeer(context.TODO(), *host.InfoFromHost(node2.Host()))) + require.NoError(t, node1.ConnectToPeer(context.TODO(), *host.InfoFromHost(unstakedNode.Host()))) badTopic := channels.TopicFromChannel(channels.SyncCommittee, sporkId) diff --git a/network/p2p/test/fixtures.go b/network/p2p/test/fixtures.go index 296b47a9c1c..7017a2fea1f 100644 --- a/network/p2p/test/fixtures.go +++ b/network/p2p/test/fixtures.go @@ -524,7 +524,7 @@ func LetNodesDiscoverEachOther(t *testing.T, ctx context.Context, nodes []p2p.Li } otherPInfo, err := utils.PeerAddressInfo(*ids[i]) require.NoError(t, err) - require.NoError(t, node.ConnectToPeerAddrInfo(ctx, otherPInfo)) + require.NoError(t, node.ConnectToPeer(ctx, otherPInfo)) } } } diff --git a/network/p2p/test/sporking_test.go b/network/p2p/test/sporking_test.go index 027407871c8..c49773d7214 100644 --- a/network/p2p/test/sporking_test.go +++ b/network/p2p/test/sporking_test.go @@ -239,7 +239,7 @@ func TestOneToKCrosstalkPrevention(t *testing.T) { require.NoError(t, err) // add node 2 as a peer of node 1 - err = node1.ConnectToPeerAddrInfo(ctx, pInfo2) + err = node1.ConnectToPeer(ctx, pInfo2) require.NoError(t, err) // let the two nodes form the mesh diff --git a/network/p2p/test/topic_validator_test.go b/network/p2p/test/topic_validator_test.go index d60dfa69ce7..2aa1571aa13 100644 --- a/network/p2p/test/topic_validator_test.go +++ b/network/p2p/test/topic_validator_test.go @@ -78,7 +78,7 @@ func TestTopicValidator_Unstaked(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) // sn1 will subscribe with is staked callback that should force the TopicValidator to drop the message received from sn2 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, isStaked)) @@ -139,7 +139,7 @@ func TestTopicValidator_PublicChannel(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) // sn1 & sn2 will subscribe with unauthenticated callback to allow it to send and receive unauthenticated messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -208,7 +208,7 @@ func TestTopicValidator_TopicMismatch(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -269,7 +269,7 @@ func TestTopicValidator_InvalidTopic(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) @@ -369,8 +369,8 @@ func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { // node1 is connected to node2, and the an1 is connected to node1 // an1 <-> sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) - require.NoError(t, an1.ConnectToPeerAddrInfo(ctx, pInfo1)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) + require.NoError(t, an1.ConnectToPeer(ctx, pInfo1)) // sn1 and sn2 subscribe to the topic with the topic validator sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -494,7 +494,7 @@ func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { // node1 is connected to node2 // sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -580,8 +580,8 @@ func TestAuthorizedSenderValidator_Ejected(t *testing.T) { // node1 is connected to node2, and the an1 is connected to node1 // an1 <-> sn1 <-> sn2 - require.NoError(t, sn1.ConnectToPeerAddrInfo(ctx, pInfo2)) - require.NoError(t, an1.ConnectToPeerAddrInfo(ctx, pInfo1)) + require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) + require.NoError(t, an1.ConnectToPeer(ctx, pInfo1)) // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) @@ -693,8 +693,8 @@ func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { require.NoError(t, err) // ln3 <-> sn1 <-> sn2 - require.NoError(t, ln1.ConnectToPeerAddrInfo(ctx, pInfo2)) - require.NoError(t, ln3.ConnectToPeerAddrInfo(ctx, pInfo1)) + require.NoError(t, ln1.ConnectToPeer(ctx, pInfo2)) + require.NoError(t, ln3.ConnectToPeer(ctx, pInfo1)) sub1, err := ln1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) require.NoError(t, err) From 5c7a8d2d582814dd2ebdd166207bdd02ec843b97 Mon Sep 17 00:00:00 2001 From: Yahya Hassanzadeh Date: Wed, 6 Sep 2023 12:33:54 -0700 Subject: [PATCH 813/815] renames middleware to network underlay --- .../p2p/subscription/subscriptionManager.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/network/p2p/subscription/subscriptionManager.go b/network/p2p/subscription/subscriptionManager.go index e1c7efb6694..b4342a94226 100644 --- a/network/p2p/subscription/subscriptionManager.go +++ b/network/p2p/subscription/subscriptionManager.go @@ -11,20 +11,20 @@ import ( // ChannelSubscriptionManager manages subscriptions of engines running on the node to channels. // Each channel should be taken by at most a single engine. type ChannelSubscriptionManager struct { - mu sync.RWMutex - engines map[channels.Channel]network.MessageProcessor - middleware network.Underlay // the Underlay interface of the network layer + mu sync.RWMutex + engines map[channels.Channel]network.MessageProcessor + networkUnderlay network.Underlay // the Underlay interface of the network layer } // NewChannelSubscriptionManager creates a new subscription manager. // Args: -// - middleware: the Underlay interface of the network layer. +// - networkUnderlay: the Underlay interface of the network layer. // Returns: // - a new subscription manager. -func NewChannelSubscriptionManager(middleware network.Underlay) *ChannelSubscriptionManager { +func NewChannelSubscriptionManager(underlay network.Underlay) *ChannelSubscriptionManager { return &ChannelSubscriptionManager{ - engines: make(map[channels.Channel]network.MessageProcessor), - middleware: middleware, + engines: make(map[channels.Channel]network.MessageProcessor), + networkUnderlay: underlay, } } @@ -39,9 +39,9 @@ func (sm *ChannelSubscriptionManager) Register(channel channels.Channel, engine return fmt.Errorf("subscriptionManager: channel already registered: %s", channel) } - // registers the channel with the middleware to let middleware start receiving messages + // registers the channel with the networkUnderlay to let networkUnderlay start receiving messages // TODO: subscribe function should be replaced by a better abstraction of the network. - err := sm.middleware.Subscribe(channel) + err := sm.networkUnderlay.Subscribe(channel) if err != nil { return fmt.Errorf("subscriptionManager: failed to subscribe to channel %s: %w", channel, err) } @@ -64,7 +64,7 @@ func (sm *ChannelSubscriptionManager) Unregister(channel channels.Channel) error return nil } - err := sm.middleware.Unsubscribe(channel) + err := sm.networkUnderlay.Unsubscribe(channel) if err != nil { return fmt.Errorf("subscriptionManager: failed to unregister from channel %s: %w", channel, err) } From cafa8932850d375e185223d448b59b57ab0bb564 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Thu, 7 Sep 2023 11:35:12 -0700 Subject: [PATCH 814/815] address review comments --- engine/execution/ingestion/engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go index 724b048d675..d8398fa4435 100644 --- a/engine/execution/ingestion/engine.go +++ b/engine/execution/ingestion/engine.go @@ -699,7 +699,7 @@ func (e *Engine) executeBlock( Hex("execution_data_id", receipt.ExecutionResult.ExecutionDataID[:]). Bool("sealed", isExecutedBlockSealed). Bool("state_changed", finalEndState != *executableBlock.StartState). - Uint64("num_txs", transactionCount(receipt.ExecutionResult)). + Uint64("num_txs", nonSystemTransactionCount(receipt.ExecutionResult)). Bool("broadcasted", broadcasted). Int64("timeSpentInMS", time.Since(startedAt).Milliseconds()). Msg("block executed") @@ -719,7 +719,7 @@ func (e *Engine) executeBlock( } -func transactionCount(result flow.ExecutionResult) uint64 { +func nonSystemTransactionCount(result flow.ExecutionResult) uint64 { count := uint64(0) for _, chunk := range result.Chunks { count += chunk.NumberOfTransactions From 4c0dd758b47fe00eb4c5fe37b18bc03cd49aaeb8 Mon Sep 17 00:00:00 2001 From: Yurii Oleksyshyn Date: Thu, 7 Sep 2023 23:45:21 +0300 Subject: [PATCH 815/815] Linted --- network/p2p/p2pnode/libp2pStream_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/network/p2p/p2pnode/libp2pStream_test.go b/network/p2p/p2pnode/libp2pStream_test.go index 8d693a501cf..dc072343f6c 100644 --- a/network/p2p/p2pnode/libp2pStream_test.go +++ b/network/p2p/p2pnode/libp2pStream_test.go @@ -55,7 +55,7 @@ func TestStreamClosing(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes) defer p2ptest.StopNodes(t, nodes, cancel) - nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) + nodeInfo1, err := utils.PeerAddressInfo(identities[1].IdentitySkeleton) require.NoError(t, err) senderWG := sync.WaitGroup{} @@ -172,7 +172,7 @@ func testCreateStream(t *testing.T, sporkId flow.Identifier, unicasts []protocol allStreamsClosedWg := sync.WaitGroup{} for i := 0; i < streamCount; i++ { allStreamsClosedWg.Add(1) - pInfo, err := utils.PeerAddressInfo(*id2) + pInfo, err := utils.PeerAddressInfo(id2.IdentitySkeleton) require.NoError(t, err) nodes[0].Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) go func() { @@ -244,7 +244,7 @@ func TestCreateStream_FallBack(t *testing.T) { allStreamsClosedWg := sync.WaitGroup{} for i := 0; i < streamCount; i++ { allStreamsClosedWg.Add(1) - pInfo, err := utils.PeerAddressInfo(otherId) + pInfo, err := utils.PeerAddressInfo(otherId.IdentitySkeleton) require.NoError(t, err) thisNode.Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) @@ -293,7 +293,7 @@ func TestCreateStreamIsConcurrencySafe(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, nodes) defer p2ptest.StopNodes(t, nodes, cancel) - nodeInfo1, err := utils.PeerAddressInfo(*identities[1]) + nodeInfo1, err := utils.PeerAddressInfo(identities[1].IdentitySkeleton) require.NoError(t, err) wg := sync.WaitGroup{} @@ -356,7 +356,7 @@ func TestNoBackoffWhenCreatingStream(t *testing.T) { defer p2ptest.StopNode(t, node1, cancel1) id2 := identities[1] - pInfo, err := utils.PeerAddressInfo(*id2) + pInfo, err := utils.PeerAddressInfo(id2.IdentitySkeleton) require.NoError(t, err) nodes[0].Host().Peerstore().AddAddrs(pInfo.ID, pInfo.Addrs, peerstore.AddressTTL) maxTimeToWait := p2pnode.MaxConnectAttempt * unicast.MaxRetryJitter * time.Millisecond @@ -516,7 +516,7 @@ func TestCreateStreamTimeoutWithUnresponsiveNode(t *testing.T) { require.NoError(t, listener.Close()) }() - silentNodeInfo, err := utils.PeerAddressInfo(silentNodeId) + silentNodeInfo, err := utils.PeerAddressInfo(silentNodeId.IdentitySkeleton) require.NoError(t, err) timeout := 1 * time.Second @@ -555,7 +555,7 @@ func TestCreateStreamIsConcurrent(t *testing.T) { p2ptest.StartNodes(t, signalerCtx, goodNodes) defer p2ptest.StopNodes(t, goodNodes, cancel) - goodNodeInfo1, err := utils.PeerAddressInfo(*goodNodeIds[1]) + goodNodeInfo1, err := utils.PeerAddressInfo(goodNodeIds[1].IdentitySkeleton) require.NoError(t, err) // create a silent node which never replies @@ -563,7 +563,7 @@ func TestCreateStreamIsConcurrent(t *testing.T) { defer func() { require.NoError(t, listener.Close()) }() - silentNodeInfo, err := utils.PeerAddressInfo(silentNodeId) + silentNodeInfo, err := utils.PeerAddressInfo(silentNodeId.IdentitySkeleton) require.NoError(t, err) // creates a stream to unresponsive node and makes sure that the stream creation is blocked

  • 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